• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 12834126339

17 Jan 2025 05:43PM UTC coverage: 57.354% (+0.04%) from 57.311%
12834126339

push

github

web-flow
Suspend worldsave script (#745)

* SaveWorldState(): if possible the script will be suspended until the
async part is finished, added to the returning struct
`ElapsedMillisecondsTotal` for the complete time it takes.

* readded removed header

* extended test by calling save without beeing able to suspend

* fixed function name

* do not suspend script during worldsave when its critical
since testscripts are fast make sure via sleep that the gameclock
changes

* docs

47 of 56 new or added lines in 4 files covered. (83.93%)

1 existing line in 1 file now uncovered.

41124 of 71702 relevant lines covered (57.35%)

384088.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

33.15
/pol-core/pol/module/uomod.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/03/02 Shinigami: internal_MoveItem - item took from container don't had the correct realm
5
 * - 2005/04/02 Shinigami: mf_CreateItemCopyAtLocation - added realm param
6
 * - 2005/04/28 Shinigami: mf_EnumerateItemsInContainer/enumerate_contents - added flag to list
7
 * content of locked container too
8
 * - 2005/04/28 Shinigami: added mf_SecureTradeWin - to init secure trade via script over long
9
 * distances
10
 *                         added mf_MoveItemToSecureTradeWin - to move item to secure trade window
11
 * via script
12
 * - 2005/04/31 Shinigami: mf_EnumerateItemsInContainer - error message added
13
 * - 2005/06/01 Shinigami: added mf_Attach - to attach a Script to a Character
14
 *                         mf_MoveItem - stupid non-realm anti-crash bugfix (e.g. for intrinsic
15
 * weapons without realm)
16
 *                         added mf_ListStaticsAtLocation - list static Items at location
17
 *                         added mf_ListStaticsNearLocation - list static Items around location
18
 *                         added mf_GetStandingLayers - get layer a mobile can stand
19
 * - 2005/06/06 Shinigami: mf_ListStatics* will return multi Items too / flags added
20
 * - 2005/07/04 Shinigami: mf_ListStatics* constants renamed
21
 *                         added Z to mf_ListStatics*Location - better specify what u want to get
22
 *                         modified Z handling of mf_ListItems*Location* and
23
 * mf_ListMobilesNearLocation*
24
 *                         better specify what u want to get
25
 *                         added realm-based coord check to mf_ListItems*Location*,
26
 *                         mf_List*InBox and mf_ListMobilesNearLocation*
27
 *                         added mf_ListStaticsInBox - list static items in box
28
 * - 2005/07/07 Shinigami: realm-based coord check in mf_List*InBox disabled
29
 * - 2005/09/02 Shinigami: mf_Attach - smaller bug which allowed u to attach more than one Script to
30
 * a Character
31
 * - 2005/09/03 Shinigami: mf_FindPath - added Flags to support non-blocking doors
32
 * - 2005/09/23 Shinigami: added mf_SendStatus - to send full status packet to support extensions
33
 * - 2005/12/06 MuadDib:   Rewrote realm handling for Move* and internal_move_item. New realm is
34
 *                         to be passed to function, and oldrealm handling is done within the
35
 * function.
36
 *                         Container's still save and check oldrealm as before to update clients.
37
 * - 2006/01/18 Shinigami: added attached_npc_ - to get attached NPC from AI-Script-Process Obj
38
 * - 2006/05/10 Shinigami: mf_ListMobilesNearLocationEx - just a logical mismatch
39
 * - 2006/05/24 Shinigami: added mf_SendCharacterRaceChanger - change Hair, Beard and Color
40
 * - 2006/06/08 Shinigami: started to add FindPath logging
41
 * - 2006/09/17 Shinigami: mf_SendEvent() will return error "Event queue is full, discarding event"
42
 * - 2006/11/25 Shinigami: mf_GetWorldHeight() fixed bug because z was'n initialized
43
 * - 2007/05/03 Turley:    added mf_GetRegionNameAtLocation - get name of justice region
44
 * - 2007/05/04 Shinigami: added mf_GetRegionName - get name of justice region by objref
45
 * - 2007/05/07 Shinigami: fixed a crash in mf_GetRegionName using NPCs; added TopLevelOwner
46
 * - 2008/07/08 Turley:    Added mf_IsStackable - Is item stackable?
47
 * - 2008/07/21 Mehdorn:   mf_ReserveItem() will return 2 if Item is already reserved by me (instead
48
 * of 1)
49
 * - 2009/07/27 MuadDib:   Packet Struct Refactoring
50
 * - 2009/08/08 MuadDib:   mf_SetRawSkill(),  mf_GetRawSkill(),  mf_ApplyRawDamage(), mf_GameStat(),
51
 *                         mf_AwardRawPoints(), old replace_properties(), mf_GetSkill() cleaned out.
52
 * - 2009/08/25 Shinigami: STLport-5.2.1 fix: additional parentheses in mf_ListMultisInBox
53
 * - 2009/09/03 MuadDib:   Changes for account related source file relocation
54
 *                         Changes for multi related source file relocation
55
 * - 2009/09/14 MuadDib:   Slot support added to creation/move to container.
56
 * - 2009/09/15 MuadDib:   Multi registration/unregistration support added.
57
 * - 2009/10/22 Turley:    added CanWalk()
58
 * - 2009/11/19 Turley:    added flag param to UpdateMobile controls if packet 0x78 or 0x77 should
59
 * be send - Tomi
60
 * - 2009/12/02 Turley:    added config.max_tile_id - Tomi
61
 * - 2009/12/17 Turley:    CloseWindow( character, type, object ) - Tomi
62
 * - 2010/01/15 Turley:    (Tomi) season stuff
63
 *                         (Tomi) SpeakPowerWords font and color params
64
 * - 2011/11/12 Tomi:      added extobj.mount
65
 */
66

67

68
#include "pol_global_config.h"
69

70
#include "uomod.h"
71
#include <cmath>
72
#include <cstddef>
73
#include <exception>
74
#include <memory>
75
#include <optional>
76
#include <stdlib.h>
77
#include <string>
78

79
#include "../../bscript/berror.h"
80
#include "../../bscript/bobject.h"
81
#include "../../bscript/bstruct.h"
82
#include "../../bscript/dict.h"
83
#include "../../bscript/impstr.h"
84
#include "../../clib/cfgelem.h"
85
#include "../../clib/clib.h"
86
#include "../../clib/clib_endian.h"
87
#include "../../clib/esignal.h"
88
#include "../../clib/logfacility.h"
89
#include "../../clib/passert.h"
90
#include "../../clib/refptr.h"
91
#include "../../clib/stlutil.h"
92
#include "../../clib/timer.h"
93
#include "../../plib/clidata.h"
94
#include "../../plib/mapcell.h"
95
#include "../../plib/mapshape.h"
96
#include "../../plib/maptile.h"
97
#include "../../plib/objtype.h"
98
#include "../../plib/staticblock.h"
99
#include "../../plib/stlastar.h"
100
#include "../../plib/systemstate.h"
101
#include "../../plib/uconst.h"
102
#include "../../plib/udatfile.h"
103
#include "../action.h"
104
#include "../cfgrepos.h"
105
#include "../containr.h"
106
#include "../core.h"
107
#include "../eventid.h"
108
#include "../fnsearch.h"
109
#include "../gameclck.h"
110
#include "../globals/object_storage.h"
111
#include "../globals/uvars.h"
112
#include "../item/item.h"
113
#include "../item/itemdesc.h"
114
#include "../layers.h"
115
#include "../lightlvl.h"
116
#include "../listenpt.h"
117
#include "../los.h"
118
#include "../menu.h"
119
#include "../mobile/charactr.h"
120
#include "../mobile/npc.h"
121
#include "../mobile/ufacing.h"
122
#include "../multi/boat.h"
123
#include "../multi/house.h"
124
#include "../multi/multi.h"
125
#include "../multi/multidef.h"
126
#include "../network/cgdata.h"
127
#include "../network/client.h"
128
#include "../network/packethelper.h"
129
#include "../network/packets.h"
130
#include "../network/pktboth.h"
131
#include "../network/pktdef.h"
132
#include "../npctmpl.h"
133
#include "../polclass.h"
134
#include "../polclock.h"
135
#include "../polobject.h"
136
#include "../polsem.h"
137
#include "../polsig.h"
138
#include "../profile.h"
139
#include "../savedata.h"
140
#include "../scrdef.h"
141
#include "../scrsched.h"
142
#include "../scrstore.h"
143
#include "../spells.h"
144
#include "../target.h"
145
#include "../ufunc.h"
146
#include "../uimport.h"
147
#include "../umanip.h"
148
#include "../uobject.h"
149
#include "../uoexec.h"
150
#include "../uopathnode.h"
151
#include "../uoscrobj.h"
152
#include "../uworld.h"
153
#include "../wthrtype.h"
154
#include "cfgmod.h"
155
#include "realms/realm.h"
156
#include "realms/realms.h"
157
#include "regions/guardrgn.h"
158
#include "regions/miscrgn.h"
159
#include "regions/resource.h"
160

161
#include <module_defs/uo.h>
162

163
namespace Pol
164
{
165
namespace Core
166
{
167
void cancel_trade( Mobile::Character* chr1 );
168
void cancel_all_trades();
169
Bscript::BObjectImp* place_item_in_secure_trade_container( Network::Client* client,
170
                                                           Items::Item* item );
171
Bscript::BObjectImp* open_trade_window( Network::Client* client, Mobile::Character* dropon );
172
void send_tip( Network::Client* client, const std::string& tiptext );
173
std::string get_textcmd_help( Mobile::Character* chr, const std::string& cmd );
174
void send_paperdoll( Network::Client* client, Mobile::Character* chr );
175
void send_skillmsg( Network::Client* client, const Mobile::Character* chr );
176
Bscript::BObjectImp* equip_from_template( Mobile::Character* chr,
177
                                          const std::string& template_name );
178
}  // namespace Core
179
namespace Module
180
{
181
using namespace Bscript;
182
using namespace Items;
183
using namespace Mobile;
184
using namespace Core;
185

186
#define CONST_DEFAULT_ZRANGE 19
187

188
class EMenuObjImp final : public PolApplicObj<Menu>
189
{
190
public:
191
  EMenuObjImp( const Menu& m ) : PolApplicObj<Menu>( &menu_type, m ) {}
×
192
  virtual const char* typeOf() const override { return "MenuRef"; }
×
193
  virtual u8 typeOfInt() const override { return OTMenuRef; }
×
194
  virtual BObjectImp* copy() const override { return new EMenuObjImp( value() ); }
×
195
};
196

197

198
UOExecutorModule::UOExecutorModule( UOExecutor& exec )
219✔
199
    : TmplExecutorModule<UOExecutorModule, Core::PolModule>( exec ),
200
      target_cursor_chr( nullptr ),
219✔
201
      menu_selection_chr( nullptr ),
219✔
202
      popup_menu_selection_chr( nullptr ),
219✔
203
      popup_menu_selection_above( nullptr ),
219✔
204
      prompt_chr( nullptr ),
219✔
205
      gump_chr( nullptr ),
219✔
206
      textentry_chr( nullptr ),
219✔
207
      resurrect_chr( nullptr ),
219✔
208
      selcolor_chr( nullptr ),
219✔
209
      attached_chr_( nullptr ),
219✔
210
      attached_npc_( nullptr ),
219✔
211
      attached_item_( nullptr ),
219✔
212
      controller_( nullptr ),
219✔
213
      reserved_items_(),
219✔
214
      target_options( 0 ),
219✔
215
      registered_for_speech_events( false )
219✔
216
{
217
}
219✔
218

219
UOExecutorModule::~UOExecutorModule()
438✔
220
{
221
  auto& uoex = uoexec();
219✔
222
  while ( !reserved_items_.empty() )
219✔
223
  {
224
    Item* item = reserved_items_.back().get();
×
225
    item->inuse( false );
×
226
    reserved_items_.pop_back();
×
227
  }
228

229
  if ( target_cursor_chr != nullptr )
219✔
230
  {
231
    // CHECKME can we cancel the cursor request?
232
    if ( target_cursor_chr->client != nullptr && target_cursor_chr->client->gd != nullptr )
×
233
      target_cursor_chr->client->gd->target_cursor_uoemod = nullptr;
×
234
    target_cursor_chr = nullptr;
×
235
  }
236
  if ( menu_selection_chr != nullptr )
219✔
237
  {
238
    if ( menu_selection_chr->client != nullptr && menu_selection_chr->client->gd != nullptr )
×
239
      menu_selection_chr->client->gd->menu_selection_uoemod = nullptr;
×
240
    menu_selection_chr = nullptr;
×
241
  }
242
  if ( popup_menu_selection_chr != nullptr )
219✔
243
  {
244
    if ( popup_menu_selection_chr->client != nullptr &&
×
245
         popup_menu_selection_chr->client->gd != nullptr )
×
246
      popup_menu_selection_chr->client->gd->popup_menu_selection_uoemod = nullptr;
×
247
    popup_menu_selection_chr = nullptr;
×
248
    popup_menu_selection_above = nullptr;
×
249
  }
250
  if ( prompt_chr != nullptr )
219✔
251
  {
252
    if ( prompt_chr->client != nullptr && prompt_chr->client->gd != nullptr )
×
253
      prompt_chr->client->gd->prompt_uoemod = nullptr;
×
254
    prompt_chr = nullptr;
×
255
  }
256
  if ( gump_chr != nullptr )
219✔
257
  {
258
    if ( gump_chr->client != nullptr && gump_chr->client->gd != nullptr )
×
259
      gump_chr->client->gd->remove_gumpmods( this );
×
260
    gump_chr = nullptr;
×
261
  }
262
  if ( textentry_chr != nullptr )
219✔
263
  {
264
    if ( textentry_chr->client != nullptr && textentry_chr->client->gd != nullptr )
×
265
      textentry_chr->client->gd->textentry_uoemod = nullptr;
×
266
    textentry_chr = nullptr;
×
267
  }
268
  if ( resurrect_chr != nullptr )
219✔
269
  {
270
    if ( resurrect_chr->client != nullptr && resurrect_chr->client->gd != nullptr )
×
271
      resurrect_chr->client->gd->resurrect_uoemod = nullptr;
×
272
    resurrect_chr = nullptr;
×
273
  }
274
  if ( selcolor_chr != nullptr )
219✔
275
  {
276
    if ( selcolor_chr->client != nullptr && selcolor_chr->client->gd != nullptr )
×
277
      selcolor_chr->client->gd->selcolor_uoemod = nullptr;
×
278
    selcolor_chr = nullptr;
×
279
  }
280
  if ( attached_chr_ != nullptr )
219✔
281
  {
282
    passert( attached_chr_->script_ex == &uoex );
×
283
    attached_chr_->script_ex = nullptr;
×
284
    attached_chr_ = nullptr;
×
285
  }
286
  if ( attached_item_.get() )
219✔
287
  {
288
    attached_item_->process( nullptr );
20✔
289
    attached_item_.clear();
20✔
290
  }
291
  if ( registered_for_speech_events )
219✔
292
  {
293
    ListenPoint::deregister_from_speech_events( &uoex );
1✔
294
  }
295
}
438✔
296

297
BObjectImp* UOExecutorModule::mf_Attach( /* Character */ )
×
298
{
299
  Character* chr;
300
  if ( getCharacterParam( 0, chr ) )
×
301
  {
302
    if ( attached_chr_ == nullptr )
×
303
    {
304
      if ( chr->script_ex == nullptr )
×
305
      {
306
        attached_chr_ = chr;
×
307
        attached_chr_->script_ex = &uoexec();
×
308

309
        return new BLong( 1 );
×
310
      }
311
      else
312
        return new BError( "Another script still attached." );
×
313
    }
314
    else
315
      return new BError( "Another character still attached." );
×
316
  }
317
  else
318
    return new BError( "Invalid parameter" );
×
319
}
320

321
BObjectImp* UOExecutorModule::mf_Detach()
×
322
{
323
  if ( attached_chr_ != nullptr )
×
324
  {
325
    passert( attached_chr_->script_ex == &uoexec() );
×
326
    attached_chr_->script_ex = nullptr;
×
327
    attached_chr_ = nullptr;
×
328
    return new BLong( 1 );
×
329
  }
330
  else
331
  {
332
    return new BLong( 0 );
×
333
  }
334
}
335

336
static bool item_create_params_ok( u32 objtype, int amount )
6,106✔
337
{
338
  return ( objtype >= UOBJ_ITEM__LOWEST && objtype <= Plib::systemstate.config.max_objtype ) &&
6,106✔
339
         amount > 0 && amount <= 60000L;
12,212✔
340
}
341

342
BObjectImp* _create_item_in_container( UContainer* cont, const ItemDesc* descriptor,
31✔
343
                                       unsigned short amount, bool force_stacking,
344
                                       std::optional<Core::Pos2d> pos, UOExecutorModule* uoemod )
345
{
346
  if ( ( Plib::tile_flags( descriptor->graphic ) & Plib::FLAG::STACKABLE ) || force_stacking )
31✔
347
  {
348
    for ( UContainer::const_iterator itr = cont->begin(); itr != cont->end(); ++itr )
26✔
349
    {
350
      Item* item = *itr;
9✔
351
      ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function
9✔
352
                                // ends
353

354
      if ( item->objtype_ == descriptor->objtype && !item->newbie() &&
3✔
355
           item->insured() == descriptor->insured && item->cursed() == descriptor->cursed &&
3✔
356
           item->color ==
3✔
357
               descriptor
358
                   ->color &&  // dave added 5/11/3, only add to existing stack if is default color
3✔
359
           item->has_only_default_cprops(
3✔
360
               descriptor ) &&  // dave added 5/11/3, only add to existing stack if default cprops
3✔
361
           ( !item->inuse() || ( uoemod && uoemod->is_reserved_to_me( item ) ) ) &&
15✔
362
           item->can_add_to_self( amount, force_stacking ) )
3✔
363
      {
364
        // DAVE added this 11/17, call can/onInsert scripts for this container
365
        Character* chr_owner = cont->GetCharacterOwner();
3✔
366
        if ( chr_owner == nullptr )
3✔
367
          if ( uoemod != nullptr )
2✔
368
            chr_owner = uoemod->controller_.get();
2✔
369

370
        // If the can insert script fails for combining a stack, we'll let the create new item code
371
        // below handle it
372
        // what if a cannInsert script modifies (adds to) the container we're iterating over? (they
373
        // shouldn't do that)
374
        // FIXME oh my, this makes no sense.  'item' in this case is already in the container.
375
        if ( !cont->can_insert_increase_stack( chr_owner, UContainer::MT_CORE_CREATED, item, amount,
3✔
376
                                               nullptr ) )
377
          continue;
×
378
        if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
3✔
379
        {
380
          return new BError( "Item was destroyed in CanInsert Script" );
×
381
        }
382
#ifdef PERGON
383
        item->ct_merge_stacks_pergon(
384
            amount );  // Pergon: Re-Calculate Property CreateTime after Adding Items to a Stack
385
#endif
386

387
        int newamount = item->getamount();
3✔
388
        newamount += amount;
3✔
389
        item->setamount( static_cast<unsigned short>( newamount ) );
3✔
390

391
        update_item_to_inrange( item );
3✔
392
        UpdateCharacterWeight( item );
3✔
393

394
        // FIXME again, this makes no sense, item is already in the container.
395
        cont->on_insert_increase_stack( chr_owner, UContainer::MT_CORE_CREATED, item, amount );
3✔
396
        if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
3✔
397
        {
398
          return new BError( "Item was destroyed in OnInsert Script" );
×
399
        }
400

401

402
        return new EItemRefObjImp( item );
3✔
403
      }
404
    }
9✔
405
  }
406
  else if ( amount != 1 && !force_stacking )
11✔
407
  {
408
    return new BError( "That item is not stackable.  Create one at a time." );
×
409
  }
410

411
  Item* item = Item::create( *descriptor );
28✔
412
  if ( item != nullptr )
28✔
413
  {
414
    ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
28✔
415
    item->setposition( cont->pos() );
28✔
416
    item->setamount( amount );
28✔
417

418
    if ( cont->can_add( *item ) )
28✔
419
    {
420
      if ( !descriptor->create_script.empty() )
27✔
421
      {
422
        BObjectImp* res =
423
            run_script_to_completion( descriptor->create_script, new EItemRefObjImp( item ) );
×
424
        if ( !res->isTrue() )
×
425
        {
426
          item->destroy();
×
427
          return res;
×
428
        }
429
        else
430
        {
431
          BObject ob( res );
×
432
        }
×
433
        if ( !cont->can_add( *item ) )
×
434
        {  // UNTESTED
435
          item->destroy();
×
436
          return new BError( "Couldn't add item to container after create script ran" );
×
437
        }
438
      }
439

440
      // run before owner is found. No need to find owner if not even able to use slots.
441
      u8 slotIndex = item->slot_index();
27✔
442
      if ( !cont->can_add_to_slot( slotIndex ) )
27✔
443
      {
444
        item->destroy();
×
445
        return new BError( "No slots available in this container" );
×
446
      }
447
      if ( !item->slot_index( slotIndex ) )
27✔
448
      {
449
        item->destroy();
×
450
        return new BError( "Couldn't set slot index on item" );
×
451
      }
452

453
      // DAVE added this 11/17, call can/onInsert scripts for this container
454
      Character* chr_owner = cont->GetCharacterOwner();
27✔
455
      if ( chr_owner == nullptr )
27✔
456
        if ( uoemod != nullptr )
20✔
457
          chr_owner = uoemod->controller_.get();
20✔
458

459
      if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_CREATED, item ) )
27✔
460
      {
461
        item->destroy();
×
462
        // FIXME: try to propogate error from returned result of the canInsert script
463
        return new BError( "Could not insert item into container." );
×
464
      }
465
      if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
27✔
466
      {
467
        return new BError( "Item was destroyed in CanInsert Script" );
×
468
      }
469

470
      if ( !pos || !cont->is_legal_posn( pos.value() ) )
27✔
471
        pos = cont->get_random_location();
23✔
472

473
      item->setposition( Core::Pos4d( pos.value(), 0, cont->realm() ) );  // TODO POS realm
27✔
474

475
      cont->add( item );
27✔
476

477
      update_item_to_inrange( item );
27✔
478
      // DAVE added this 11/17, refresh owner's weight on item insert
479
      UpdateCharacterWeight( item );
27✔
480

481
      cont->on_insert_add_item( chr_owner, UContainer::MT_CORE_CREATED, item );
27✔
482
      if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
27✔
483
      {
484
        return new BError( "Item was destroyed in OnInsert Script" );
×
485
      }
486

487
      return new EItemRefObjImp( item );
27✔
488
    }
489
    else
490
    {
491
      item->destroy();
1✔
492
      return new BError( "That container is full" );
1✔
493
    }
494
  }
28✔
495
  else
496
  {
497
    return new BError( "Failed to create that item type" );
×
498
  }
499
}
500

501
BObjectImp* UOExecutorModule::mf_CreateItemInContainer()
27✔
502
{
503
  Item* item;
504
  const ItemDesc* descriptor;
505
  int amount;
506
  int px;
507
  int py;
508

509
  if ( getItemParam( 0, item ) && getObjtypeParam( 1, descriptor ) && getParam( 2, amount ) &&
54✔
510
       getParam( 3, px, -1, 65535 ) && getParam( 4, py, -1, 65535 ) &&
81✔
511
       item_create_params_ok( descriptor->objtype, amount ) )
27✔
512
  {
513
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
27✔
514
    {
515
      std::optional<Core::Pos2d> pos;
27✔
516
      if ( px >= 0 && py >= 0 )
27✔
517
        pos = Core::Pos2d( static_cast<u16>( px ), static_cast<u16>( py ) );
2✔
518
      return _create_item_in_container( static_cast<UContainer*>( item ), descriptor,
27✔
519
                                        static_cast<unsigned short>( amount ), false, pos, this );
27✔
520
    }
521
    else
522
    {
523
      return new BError( "That is not a container" );
×
524
    }
525
  }
526
  else
527
  {
528
    return new BError( "A parameter was invalid" );
×
529
  }
530
}
531

532
BObjectImp* UOExecutorModule::mf_CreateItemInInventory()
1✔
533
{
534
  Item* item;
535
  const ItemDesc* descriptor;
536
  int amount;
537
  int px;
538
  int py;
539

540
  if ( getItemParam( 0, item ) && getObjtypeParam( 1, descriptor ) && getParam( 2, amount ) &&
2✔
541
       getParam( 3, px, -1, 65535 ) && getParam( 4, py, -1, 65535 ) &&
3✔
542
       item_create_params_ok( descriptor->objtype, amount ) )
1✔
543
  {
544
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
1✔
545
    {
546
      std::optional<Core::Pos2d> pos;
1✔
547
      if ( px >= 0 && py >= 0 )
1✔
548
        pos = Core::Pos2d( static_cast<u16>( px ), static_cast<u16>( py ) );
1✔
549
      return _create_item_in_container( static_cast<UContainer*>( item ), descriptor,
1✔
550
                                        static_cast<unsigned short>( amount ), true, pos, this );
1✔
551
    }
552
    else
553
    {
554
      return new BError( "That is not a container" );
×
555
    }
556
  }
557
  else
558
  {
559
    return new BError( "A parameter was invalid" );
×
560
  }
561
}
562

563

564
BObjectImp* UOExecutorModule::mf_Broadcast()
×
565
{
566
  const char* text;
567
  unsigned short font;
568
  unsigned short color;
569
  unsigned short requiredCmdLevel;
570
  text = exec.paramAsString( 0 );
×
571
  if ( text && getParam( 1, font ) &&     // todo: getFontParam
×
572
       getParam( 2, color ) &&            // todo: getColorParam
×
573
       getParam( 3, requiredCmdLevel ) )  // todo: getRequiredCmdLevelParam
×
574
  {
575
    if ( !Bscript::String::hasUTF8Characters( text ) )
×
576
      Core::broadcast( text, font, color, requiredCmdLevel );
×
577
    else
578
      Core::broadcast_unicode( text, "ENU", font, color, requiredCmdLevel );
×
579
    return new BLong( 1 );
×
580
  }
581
  else
582
  {
583
    return nullptr;
×
584
  }
585
}
586

587
/* Special containers (specifically, bankboxes, but probably any other
588
   "invisible" accessible container) seem to work thus:
589
   They sit in layer 0x1D.  The player is told to "wear_item" the item,
590
   then the gump and container contents are sent.
591
   We'll put a reference to this item in the character's additional_legal_items
592
   container, which is flushed whenever he moves.
593
   It might be better to actually put it in that layer, because that way
594
   it implicitly is at the same location (location of the wornitems container)
595
   */
596
BObjectImp* UOExecutorModule::mf_SendOpenSpecialContainer()
×
597
{
598
  Character* chr;
599
  Item* item;
600
  if ( !getCharacterParam( 0, chr ) || !getItemParam( 1, item ) )
×
601
  {
602
    return new BError( "Invalid parameter type" );
×
603
  }
604

605
  if ( !chr->has_active_client() )
×
606
  {
607
    return new BError( "No client attached" );
×
608
  }
609
  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
610
  {
611
    return new BError( "That isn't a container" );
×
612
  }
613

614
  u8 save_layer = item->layer;
×
615
  item->layer = LAYER_BANKBOX;
×
616
  send_wornitem( chr->client, chr, item );
×
617
  item->layer = save_layer;
×
618
  item->setposition( chr->pos() );
×
619
  item->double_click( chr->client );  // open the container on the client's screen
×
620
  chr->add_remote_container( item );
×
621

622
  return new BLong( 1 );
×
623
}
624

625

626
BObjectImp* UOExecutorModule::mf_SecureTradeWin()
×
627
{
628
  Character* chr;
629
  Character* chr2;
630
  if ( !getCharacterParam( 0, chr ) || !getCharacterParam( 1, chr2 ) )
×
631
  {
632
    return new BError( "Invalid parameter type." );
×
633
  }
634

635
  if ( chr == chr2 )
×
636
  {
637
    return new BError( "You can't trade with yourself." );
×
638
  }
639

640
  if ( !chr->has_active_client() )
×
641
  {
642
    return new BError( "No client attached." );
×
643
  }
644
  if ( !chr2->has_active_client() )
×
645
  {
646
    return new BError( "No client attached." );
×
647
  }
648

649
  return Core::open_trade_window( chr->client, chr2 );
×
650
}
651

652
BObjectImp* UOExecutorModule::mf_CloseTradeWindow()
×
653
{
654
  Character* chr;
655
  if ( !getCharacterParam( 0, chr ) )
×
656
  {
657
    return new BError( "Invalid parameter type." );
×
658
  }
659
  if ( !chr->trading_with )
×
660
    return new BError( "Mobile is not currently trading with anyone." );
×
661

662
  Core::cancel_trade( chr );
×
663
  return new BLong( 1 );
×
664
}
665

666
BObjectImp* UOExecutorModule::mf_SendViewContainer()
7✔
667
{
668
  Character* chr;
669
  Item* item;
670
  if ( !getCharacterParam( 0, chr ) || !getItemParam( 1, item ) )
7✔
671
  {
672
    return new BError( "Invalid parameter type" );
×
673
  }
674

675
  if ( !chr->has_active_client() )
7✔
676
  {
677
    return new BError( "No client attached" );
×
678
  }
679
  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
7✔
680
  {
681
    return new BError( "That isn't a container" );
×
682
  }
683
  UContainer* cont = static_cast<UContainer*>( item );
7✔
684
  chr->client->pause();
7✔
685
  send_open_gump( chr->client, *cont );
7✔
686
  send_container_contents( chr->client, *cont );
7✔
687
  chr->client->restart();
7✔
688
  return new BLong( 1 );
7✔
689
}
690

691
BObjectImp* UOExecutorModule::mf_FindObjtypeInContainer()
×
692
{
693
  Item* item;
694
  unsigned int objtype;
695
  int flags;
696
  if ( !getItemParam( 0, item ) || !getObjtypeParam( 1, objtype ) )
×
697
  {
698
    return new BError( "Invalid parameter type" );
×
699
  }
700
  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
701
  {
702
    return new BError( "That is not a container" );
×
703
  }
704
  if ( !getParam( 2, flags ) )
×
705
    flags = 0;
×
706

707
  UContainer* cont = static_cast<UContainer*>( item );
×
708
  Item* found = cont->find_objtype( objtype, flags );
×
709
  if ( found == nullptr )
×
710
    return new BError( "No items were found" );
×
711
  else
712
    return new EItemRefObjImp( found );
×
713
}
714

715

716
BObjectImp* UOExecutorModule::mf_SendSysMessage()
×
717
{
718
  Character* chr;
719
  const String* ptext;
720
  unsigned short font;
721
  unsigned short color;
722

723
  if ( getCharacterParam( 0, chr ) && ( ( ptext = getStringParam( 1 ) ) != nullptr ) &&
×
724
       getParam( 2, font ) && getParam( 3, color ) )
×
725
  {
726
    if ( chr->has_active_client() )
×
727
    {
728
      if ( !ptext->hasUTF8Characters() )
×
729
        send_sysmessage( chr->client, ptext->data(), font, color );
×
730
      else
731
        Core::send_sysmessage_unicode( chr->client, ptext->value(), "ENU", font, color );
×
732
      return new BLong( 1 );
×
733
    }
734
    else
735
    {
736
      return new BError( "Mobile has no active client" );
×
737
    }
738
  }
739
  else
740
  {
741
    return new BError( "Invalid parameter type" );
×
742
  }
743
}
744

745
BObjectImp* UOExecutorModule::mf_PrintTextAbove()
1✔
746
{
747
  UObject* obj;
748
  const String* ptext;
749
  unsigned short font;
750
  unsigned short color;
751
  int journal_print;
752

753
  if ( getUObjectParam( 0, obj ) && getStringParam( 1, ptext ) && getParam( 2, font ) &&
2✔
754
       getParam( 3, color ) && getParam( 4, journal_print ) )
2✔
755
  {
756
    if ( !ptext->hasUTF8Characters() )
1✔
757
      return new BLong( say_above( obj, ptext->data(), font, color, journal_print ) );
1✔
758
    else
759
      return new BLong(
760
          say_above_unicode( obj, ptext->value(), "ENU", font, color, journal_print ) );
×
761
  }
762
  else
763
  {
764
    return new BError( "A parameter was invalid" );
×
765
  }
766
}
767
BObjectImp* UOExecutorModule::mf_PrintTextAbovePrivate()
×
768
{
769
  Character* chr;
770
  UObject* obj;
771
  const String* ptext;
772
  unsigned short font;
773
  unsigned short color;
774
  int journal_print;
775

776
  if ( getUObjectParam( 0, obj ) && getStringParam( 1, ptext ) && getCharacterParam( 2, chr ) &&
×
777
       getParam( 3, font ) && getParam( 4, color ) && getParam( 5, journal_print ) )
×
778
  {
779
    if ( !ptext->hasUTF8Characters() )
×
780
      return new BLong( private_say_above( chr, obj, ptext->data(), font, color, journal_print ) );
×
781
    else
782
      return new BLong( private_say_above_unicode( chr, obj, ptext->value(), "ENU", font, color ) );
×
783
  }
784
  else
785
  {
786
    return new BError( "A parameter was invalid" );
×
787
  }
788
}
789

790
// const int TGTOPT_NOCHECK_LOS = 0x0000; // currently unused
791
const int TGTOPT_CHECK_LOS = 0x0001;
792
const int TGTOPT_HARMFUL = 0x0002;
793
const int TGTOPT_HELPFUL = 0x0004;
794
const int TGTOPT_ALLOW_NONLOCAL = 0x0008;
795

796
void handle_script_cursor( Character* chr, UObject* obj )
3✔
797
{
798
  if ( chr != nullptr && chr->client->gd->target_cursor_uoemod != nullptr )
3✔
799
  {
800
    auto& uoex = chr->client->gd->target_cursor_uoemod->uoexec();
3✔
801
    if ( obj != nullptr )
3✔
802
    {
803
      if ( obj->ismobile() )
2✔
804
      {
805
        Character* targetted_chr = static_cast<Character*>( obj );
1✔
806
        if ( chr->client->gd->target_cursor_uoemod->target_options & TGTOPT_HARMFUL )
1✔
807
        {
808
          targetted_chr->inform_engaged( chr );
1✔
809
          chr->repsys_on_attack( targetted_chr );
1✔
810
        }
811
        else if ( chr->client->gd->target_cursor_uoemod->target_options & TGTOPT_HELPFUL )
×
812
        {
813
          chr->repsys_on_help( targetted_chr );
×
814
        }
815
      }
816
      uoex.ValueStack.back().set( new BObject( obj->make_ref() ) );
2✔
817
    }
818
    // even on cancel, we wake the script up.
819
    uoex.revive();
3✔
820
    chr->client->gd->target_cursor_uoemod->target_cursor_chr = nullptr;
3✔
821
    chr->client->gd->target_cursor_uoemod = nullptr;
3✔
822
  }
823
}
3✔
824

825

826
BObjectImp* UOExecutorModule::mf_Target()
3✔
827
{
828
  Character* chr;
829
  if ( !getCharacterParam( 0, chr ) )
3✔
830
  {
831
    return new BError( "Invalid parameter type" );
×
832
  }
833
  if ( !chr->has_active_client() )
3✔
834
  {
835
    return new BError( "No client connected" );
×
836
  }
837
  if ( chr->target_cursor_busy() )
3✔
838
  {
839
    return new BError( "Client busy with another target cursor" );
×
840
  }
841

842
  if ( !getParam( 1, target_options ) )
3✔
843
    target_options = TGTOPT_CHECK_LOS;
×
844

845
  PKTBI_6C::CURSOR_TYPE crstype;
846

847
  if ( target_options & TGTOPT_HARMFUL )
3✔
848
    crstype = PKTBI_6C::CURSOR_TYPE_HARMFUL;
1✔
849
  else if ( target_options & TGTOPT_HELPFUL )
2✔
850
    crstype = PKTBI_6C::CURSOR_TYPE_HELPFUL;
×
851
  else
852
    crstype = PKTBI_6C::CURSOR_TYPE_NEUTRAL;
2✔
853

854
  if ( !uoexec().suspend() )
3✔
855
  {
856
    DEBUGLOGLN(
×
857
        "Script Error in '{}' PC={}: \n"
858
        "\tCall to function UO::Target():\n"
859
        "\tThe execution of this script can't be blocked!",
860
        scriptname(), exec.PC );
×
861
    return new Bscript::BError( "Script can't be blocked" );
×
862
  }
863

864
  TargetCursor* tgt_cursor = nullptr;
3✔
865

866
  bool is_los_checked = ( target_options & TGTOPT_CHECK_LOS ) && !chr->ignores_line_of_sight();
3✔
867
  bool allow_nonlocal = ( target_options & TGTOPT_ALLOW_NONLOCAL );
3✔
868
  if ( is_los_checked )
3✔
869
  {
870
    if ( allow_nonlocal )
1✔
871
    {
872
      tgt_cursor = &gamestate.target_cursors.los_checked_allow_nonlocal_script_cursor;
1✔
873
    }
874
    else
875
    {
876
      tgt_cursor = &gamestate.target_cursors.los_checked_script_cursor;
×
877
    }
878
  }
879
  else
880
  {
881
    if ( allow_nonlocal )
2✔
882
    {
883
      tgt_cursor = &gamestate.target_cursors.nolos_checked_allow_nonlocal_script_cursor;
1✔
884
    }
885
    else
886
    {
887
      tgt_cursor = &gamestate.target_cursors.nolos_checked_script_cursor;
1✔
888
    }
889
  }
890

891
  tgt_cursor->send_object_cursor( chr->client, crstype );
3✔
892

893
  chr->client->gd->target_cursor_uoemod = this;
3✔
894
  target_cursor_chr = chr;
3✔
895

896
  return new BLong( 0 );
3✔
897
}
898

899
BObjectImp* UOExecutorModule::mf_CancelTarget()
×
900
{
901
  Character* chr;
902
  if ( getCharacterParam( 0, chr ) )
×
903
  {
904
    if ( chr->has_active_client() )
×
905
    {
906
      if ( chr->target_cursor_busy() )
×
907
      {
908
        Network::PktHelper::PacketOut<Network::PktOut_6C> msg;
×
909
        msg->Write<u8>( PKTBI_6C::UNK1_00 );
×
910
        msg->offset += 4;  // u32 target_cursor_serial
×
911
        msg->Write<u8>( 0x3u );
×
912
        // rest 0
913
        msg.Send( chr->client, sizeof msg->buffer );
×
914
        return new BLong( 0 );
×
915
      }
×
916
      else
917
      {
918
        return new BError( "Client does not have an active target cursor" );
×
919
      }
920
    }
921
    else
922
    {
923
      return new BError( "No client connected" );
×
924
    }
925
  }
926
  else
927
  {
928
    return new BError( "Invalid parameter type" );
×
929
  }
930
}
931

932
void handle_coord_cursor( Character* chr, PKTBI_6C* msg )
1✔
933
{
934
  if ( chr != nullptr && chr->client->gd->target_cursor_uoemod != nullptr )
1✔
935
  {
936
    auto& uoex = chr->client->gd->target_cursor_uoemod->uoexec();
1✔
937
    if ( msg != nullptr )
1✔
938
    {
939
      auto pos = Core::Pos3d( cfBEu16( msg->x ), cfBEu16( msg->y ), msg->z );
1✔
940
      BStruct* arr = new BStruct;
1✔
941
      arr->addMember( "x", new BLong( pos.x() ) );
1✔
942
      arr->addMember( "y", new BLong( pos.y() ) );
1✔
943
      arr->addMember( "z", new BLong( pos.z() ) );
1✔
944
      //      FIXME: Objtype CANNOT be trusted! Scripts must validate this, or, we must
945
      //      validate right here. Should we check map/static, or let that reside
946
      //      for scripts to run ListStatics? In theory, using Injection or similar,
947
      //      you could mine where no mineable tiles are by faking objtype in packet
948
      //      and still get the resources?
949
      arr->addMember( "objtype", new BLong( cfBEu16( msg->graphic ) ) );
1✔
950

951
      u32 selected_serial = cfBEu32( msg->selected_serial );
1✔
952
      if ( selected_serial )
1✔
953
      {
954
        UObject* obj = system_find_object( selected_serial );
×
955
        if ( obj )
×
956
        {
957
          arr->addMember( obj->target_tag(), obj->make_ref() );
×
958
        }
959
      }
960

961
      //      Never trust packet's objtype is the reason here. They should never
962
      //      target on other realms. DUH!
963
      arr->addMember( "realm", new String( chr->realm()->name() ) );
1✔
964

965
      Multi::UMulti* multi = chr->realm()->find_supporting_multi( pos );
1✔
966
      if ( multi != nullptr )
1✔
967
        arr->addMember( "multi", multi->make_ref() );
×
968

969
      uoex.ValueStack.back().set( new BObject( arr ) );
1✔
970
    }
971

972
    uoex.revive();
1✔
973
    chr->client->gd->target_cursor_uoemod->target_cursor_chr = nullptr;
1✔
974
    chr->client->gd->target_cursor_uoemod = nullptr;
1✔
975
  }
976
}
1✔
977

978
BObjectImp* UOExecutorModule::mf_TargetCoordinates()
1✔
979
{
980
  Character* chr;
981
  if ( !getCharacterParam( 0, chr ) )
1✔
982
  {
983
    return new BError( "Invalid parameter type" );
×
984
  }
985
  if ( !chr->has_active_client() )
1✔
986
  {
987
    return new BError( "Mobile has no active client" );
×
988
  }
989
  if ( chr->target_cursor_busy() )
1✔
990
  {
991
    return new BError( "Client has an active target cursor" );
×
992
  }
993

994
  if ( !uoexec().suspend() )
1✔
995
  {
996
    DEBUGLOGLN(
×
997
        "Script Error in '{}' PC={}: \n"
998
        "\tCall to function UO::TargetCoordinates():\n"
999
        "\tThe execution of this script can't be blocked!",
1000
        scriptname(), exec.PC );
×
1001
    return new Bscript::BError( "Script can't be blocked" );
×
1002
  }
1003

1004
  gamestate.target_cursors.script_cursor2.send_coord_cursor( chr->client );
1✔
1005
  chr->client->gd->target_cursor_uoemod = this;
1✔
1006
  target_cursor_chr = chr;
1✔
1007
  return new BLong( 0 );
1✔
1008
}
1009

1010
BObjectImp* UOExecutorModule::mf_TargetMultiPlacement()
×
1011
{
1012
  Character* chr;
1013
  unsigned int objtype, hue;
1014
  int flags;
1015
  int xoffset, yoffset;
1016
  if ( !( getCharacterParam( 0, chr ) && getObjtypeParam( 1, objtype ) && getParam( 2, flags ) &&
×
1017
          getParam( 3, xoffset ) && getParam( 4, yoffset ) && getParam( 5, hue ) ) )
×
1018
  {
1019
    return new BError( "Invalid parameter type" );
×
1020
  }
1021

1022

1023
  if ( !chr->has_active_client() )
×
1024
  {
1025
    return new BError( "No client attached" );
×
1026
  }
1027

1028
  if ( chr->target_cursor_busy() )
×
1029
  {
1030
    return new BError( "Client busy with another target cursor" );
×
1031
  }
1032

1033
  if ( find_itemdesc( objtype ).type != ItemDesc::BOATDESC &&
×
1034
       find_itemdesc( objtype ).type != ItemDesc::HOUSEDESC )
×
1035
  {
1036
    return new BError( "Object Type is out of range for Multis" );
×
1037
  }
1038

1039
  if ( !uoexec().suspend() )
×
1040
  {
1041
    DEBUGLOGLN(
×
1042
        "Script Error in '{}' PC={}: \n"
1043
        "\tCall to function UO::TargetMultiPlacement():\n"
1044
        "\tThe execution of this script can't be blocked!",
1045
        scriptname(), exec.PC );
×
1046
    return new Bscript::BError( "Script can't be blocked" );
×
1047
  }
1048

1049
  chr->client->gd->target_cursor_uoemod = this;
×
1050
  target_cursor_chr = chr;
×
1051

1052
  gamestate.target_cursors.multi_placement_cursor.send_placemulti(
×
1053
      chr->client, objtype, flags, (s16)xoffset, (s16)yoffset, hue );
1054

1055
  return new BLong( 0 );
×
1056
}
1057

1058
BObjectImp* UOExecutorModule::mf_GetObjType()
×
1059
{
1060
  Item* item;
1061
  Character* chr;
1062

1063
  if ( getItemParam( 0, item ) )
×
1064
  {
1065
    return new BLong( item->objtype_ );
×
1066
  }
1067
  else if ( getCharacterParam( 0, chr ) )
×
1068
  {
1069
    return new BLong( chr->objtype_ );
×
1070
  }
1071
  else
1072
  {
1073
    return new BLong( 0 );
×
1074
  }
1075
}
1076

1077
// FIXME character needs an Accessible that takes an Item*
1078
BObjectImp* UOExecutorModule::mf_Accessible()
×
1079
{
1080
  Character* chr;
1081
  Item* item;
1082
  int range;
1083

1084
  if ( getCharacterParam( 0, chr ) && getItemParam( 1, item ) )
×
1085
  {
1086
    // Range defaults to -1 if it's not defined in the .em file (old scripts)
1087
    // or the user provides a weird object.
1088
    if ( exec.numParams() < 3 || !getParam( 2, range ) )
×
1089
      range = -1;
×
1090

1091
    if ( chr->can_access( item, range ) )
×
1092
      return new BLong( 1 );
×
1093
    else
1094
      return new BLong( 0 );
×
1095
  }
1096
  return new BLong( 0 );
×
1097
}
1098

1099
BObjectImp* UOExecutorModule::mf_GetAmount()
×
1100
{
1101
  Item* item;
1102

1103
  if ( getItemParam( 0, item ) )
×
1104
  {
1105
    return new BLong( item->getamount() );
×
1106
  }
1107
  else
1108
  {
1109
    return new BLong( 0 );
×
1110
  }
1111
}
1112

1113

1114
// FIXME, copy-pasted from NPC, copy-pasted to CreateItemInContainer
1115
BObjectImp* UOExecutorModule::mf_CreateItemInBackpack()
3✔
1116
{
1117
  Character* chr;
1118
  const ItemDesc* descriptor;
1119
  unsigned short amount;
1120
  int px;
1121
  int py;
1122

1123
  if ( getCharacterParam( 0, chr ) && getObjtypeParam( 1, descriptor ) && getParam( 2, amount ) &&
6✔
1124
       getParam( 3, px, -1, 65535 ) && getParam( 4, py, -1, 65535 ) &&
9✔
1125
       item_create_params_ok( descriptor->objtype, amount ) )
3✔
1126
  {
1127
    UContainer* backpack = chr->backpack();
3✔
1128
    if ( backpack != nullptr )
3✔
1129
    {
1130
      std::optional<Core::Pos2d> pos;
3✔
1131
      if ( px >= 0 && py >= 0 )
3✔
1132
        pos = Core::Pos2d( static_cast<u16>( px ), static_cast<u16>( py ) );
1✔
1133
      return _create_item_in_container( backpack, descriptor, amount, false, pos, this );
3✔
1134
    }
1135
    else
1136
    {
1137
      return new BError( "Character has no backpack." );
×
1138
    }
1139
  }
1140
  else
1141
  {
1142
    return new BError( "A parameter was invalid." );
×
1143
  }
1144
}
1145

1146
BObjectImp* _complete_create_item_at_location( Item* item, const Core::Pos4d& pos )
6,075✔
1147
{
1148
  item->setposition( pos );
6,075✔
1149

1150
  // ITEMDESCTODO: use the original descriptor
1151
  const ItemDesc& id = find_itemdesc( item->objtype_ );
6,075✔
1152
  if ( !id.create_script.empty() )
6,075✔
1153
  {
1154
    BObjectImp* res = run_script_to_completion( id.create_script, new EItemRefObjImp( item ) );
×
1155
    if ( !res->isTrue() )
×
1156
    {
1157
      item->destroy();
×
1158
      return res;
×
1159
    }
1160
    else
1161
    {
1162
      BObject ob( res );
×
1163
    }
×
1164
  }
1165

1166
  update_item_to_inrange( item );
6,075✔
1167
  add_item_to_world( item );
6,075✔
1168
  register_with_supporting_multi( item );
6,075✔
1169
  return new EItemRefObjImp( item );
6,075✔
1170
}
1171

1172
BObjectImp* UOExecutorModule::mf_CreateItemAtLocation( /* x,y,z,objtype,amount,realm */ )
6,075✔
1173
{
1174
  Core::Pos4d pos;
6,075✔
1175
  const ItemDesc* itemdesc;
1176
  unsigned short amount;
1177
  if ( getPos4dParam( 0, 1, 2, 5, &pos ) && getObjtypeParam( 3, itemdesc ) &&
12,150✔
1178
       getParam( 4, amount, 1, 60000 ) && item_create_params_ok( itemdesc->objtype, amount ) )
12,150✔
1179
  {
1180
    if ( !( Plib::tile_flags( itemdesc->graphic ) & Plib::FLAG::STACKABLE ) && ( amount != 1 ) )
6,075✔
1181
    {
1182
      return new BError( "That item is not stackable.  Create one at a time." );
×
1183
    }
1184

1185
    Item* item = Item::create( *itemdesc );
6,075✔
1186
    if ( item != nullptr )
6,075✔
1187
    {
1188
      item->setamount( amount );
6,075✔
1189
      return _complete_create_item_at_location( item, pos );
6,075✔
1190
    }
1191
    else
1192
    {
1193
      return new BError( "Unable to create item of objtype " + Clib::hexint( itemdesc->objtype ) );
×
1194
    }
1195
  }
1196
  return new BError( "Invalid parameter type" );
×
1197
}
1198

1199
BObjectImp* UOExecutorModule::mf_CreateItemCopyAtLocation( /* x,y,z,item,realm */ )
×
1200
{
1201
  Core::Pos4d pos;
×
1202
  Item* origitem;
1203
  if ( getPos4dParam( 0, 1, 2, 4, &pos ) && getItemParam( 3, origitem ) )
×
1204
  {
1205
    if ( origitem->script_isa( POLCLASS_MULTI ) )
×
1206
      return new BError( "This function does not work with Multi objects." );
×
1207

1208
    Item* item = origitem->clone();
×
1209
    if ( item != nullptr )
×
1210
    {
1211
      return _complete_create_item_at_location( item, pos );
×
1212
    }
1213
    else
1214
    {
1215
      return new BError( "Unable to clone item" );
×
1216
    }
1217
  }
1218
  else
1219
  {
1220
    return new BError( "Invalid parameter type" );
×
1221
  }
1222
}
1223

1224
BObjectImp* UOExecutorModule::mf_CreateMultiAtLocation( /* x,y,z,objtype,flags,realm */ )
34✔
1225
{
1226
  Core::Pos4d pos;
34✔
1227
  const ItemDesc* descriptor;
1228
  int flags = 0;
34✔
1229
  if ( !( getPos4dParam( 0, 1, 2, 5, &pos ) && getParam( 4, flags ) &&
68✔
1230
          getObjtypeParam( 3, descriptor ) ) )
34✔
1231
  {
1232
    return new BError( "Invalid parameter type" );
×
1233
  }
1234
  if ( descriptor->type != ItemDesc::BOATDESC && descriptor->type != ItemDesc::HOUSEDESC )
34✔
1235
  {
1236
    return new BError( "That objtype is not a Multi" );
×
1237
  }
1238

1239
  return Multi::UMulti::scripted_create( *descriptor, pos, flags );
34✔
1240
}
1241

1242
void replace_properties( Clib::ConfigElem& elem, BStruct* custom )
6✔
1243
{
1244
  for ( BStruct::Contents::const_iterator citr = custom->contents().begin(),
12✔
1245
                                          end = custom->contents().end();
6✔
1246
        citr != end; ++citr )
42✔
1247
  {
1248
    const std::string& name = ( *citr ).first;
36✔
1249
    BObjectImp* ref = ( *citr ).second->impptr();
36✔
1250

1251
    if ( name == "CProps" )
36✔
1252
    {
1253
      if ( auto* cpropdict = impptrIf<BDictionary>( ref ) )
5✔
1254
      {
1255
        const BDictionary::Contents& cprop_cont = cpropdict->contents();
5✔
1256
        BDictionary::Contents::const_iterator itr;
5✔
1257
        for ( itr = cprop_cont.begin(); itr != cprop_cont.end(); ++itr )
10✔
1258
        {
1259
          elem.add_prop( "cprop", ( ( *itr ).first->getStringRep() + "\t" +
5✔
1260
                                    ( *itr ).second->impptr()->pack() ) );
10✔
1261
        }
1262
      }
1263
      else
1264
      {
1265
        throw std::runtime_error( "NPC override_properties: CProps must be a dictionary, but is: " +
×
1266
                                  std::string( ref->typeOf() ) );
×
1267
      }
1268
    }
1269
    else
1270
    {
1271
      elem.clear_prop( name.c_str() );
31✔
1272
      elem.add_prop( name, ref->getStringRep() );
31✔
1273
    }
1274
  }
1275
}
6✔
1276

1277
BObjectImp* UOExecutorModule::mf_CreateNpcFromTemplate()
54✔
1278
{
1279
  const String* tmplname;
1280
  Core::Pos4d pos;
54✔
1281
  int forceInt;
1282
  bool forceLocation;
1283
  if ( !( getStringParam( 0, tmplname ) && getPos4dParam( 1, 2, 3, 5, &pos ) ) )
54✔
1284
  {
1285
    return new BError( "Invalid parameter type" );
×
1286
  }
1287
  BObjectImp* imp = getParamImp( 4 );
54✔
1288
  BStruct* custom_struct = nullptr;
54✔
1289
  if ( imp->isa( BObjectImp::OTLong ) )
54✔
1290
  {
1291
    custom_struct = nullptr;
48✔
1292
  }
1293
  else if ( imp->isa( BObjectImp::OTStruct ) )
6✔
1294
  {
1295
    custom_struct = static_cast<BStruct*>( imp );
6✔
1296
  }
1297
  else
1298
  {
1299
    return new BError( std::string( "Parameter 4 must be a Struct or Integer(0), got " ) +
×
1300
                       BObjectImp::typestr( imp->type() ) );
×
1301
  }
1302
  if ( !getParam( 6, forceInt ) )
54✔
1303
  {
1304
    forceLocation = false;
×
1305
  }
1306
  else
1307
  {
1308
    forceLocation = forceInt ? true : false;
54✔
1309
  }
1310

1311
  Clib::ConfigElem elem;
54✔
1312
  START_PROFILECLOCK( npc_search );
54✔
1313
  bool found = FindNpcTemplate( tmplname->data(), elem );
54✔
1314
  STOP_PROFILECLOCK( npc_search );
54✔
1315
  INC_PROFILEVAR( npc_searches );
54✔
1316

1317
  if ( !found )
54✔
1318
  {
1319
    return new BError( "NPC template '" + tmplname->value() + "' not found" );
×
1320
  }
1321
  Plib::MOVEMODE movemode = Character::decode_movemode( elem.read_string( "MoveMode", "L" ) );
54✔
1322

1323
  short newz;
1324
  Multi::UMulti* dummy_multi = nullptr;
54✔
1325
  Item* dummy_walkon = nullptr;
54✔
1326
  if ( !pos.realm()->walkheight( pos.xy(), pos.z(), &newz, &dummy_multi, &dummy_walkon, true,
54✔
1327
                                 movemode ) )
1328
  {
1329
    if ( !forceLocation )
2✔
1330
      return new BError( "Not a valid location for an NPC!" );
×
1331
  }
1332
  if ( !forceLocation )
54✔
1333
    pos.z( Clib::clamp_convert<s8>( newz ) );
51✔
1334

1335

1336
  NpcRef npc;
54✔
1337
  // readProperties can throw, if stuff is missing.
1338
  try
1339
  {
1340
    npc.set( new NPC( elem.remove_ushort( "OBJTYPE" ), elem ) );
54✔
1341
    npc->set_dirty();
54✔
1342
    elem.clear_prop( "Serial" );
54✔
1343
    elem.clear_prop( "X" );
54✔
1344
    elem.clear_prop( "Y" );
54✔
1345
    elem.clear_prop( "Z" );
54✔
1346

1347
    elem.add_prop( "Serial", GetNextSerialNumber() );
54✔
1348
    // FIXME sanity check
1349
    elem.add_prop( "X", pos.x() );
54✔
1350
    elem.add_prop( "Y", pos.y() );
54✔
1351
    elem.add_prop( "Z", (s16)pos.z() );
54✔
1352
    elem.add_prop( "Realm", pos.realm()->name() );
54✔
1353
    if ( custom_struct != nullptr )
54✔
1354
      replace_properties( elem, custom_struct );
6✔
1355
    npc->readPropertiesForNewNPC( elem );
54✔
1356
    ////HASH
1357
    objStorageManager.objecthash.Insert( npc.get() );
54✔
1358
    ////
1359

1360

1361
    // characters.push_back( npc.get() );
1362
    SetCharacterWorldPosition( npc.get(), Realms::WorldChangeReason::NpcCreate );
54✔
1363
    WorldIterator<OnlinePlayerFilter>::InMaxVisualRange(
54✔
1364
        npc.get(),
54✔
1365
        [&]( Character* zonechr )
54✔
1366
        {
1367
          if ( zonechr->in_visual_range( npc.get() ) )
6✔
1368
            send_char_data( zonechr->client, npc.get() );
6✔
1369
        } );
6✔
1370
    pos.realm()->notify_entered( *npc );
54✔
1371
    if ( dummy_multi )
54✔
1372
    {
1373
      dummy_multi->register_object( npc.get() );
9✔
1374
      if ( npc->registered_multi == 0 )
9✔
1375
      {
1376
        npc->registered_multi = dummy_multi->serial;
9✔
1377
        dummy_multi->walk_on( npc.get() );
9✔
1378
      }
1379
    }
1380
    else
1381
    {
1382
      if ( npc->registered_multi > 0 )
45✔
1383
      {
1384
        Multi::UMulti* multi = system_find_multi( npc->registered_multi );
×
1385
        if ( multi != nullptr )
×
1386
        {
1387
          multi->unregister_object( npc.get() );
×
1388
        }
1389
        npc->registered_multi = 0;
×
1390
      }
1391
    }
1392
    return new ECharacterRefObjImp( npc.get() );
54✔
1393
  }
1394
  catch ( std::exception& ex )
×
1395
  {
1396
    if ( npc.get() != nullptr )
×
1397
      npc->destroy();
×
1398
    return new BError( "Exception detected trying to create npc from template '" +
×
1399
                       tmplname->value() + "': " + ex.what() );
×
1400
  }
×
1401
}
54✔
1402

1403

1404
BObjectImp* UOExecutorModule::mf_SubtractAmount()
×
1405
{
1406
  Item* item;
1407
  unsigned short amount;
1408

1409
  if ( getItemParam( 0, item ) && getParam( 1, amount, 1, item->itemdesc().stack_limit ) )
×
1410
  {
1411
    if ( item->has_gotten_by() )
×
1412
      item->gotten_by()->clear_gotten_item();
×
1413
    else if ( item->inuse() && !is_reserved_to_me( item ) )
×
1414
      return new BError( "That item is being used." );
×
1415
    subtract_amount_from_item( item, amount );
×
1416
    return new BLong( 1 );
×
1417
  }
1418
  else
1419
  {
1420
    return new BError( "Invalid parameter type" );
×
1421
  }
1422
}
1423

1424
BObjectImp* UOExecutorModule::mf_AddAmount()
×
1425
{
1426
  Item* item;
1427
  unsigned short amount;
1428

1429
  if ( getItemParam( 0, item ) && getParam( 1, amount, 1, MAX_STACK_ITEMS ) )
×
1430
  {
1431
    if ( item->inuse() && !is_reserved_to_me( item ) )
×
1432
    {
1433
      return new BError( "That item is being used." );
×
1434
    }
1435
    if ( ~Plib::tile_flags( item->graphic ) & Plib::FLAG::STACKABLE )
×
1436
    {
1437
      return new BError( "That item type is not stackable." );
×
1438
    }
1439
    if ( !item->can_add_to_self( amount, false ) )
×
1440
    {
1441
      return new BError( "Can't add that much to that stack" );
×
1442
    }
1443

1444
    unsigned short newamount = item->getamount();
×
1445
    newamount += amount;
×
1446
    item->setamount( newamount );
×
1447
    update_item_to_inrange( item );
×
1448

1449
    // DAVE added this 12/05: if in a Character's pack, update weight.
1450
    UpdateCharacterWeight( item );
×
1451

1452
    return new EItemRefObjImp( item );
×
1453
  }
1454
  else
1455
  {
1456
    return new BError( "Invalid parameter type" );
×
1457
  }
1458
}
1459

1460
BObjectImp* UOExecutorModule::mf_PerformAction()
×
1461
{
1462
  Character* chr;
1463
  unsigned short actionval;
1464
  unsigned short framecount, repeatcount;
1465
  unsigned short backward, repeatflag, delay;
1466
  if ( getCharacterParam( 0, chr ) && getParam( 1, actionval ) && getParam( 2, framecount ) &&
×
1467
       getParam( 3, repeatcount ) && getParam( 4, backward ) && getParam( 5, repeatflag ) &&
×
1468
       getParam( 6, delay ) )
×
1469
  {
1470
    if ( !UACTION_IS_VALID( actionval ) )
×
1471
    {
1472
      return new BError( "Invalid parameter 'action'" );
×
1473
    }
1474

1475
    UACTION action = static_cast<UACTION>( actionval );
×
1476
    send_action_to_inrange(
×
1477
        chr, action, framecount, repeatcount, static_cast<DIRECTION_FLAG_OLD>( backward ),
1478
        static_cast<REPEAT_FLAG_OLD>( repeatflag ), static_cast<unsigned char>( delay ) );
1479
    return new BLong( 1 );
×
1480
  }
1481
  else
1482
  {
1483
    return new BError( "Invalid parameter" );
×
1484
  }
1485
}
1486

1487
BObjectImp* UOExecutorModule::mf_PlaySoundEffect()
×
1488
{
1489
  UObject* chr;
1490
  int effect;
1491
  if ( getUObjectParam( 0, chr ) && getParam( 1, effect ) )
×
1492
  {
1493
    play_sound_effect( chr, static_cast<u16>( effect ) );
×
1494
    return new BLong( 1 );
×
1495
  }
1496
  else
1497
  {
1498
    return new BError( "Invalid parameter" );
×
1499
  }
1500
}
1501

1502
BObjectImp* UOExecutorModule::mf_PlaySoundEffectPrivate()
×
1503
{
1504
  UObject* center;
1505
  int effect;
1506
  Character* forchr;
1507
  if ( getUObjectParam( 0, center ) && getParam( 1, effect ) && getCharacterParam( 2, forchr ) )
×
1508
  {
1509
    play_sound_effect_private( center, static_cast<u16>( effect ), forchr );
×
1510
    return new BLong( 1 );
×
1511
  }
1512
  else
1513
  {
1514
    return new BError( "Invalid parameter" );
×
1515
  }
1516
}
1517

1518
BObjectImp* UOExecutorModule::mf_PlaySoundEffectXYZ()
×
1519
{
1520
  Core::Pos4d center;
×
1521
  int effect;
1522
  if ( getPos4dParam( 0, 1, 2, 4, &center ) && getParam( 3, effect ) )
×
1523
  {
1524
    play_sound_effect_xyz( center, static_cast<u16>( effect ) );
×
1525
    return new BLong( 1 );
×
1526
  }
1527
  else
1528
  {
1529
    return new BError( "Invalid parameter" );
×
1530
  }
1531
}
1532

1533
BObjectImp* UOExecutorModule::mf_PlayMusic( /* char, music_id */ )
×
1534
{
1535
  Character* chr;
1536
  int musicid;
1537
  if ( getCharacterParam( 0, chr ) && getParam( 1, musicid ) )
×
1538
  {
1539
    send_midi( chr->client, static_cast<u16>( musicid ) );
×
1540
    return new BLong( 1 );
×
1541
  }
1542
  else
1543
  {
1544
    return new BError( "Invalid parameter" );
×
1545
  }
1546
}
1547

1548
void menu_selection_made( Network::Client* client, MenuItem* mi, PKTIN_7D* msg )
×
1549
{
1550
  if ( client != nullptr )
×
1551
  {
1552
    Character* chr = client->chr;
×
1553
    if ( chr != nullptr && chr->client->gd->menu_selection_uoemod != nullptr )
×
1554
    {
1555
      auto& uoex = chr->client->gd->menu_selection_uoemod->uoexec();
×
1556
      if ( mi != nullptr && msg != nullptr )
×
1557
      {
1558
        BStruct* selection = new BStruct;
×
1559
        // FIXME should make sure objtype and choice are within valid range.
1560
        selection->addMember( "objtype", new BLong( mi->objtype_ ) );
×
1561
        selection->addMember( "graphic", new BLong( mi->graphic_ ) );
×
1562
        selection->addMember( "index",
×
1563
                              new BLong( cfBEu16( msg->choice ) ) );  // this has been validated
×
1564
        selection->addMember( "color", new BLong( mi->color_ ) );
×
1565
        uoex.ValueStack.back().set( new BObject( selection ) );
×
1566
      }
1567
      // 0 is already on the value stack, for the case of cancellation.
1568
      uoex.revive();
×
1569
      chr->client->gd->menu_selection_uoemod->menu_selection_chr = nullptr;
×
1570
      chr->client->gd->menu_selection_uoemod = nullptr;
×
1571
    }
1572
  }
1573
}
×
1574

1575
bool UOExecutorModule::getDynamicMenuParam( unsigned param, Menu*& menu )
×
1576
{
1577
  BApplicObjBase* aob = getApplicObjParam( param, &menu_type );
×
1578
  if ( aob != nullptr )
×
1579
  {
1580
    EMenuObjImp* menu_imp = static_cast<EMenuObjImp*>( aob );
×
1581
    menu = &menu_imp->value();
×
1582
    return true;
×
1583
  }
1584
  else
1585
  {
1586
    return false;
×
1587
  }
1588
}
1589

1590
bool UOExecutorModule::getStaticOrDynamicMenuParam( unsigned param, Menu*& menu )
×
1591
{
1592
  BObjectImp* imp = getParamImp( param );
×
1593
  if ( imp->isa( BObjectImp::OTString ) )
×
1594
  {
1595
    String* pmenuname = static_cast<String*>( imp );
×
1596
    menu = Menu::find_menu( pmenuname->data() );
×
1597
    return ( menu != nullptr );
×
1598
  }
1599
  else if ( imp->isa( BObjectImp::OTApplicObj ) )
×
1600
  {
1601
    BApplicObjBase* aob = static_cast<BApplicObjBase*>( imp );
×
1602
    if ( aob->object_type() == &menu_type )
×
1603
    {
1604
      EMenuObjImp* menu_imp = static_cast<EMenuObjImp*>( aob );
×
1605
      menu = &menu_imp->value();
×
1606
      return true;
×
1607
    }
1608
  }
1609
  DEBUGLOGLN( "SelectMenuItem: expected a menu name (static menu) or a CreateMenu() dynamic menu" );
×
1610
  return false;
×
1611
}
1612

1613
BObjectImp* UOExecutorModule::mf_SelectMenuItem2()
×
1614
{
1615
  Character* chr;
1616
  Menu* menu;
1617

1618
  if ( !getCharacterParam( 0, chr ) || !getStaticOrDynamicMenuParam( 1, menu ) ||
×
1619
       ( chr->client->gd->menu_selection_uoemod != nullptr ) )
×
1620
  {
1621
    return new BError( "Invalid parameter" );
×
1622
  }
1623

1624
  if ( menu == nullptr || !chr->has_active_client() || menu->menuitems_.empty() )
×
1625
  {
1626
    return new BError( "Client is busy, or menu is empty" );
×
1627
  }
1628

1629
  if ( !send_menu( chr->client, menu ) )
×
1630
  {
1631
    return new BError( "Menu too large" );
×
1632
  }
1633

1634
  if ( !uoexec().suspend() )
×
1635
  {
1636
    DEBUGLOGLN(
×
1637
        "Script Error in '{}' PC={}: \n"
1638
        "\tCall to function UO::SelectMenuItem():\n"
1639
        "\tThe execution of this script can't be blocked!",
1640
        scriptname(), exec.PC );
×
1641
    return new Bscript::BError( "Script can't be blocked" );
×
1642
  }
1643

1644
  chr->client->gd->menu = menu->getWeakPtr();
×
1645
  chr->client->gd->on_menu_selection = menu_selection_made;
×
1646
  chr->client->gd->menu_selection_uoemod = this;
×
1647
  menu_selection_chr = chr;
×
1648

1649
  return new BLong( 0 );
×
1650
}
1651

1652
void append_objtypes( ObjArray* objarr, Menu* menu )
×
1653
{
1654
  for ( unsigned i = 0; i < menu->menuitems_.size(); ++i )
×
1655
  {
1656
    MenuItem* mi = &menu->menuitems_[i];
×
1657

1658
    if ( mi->submenu_id )
×
1659
    {
1660
      // Code Analyze: Commented out and replaced with tmp_menu due to hiding
1661
      // menu passed to function.
1662
      //      Menu* menu = find_menu( mi->submenu_id );
1663
      Menu* tmp_menu = Menu::find_menu( mi->submenu_id );
×
1664
      if ( tmp_menu != nullptr )
×
1665
        append_objtypes( objarr, tmp_menu );
×
1666
    }
1667
    else
1668
    {
1669
      objarr->addElement( new BLong( mi->objtype_ ) );
×
1670
    }
1671
  }
1672
}
×
1673

1674
BObjectImp* UOExecutorModule::mf_GetMenuObjTypes()
×
1675
{
1676
  Menu* menu;
1677
  if ( getStaticOrDynamicMenuParam( 0, menu ) )
×
1678
  {
1679
    std::unique_ptr<ObjArray> objarr( new ObjArray );
×
1680

1681
    append_objtypes( objarr.get(), menu );
×
1682

1683
    return objarr.release();
×
1684
  }
×
1685
  return new BLong( 0 );
×
1686
}
1687

1688
BObjectImp* UOExecutorModule::mf_ApplyConstraint()
×
1689
{
1690
  ObjArray* arr;
1691
  StoredConfigFile* cfile;
1692
  const String* propname_str;
1693
  int amthave;
1694

1695
  if ( getObjArrayParam( 0, arr ) && getStoredConfigFileParam( *this, 1, cfile ) &&
×
1696
       getStringParam( 2, propname_str ) && getParam( 3, amthave ) )
×
1697
  {
1698
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
1699

1700
    for ( unsigned i = 0; i < arr->ref_arr.size(); i++ )
×
1701
    {
1702
      BObjectRef& ref = arr->ref_arr[i];
×
1703

1704
      BObject* bo = ref.get();
×
1705
      if ( bo == nullptr )
×
1706
        continue;
×
1707
      if ( !bo->isa( BObjectImp::OTLong ) )
×
1708
        continue;
×
1709
      BLong* blong = bo->impptr<BLong>();
×
1710
      unsigned int objtype = static_cast<u32>( blong->value() );
×
1711

1712
      ref_ptr<StoredConfigElem> celem = cfile->findelem( objtype );
×
1713
      if ( celem.get() == nullptr )
×
1714
        continue;
×
1715

1716
      BObjectImp* propval = celem->getimp( propname_str->value() );
×
1717
      if ( propval == nullptr )
×
1718
        continue;
×
1719
      if ( !propval->isa( BObjectImp::OTLong ) )
×
1720
        continue;
×
1721
      BLong* amtreq = static_cast<BLong*>( propval );
×
1722
      if ( amtreq->value() > amthave )
×
1723
        continue;
×
1724
      newarr->addElement( new BLong( objtype ) );
×
1725
    }
×
1726

1727
    return newarr.release();
×
1728
  }
×
1729

1730
  return new UninitObject;
×
1731
}
1732

1733
BObjectImp* UOExecutorModule::mf_CreateMenu()
×
1734
{
1735
  const String* title;
1736
  if ( getStringParam( 0, title ) )
×
1737
  {
1738
    Menu temp;
×
1739
    temp.menu_id = 0;
×
1740
    Clib::stracpy( temp.title, title->data(), sizeof temp.title );
×
1741
    return new EMenuObjImp( temp );
×
1742
  }
×
1743
  return new BLong( 0 );
×
1744
}
1745

1746
BObjectImp* UOExecutorModule::mf_AddMenuItem()
×
1747
{
1748
  Menu* menu;
1749
  unsigned int objtype;
1750
  const String* text;
1751
  unsigned short color;
1752

1753
  if ( getDynamicMenuParam( 0, menu ) && getObjtypeParam( 1, objtype ) &&
×
1754
       getStringParam( 2, text ) && getParam( 3, color ) )
×
1755
  {
1756
    menu->menuitems_.push_back( MenuItem() );
×
1757
    MenuItem* mi = &menu->menuitems_.back();
×
1758
    mi->objtype_ = objtype;
×
1759
    mi->graphic_ = getgraphic( objtype );
×
1760
    Clib::stracpy( mi->title, text->data(), sizeof mi->title );
×
1761
    mi->color_ = color & settingsManager.ssopt.item_color_mask;
×
1762
    return new BLong( 1 );
×
1763
  }
1764
  else
1765
  {
1766
    return new BLong( 0 );
×
1767
  }
1768
}
1769

1770
BObjectImp* UOExecutorModule::mf_GetObjProperty()
27✔
1771
{
1772
  UObject* uobj;
1773
  const String* propname_str;
1774
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
27✔
1775
  {
1776
    std::string val;
27✔
1777
    if ( uobj->getprop( propname_str->value(), val ) )
27✔
1778
    {
1779
      return BObjectImp::unpack( val.c_str() );
26✔
1780
    }
1781
    else
1782
    {
1783
      return new BError( "Property not found" );
1✔
1784
    }
1785
  }
27✔
1786
  else
1787
  {
1788
    return new BError( "Invalid parameter type" );
×
1789
  }
1790
}
1791

1792
BObjectImp* UOExecutorModule::mf_SetObjProperty()
5✔
1793
{
1794
  UObject* uobj;
1795
  const String* propname_str;
1796
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
5✔
1797
  {
1798
    BObjectImp* propval = getParamImp( 2 );
5✔
1799
    uobj->setprop( propname_str->value(), propval->pack() );
5✔
1800
    return new BLong( 1 );
5✔
1801
  }
1802
  else
1803
  {
1804
    return new BError( "Invalid parameter type" );
×
1805
  }
1806
}
1807

1808
BObjectImp* UOExecutorModule::mf_EraseObjProperty()
×
1809
{
1810
  UObject* uobj;
1811
  const String* propname_str;
1812
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
×
1813
  {
1814
    uobj->eraseprop( propname_str->value() );
×
1815
    return new BLong( 1 );
×
1816
  }
1817
  else
1818
  {
1819
    return new BError( "Invalid parameter type" );
×
1820
  }
1821
}
1822
BObjectImp* UOExecutorModule::mf_GetObjPropertyNames()
×
1823
{
1824
  UObject* uobj;
1825
  if ( getUObjectParam( 0, uobj ) )
×
1826
  {
1827
    std::vector<std::string> propnames;
×
1828
    uobj->getpropnames( propnames );
×
1829
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
1830
    for ( unsigned i = 0; i < propnames.size(); ++i )
×
1831
    {
1832
      arr->addElement( new String( propnames[i] ) );
×
1833
    }
1834
    return arr.release();
×
1835
  }
×
1836
  else
1837
  {
1838
    return new BError( "Invalid parameter type" );
×
1839
  }
1840
}
1841

1842

1843
BObjectImp* UOExecutorModule::mf_GetGlobalProperty()
46✔
1844
{
1845
  const String* propname_str;
1846
  if ( getStringParam( 0, propname_str ) )
46✔
1847
  {
1848
    std::string val;
46✔
1849
    if ( gamestate.global_properties->getprop( propname_str->value(), val ) )
46✔
1850
    {
1851
      return BObjectImp::unpack( val.c_str() );
40✔
1852
    }
1853
    else
1854
    {
1855
      return new BError( "Property not found" );
6✔
1856
    }
1857
  }
46✔
1858
  else
1859
  {
1860
    return new BError( "Invalid parameter type" );
×
1861
  }
1862
}
1863

1864
BObjectImp* UOExecutorModule::mf_SetGlobalProperty()
24✔
1865
{
1866
  const String* propname_str;
1867
  if ( exec.getStringParam( 0, propname_str ) )
24✔
1868
  {
1869
    BObjectImp* propval = exec.getParamImp( 1 );
24✔
1870
    gamestate.global_properties->setprop( propname_str->value(), propval->pack() );
24✔
1871
    return new BLong( 1 );
24✔
1872
  }
1873
  else
1874
  {
1875
    return new BError( "Invalid parameter type" );
×
1876
  }
1877
}
1878

1879
BObjectImp* UOExecutorModule::mf_EraseGlobalProperty()
×
1880
{
1881
  const String* propname_str;
1882
  if ( getStringParam( 0, propname_str ) )
×
1883
  {
1884
    gamestate.global_properties->eraseprop( propname_str->value() );
×
1885
    return new BLong( 1 );
×
1886
  }
1887
  else
1888
  {
1889
    return new BError( "Invalid parameter type" );
×
1890
  }
1891
}
1892

1893
BObjectImp* UOExecutorModule::mf_GetGlobalPropertyNames()
×
1894
{
1895
  std::vector<std::string> propnames;
×
1896
  gamestate.global_properties->getpropnames( propnames );
×
1897
  std::unique_ptr<ObjArray> arr( new ObjArray );
×
1898
  for ( unsigned i = 0; i < propnames.size(); ++i )
×
1899
  {
1900
    arr->addElement( new String( propnames[i] ) );
×
1901
  }
1902
  return arr.release();
×
1903
}
×
1904

1905
BObjectImp* UOExecutorModule::mf_PlayMovingEffect()
×
1906
{
1907
  UObject* src;
1908
  UObject* dst;
1909
  unsigned short effect;
1910
  int speed;
1911
  int loop;
1912
  int explode;
1913
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) && getParam( 2, effect ) &&
×
1914
       getParam( 3, speed, UCHAR_MAX ) && getParam( 4, loop, UCHAR_MAX ) &&
×
1915
       getParam( 5, explode, UCHAR_MAX ) )
×
1916
  {
1917
    if ( src->realm() != dst->realm() )
×
1918
      return new BError( "Realms must match" );
×
1919
    play_moving_effect( src, dst, effect, static_cast<unsigned char>( speed ),
×
1920
                        static_cast<unsigned char>( loop ), static_cast<unsigned char>( explode ) );
1921
    return new BLong( 1 );
×
1922
  }
1923
  else
1924
  {
1925
    return new BLong( 0 );
×
1926
  }
1927
}
1928

1929
BObjectImp* UOExecutorModule::mf_PlayMovingEffectXYZ()
×
1930
{
1931
  unsigned short effect;
1932
  int speed;
1933
  int loop;
1934
  int explode;
1935
  Realms::Realm* realm;
1936
  Pos3d src, dst;
×
1937
  if ( getRealmParam( 10, &realm ) && getPos3dParam( 0, 1, 2, &src, realm ) &&
×
1938
       getPos3dParam( 3, 4, 5, &dst, realm ) && getParam( 6, effect ) &&
×
1939
       getParam( 7, speed, UCHAR_MAX ) && getParam( 8, loop, UCHAR_MAX ) &&
×
1940
       getParam( 9, explode, UCHAR_MAX ) )
×
1941
  {
1942
    play_moving_effect2( src, dst, effect, static_cast<signed char>( speed ),
×
1943
                         static_cast<signed char>( loop ), static_cast<signed char>( explode ),
×
1944
                         realm );
1945
    return new BLong( 1 );
×
1946
  }
1947
  else
1948
  {
1949
    return nullptr;
×
1950
  }
1951
}
1952

1953
// FIXME add/test:stationary
1954

1955
BObjectImp* UOExecutorModule::mf_PlayObjectCenteredEffect()
×
1956
{
1957
  UObject* src;
1958
  unsigned short effect;
1959
  int speed;
1960
  int loop;
1961
  if ( getUObjectParam( 0, src ) && getParam( 1, effect ) && getParam( 2, speed, UCHAR_MAX ) &&
×
1962
       getParam( 3, loop, UCHAR_MAX ) )
×
1963
  {
1964
    play_object_centered_effect( src, effect, static_cast<unsigned char>( speed ),
×
1965
                                 static_cast<unsigned char>( loop ) );
1966
    return new BLong( 1 );
×
1967
  }
1968
  else
1969
  {
1970
    return nullptr;
×
1971
  }
1972
}
1973

1974
BObjectImp* UOExecutorModule::mf_PlayStationaryEffect()
×
1975
{
1976
  Pos4d pos;
×
1977
  unsigned short effect;
1978
  int speed, loop, explode;
1979
  if ( getPos4dParam( 0, 1, 2, 7, &pos ) && getParam( 3, effect ) &&
×
1980
       getParam( 4, speed, UCHAR_MAX ) && getParam( 5, loop, UCHAR_MAX ) &&
×
1981
       getParam( 6, explode, UCHAR_MAX ) )
×
1982
  {
1983
    play_stationary_effect( pos, effect, static_cast<unsigned char>( speed ),
×
1984
                            static_cast<unsigned char>( loop ),
1985
                            static_cast<unsigned char>( explode ) );
1986
    return new BLong( 1 );
×
1987
  }
1988
  else
1989
  {
1990
    return new BError( "Invalid parameter" );
×
1991
  }
1992
}
1993

1994

1995
BObjectImp* UOExecutorModule::mf_PlayMovingEffectEx()
×
1996
{
1997
  UObject* src;
1998
  UObject* dst;
1999
  unsigned short effect;
2000
  int speed;
2001
  int duration;
2002
  int hue;
2003
  int render;
2004
  int direction;
2005
  int explode;
2006
  unsigned short effect3d;
2007
  unsigned short effect3dexplode;
2008
  unsigned short effect3dsound;
2009

2010
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) && getParam( 2, effect ) &&
×
2011
       getParam( 3, speed, UCHAR_MAX ) && getParam( 4, duration, UCHAR_MAX ) &&
×
2012
       getParam( 5, hue, INT_MAX ) && getParam( 6, render, INT_MAX ) &&
×
2013
       getParam( 7, direction, UCHAR_MAX ) && getParam( 8, explode, UCHAR_MAX ) &&
×
2014
       getParam( 9, effect3d ) && getParam( 10, effect3dexplode ) && getParam( 11, effect3dsound ) )
×
2015
  {
2016
    if ( src->realm() != dst->realm() )
×
2017
      return new BError( "Realms must match" );
×
2018
    play_moving_effect_ex(
×
2019
        src, dst, effect, static_cast<unsigned char>( speed ),
2020
        static_cast<unsigned char>( duration ), static_cast<unsigned int>( hue ),
2021
        static_cast<unsigned int>( render ), static_cast<unsigned char>( direction ),
2022
        static_cast<unsigned char>( explode ), effect3d, effect3dexplode, effect3dsound );
2023
    return new BLong( 1 );
×
2024
  }
2025
  else
2026
  {
2027
    return new BLong( 0 );
×
2028
  }
2029
}
2030

2031
BObjectImp* UOExecutorModule::mf_PlayMovingEffectXYZEx()
×
2032
{
2033
  Pos3d src, dst;
×
2034
  Realms::Realm* realm;
2035

2036
  unsigned short effect;
2037
  int speed;
2038
  int duration;
2039
  int hue;
2040
  int render;
2041
  int direction;
2042
  int explode;
2043
  unsigned short effect3d;
2044
  unsigned short effect3dexplode;
2045
  unsigned short effect3dsound;
2046

2047
  if ( getRealmParam( 6, &realm ) && getPos3dParam( 0, 1, 2, &src, realm ) &&
×
2048
       getPos3dParam( 3, 4, 5, &dst, realm ) && getParam( 7, effect ) &&
×
2049
       getParam( 8, speed, UCHAR_MAX ) && getParam( 9, duration, UCHAR_MAX ) &&
×
2050
       getParam( 10, hue, INT_MAX ) && getParam( 11, render, INT_MAX ) &&
×
2051
       getParam( 12, direction, UCHAR_MAX ) && getParam( 13, explode, UCHAR_MAX ) &&
×
2052
       getParam( 14, effect3d ) && getParam( 15, effect3dexplode ) &&
×
2053
       getParam( 16, effect3dsound ) )
×
2054
  {
2055
    play_moving_effect2_ex(
×
2056
        src, dst, realm, effect, static_cast<unsigned char>( speed ),
2057
        static_cast<unsigned char>( duration ), static_cast<unsigned int>( hue ),
2058
        static_cast<unsigned int>( render ), static_cast<unsigned char>( direction ),
2059
        static_cast<unsigned char>( explode ), effect3d, effect3dexplode, effect3dsound );
2060
    return new BLong( 1 );
×
2061
  }
2062
  else
2063
  {
2064
    return nullptr;
×
2065
  }
2066
}
2067

2068
BObjectImp* UOExecutorModule::mf_PlayObjectCenteredEffectEx()
×
2069
{
2070
  UObject* src;
2071
  unsigned short effect;
2072
  int speed;
2073
  int duration;
2074
  int hue;
2075
  int render;
2076
  int layer;
2077
  unsigned short effect3d;
2078

2079
  if ( getUObjectParam( 0, src ) && getParam( 1, effect ) && getParam( 2, speed, UCHAR_MAX ) &&
×
2080
       getParam( 3, duration, UCHAR_MAX ) && getParam( 4, hue, INT_MAX ) &&
×
2081
       getParam( 5, render, INT_MAX ) && getParam( 6, layer, UCHAR_MAX ) &&
×
2082
       getParam( 7, effect3d ) )
×
2083
  {
2084
    play_object_centered_effect_ex(
×
2085
        src, effect, static_cast<unsigned char>( speed ), static_cast<unsigned char>( duration ),
2086
        static_cast<unsigned int>( hue ), static_cast<unsigned int>( render ),
2087
        static_cast<unsigned char>( layer ), effect3d );
2088
    return new BLong( 1 );
×
2089
  }
2090
  else
2091
  {
2092
    return nullptr;
×
2093
  }
2094
}
2095

2096
BObjectImp* UOExecutorModule::mf_PlayStationaryEffectEx()
×
2097
{
2098
  Pos4d pos;
×
2099
  unsigned short effect;
2100
  int speed;
2101
  int duration;
2102
  int hue;
2103
  int render;
2104
  unsigned short effect3d;
2105

2106
  if ( getPos4dParam( 0, 1, 2, 3, &pos ) && getParam( 4, effect ) &&
×
2107
       getParam( 5, speed, UCHAR_MAX ) && getParam( 6, duration, UCHAR_MAX ) &&
×
2108
       getParam( 7, hue, INT_MAX ) && getParam( 8, render, INT_MAX ) && getParam( 9, effect3d ) )
×
2109
  {
2110
    play_stationary_effect_ex(
×
2111
        pos, effect, static_cast<unsigned char>( speed ), static_cast<unsigned char>( duration ),
2112
        static_cast<unsigned int>( hue ), static_cast<unsigned int>( render ), effect3d );
2113
    return new BLong( 1 );
×
2114
  }
2115
  else
2116
  {
2117
    return new BError( "Invalid parameter" );
×
2118
  }
2119
}
2120

2121
BObjectImp* UOExecutorModule::mf_PlayLightningBoltEffect()
×
2122
{
2123
  UObject* center;
2124
  if ( getUObjectParam( 0, center ) )
×
2125
  {
2126
    play_lightning_bolt_effect( center );
×
2127
    return new BLong( 1 );
×
2128
  }
2129
  else
2130
  {
2131
    return new BLong( 0 );
×
2132
  }
2133
}
2134

2135
BObjectImp* UOExecutorModule::mf_ListItemsNearLocation( /* x, y, z, range, realm */ )
×
2136
{
2137
  Core::Pos2d pos;
×
2138
  int z;
2139
  u16 range;
2140
  Realms::Realm* realm;
2141

2142
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2143
       getParam( 3, range ) )
×
2144
  {
2145
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2146
    WorldIterator<ItemFilter>::InRange(
×
2147
        pos, realm, range,
2148
        [&]( Item* item )
×
2149
        {
2150
          if ( item->in_range( pos, range ) )
×
2151
          {
2152
            if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2153
              newarr->addElement( item->make_ref() );
×
2154
          }
2155
        } );
×
2156

2157
    return newarr.release();
×
2158
  }
×
2159

2160
  return nullptr;
×
2161
}
2162

2163
Range3d UOExecutorModule::internal_InBoxAreaChecks( const Pos2d& p1, int z1, const Pos2d& p2,
10✔
2164
                                                    int z2, Realms::Realm* realm )
2165
{
2166
  // no error checks for out of realm range on purpose
2167
  if ( ( z1 > z2 ) && z1 != LIST_IGNORE_Z && z2 != LIST_IGNORE_Z )
10✔
2168
    std::swap( z1, z2 );
×
2169
  if ( z1 == LIST_IGNORE_Z )
10✔
2170
    z1 = ZCOORD_MIN;
×
2171
  if ( z2 == LIST_IGNORE_Z )
10✔
2172
    z2 = ZCOORD_MAX;
×
2173
  return Range3d( Pos3d( p1, Clib::clamp_convert<s8>( z1 ) ),
10✔
2174
                  Pos3d( p2, Clib::clamp_convert<s8>( z2 ) ), realm );
20✔
2175
}
2176

2177
BObjectImp* UOExecutorModule::mf_ListObjectsInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
×
2178
{
2179
  int z1, z2;
2180
  Pos2d p1, p2;
×
2181
  Realms::Realm* realm;
2182

2183
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
×
2184
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
×
2185
  {
2186
    return new BError( "Invalid parameter" );
×
2187
  }
2188
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
×
2189
  z1 = box.nw_b().z();
×
2190
  z2 = box.se_t().z();
×
2191

2192
  std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2193
  WorldIterator<MobileFilter>::InBox( box.range(), realm,
×
2194
                                      [&]( Mobile::Character* chr )
×
2195
                                      {
2196
                                        if ( chr->z() >= z1 && chr->z() <= z2 )
×
2197
                                        {
2198
                                          newarr->addElement( chr->make_ref() );
×
2199
                                        }
2200
                                      } );
×
2201
  WorldIterator<ItemFilter>::InBox( box.range(), realm,
×
2202
                                    [&]( Items::Item* item )
×
2203
                                    {
2204
                                      if ( item->z() >= z1 && item->z() <= z2 )
×
2205
                                      {
2206
                                        newarr->addElement( item->make_ref() );
×
2207
                                      }
2208
                                    } );
×
2209

2210
  return newarr.release();
×
2211
}
×
2212

2213
BObjectImp* UOExecutorModule::mf_ListItemsInBoxOfObjType(
2✔
2214
    /* objtype, x1, y1, z1, x2, y2, z2, realm */ )
2215
{
2216
  unsigned int objtype;
2217
  int z1, z2;
2218
  Pos2d p1, p2;
2✔
2219
  Realms::Realm* realm;
2220

2221
  if ( !( getParam( 0, objtype ) && getPos2dParam( 1, 2, &p1 ) && getParam( 3, z1 ) &&
4✔
2222
          getPos2dParam( 4, 5, &p2 ) && getParam( 6, z2 ) && getRealmParam( 7, &realm ) ) )
2✔
2223
  {
2224
    return new BError( "Invalid parameter" );
×
2225
  }
2226
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
2✔
2227
  z1 = box.nw_b().z();
2✔
2228
  z2 = box.se_t().z();
2✔
2229

2230
  std::unique_ptr<ObjArray> newarr( new ObjArray );
2✔
2231
  WorldIterator<ItemFilter>::InBox(
4✔
2232
      box.range(), realm,
2✔
2233
      [&]( Items::Item* item )
2✔
2234
      {
2235
        if ( item->z() >= z1 && item->z() <= z2 && item->objtype_ == objtype )
39✔
2236
        {
2237
          newarr->addElement( item->make_ref() );
39✔
2238
        }
2239
      } );
39✔
2240

2241
  return newarr.release();
2✔
2242
}
2✔
2243

2244
BObjectImp* UOExecutorModule::mf_ListObjectsInBoxOfClass(
1✔
2245
    /* POL_Class, x1, y1, z1, x2, y2, z2, realm */ )
2246
{
2247
  unsigned int POL_Class;
2248
  int z1, z2;
2249
  Pos2d p1, p2;
1✔
2250
  Realms::Realm* realm;
2251

2252
  if ( !( getParam( 0, POL_Class ) && getPos2dParam( 1, 2, &p1 ) && getParam( 3, z1 ) &&
2✔
2253
          getPos2dParam( 4, 5, &p2 ) && getParam( 6, z2 ) && getRealmParam( 7, &realm ) ) )
1✔
2254
  {
2255
    return new BError( "Invalid parameter" );
×
2256
  }
2257
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
1✔
2258
  z1 = box.nw_b().z();
1✔
2259
  z2 = box.se_t().z();
1✔
2260

2261
  std::unique_ptr<ObjArray> newarr( new ObjArray );
1✔
2262
  WorldIterator<MobileFilter>::InBox(
2✔
2263
      box.range(), realm,
1✔
2264
      [&]( Mobile::Character* chr )
1✔
2265
      {
2266
        if ( chr->z() >= z1 && chr->z() <= z2 && chr->script_isa( POL_Class ) )
×
2267
        {
2268
          newarr->addElement( chr->make_ref() );
×
2269
        }
2270
      } );
×
2271
  WorldIterator<ItemFilter>::InBox(
2✔
2272
      box.range(), realm,
1✔
2273
      [&]( Items::Item* item )
1✔
2274
      {
2275
        if ( item->z() >= z1 && item->z() <= z2 && item->script_isa( POL_Class ) )
1✔
2276
        {
2277
          newarr->addElement( item->make_ref() );
1✔
2278
        }
2279
      } );
1✔
2280

2281
  return newarr.release();
1✔
2282
}
1✔
2283

2284
BObjectImp* UOExecutorModule::mf_ListMobilesInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
2✔
2285
{
2286
  int z1, z2;
2287
  Pos2d p1, p2;
2✔
2288
  Realms::Realm* realm;
2289

2290
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
4✔
2291
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
2✔
2292
  {
2293
    return new BError( "Invalid parameter" );
×
2294
  }
2295
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
2✔
2296
  z1 = box.nw_b().z();
2✔
2297
  z2 = box.se_t().z();
2✔
2298
  std::unique_ptr<ObjArray> newarr( new ObjArray );
2✔
2299
  WorldIterator<MobileFilter>::InBox( box.range(), realm,
2✔
2300
                                      [&]( Mobile::Character* chr )
2✔
2301
                                      {
2302
                                        if ( chr->z() >= z1 && chr->z() <= z2 )
2✔
2303
                                        {
2304
                                          newarr->addElement( chr->make_ref() );
2✔
2305
                                        }
2306
                                      } );
2✔
2307

2308
  return newarr.release();
2✔
2309
}
2✔
2310

2311
BObjectImp* UOExecutorModule::mf_ListMultisInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
4✔
2312
{
2313
  int z1, z2;
2314
  Pos2d p1, p2;
4✔
2315
  Realms::Realm* realm;
2316

2317
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
8✔
2318
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
4✔
2319
  {
2320
    return new BError( "Invalid parameter" );
×
2321
  }
2322

2323
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
4✔
2324
  z1 = box.nw_b().z();
4✔
2325
  z2 = box.se_t().z();
4✔
2326

2327
  std::unique_ptr<ObjArray> newarr( new ObjArray );
4✔
2328

2329
  // extend the coords to find the center item
2330
  // but only as parameter for the filter function
2331
  Vec2d urange{ Clib::clamp_convert<s16>( gamestate.max_update_range ),
4✔
2332
                Clib::clamp_convert<s16>( gamestate.max_update_range ) };
4✔
2333
  Range2d boxrange( box.nw() - urange, box.se() + urange, realm );
4✔
2334

2335
  // search for multis.  this is tricky, since the center might lie outside the box
2336
  WorldIterator<MultiFilter>::InBox(
4✔
2337
      boxrange, realm,
2338
      [&]( Multi::UMulti* multi )
4✔
2339
      {
2340
        if ( !box.intersect( multi->current_box() ) )
5✔
2341
          return;
1✔
2342
        const Multi::MultiDef& md = multi->multidef();
4✔
2343
        // some part of it is contained in the box.  Look at the
2344
        // individual statics, to see if any of them lie within.
2345
        for ( const auto& citr : md.components )
34✔
2346
        {
2347
          const Multi::MULTI_ELEM* elem = citr.second;
34✔
2348
          Pos3d absp = multi->pos3d() + elem->relpos;
34✔
2349
          if ( box.contains( absp.xy() ) )
34✔
2350
          {
2351
            // do Z checking
2352
            int height = Plib::tileheight( getgraphic( elem->objtype ) );
4✔
2353
            int top = absp.z() + height;
4✔
2354

2355
            if ( ( z1 <= absp.z() && absp.z() <= z2 ) ||  // bottom point lies between
8✔
2356
                 ( z1 <= top && top <= z2 ) ||            // top lies between
8✔
2357
                 ( top >= z2 && absp.z() <= z1 ) )        // spans
×
2358
            {
2359
              newarr->addElement( multi->make_ref() );
4✔
2360
              break;  // out of for
4✔
2361
            }
2362
          }
2363
        }
2364
      } );
2365

2366
  return newarr.release();
4✔
2367
}
4✔
2368

2369
BObjectImp* UOExecutorModule::mf_ListStaticsInBox( /* x1, y1, z1, x2, y2, z2, flags, realm */ )
1✔
2370
{
2371
  int z1, z2;
2372
  Pos2d p1, p2;
1✔
2373
  int flags;
2374
  Realms::Realm* realm;
2375

2376
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
2✔
2377
          getParam( 5, z2 ) && getParam( 6, flags ) && getRealmParam( 7, &realm ) ) )
1✔
2378
  {
2379
    return new BError( "Invalid parameter" );
×
2380
  }
2381

2382
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
1✔
2383
  z1 = box.nw_b().z();
1✔
2384
  z2 = box.se_t().z();
1✔
2385

2386
  std::unique_ptr<ObjArray> newarr( new ObjArray );
1✔
2387

2388
  for ( const auto& pos : box.range() )
5✔
2389
  {
2390
    if ( !( flags & ITEMS_IGNORE_STATICS ) )
2✔
2391
    {
2392
      Plib::StaticEntryList slist;
2✔
2393
      realm->getstatics( slist, pos );
2✔
2394

2395
      for ( unsigned i = 0; i < slist.size(); ++i )
4✔
2396
      {
2397
        if ( ( z1 <= slist[i].z ) && ( slist[i].z <= z2 ) )
2✔
2398
        {
2399
          std::unique_ptr<BStruct> arr( new BStruct );
2✔
2400
          arr->addMember( "x", new BLong( pos.x() ) );
2✔
2401
          arr->addMember( "y", new BLong( pos.y() ) );
2✔
2402
          arr->addMember( "z", new BLong( slist[i].z ) );
2✔
2403
          arr->addMember( "objtype", new BLong( slist[i].objtype ) );
2✔
2404
          arr->addMember( "hue", new BLong( slist[i].hue ) );
2✔
2405
          newarr->addElement( arr.release() );
2✔
2406
        }
2✔
2407
      }
2408
    }
2✔
2409

2410
    if ( !( flags & ITEMS_IGNORE_MULTIS ) )
2✔
2411
    {
2412
      Plib::StaticList mlist;
2✔
2413
      realm->readmultis( mlist, pos );
2✔
2414

2415
      for ( unsigned i = 0; i < mlist.size(); ++i )
2✔
2416
      {
2417
        if ( ( z1 <= mlist[i].z ) && ( mlist[i].z <= z2 ) )
×
2418
        {
2419
          std::unique_ptr<BStruct> arr( new BStruct );
×
2420
          arr->addMember( "x", new BLong( pos.x() ) );
×
2421
          arr->addMember( "y", new BLong( pos.y() ) );
×
2422
          arr->addMember( "z", new BLong( mlist[i].z ) );
×
2423
          arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
×
2424
          newarr->addElement( arr.release() );
×
2425
        }
×
2426
      }
2427
    }
2✔
2428
  }
2429
  return newarr.release();
1✔
2430
}
1✔
2431

2432
BObjectImp* UOExecutorModule::mf_ListItemsNearLocationOfType( /* x, y, z, range, objtype, realm */ )
7✔
2433
{
2434
  Core::Pos2d pos;
7✔
2435
  int z;
2436
  u16 range;
2437
  unsigned int objtype;
2438
  Realms::Realm* realm;
2439

2440
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
14✔
2441
       getParam( 3, range ) && getObjtypeParam( 4, objtype ) )
14✔
2442
  {
2443
    std::unique_ptr<ObjArray> newarr( new ObjArray );
7✔
2444
    WorldIterator<ItemFilter>::InRange(
7✔
2445
        pos, realm, range,
2446
        [&]( Items::Item* item )
7✔
2447
        {
2448
          if ( item->objtype_ == objtype && item->in_range( pos, range ) )
13✔
2449
          {
2450
            if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
12✔
2451
              newarr->addElement( item->make_ref() );
12✔
2452
          }
2453
        } );
13✔
2454

2455
    return newarr.release();
7✔
2456
  }
7✔
2457

2458
  return nullptr;
×
2459
}
2460

2461

2462
BObjectImp* UOExecutorModule::mf_ListItemsAtLocation( /* x, y, z, realm */ )
×
2463
{
2464
  Pos2d pos;
×
2465
  int z;
2466
  Realms::Realm* realm;
2467

2468
  if ( getRealmParam( 3, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) )
×
2469
  {
2470
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2471
    WorldIterator<ItemFilter>::InRange( pos, realm, 0,
×
2472
                                        [&]( Items::Item* item )
×
2473
                                        {
2474
                                          if ( item->pos2d() == pos )
×
2475
                                          {
2476
                                            if ( ( z == LIST_IGNORE_Z ) || ( item->z() == z ) )
×
2477
                                              newarr->addElement( item->make_ref() );
×
2478
                                          }
2479
                                        } );
×
2480

2481
    return newarr.release();
×
2482
  }
×
2483

2484
  return nullptr;
×
2485
}
2486

2487
BObjectImp* UOExecutorModule::mf_ListGhostsNearLocation()
×
2488
{
2489
  Pos2d pos;
×
2490
  int z;
2491
  u16 range;
2492
  Realms::Realm* realm;
2493

2494
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2495
       getParam( 3, range ) )
×
2496
  {
2497
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2498
    WorldIterator<PlayerFilter>::InRange(
×
2499
        pos, realm, range,
2500
        [&]( Mobile::Character* chr )
×
2501
        {
2502
          if ( chr->dead() && ( abs( chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2503
          {
2504
            newarr->addElement( chr->make_ref() );
×
2505
          }
2506
        } );
×
2507

2508
    return newarr.release();
×
2509
  }
×
2510
  else
2511
  {
2512
    return new BError( "Invalid parameter" );
×
2513
  }
2514
}
2515

2516
const unsigned LMBLEX_FLAG_NORMAL = 0x01;
2517
const unsigned LMBLEX_FLAG_HIDDEN = 0x02;
2518
const unsigned LMBLEX_FLAG_DEAD = 0x04;
2519
const unsigned LMBLEX_FLAG_CONCEALED = 0x8;
2520
const unsigned LMBLEX_FLAG_PLAYERS_ONLY = 0x10;
2521
const unsigned LMBLEX_FLAG_NPC_ONLY = 0x20;
2522

2523
BObjectImp* UOExecutorModule::mf_ListMobilesNearLocationEx( /* x, y, z, range, flags, realm */ )
×
2524
{
2525
  Pos2d pos;
×
2526
  Realms::Realm* realm;
2527
  int z, flags;
2528
  u16 range;
2529

2530
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2531
       getParam( 3, range ) && getParam( 4, flags ) )
×
2532
  {
2533
    bool inc_normal = ( flags & LMBLEX_FLAG_NORMAL ) ? true : false;
×
2534
    bool inc_hidden = ( flags & LMBLEX_FLAG_HIDDEN ) ? true : false;
×
2535
    bool inc_dead = ( flags & LMBLEX_FLAG_DEAD ) ? true : false;
×
2536
    bool inc_concealed = ( flags & LMBLEX_FLAG_CONCEALED ) ? true : false;
×
2537
    bool inc_players_only = ( flags & LMBLEX_FLAG_PLAYERS_ONLY ) ? true : false;
×
2538
    bool inc_npc_only = ( flags & LMBLEX_FLAG_NPC_ONLY ) ? true : false;
×
2539

2540
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2541

2542
    auto fill_mobs = [&]( Mobile::Character* _chr )
×
2543
    {
2544
      if ( ( inc_hidden && _chr->hidden() ) || ( inc_dead && _chr->dead() ) ||
×
2545
           ( inc_concealed && _chr->concealed() ) ||
×
2546
           ( inc_normal && !( _chr->hidden() || _chr->dead() || _chr->concealed() ) ) )
×
2547
      {
2548
        if ( ( z == LIST_IGNORE_Z ) || ( abs( _chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2549
        {
2550
          if ( !inc_players_only && !inc_npc_only )
×
2551
            newarr->addElement( _chr->make_ref() );
×
2552
          else if ( inc_players_only && _chr->client )
×
2553
            newarr->addElement( _chr->make_ref() );
×
2554
          else if ( inc_npc_only && _chr->isa( UOBJ_CLASS::CLASS_NPC ) )
×
2555
            newarr->addElement( _chr->make_ref() );
×
2556
        }
2557
      }
2558
    };
×
2559
    if ( inc_players_only )
×
2560
      WorldIterator<PlayerFilter>::InRange( pos, realm, range, fill_mobs );
×
2561
    else if ( inc_npc_only )
×
2562
      WorldIterator<NPCFilter>::InRange( pos, realm, range, fill_mobs );
×
2563
    else
2564
      WorldIterator<MobileFilter>::InRange( pos, realm, range, fill_mobs );
×
2565

2566
    return newarr.release();
×
2567
  }
×
2568
  else
2569
  {
2570
    return new BError( "Invalid parameter" );
×
2571
  }
2572
}
2573

2574
BObjectImp* UOExecutorModule::mf_ListMobilesNearLocation( /* x, y, z, range, realm */ )
×
2575
{
2576
  Pos2d pos;
×
2577
  int z;
2578
  u16 range;
2579
  Realms::Realm* realm;
2580

2581
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2582
       getParam( 3, range ) )
×
2583
  {
2584
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2585
    WorldIterator<MobileFilter>::InRange(
×
2586
        pos, realm, range,
2587
        [&]( Mobile::Character* chr )
×
2588
        {
2589
          if ( ( !chr->concealed() ) && ( !chr->hidden() ) && ( !chr->dead() ) )
×
2590
            if ( ( z == LIST_IGNORE_Z ) || ( abs( chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2591
              newarr->addElement( chr->make_ref() );
×
2592
        } );
×
2593
    return newarr.release();
×
2594
  }
×
2595
  else
2596
  {
2597
    return new BError( "Invalid parameter" );
×
2598
  }
2599
}
2600

2601
BObjectImp* UOExecutorModule::mf_ListMobilesInLineOfSight()
×
2602
{
2603
  UObject* obj;
2604
  int range;
2605
  if ( getUObjectParam( 0, obj ) && getParam( 1, range ) )
×
2606
  {
2607
    obj = obj->toplevel_owner();
×
2608
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2609
    WorldIterator<MobileFilter>::InRange(
×
2610
        obj, range,
2611
        [&]( Mobile::Character* chr )
×
2612
        {
2613
          if ( chr->dead() || chr->hidden() || chr->concealed() )
×
2614
            return;
×
2615
          if ( chr == obj )
×
2616
            return;
×
2617
          if ( ( abs( chr->z() - obj->z() ) < CONST_DEFAULT_ZRANGE ) )
×
2618
          {
2619
            if ( obj->realm()->has_los( *obj, *chr ) )
×
2620
            {
2621
              newarr->addElement( chr->make_ref() );
×
2622
            }
2623
          }
2624
        } );
2625

2626
    return newarr.release();
×
2627
  }
×
2628
  else
2629
  {
2630
    return new BError( "Invalid parameter" );
×
2631
  }
2632
}
2633

2634
BObjectImp* UOExecutorModule::mf_ListOfflineMobilesInRealm( /*realm*/ )
×
2635
{
2636
  Realms::Realm* realm = nullptr;
×
2637

2638
  if ( getRealmParam( 0, &realm ) )
×
2639
  {
2640
    std::unique_ptr<ObjArray> newarr( new ObjArray() );
×
2641

2642
    for ( const auto& objitr : Pol::Core::objStorageManager.objecthash )
×
2643
    {
2644
      UObject* obj = objitr.second.get();
×
2645
      if ( !obj->ismobile() || obj->isa( UOBJ_CLASS::CLASS_NPC ) )
×
2646
        continue;
×
2647

2648
      Character* chr = static_cast<Character*>( obj );
×
2649
      if ( chr->logged_in() || chr->realm() != realm || chr->orphan() )
×
2650
        continue;
×
2651

2652
      newarr->addElement( new EOfflineCharacterRefObjImp( chr ) );
×
2653
    }
2654
    return newarr.release();
×
2655
  }
×
2656
  else
2657
  {
2658
    return new BError( "Invalid parameter" );
×
2659
  }
2660
}
2661

2662
// keep this in sync with UO.EM
2663
const int LH_FLAG_LOS = 1;             // only include those in LOS
2664
const int LH_FLAG_INCLUDE_HIDDEN = 2;  // include hidden characters
2665
BObjectImp* UOExecutorModule::mf_ListHostiles()
×
2666
{
2667
  Character* chr;
2668
  u16 range;
2669
  int flags;
2670
  if ( getCharacterParam( 0, chr ) && getParam( 1, range ) && getParam( 2, flags ) )
×
2671
  {
2672
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
2673

2674
    for ( auto& hostile : chr->hostiles() )
×
2675
    {
2676
      if ( hostile->concealed() )
×
2677
        continue;
×
2678
      if ( ( flags & LH_FLAG_LOS ) && !chr->realm()->has_los( *chr, *hostile ) )
×
2679
        continue;
×
2680
      if ( ( ~flags & LH_FLAG_INCLUDE_HIDDEN ) && hostile->hidden() )
×
2681
        continue;
×
2682
      if ( !chr->in_range( hostile, range ) )
×
2683
        continue;
×
2684
      arr->addElement( hostile->make_ref() );
×
2685
    }
2686

2687
    return arr.release();
×
2688
  }
×
2689
  else
2690
  {
2691
    return new BError( "Invalid parameter" );
×
2692
  }
2693
}
2694

2695
BObjectImp* UOExecutorModule::mf_CheckLineOfSight()
×
2696
{
2697
  UObject* src;
2698
  UObject* dst;
2699
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) )
×
2700
  {
2701
    return new BLong( src->realm()->has_los( *src, *dst->toplevel_owner() ) );
×
2702
  }
2703
  else
2704
  {
2705
    return new BLong( 0 );
×
2706
  }
2707
}
2708

2709
BObjectImp* UOExecutorModule::mf_CheckLosAt()
×
2710
{
2711
  UObject* src;
2712
  Core::Pos3d pos;
×
2713
  if ( getUObjectParam( 0, src ) && getPos3dParam( 1, 2, 3, &pos, src->realm() ) )
×
2714
  {
2715
    LosObj tgt( Core::Pos4d( pos, src->realm() ) );
×
2716
    return new BLong( src->realm()->has_los( *src, tgt ) );
×
2717
  }
2718
  return nullptr;
×
2719
}
2720

2721
BObjectImp* UOExecutorModule::mf_CheckLosBetween()
×
2722
{
2723
  Core::Pos3d p1, p2;
×
2724
  Realms::Realm* realm;
2725
  if ( getRealmParam( 6, &realm ) && getPos3dParam( 0, 1, 2, &p1, realm ) &&
×
2726
       getPos3dParam( 3, 4, 5, &p2, realm ) )
×
2727
  {
2728
    LosObj att( Core::Pos4d( p1, realm ) );
×
2729
    LosObj tgt( Core::Pos4d( p2, realm ) );
×
2730
    return new BLong( realm->has_los( att, tgt ) );
×
2731
  }
2732
  return nullptr;
×
2733
}
2734

2735
BObjectImp* UOExecutorModule::mf_DestroyItem()
6,112✔
2736
{
2737
  Item* item;
2738
  if ( getItemParam( 0, item ) )
6,112✔
2739
  {
2740
    if ( item->has_gotten_by() )
6,111✔
2741
      item->gotten_by()->clear_gotten_item();
×
2742
    else if ( item->inuse() && !is_reserved_to_me( item ) )
6,111✔
2743
      return new BError( "That item is being used." );
×
2744
    else if ( item->script_isa( POLCLASS_MULTI ) )
6,111✔
2745
      return new BError( "That item is a multi. Use uo::DestroyMulti instead." );
×
2746

2747
    const ItemDesc& id = find_itemdesc( item->objtype_ );
6,111✔
2748
    if ( !id.destroy_script.empty() )
6,111✔
2749
    {
2750
      BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( item ) );
×
2751
      if ( res->isTrue() )
×
2752
      {  // destruction is okay
2753
        BObject ob( res );
×
2754
      }
×
2755
      else
2756
      {
2757
        // destruction isn't okay!
2758
        return res;
×
2759
      }
2760
    }
2761
    UpdateCharacterOnDestroyItem( item );
6,111✔
2762
    UpdateCharacterWeight( item );
6,111✔
2763
    destroy_item( item );
6,111✔
2764
    return new BLong( 1 );
6,111✔
2765
  }
2766
  else
2767
  {
2768
    return new BError( "Invalid parameter type" );
1✔
2769
  }
2770
}
2771

2772
BObjectImp* UOExecutorModule::mf_SetName()
6✔
2773
{
2774
  UObject* obj;
2775
  const String* name_str;
2776

2777
  if ( getUObjectParam( 0, obj ) && getStringParam( 1, name_str ) )
6✔
2778
  {
2779
    obj->setname( name_str->value() );
6✔
2780
    return new BLong( 1 );
6✔
2781
  }
2782
  else
2783
  {
2784
    return new BLong( 0 );
×
2785
  }
2786
}
2787

2788
BObjectImp* UOExecutorModule::mf_GetPosition()
×
2789
{
2790
  UObject* obj;
2791
  if ( getUObjectParam( 0, obj ) )
×
2792
  {
2793
    std::unique_ptr<BStruct> arr( new BStruct );
×
2794
    arr->addMember( "x", new BLong( obj->x() ) );
×
2795
    arr->addMember( "y", new BLong( obj->y() ) );
×
2796
    arr->addMember( "z", new BLong( obj->z() ) );
×
2797
    return arr.release();
×
2798
  }
×
2799
  else
2800
  {
2801
    return new BLong( 0 );
×
2802
  }
2803
}
2804

2805
BObjectImp* UOExecutorModule::mf_EnumerateItemsInContainer()
6✔
2806
{
2807
  Item* item;
2808
  if ( getItemParam( 0, item ) )
6✔
2809
  {
2810
    int flags = 0;
6✔
2811
    if ( exec.fparams.size() >= 2 )
6✔
2812
    {
2813
      if ( !getParam( 1, flags ) )
6✔
2814
        return new BError( "Invalid parameter type" );
×
2815
    }
2816

2817
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
6✔
2818
    {
2819
      std::unique_ptr<ObjArray> newarr( new ObjArray );
6✔
2820

2821
      static_cast<UContainer*>( item )->enumerate_contents( newarr.get(), flags );
6✔
2822

2823
      return newarr.release();
6✔
2824
    }
6✔
2825

2826
    return nullptr;
×
2827
  }
2828
  else
2829
    return new BError( "Invalid parameter type" );
×
2830
}
2831

2832
BObjectImp* UOExecutorModule::mf_EnumerateOnlineCharacters()
×
2833
{
2834
  std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2835

2836
  for ( Clients::const_iterator itr = networkManager.clients.begin(),
×
2837
                                end = networkManager.clients.end();
×
2838
        itr != end; ++itr )
×
2839
  {
2840
    if ( ( *itr )->chr != nullptr )
×
2841
    {
2842
      newarr->addElement( new ECharacterRefObjImp( ( *itr )->chr ) );
×
2843
    }
2844
  }
2845

2846
  return newarr.release();
×
2847
}
×
2848

2849
BObjectImp* UOExecutorModule::mf_RegisterForSpeechEvents()
1✔
2850
{
2851
  UObject* center;
2852
  int range;
2853
  if ( getUObjectParam( 0, center ) && getParam( 1, range ) )
1✔
2854
  {
2855
    int flags = 0;
1✔
2856
    if ( exec.hasParams( 3 ) )
1✔
2857
    {
2858
      if ( !getParam( 2, flags ) )
1✔
2859
        return new BError( "Invalid parameter type" );
×
2860
    }
2861
    if ( !registered_for_speech_events )
1✔
2862
    {
2863
      ListenPoint::register_for_speech_events( center, &uoexec(), range, flags );
1✔
2864
      registered_for_speech_events = true;
1✔
2865
      return new BLong( 1 );
1✔
2866
    }
2867
    else
2868
    {
2869
      return new BError( "Already registered for speech events" );
×
2870
    }
2871
  }
2872
  else
2873
  {
2874
    return new BError( "Invalid parameter type" );
×
2875
  }
2876
}
2877

2878
BObjectImp* UOExecutorModule::mf_EnableEvents()
5✔
2879
{
2880
  int eventmask;
2881
  if ( getParam( 0, eventmask ) )
5✔
2882
  {
2883
    auto& uoex = uoexec();
5✔
2884
    if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA | EVID_SPOKE ) )
5✔
2885
    {
2886
      unsigned short range;
2887
      if ( getParam( 1, range, 0, 32 ) )
5✔
2888
      {
2889
        if ( eventmask & ( EVID_SPOKE ) )
5✔
2890
          uoex.speech_size = range;
2✔
2891
        if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA ) )
5✔
2892
          uoex.area_size = range;
3✔
2893
      }
2894
      else
2895
      {
2896
        return nullptr;
×
2897
      }
2898

2899

2900
      unsigned short evopts = 0;
5✔
2901
      if ( exec.hasParams( 2 ) && getParam( 2, evopts ) )
5✔
2902
      {
2903
        if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA ) )
5✔
2904
          uoex.area_mask = static_cast<unsigned char>( evopts & 255 );
3✔
2905
      }
2906
    }
2907
    uoex.eventmask |= eventmask;
5✔
2908
    return new BLong( uoex.eventmask );
5✔
2909
  }
2910
  else
2911
  {
2912
    return new BError( "Invalid parameter" );
×
2913
  }
2914
}
2915

2916
BObjectImp* UOExecutorModule::mf_DisableEvents()
×
2917
{
2918
  int eventmask;
2919
  if ( getParam( 0, eventmask ) )
×
2920
  {
2921
    auto& uoex = uoexec();
×
2922
    uoex.eventmask &= ~eventmask;
×
2923

2924
    return new BLong( uoex.eventmask );
×
2925
  }
2926
  else
2927
  {
2928
    return new BError( "Invalid parameter" );
×
2929
  }
2930
}
2931

2932
BObjectImp* UOExecutorModule::mf_Resurrect()
×
2933
{
2934
  Character* chr;
2935
  if ( getCharacterParam( 0, chr ) )
×
2936
  {
2937
    if ( !chr->dead() )
×
2938
      return new BError( "That is not dead" );
×
2939
    int flags = 0;
×
2940
    if ( exec.hasParams( 2 ) )
×
2941
    {
2942
      if ( !getParam( 1, flags ) )
×
2943
        return new BError( "Invalid parameter type" );
×
2944
    }
2945

2946
    if ( ~flags & RESURRECT_FORCELOCATION )
×
2947
    {
2948
      // we want doors to block ghosts in this case.
2949
      bool doors_block = !( chr->graphic == UOBJ_GAMEMASTER ||
×
2950
                            chr->cached_settings.get( PRIV_FLAGS::IGNORE_DOORS ) );
×
2951
      short newz;
2952
      Multi::UMulti* supporting_multi;
2953
      Item* walkon_item;
2954
      if ( !chr->realm()->walkheight( chr->pos2d(), chr->z(), &newz, &supporting_multi,
×
2955
                                      &walkon_item, doors_block, chr->movemode ) )
×
2956
      {
2957
        return new BError( "That location is blocked" );
×
2958
      }
2959
    }
2960

2961
    chr->resurrect();
×
2962
    return new BLong( 1 );
×
2963
  }
2964
  else
2965
  {
2966
    return new BError( "Invalid parameter type" );
×
2967
  }
2968
}
2969

2970
BObjectImp* UOExecutorModule::mf_SystemFindObjectBySerial()
20✔
2971
{
2972
  int serial;
2973
  if ( getParam( 0, serial ) )
20✔
2974
  {
2975
    int sysfind_flags = 0;
20✔
2976
    if ( exec.hasParams( 2 ) )
20✔
2977
    {
2978
      if ( !getParam( 1, sysfind_flags ) )
20✔
2979
        return new BError( "Invalid parameter type" );
×
2980
    }
2981
    if ( IsCharacter( serial ) )
20✔
2982
    {
2983
      Character* chr = system_find_mobile( serial );
3✔
2984
      if ( chr != nullptr )
3✔
2985
      {
2986
        if ( sysfind_flags & SYSFIND_SEARCH_OFFLINE_MOBILES )
2✔
2987
          return new EOfflineCharacterRefObjImp( chr );
×
2988
        else
2989
          return new ECharacterRefObjImp( chr );
2✔
2990
      }
2991
      else
2992
      {
2993
        return new BError( "Character not found" );
1✔
2994
      }
2995
    }
2996
    else
2997
    {
2998
      // dave changed this 3/8/3: objecthash (via system_find_item) will find any kind of item, so
2999
      // don't need system_find_multi here.
3000
      Item* item = system_find_item( serial );
17✔
3001

3002
      if ( item != nullptr )
17✔
3003
      {
3004
        return item->make_ref();
16✔
3005
      }
3006

3007
      return new BError( "Item not found." );
1✔
3008
    }
3009
  }
3010
  else
3011
  {
3012
    return new BError( "Invalid parameter type" );
×
3013
  }
3014
}
3015

3016
BObjectImp* UOExecutorModule::mf_SaveWorldState()
2✔
3017
{
3018
  update_gameclock();
2✔
3019
  cancel_all_trades();
2✔
3020

3021
  PolClockPauser pauser;
2✔
3022
  // do not suspend when critical, to keep defined state of a critical block
3023
  if ( !uoexec().critical() && uoexec().suspend() )
2✔
3024
  {
3025
    Tools::Timer<> total_timer;
1✔
3026
    auto res = write_data(
1✔
3027
        [uoexec = uoexec().weakptr.non_owning(), total_timer = std::move( total_timer )](
2✔
3028
            bool result, u32 clean_writes, u32 dirty_writes, s64 ellapsed ) mutable
3029
        {
3030
          Core::PolLock lck;
1✔
3031
          if ( !uoexec.exists() )
1✔
NEW
3032
            return;
×
3033
          if ( result )
1✔
3034
          {
3035
            auto* ret = new Bscript::BStruct();
1✔
3036
            ret->addMember( "DirtyObjects", new Bscript::BLong( dirty_writes ) );
1✔
3037
            ret->addMember( "CleanObjects", new Bscript::BLong( clean_writes ) );
1✔
3038
            ret->addMember( "ElapsedMilliseconds",
1✔
3039
                            new Bscript::BLong( Clib::clamp_convert<int>( ellapsed ) ) );
1✔
3040
            ret->addMember(
1✔
3041
                "ElapsedMillisecondsTotal",
3042
                new Bscript::BLong( Clib::clamp_convert<int>( total_timer.ellapsed() ) ) );
1✔
3043
            uoexec.get_weakptr()->ValueStack.back().set( new Bscript::BObject( ret ) );
1✔
3044
          }
3045
          else
3046
          {
NEW
3047
            uoexec.get_weakptr()->ValueStack.back().set(
×
NEW
3048
                new Bscript::BObject( new Bscript::BError( "Failed to save world" ) ) );
×
3049
          }
3050
          uoexec.get_weakptr()->revive();
1✔
3051
        } );
1✔
3052
    if ( !res )
1✔
3053
    {
NEW
3054
      uoexec().revive();
×
NEW
3055
      return new BError( "pol.cfg has InhibitSaves=1" );
×
3056
    }
3057
    if ( *res )
1✔
3058
      return new BLong( 0 );  // callback will be called
1✔
NEW
3059
    uoexec().revive();
×
3060
    return new BError( "Failed to save world" );
×
3061
  }
1✔
3062

3063
  // non waiting version
3064
  u32 dirty, clean;
3065
  s64 elapsed_ms;
3066
  auto res = write_data( {}, &dirty, &clean, &elapsed_ms );
1✔
3067
  if ( !res )
1✔
NEW
3068
    return new BError( "pol.cfg has InhibitSaves=1" );
×
3069
  if ( *res )
1✔
3070
  {
3071
    BStruct* ret = new BStruct();
1✔
3072
    ret->addMember( "DirtyObjects", new BLong( dirty ) );
1✔
3073
    ret->addMember( "CleanObjects", new BLong( clean ) );
1✔
3074
    ret->addMember( "ElapsedMilliseconds", new BLong( Clib::clamp_convert<int>( elapsed_ms ) ) );
1✔
3075
    return ret;
1✔
3076
  }
NEW
3077
  return new BError( "Failed to save world" );
×
3078
}
2✔
3079

3080

3081
BObjectImp* UOExecutorModule::mf_SetRegionLightLevel()
×
3082
{
3083
  const String* region_name_str;
3084
  int lightlevel;
3085
  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, lightlevel ) ) )
×
3086
  {
3087
    return new BError( "Invalid Parameter type" );
×
3088
  }
3089

3090
  if ( !VALID_LIGHTLEVEL( lightlevel ) )
×
3091
  {
3092
    return new BError( "Light Level is out of range" );
×
3093
  }
3094

3095
  LightRegion* lightregion = gamestate.lightdef->getregion( region_name_str->value() );
×
3096
  if ( lightregion == nullptr )
×
3097
  {
3098
    return new BError( "Light region not found" );
×
3099
  }
3100

3101
  SetRegionLightLevel( lightregion, lightlevel );
×
3102
  return new BLong( 1 );
×
3103
}
3104

3105
BObjectImp* UOExecutorModule::mf_SetRegionWeatherLevel()
×
3106
{
3107
  const String* region_name_str;
3108
  int type;
3109
  int severity;
3110
  int aux;
3111
  int lightoverride;
3112
  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, type ) && getParam( 2, severity ) &&
×
3113
          getParam( 3, aux ) && getParam( 4, lightoverride ) ) )
×
3114
  {
3115
    return new BError( "Invalid Parameter type" );
×
3116
  }
3117

3118
  WeatherRegion* weatherregion = gamestate.weatherdef->getregion( region_name_str->value() );
×
3119
  if ( weatherregion == nullptr )
×
3120
  {
3121
    return new BError( "Weather region not found" );
×
3122
  }
3123

3124
  SetRegionWeatherLevel( weatherregion, type, severity, aux, lightoverride );
×
3125

3126
  return new BLong( 1 );
×
3127
}
3128
BObjectImp* UOExecutorModule::mf_AssignRectToWeatherRegion()
×
3129
{
3130
  const String* region_name_str;
3131
  Pos2d nw, se;
×
3132
  Realms::Realm* realm;
3133

3134
  if ( !( getStringParam( 0, region_name_str ) && getRealmParam( 5, &realm ) &&
×
3135
          getPos2dParam( 1, 2, &nw, realm ) && getPos2dParam( 3, 4, &se, realm ) ) )
×
3136
  {
3137
    return new BError( "Invalid Parameter type" );
×
3138
  }
3139

3140
  bool res = gamestate.weatherdef->assign_zones_to_region( region_name_str->data(),
×
3141
                                                           Range2d( nw, se, nullptr ), realm );
×
3142
  if ( res )
×
3143
    return new BLong( 1 );
×
3144
  else
3145
    return new BError( "Weather region not found" );
×
3146
}
3147

3148
BObjectImp* UOExecutorModule::mf_Distance()
×
3149
{
3150
  UObject* obj1;
3151
  UObject* obj2;
3152
  if ( getUObjectParam( 0, obj1 ) && getUObjectParam( 1, obj2 ) )
×
3153
    return new BLong( obj1->distance_to( obj2->toplevel_pos() ) );
×
3154
  return new BError( "Invalid parameter type" );
×
3155
}
3156

3157
BObjectImp* UOExecutorModule::mf_DistanceEuclidean()
×
3158
{
3159
  UObject* obj1;
3160
  UObject* obj2;
3161
  if ( getUObjectParam( 0, obj1 ) && getUObjectParam( 1, obj2 ) )
×
3162
  {
3163
    const UObject* tobj1 = obj1->toplevel_owner();
×
3164
    const UObject* tobj2 = obj2->toplevel_owner();
×
3165
    return new Double( sqrt( pow( (double)( tobj1->x() - tobj2->x() ), 2 ) +
×
3166
                             pow( (double)( tobj1->y() - tobj2->y() ), 2 ) ) );
×
3167
  }
3168
  else
3169
  {
3170
    return new BError( "Invalid parameter type" );
×
3171
  }
3172
}
3173

3174
BObjectImp* UOExecutorModule::mf_CoordinateDistance()
×
3175
{
3176
  Pos2d pos1, pos2;
×
3177
  if ( !getPos2dParam( 0, 1, &pos1 ) || !getPos2dParam( 2, 3, &pos2 ) )
×
3178
    return new BError( "Invalid parameter type" );
×
3179
  return new BLong( pos1.pol_distance( pos2 ) );
×
3180
}
3181

3182
BObjectImp* UOExecutorModule::mf_CoordinateDistanceEuclidean()
×
3183
{
3184
  unsigned short x1, y1, x2, y2;
3185
  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
×
3186
  {
3187
    return new BError( "Invalid parameter type" );
×
3188
  }
3189
  return new Double( sqrt( pow( (double)( x1 - x2 ), 2 ) + pow( (double)( y1 - y2 ), 2 ) ) );
×
3190
}
3191

3192
BObjectImp* UOExecutorModule::mf_GetCoordsInLine()
×
3193
{
3194
  int x1, y1, x2, y2;
3195
  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
×
3196
  {
3197
    return new BError( "Invalid parameter type" );
×
3198
  }
3199
  else if ( x1 == x2 && y1 == y2 )
×
3200
  {  // Same exact coordinates ... just give them the coordinate back!
3201
    ObjArray* coords = new ObjArray;
×
3202
    BStruct* point = new BStruct;
×
3203
    point->addMember( "x", new BLong( x1 ) );
×
3204
    point->addMember( "y", new BLong( y1 ) );
×
3205
    coords->addElement( point );
×
3206
    return coords;
×
3207
  }
3208

3209
  double dx = abs( x2 - x1 ) + 0.5;
×
3210
  double dy = abs( y2 - y1 ) + 0.5;
×
3211
  int vx = 0, vy = 0;
×
3212

3213
  if ( x2 > x1 )
×
3214
    vx = 1;
×
3215
  else if ( x2 < x1 )
×
3216
    vx = -1;
×
3217
  if ( y2 > y1 )
×
3218
    vy = 1;
×
3219
  else if ( y2 < y1 )
×
3220
    vy = -1;
×
3221

3222
  std::unique_ptr<ObjArray> coords( new ObjArray );
×
3223
  if ( dx >= dy )
×
3224
  {
3225
    dy = dy / dx;
×
3226

3227
    for ( int c = 0; c <= dx; c++ )
×
3228
    {
3229
      int point_x = x1 + ( c * vx );
×
3230

3231
      double float_y = double( c ) * double( vy ) * dy;
×
3232
      if ( float_y - floor( float_y ) >= 0.5 )
×
3233
        float_y = ceil( float_y );
×
3234
      int point_y = int( float_y ) + y1;
×
3235

3236
      std::unique_ptr<BStruct> point( new BStruct );
×
3237
      point->addMember( "x", new BLong( point_x ) );
×
3238
      point->addMember( "y", new BLong( point_y ) );
×
3239
      coords->addElement( point.release() );
×
3240
    }
×
3241
  }
3242
  else
3243
  {
3244
    dx = dx / dy;
×
3245
    for ( int c = 0; c <= dy; c++ )
×
3246
    {
3247
      int point_y = y1 + ( c * vy );
×
3248

3249
      double float_x = double( c ) * double( vx ) * dx;
×
3250
      if ( float_x - floor( float_x ) >= 0.5 )
×
3251
        float_x = ceil( float_x );
×
3252
      int point_x = int( float_x ) + x1;
×
3253

3254
      std::unique_ptr<BStruct> point( new BStruct );
×
3255
      point->addMember( "x", new BLong( point_x ) );
×
3256
      point->addMember( "y", new BLong( point_y ) );
×
3257
      coords->addElement( point.release() );
×
3258
    }
×
3259
  }
3260
  return coords.release();
×
3261
}
×
3262

3263
BObjectImp* UOExecutorModule::mf_GetFacing()
×
3264
{
3265
  unsigned short from_x, from_y, to_x, to_y;
3266
  if ( !( getParam( 0, from_x ) && getParam( 1, from_y ) && getParam( 2, to_x ) &&
×
3267
          getParam( 3, to_y ) ) )
×
3268
  {
3269
    return new BError( "Invalid parameter type" );
×
3270
  }
3271

3272
  double x = to_x - from_x;
×
3273
  double y = to_y - from_y;
×
3274
  double pi = acos( double( -1 ) );
×
3275
  double r = sqrt( x * x + y * y );
×
3276

3277
  double angle = ( ( acos( x / r ) * 180.0 ) / pi );
×
3278

3279
  double y_check = ( ( asin( y / r ) * 180.0 ) / pi );
×
3280
  if ( y_check < 0 )
×
3281
  {
3282
    angle = 360 - angle;
×
3283
  }
3284

3285
  unsigned short facing = ( ( short( angle / 40 ) + 10 ) % 8 );
×
3286

3287
  return new BLong( facing );
×
3288
}
3289

3290
// FIXME : Should we do an Orphan check here as well? Ugh.
3291
void true_extricate( Item* item )
16✔
3292
{
3293
  send_remove_object_to_inrange( item );
16✔
3294
  if ( item->container != nullptr )
16✔
3295
  {
3296
    item->extricate();
1✔
3297
  }
3298
  else
3299
  {
3300
    remove_item_from_world( item );
15✔
3301
  }
3302
}
16✔
3303

3304
BObjectImp* UOExecutorModule::mf_MoveItemToContainer()
7✔
3305
{
3306
  Item* item;
3307
  Item* cont_item;
3308
  int px;
3309
  int py;
3310
  int add_to_existing_stack;
3311
  if ( !( getItemParam( 0, item ) && getItemParam( 1, cont_item ) && getParam( 2, px, -1, 65535 ) &&
14✔
3312
          getParam( 3, py, -1, 65535 ) && getParam( 4, add_to_existing_stack, 0, 2 ) ) )
7✔
3313
  {
3314
    return new BError( "Invalid parameter type" );
×
3315
  }
3316

3317
  ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
7✔
3318
  if ( !item->movable() )
7✔
3319
  {
3320
    Character* chr = controller_.get();
×
3321
    if ( chr == nullptr || !chr->can_move( item ) )
×
3322
      return new BError( "That is immobile" );
×
3323
  }
3324
  if ( item->inuse() && !is_reserved_to_me( item ) )
7✔
3325
  {
3326
    return new BError( "That item is being used." );
×
3327
  }
3328

3329

3330
  if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
7✔
3331
  {
3332
    return new BError( "Non-container selected as target" );
×
3333
  }
3334
  UContainer* cont = static_cast<UContainer*>( cont_item );
7✔
3335

3336
  if ( cont->serial == item->serial )
7✔
3337
  {
3338
    return new BError( "Can't put a container into itself" );
×
3339
  }
3340
  if ( is_a_parent( cont, item->serial ) )
7✔
3341
  {
3342
    return new BError( "Can't put a container into an item in itself" );
×
3343
  }
3344
  if ( !cont->can_add( *item ) )
7✔
3345
  {
3346
    return new BError( "Container is too full to add that" );
×
3347
  }
3348
  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3349
  Character* chr_owner = cont->GetCharacterOwner();
7✔
3350
  if ( chr_owner == nullptr )
7✔
3351
    if ( controller_.get() != nullptr )
6✔
3352
      chr_owner = controller_.get();
×
3353

3354
  // daved changed order 1/26/3 check canX scripts before onX scripts.
3355
  UContainer* oldcont = item->container;
7✔
3356
  Item* existing_stack = nullptr;
7✔
3357

3358
  if ( ( oldcont != nullptr ) &&
8✔
3359
       ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
1✔
3360
    return new BError( "Could not remove item from its container." );
×
3361
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3362
  {
3363
    return new BError( "Item was destroyed in CanRemove script" );
×
3364
  }
3365

3366
  if ( add_to_existing_stack )
7✔
3367
  {
3368
    existing_stack = cont->find_addable_stack( item );
×
3369
    if ( existing_stack != nullptr )
×
3370
    {
3371
      if ( !cont->can_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack,
×
3372
                                             item->getamount(), item ) )
×
3373
        return new BError( "Could not add to existing stack" );
×
3374
    }
3375
    else if ( add_to_existing_stack == 2 )
×
3376
      add_to_existing_stack = 0;
×
3377
    else
3378
      return new BError( "There is no existing stack" );
×
3379
  }
3380

3381
  if ( !add_to_existing_stack )
7✔
3382
    if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item ) )
7✔
3383
      return new BError( "Could not insert item into container." );
×
3384

3385
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3386
  {
3387
    return new BError( "Item was destroyed in CanInsert Script" );
×
3388
  }
3389

3390
  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
7✔
3391
    return new BError( "Item cannot be unequipped" );
×
3392
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3393
  {
3394
    return new BError( "Item was destroyed in Equip Script" );
×
3395
  }
3396

3397
  if ( oldcont != nullptr )
7✔
3398
  {
3399
    oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
1✔
3400
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
1✔
3401
    {
3402
      return new BError( "Item was destroyed in OnRemove script" );
×
3403
    }
3404
  }
3405

3406
  if ( !add_to_existing_stack )
7✔
3407
  {
3408
    u8 slotIndex = item->slot_index();
7✔
3409
    if ( !cont->can_add_to_slot( slotIndex ) )
7✔
3410
    {
3411
      item->destroy();
×
3412
      return new BError( "No slots available in new container" );
×
3413
    }
3414
    if ( !item->slot_index( slotIndex ) )
7✔
3415
    {
3416
      item->destroy();
×
3417
      return new BError( "Couldn't set slot index on item" );
×
3418
    }
3419

3420
    Core::Pos2d cntpos;
7✔
3421
    if ( px < 0 || py < 0 )
7✔
3422
      cntpos = cont->get_random_location();
7✔
3423
    else
3424
    {
3425
      cntpos.x( static_cast<u16>( px ) ).y( static_cast<u16>( py ) );
×
3426
      if ( !cont->is_legal_posn( cntpos ) )
×
3427
        cntpos = cont->get_random_location();
×
3428
    }
3429

3430
    true_extricate( item );
7✔
3431

3432
    item->setposition( Core::Pos4d( cntpos, 0, cont->realm() ) );  // TODO POS realm
7✔
3433

3434
    cont->add( item );
7✔
3435
    update_item_to_inrange( item );
7✔
3436
    // DAVE added this 11/17: if in a Character's pack, update weight.
3437
    UpdateCharacterWeight( item );
7✔
3438

3439
    cont->on_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item );
7✔
3440
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3441
    {
3442
      return new BError( "Item was destroyed in OnInsert script" );
×
3443
    }
3444
  }
3445
  else
3446
  {
3447
    u16 amount = item->getamount();
×
3448
    true_extricate( item );
×
3449
    existing_stack->add_to_self( item );
×
3450
    update_item_to_inrange( existing_stack );
×
3451
    UpdateCharacterWeight( existing_stack );
×
3452

3453
    cont->on_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack, amount );
×
3454
  }
3455

3456
  return new BLong( 1 );
7✔
3457
}
7✔
3458

3459

3460
BObjectImp* UOExecutorModule::mf_MoveItemToSecureTradeWin()
×
3461
{
3462
  Item* item;
3463
  Character* chr;
3464
  if ( !( getItemParam( 0, item ) && getCharacterParam( 1, chr ) ) )
×
3465
  {
3466
    return new BError( "Invalid parameter type" );
×
3467
  }
3468

3469
  ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
×
3470
  if ( !item->movable() )
×
3471
  {
3472
    Character* _chr = controller_.get();
×
3473
    if ( _chr == nullptr || !_chr->can_move( item ) )
×
3474
      return new BError( "That is immobile" );
×
3475
  }
3476
  if ( item->inuse() && !is_reserved_to_me( item ) )
×
3477
  {
3478
    return new BError( "That item is being used." );
×
3479
  }
3480

3481
  // daved changed order 1/26/3 check canX scripts before onX scripts.
3482
  UContainer* oldcont = item->container;
×
3483

3484
  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3485
  Character* chr_owner = nullptr;
×
3486
  if ( oldcont != nullptr )
×
3487
    chr_owner = oldcont->GetCharacterOwner();
×
3488
  if ( chr_owner == nullptr )
×
3489
    if ( controller_.get() != nullptr )
×
3490
      chr_owner = controller_.get();
×
3491

3492
  if ( ( oldcont != nullptr ) &&
×
3493
       ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
×
3494
    return new BError( "Could not remove item from its container." );
×
3495
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3496
  {
3497
    return new BError( "Item was destroyed in CanRemove script" );
×
3498
  }
3499

3500
  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
×
3501
    return new BError( "Item cannot be unequipped" );
×
3502
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3503
  {
3504
    return new BError( "Item was destroyed in Equip Script" );
×
3505
  }
3506

3507
  if ( oldcont != nullptr )
×
3508
  {
3509
    oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
×
3510
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3511
    {
3512
      return new BError( "Item was destroyed in OnRemove script" );
×
3513
    }
3514
  }
3515

3516
  true_extricate( item );
×
3517

3518
  return place_item_in_secure_trade_container( chr->client, item );
×
3519
}
×
3520

3521
BObjectImp* UOExecutorModule::mf_EquipItem()
9✔
3522
{
3523
  Character* chr;
3524
  Item* item;
3525
  if ( getCharacterParam( 0, chr ) && getItemParam( 1, item ) )
9✔
3526
  {
3527
    ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
9✔
3528
    if ( !item->movable() )
9✔
3529
    {
3530
      Character* _chr = controller_.get();
×
3531
      if ( _chr == nullptr || !_chr->can_move( item ) )
×
3532
        return new BError( "That is immobile" );
×
3533
    }
3534

3535
    if ( item->inuse() && !is_reserved_to_me( item ) )
9✔
3536
    {
3537
      return new BError( "That item is being used." );
×
3538
    }
3539

3540
    if ( !chr->equippable( item ) || !item->check_equiptest_scripts( chr ) )
9✔
3541
    {
3542
      return new BError( "That item is not equippable by that character" );
×
3543
    }
3544
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
9✔
3545
    {
3546
      return new BError( "Item was destroyed in EquipTest script" );
×
3547
    }
3548

3549
    item->layer = Plib::tilelayer( item->graphic );
9✔
3550

3551
    if ( item->has_equip_script() )
9✔
3552
    {
3553
      BObjectImp* res = item->run_equip_script( chr, false );
×
3554
      if ( !res->isTrue() )
×
3555
        return res;
×
3556
      else
3557
        BObject obj( res );
×
3558
    }
3559

3560

3561
    true_extricate( item );
9✔
3562

3563
    // at this point, 'item' is free - doesn't belong to the world, or a container.
3564
    chr->equip( item );
9✔
3565
    send_wornitem_to_inrange( chr, item );
9✔
3566

3567
    return new BLong( 1 );
9✔
3568
  }
9✔
3569
  else
3570
  {
3571
    return new BError( "Invalid parameter type" );
×
3572
  }
3573
}
3574

3575
BObjectImp* UOExecutorModule::mf_RestartScript()
1✔
3576
{
3577
  UObject* obj;
3578
  if ( !getUObjectParam( 0, obj ) )
1✔
3579
    return new BError( "Invalid parameter" );
×
3580

3581
  if ( obj->script_isa( POLCLASS_NPC ) )
1✔
3582
  {
3583
    NPC* npc = static_cast<NPC*>( obj );
×
3584
    npc->restart_script();
×
3585
    return new BLong( 1 );
×
3586
  }
3587
  else if ( obj->script_isa( POLCLASS_ITEM ) )
1✔
3588
  {
3589
    Item* item = static_cast<Item*>( obj );
1✔
3590
    item->stop_control_script();
1✔
3591
    return new BLong( item->start_control_script() );
1✔
3592
  }
3593
  else
3594
  {
3595
    return new BError( "RestartScript only operates on NPCs and Items" );
×
3596
  }
3597
}
3598

3599

3600
BObjectImp* UOExecutorModule::mf_GetHarvestDifficulty()
×
3601
{
3602
  const String* resource;
3603
  unsigned short tiletype;
3604
  Pos2d pos;
×
3605
  Realms::Realm* realm;
3606
  if ( getStringParam( 0, resource ) && getRealmParam( 4, &realm ) && getPos2dParam( 1, 2, &pos ) &&
×
3607
       getParam( 3, tiletype ) )
×
3608
  {
3609
    return get_harvest_difficulty( resource->data(), Pos4d( pos, 0, realm ), tiletype );
×
3610
  }
3611
  else
3612
  {
3613
    return new BError( "Invalid parameter" );
×
3614
  }
3615
}
3616

3617
BObjectImp* UOExecutorModule::mf_HarvestResource()
×
3618
{
3619
  Pos2d pos;
×
3620
  Realms::Realm* realm;
3621
  const String* resource;
3622
  int b;
3623
  int n;
3624

3625
  if ( getStringParam( 0, resource ) && getRealmParam( 5, &realm ) &&
×
3626
       getPos2dParam( 1, 2, &pos, realm ) && getParam( 3, b ) && getParam( 4, n ) )
×
3627
  {
3628
    if ( b <= 0 )
×
3629
      return new BError( "b must be >= 0" );
×
3630
    return harvest_resource( resource->data(), Pos4d( pos, 0, realm ), b, n );
×
3631
  }
3632
  else
3633
  {
3634
    return new BError( "Invalid parameter" );
×
3635
  }
3636
}
3637

3638
BObjectImp* UOExecutorModule::mf_GetRegionName( /* objref */ )
×
3639
{
3640
  UObject* obj;
3641

3642
  if ( getUObjectParam( 0, obj ) )
×
3643
  {
3644
    JusticeRegion* justice_region;
3645
    if ( obj->isa( UOBJ_CLASS::CLASS_ITEM ) )
×
3646
      obj = obj->toplevel_owner();
×
3647

3648
    if ( obj->isa( UOBJ_CLASS::CLASS_CHARACTER ) )
×
3649
    {
3650
      Character* chr = static_cast<Character*>( obj );
×
3651

3652
      if ( chr->logged_in() )
×
3653
        justice_region = chr->client->gd->justice_region;
×
3654
      else
3655
        justice_region = gamestate.justicedef->getregion( chr->pos() );
×
3656
    }
3657
    else
3658
      justice_region = gamestate.justicedef->getregion( obj->pos() );
×
3659

3660
    if ( justice_region == nullptr )
×
3661
      return new BError( "No Region defined at this Location" );
×
3662
    else
3663
      return new String( justice_region->region_name() );
×
3664
  }
3665
  else
3666
    return new BError( "Invalid parameter" );
×
3667
}
3668

3669
BObjectImp* UOExecutorModule::mf_GetRegionNameAtLocation( /* x, y, realm */ )
×
3670
{
3671
  Pos2d pos;
×
3672
  Realms::Realm* realm;
3673
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos ) )
×
3674
  {
3675
    JusticeRegion* justice_region = gamestate.justicedef->getregion( Pos4d( pos, 0, realm ) );
×
3676
    if ( justice_region == nullptr )
×
3677
      return new BError( "No Region defined at this Location" );
×
3678
    else
3679
      return new String( justice_region->region_name() );
×
3680
  }
3681
  else
3682
    return new BError( "Invalid parameter" );
×
3683
}
3684

3685
BObjectImp* UOExecutorModule::mf_GetRegionString()
×
3686
{
3687
  const String* resource;
3688
  const String* propname;
3689
  Pos2d pos;
×
3690
  Realms::Realm* realm;
3691
  if ( getStringParam( 0, resource ) && getRealmParam( 4, &realm ) && getPos2dParam( 1, 2, &pos ) &&
×
3692
       getStringParam( 3, propname ) )
×
3693
  {
3694
    return get_region_string( resource->data(), Pos4d( pos, 0, realm ), propname->value() );
×
3695
  }
3696
  else
3697
  {
3698
    return new BError( "Invalid parameter" );
×
3699
  }
3700
}
3701

3702
BObjectImp* UOExecutorModule::mf_GetRegionLightLevelAtLocation( /* x, y, realm */ )
×
3703
{
3704
  Pos2d pos;
×
3705
  Realms::Realm* realm;
3706

3707
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos ) )
×
3708
  {
3709
    LightRegion* light_region = gamestate.lightdef->getregion( Pos4d( pos, 0, realm ) );
×
3710
    int lightlevel;
3711
    if ( light_region != nullptr )
×
3712
      lightlevel = light_region->lightlevel;
×
3713
    else
3714
      lightlevel = settingsManager.ssopt.default_light_level;
×
3715
    return new BLong( lightlevel );
×
3716
  }
3717
  else
3718
  {
3719
    return new BError( "Invalid parameter" );
×
3720
  }
3721
}
3722

3723

3724
BObjectImp* UOExecutorModule::mf_EquipFromTemplate()
1✔
3725
{
3726
  Character* chr;
3727
  const String* template_name;
3728
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, template_name ) )
1✔
3729
  {
3730
    return equip_from_template( chr, template_name->value() );
1✔
3731
  }
3732
  else
3733
  {
3734
    return new BError( "Invalid parameter" );
×
3735
  }
3736
}
3737

3738
// FIXME: Use a PrivUpdater here
3739
BObjectImp* UOExecutorModule::mf_GrantPrivilege()
2✔
3740
{
3741
  Character* chr;
3742
  const String* privstr;
3743
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, privstr ) )
2✔
3744
  {
3745
    chr->grant_privilege( privstr->data() );
2✔
3746
    return new BLong( 1 );
2✔
3747
  }
3748
  else
3749
  {
3750
    return new BError( "Invalid parameter" );
×
3751
  }
3752
}
3753

3754
// FIXME: Use a PrivUpdater here
3755
BObjectImp* UOExecutorModule::mf_RevokePrivilege()
×
3756
{
3757
  Character* chr;
3758
  const String* privstr;
3759
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, privstr ) )
×
3760
  {
3761
    chr->revoke_privilege( privstr->data() );
×
3762
    return new BLong( 1 );
×
3763
  }
3764
  else
3765
  {
3766
    return new BError( "Invalid parameter" );
×
3767
  }
3768
}
3769

3770
BObjectImp* UOExecutorModule::mf_ReadGameClock()
11✔
3771
{
3772
  return new BLong( read_gameclock() );
11✔
3773
}
3774

3775
unsigned char decode_xdigit( unsigned char ch )
×
3776
{
3777
  if ( ch >= '0' && ch <= '9' )
×
3778
    ch -= '0';
×
3779
  else if ( ch >= 'A' && ch <= 'F' )
×
3780
    ch = ch - 'A' + 0xa;
×
3781
  else if ( ch >= 'a' && ch <= 'f' )
×
3782
    ch = ch - 'a' + 0xa;
×
3783

3784
  return ch;
×
3785
}
3786

3787
BObjectImp* UOExecutorModule::mf_SendPacket()
×
3788
{
3789
  Character* chr;
3790
  Network::Client* client;
3791
  const String* str;
3792
  if ( getCharacterOrClientParam( 0, chr, client ) && getStringParam( 1, str ) )
×
3793
  {
3794
    if ( str->length() % 2 > 0 )
×
3795
    {
3796
      return new BError( "Invalid packet string length." );
×
3797
    }
3798
    Network::PktHelper::PacketOut<Network::EncryptedPktBuffer>
3799
        buffer;  // encryptedbuffer is the only one without getID buffer[0]
×
3800
    unsigned char* buf = reinterpret_cast<unsigned char*>( buffer->getBuffer() );
×
3801
    const char* s = str->data();
×
3802
    while ( buffer->offset < 2000 && isxdigit( s[0] ) && isxdigit( s[1] ) )
×
3803
    {
3804
      unsigned char ch;
3805
      ch = ( decode_xdigit( s[0] ) << 4 ) | decode_xdigit( s[1] );
×
3806
      *( buf++ ) = ch;
×
3807
      buffer->offset++;
×
3808
      s += 2;
×
3809
    }
3810
    if ( chr != nullptr )
×
3811
    {
3812
      if ( chr->has_active_client() )
×
3813
      {
3814
        buffer.Send( chr->client );
×
3815
        return new BLong( 1 );
×
3816
      }
3817
      else
3818
      {
3819
        return new BError( "No client attached" );
×
3820
      }
3821
    }
3822
    else if ( client != nullptr )
×
3823
    {
3824
      if ( client->isConnected() )
×
3825
      {
3826
        buffer.Send( client );
×
3827
        return new BLong( 1 );
×
3828
      }
3829
      else
3830
      {
3831
        return new BError( "Client is disconnected" );
×
3832
      }
3833
    }
3834
    else
3835
    {
3836
      return new BError( "Invalid parameter type" );
×
3837
    }
3838
  }
×
3839
  else
3840
  {
3841
    return new BError( "Invalid parameter type" );
×
3842
  }
3843
}
3844

3845
BObjectImp* UOExecutorModule::mf_SendQuestArrow()
×
3846
{
3847
  Character* chr;
3848
  int x, y;
3849
  u32 arrowid = 0;
×
3850

3851
  if ( getCharacterParam( 0, chr ) && getParam( 1, x, -1, 1000000 ) &&
×
3852
       getParam( 2, y, -1, 1000000 ) )  // max values checked below
×
3853
  {
3854
    if ( !chr->has_active_client() )
×
3855
      return new BError( "No client attached" );
×
3856
    {
3857
      int arrow_id;
3858
      if ( exec.getParam( 3, arrow_id ) )
×
3859
      {
3860
        if ( arrow_id < 1 )
×
3861
          return new BError( "ArrowID out of range" );
×
3862
        arrowid = static_cast<u32>( arrow_id );
×
3863
      }
3864
      else
3865
        arrowid = uoexec().pid();
×
3866
    }
3867
    bool usesNewPktSize = ( chr->client->ClientType & Network::CLIENTTYPE_7090 ) > 0;
×
3868

3869
    Network::PktHelper::PacketOut<Network::PktOut_BA> msg;
×
3870
    if ( x == -1 && y == -1 )
×
3871
    {
3872
      msg->Write<u8>( PKTOUT_BA_ARROW_OFF );
×
3873
      msg->offset += 4;  // u16 x_tgt,y_tgt
×
3874
      if ( usesNewPktSize )
×
3875
      {
3876
        if ( !arrowid )
×
3877
        {
3878
          return new BError( "ArrowID must be supplied for cancelation." );
×
3879
        }
3880
        else
3881
        {
3882
          msg->Write<u32>( static_cast<u32>( arrowid & 0xFFFFFFFF ) );
×
3883
        }
3884
      }
3885
    }
3886
    else
3887
    {
3888
      auto pos = Core::Pos2d( Clib::clamp_convert<u16>( x ), Clib::clamp_convert<u16>( y ) );
×
3889
      if ( !chr->realm()->valid( pos ) )
×
3890
        return new BError( "Invalid Coordinates for Realm" );
×
3891
      msg->Write<u8>( PKTOUT_BA_ARROW_ON );
×
3892
      msg->WriteFlipped<u16>( pos.x() );
×
3893
      msg->WriteFlipped<u16>( pos.y() );
×
3894
      if ( usesNewPktSize )
×
3895
        msg->Write<u32>( static_cast<u32>( arrowid & 0xFFFFFFFF ) );
×
3896
    }
3897
    msg.Send( chr->client );
×
3898
    return new BLong( arrowid );
×
3899
  }
×
3900
  else
3901
  {
3902
    return new BError( "Invalid parameter" );
×
3903
  }
3904
}
3905

3906
BObjectImp* UOExecutorModule::mf_ConsumeReagents()
×
3907
{
3908
  Character* chr;
3909
  int spellid;
3910
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) )
×
3911
  {
3912
    if ( !VALID_SPELL_ID( spellid ) )
×
3913
    {
3914
      return new BError( "Spell ID out of range" );
×
3915
    }
3916
    USpell* spell = gamestate.spells[spellid];
×
3917
    if ( spell == nullptr )
×
3918
    {
3919
      return new BError( "No such spell" );
×
3920
    }
3921

3922
    return new BLong( spell->consume_reagents( chr ) ? 1 : 0 );
×
3923
  }
3924
  else
3925
  {
3926
    return new BError( "Invalid parameter" );
×
3927
  }
3928
}
3929

3930
BObjectImp* UOExecutorModule::mf_StartSpellEffect()
×
3931
{
3932
  Character* chr;
3933
  int spellid;
3934
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) )
×
3935
  {
3936
    if ( !VALID_SPELL_ID( spellid ) )
×
3937
    {
3938
      return new BError( "Spell ID out of range" );
×
3939
    }
3940
    USpell* spell = gamestate.spells[spellid];
×
3941
    if ( spell == nullptr )
×
3942
    {
3943
      return new BError( "No such spell" );
×
3944
    }
3945

3946
    spell->cast( chr );
×
3947
    return new BLong( 1 );
×
3948
  }
3949
  else
3950
  {
3951
    return new BError( "Invalid parameter" );
×
3952
  }
3953
}
3954
BObjectImp* UOExecutorModule::mf_GetSpellDifficulty()
×
3955
{
3956
  int spellid;
3957
  if ( getParam( 0, spellid ) )
×
3958
  {
3959
    if ( !VALID_SPELL_ID( spellid ) )
×
3960
    {
3961
      return new BError( "Spell ID out of range" );
×
3962
    }
3963
    USpell* spell = gamestate.spells[spellid];
×
3964
    if ( spell == nullptr )
×
3965
    {
3966
      return new BError( "No such spell" );
×
3967
    }
3968

3969
    return new BLong( spell->difficulty() );
×
3970
  }
3971
  else
3972
  {
3973
    return new BError( "Invalid parameter" );
×
3974
  }
3975
}
3976
BObjectImp* UOExecutorModule::mf_SpeakPowerWords()
×
3977
{
3978
  Character* chr;
3979
  int spellid;
3980
  unsigned short font;
3981
  unsigned short color;
3982

3983
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) && getParam( 2, font ) &&
×
3984
       getParam( 3, color ) )
×
3985
  {
3986
    if ( !VALID_SPELL_ID( spellid ) )
×
3987
    {
3988
      return new BError( "Spell ID out of range" );
×
3989
    }
3990
    USpell* spell = gamestate.spells[spellid];
×
3991
    if ( spell == nullptr )
×
3992
    {
3993
      return new BError( "No such spell" );
×
3994
    }
3995

3996
    spell->speak_power_words( chr, font, color );
×
3997

3998
    return new BLong( 1 );
×
3999
  }
4000
  else
4001
  {
4002
    return new BError( "Invalid parameter" );
×
4003
  }
4004
}
4005

4006
BObjectImp* UOExecutorModule::mf_ListEquippedItems()
×
4007
{
4008
  Character* chr;
4009
  if ( getCharacterParam( 0, chr ) )
×
4010
  {
4011
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
4012
    for ( int layer = LAYER_EQUIP__LOWEST; layer <= LAYER_EQUIP__HIGHEST; ++layer )
×
4013
    {
4014
      Item* item = chr->wornitem( layer );
×
4015
      if ( item != nullptr )
×
4016
      {
4017
        arr->addElement( new EItemRefObjImp( item ) );
×
4018
      }
4019
    }
4020
    return arr.release();
×
4021
  }
×
4022
  else
4023
  {
4024
    return new BError( "Invalid parameter" );
×
4025
  }
4026
}
4027

4028
BObjectImp* UOExecutorModule::mf_GetEquipmentByLayer()
2✔
4029
{
4030
  Character* chr;
4031
  int layer;
4032
  if ( getCharacterParam( 0, chr ) && getParam( 1, layer ) )
2✔
4033
  {
4034
    if ( layer < LOWEST_LAYER || layer > HIGHEST_LAYER )
2✔
4035
    {
4036
      return new BError( "Invalid layer" );
×
4037
    }
4038

4039
    Item* item = chr->wornitem( layer );
2✔
4040
    if ( item == nullptr )
2✔
4041
    {
4042
      return new BError( "Nothing equipped on that layer." );
1✔
4043
    }
4044
    else
4045
    {
4046
      return new EItemRefObjImp( item );
1✔
4047
    }
4048
  }
4049
  else
4050
  {
4051
    return new BError( "Invalid parameter" );
×
4052
  }
4053
}
4054

4055
BObjectImp* UOExecutorModule::mf_DisconnectClient()
×
4056
{
4057
  Character* chr;
4058
  Network::Client* client;
4059

4060
  if ( getCharacterOrClientParam( 0, chr, client ) )
×
4061
  {
4062
    if ( chr != nullptr )
×
4063
    {
4064
      if ( !chr->has_active_client() )
×
4065
        return new BError( "No client attached" );
×
4066

4067
      client = chr->client;
×
4068
    }
4069

4070
    if ( client != nullptr )
×
4071
    {
4072
      if ( client->isConnected() )
×
4073
      {
4074
        client->Disconnect();
×
4075
        return new BLong( 1 );
×
4076
      }
4077
      else
4078
        return new BError( "Client is disconnected" );
×
4079
    }
4080
    else
4081
      return new BError( "Invalid parameter type" );
×
4082
  }
4083
  else
4084
  {
4085
    return new BError( "Invalid parameter type" );
×
4086
  }
4087
}
4088

4089
BObjectImp* UOExecutorModule::mf_GetMapInfo()
34✔
4090
{
4091
  Core::Pos2d pos;
34✔
4092
  Realms::Realm* realm;
4093

4094
  // note that this uses WORLD_MAX_X, not WORLD_X,
4095
  // because we can't read the outermost edge of the map
4096
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos, realm ) )
34✔
4097
  {
4098
    Plib::MAPTILE_CELL cell = realm->getmaptile( pos );
34✔
4099
    std::unique_ptr<BStruct> result( new BStruct );
34✔
4100
    result->addMember( "z", new BLong( cell.z ) );
34✔
4101
    result->addMember( "landtile", new BLong( cell.landtile ) );
34✔
4102

4103
    return result.release();
34✔
4104
  }
34✔
4105
  else
4106
  {
4107
    return new BError( "Invalid parameter" );
×
4108
  }
4109
}
4110
BObjectImp* UOExecutorModule::mf_GetWorldHeight()
×
4111
{
4112
  Core::Pos2d pos;
×
4113
  Realms::Realm* realm;
4114
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos, realm ) )
×
4115
  {
4116
    short z = -255;
×
4117
    if ( realm->lowest_standheight( pos, &z ) )
×
4118
      return new BLong( z );
×
4119
    else
4120
      return new BError( "Nowhere" );
×
4121
  }
4122
  else
4123
  {
4124
    return new BError( "Invalid parameter" );
×
4125
  }
4126
}
4127

4128
BObjectImp* UOExecutorModule::mf_GetObjtypeByName()
×
4129
{
4130
  const String* namestr;
4131
  if ( getStringParam( 0, namestr ) )
×
4132
  {
4133
    unsigned int objtype = get_objtype_byname( namestr->data() );
×
4134
    if ( objtype != 0 )
×
4135
      return new BLong( objtype );
×
4136
    else
4137
      return new BError( "No objtype by that name" );
×
4138
  }
4139
  else
4140
  {
4141
    return new BError( "Invalid parameter" );
×
4142
  }
4143
}
4144

4145
BObjectImp* UOExecutorModule::mf_SendEvent()
×
4146
{
4147
  Character* chr;
4148
  if ( getCharacterParam( 0, chr ) )
×
4149
  {
4150
    BObjectImp* event = exec.getParamImp( 1 );
×
4151
    if ( event != nullptr )
×
4152
    {
4153
      if ( chr->isa( UOBJ_CLASS::CLASS_NPC ) )
×
4154
      {
4155
        NPC* npc = static_cast<NPC*>( chr );
×
4156
        // event->add_ref(); // UNTESTED
4157
        return npc->send_event_script( event->copy() );
×
4158
      }
4159
      else
4160
      {
4161
        return new BError( "That mobile is not an NPC" );
×
4162
      }
4163
    }
4164
    else
4165
    {
4166
      return new BError( "Huh?  Not enough parameters" );
×
4167
    }
4168
  }
4169
  else
4170
  {
4171
    return new BError( "Invalid parameter" );
×
4172
  }
4173
}
4174

4175
BObjectImp* UOExecutorModule::mf_DestroyMulti()
29✔
4176
{
4177
  Multi::UMulti* multi;
4178
  if ( getMultiParam( 0, multi ) )
29✔
4179
  {
4180
    const ItemDesc& id = find_itemdesc( multi->objtype_ );
29✔
4181
    if ( !id.destroy_script.empty() )
29✔
4182
    {
4183
      BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( multi ) );
×
4184
      if ( !res->isTrue() )
×
4185
      {  // destruction is okay
4186
        return res;
×
4187
      }
4188
    }
4189

4190
    Multi::UBoat* boat = multi->as_boat();
29✔
4191
    if ( boat != nullptr )
29✔
4192
    {
4193
      return Multi::destroy_boat( boat );
16✔
4194
    }
4195
    Multi::UHouse* house = multi->as_house();
13✔
4196
    if ( house != nullptr )
13✔
4197
    {
4198
      return Multi::destroy_house( house );
13✔
4199
    }
4200
    return new BError( "WTF!? Don't know what kind of multi that is!" );
×
4201
  }
4202
  else
4203
  {
4204
    return new BError( "Invalid parameter type" );
×
4205
  }
4206
}
4207

4208

4209
BObjectImp* UOExecutorModule::mf_GetMultiDimensions()
×
4210
{
4211
  u16 multiid;
4212
  if ( getParam( 0, multiid ) )
×
4213
  {
4214
    if ( !Multi::MultiDefByMultiIDExists( multiid ) )
×
4215
      return new BError( "MultiID not found" );
×
4216

4217
    const Multi::MultiDef& md = *Multi::MultiDefByMultiID( multiid );
×
4218
    std::unique_ptr<BStruct> ret( new BStruct );
×
4219
    ret->addMember( "xmin", new BLong( md.minrxyz.x() ) );
×
4220
    ret->addMember( "xmax", new BLong( md.maxrxyz.x() ) );
×
4221
    ret->addMember( "ymin", new BLong( md.minrxyz.y() ) );
×
4222
    ret->addMember( "ymax", new BLong( md.maxrxyz.y() ) );
×
4223
    return ret.release();
×
4224
  }
×
4225
  else
4226
    return new BError( "Invalid parameter" );
×
4227
}
4228

4229
BObjectImp* UOExecutorModule::mf_SetScriptController()
×
4230
{
4231
  Character* old_controller = controller_.get();
×
4232
  BObjectImp* param0 = getParamImp( 0 );
×
4233
  bool handled = false;
×
4234

4235
  if ( auto* lng = impptrIf<BLong>( param0 ) )
×
4236
  {
4237
    if ( lng->value() == 0 )
×
4238
    {
4239
      controller_.clear();
×
4240
      handled = true;
×
4241
    }
4242
  }
4243

4244
  if ( !handled )
×
4245
  {
4246
    Character* chr;
4247
    if ( getCharacterParam( 0, chr ) )
×
4248
      controller_.set( chr );
×
4249
    else
4250
      controller_.clear();
×
4251
  }
4252

4253
  if ( old_controller )
×
4254
    return new ECharacterRefObjImp( old_controller );
×
4255
  else
4256
    return new BLong( 0 );
×
4257
}
4258

4259
BObjectImp* UOExecutorModule::mf_GetStandingHeight()
6✔
4260
{
4261
  Core::Pos4d pos;
6✔
4262
  if ( getPos4dParam( 0, 1, 2, 3, &pos ) )
6✔
4263
  {
4264
    short newz;
4265
    Multi::UMulti* multi;
4266
    Item* walkon;
4267
    if ( pos.realm()->lowest_walkheight( pos.xy(), pos.z(), &newz, &multi, &walkon, true,
6✔
4268
                                         Plib::MOVEMODE_LAND ) )
4269
    {
4270
      std::unique_ptr<BStruct> arr( new BStruct );
6✔
4271
      arr->addMember( "z", new BLong( newz ) );
6✔
4272
      if ( multi != nullptr )
6✔
4273
        arr->addMember( "multi", new EMultiRefObjImp( multi ) );
6✔
4274
      return arr.release();
6✔
4275
    }
6✔
4276
    else
4277
    {
4278
      return new BError( "Can't stand there" );
×
4279
    }
4280
  }
4281
  else
4282
  {
4283
    return new BError( "Invalid parameter type" );
×
4284
  }
4285
}
4286

4287
BObjectImp* UOExecutorModule::mf_GetStandingLayers( /* x, y, flags, realm, includeitems */ )
×
4288
{
4289
  Core::Pos2d pos;
×
4290
  int flags;
4291
  Realms::Realm* realm;
4292
  int includeitems;
4293

4294
  if ( getRealmParam( 3, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, flags ) &&
×
4295
       getParam( 4, includeitems ) )
×
4296
  {
4297
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4298

4299
    Plib::MapShapeList mlist;
×
4300
    Core::ItemsVector ivec;
×
4301
    realm->readmultis( mlist, pos, flags );
×
4302
    realm->getmapshapes( mlist, pos, flags );
×
4303
    if ( includeitems )
×
4304
    {
4305
      realm->readdynamics( mlist, pos, ivec, false, flags );
×
4306
    }
4307

4308
    for ( unsigned i = 0; i < mlist.size(); ++i )
×
4309
    {
4310
      std::unique_ptr<BStruct> arr( new BStruct );
×
4311

4312
      if ( mlist[i].flags & ( Plib::FLAG::MOVELAND | Plib::FLAG::MOVESEA ) )
×
4313
        arr->addMember( "z", new BLong( mlist[i].z + mlist[i].height ) );
×
4314
      else
4315
        arr->addMember( "z", new BLong( mlist[i].z ) );
×
4316

4317
      arr->addMember( "height", new BLong( mlist[i].height ) );
×
4318
      arr->addMember( "flags", new BLong( mlist[i].flags ) );
×
4319
      newarr->addElement( arr.release() );
×
4320
    }
×
4321

4322
    return newarr.release();
×
4323
  }
×
4324
  else
4325
    return new BError( "Invalid parameter type" );
×
4326
}
4327

4328
BObjectImp*
4329
UOExecutorModule::mf_GetStandingCoordinates() /* x, y, radius, minz, maxz, realm := _DEFAULT_REALM,
1✔
4330
                                                 movemode := "L", doors_block = 0 */
4331
{
4332
  s16 r, minz, maxz;
4333
  int doors_block;
4334
  const String* movemodename;
4335
  Core::Pos2d pos;
1✔
4336
  Realms::Realm* realm;
4337

4338
  if ( !getRealmParam( 5, &realm ) )
1✔
4339
    return new BError( "Realm not found" );
×
4340

4341
  if ( !( getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, r ) && getParam( 3, minz ) &&
2✔
4342
          getParam( 4, maxz ) && getStringParam( 6, movemodename ) && getParam( 7, doors_block ) ) )
1✔
4343
  {
4344
    return new BError( "Invalid parameter type" );
×
4345
  }
4346

4347
  Plib::MOVEMODE movemode = Character::decode_movemode( movemodename->value() );
1✔
4348

4349
  std::unique_ptr<ObjArray> result( new ObjArray );
1✔
4350

4351
  // Iterate through all tiles in range and populate the return array with valid standing locations
4352
  Core::Vec2d radius( r, r );
1✔
4353
  Core::Pos2d tl = pos - radius;
1✔
4354
  Core::Pos2d br = pos + radius;
1✔
4355
  Core::Range2d range( tl, br, realm );
1✔
4356
  for ( const auto& tile : range )
243✔
4357
  {
4358
    auto layers = realm->get_walkheights( tile, minz, maxz, movemode, doors_block );
121✔
4359
    for ( const auto& layer : layers )
154✔
4360
    {
4361
      std::unique_ptr<BStruct> height_struct( new BStruct );
33✔
4362
      auto z = std::get<0>( layer );
33✔
4363
      auto multi = std::get<1>( layer );
33✔
4364

4365
      // Figure out which members to stick in the struct -- we only add multi it exists
4366
      height_struct->addMember( "x", new BLong( tile.x() ) );
33✔
4367
      height_struct->addMember( "y", new BLong( tile.y() ) );
33✔
4368
      height_struct->addMember( "z", new BLong( z ) );
33✔
4369
      if ( multi != nullptr )
33✔
4370
      {
4371
        height_struct->addMember( "multi", new EMultiRefObjImp( multi ) );
×
4372
      }
4373

4374
      // Add struct to the return array
4375
      result->addElement( height_struct.release() );
33✔
4376
    }
33✔
4377
  }
121✔
4378

4379
  return result.release();
1✔
4380
}
1✔
4381

4382
BObjectImp* UOExecutorModule::mf_ReserveItem()
3✔
4383
{
4384
  Item* item;
4385
  if ( getItemParam( 0, item ) )
3✔
4386
  {
4387
    if ( item->inuse() )
3✔
4388
    {
4389
      if ( is_reserved_to_me( item ) )
2✔
4390
        return new BLong( 2 );
2✔
4391
      else
4392
        return new BError( "That item is already being used." );
×
4393
    }
4394
    item->inuse( true );
1✔
4395
    reserved_items_.push_back( ItemRef( item ) );
1✔
4396
    return new BLong( 1 );
1✔
4397
  }
4398
  else
4399
  {
4400
    return new BError( "Invalid parameter" );
×
4401
  }
4402
}
4403

4404
BObjectImp* UOExecutorModule::mf_ReleaseItem()
3✔
4405
{
4406
  Item* item;
4407
  if ( getItemParam( 0, item ) )
3✔
4408
  {
4409
    if ( item->inuse() )
3✔
4410
    {
4411
      for ( unsigned i = 0; i < reserved_items_.size(); ++i )
3✔
4412
      {
4413
        if ( reserved_items_[i].get() == item )
3✔
4414
        {
4415
          item->inuse( false );
3✔
4416
          reserved_items_[i] = reserved_items_.back();
3✔
4417
          reserved_items_.pop_back();
3✔
4418
          return new BLong( 1 );
3✔
4419
        }
4420
      }
4421
      return new BError( "That item is not reserved by this script." );
×
4422
    }
4423
    else
4424
    {
4425
      return new BError( "That item is not reserved." );
×
4426
    }
4427
  }
4428
  else
4429
  {
4430
    return new BError( "Invalid parameter" );
×
4431
  }
4432
}
4433

4434

4435
BObjectImp* UOExecutorModule::mf_SendSkillWindow()
×
4436
{
4437
  Character *towhom, *forwhom;
4438
  if ( getCharacterParam( 0, towhom ) && getCharacterParam( 1, forwhom ) )
×
4439
  {
4440
    if ( towhom->has_active_client() )
×
4441
    {
4442
      send_skillmsg( towhom->client, forwhom );
×
4443
      return new BLong( 1 );
×
4444
    }
4445
    else
4446
    {
4447
      return new BError( "No client attached" );
×
4448
    }
4449
  }
4450
  else
4451
  {
4452
    return new BError( "Invalid parameter type" );
×
4453
  }
4454
}
4455

4456

4457
BObjectImp* UOExecutorModule::mf_OpenPaperdoll()
×
4458
{
4459
  Character *towhom, *forwhom;
4460
  if ( getCharacterParam( 0, towhom ) && getCharacterParam( 1, forwhom ) )
×
4461
  {
4462
    if ( towhom->has_active_client() )
×
4463
    {
4464
      send_paperdoll( towhom->client, forwhom );
×
4465
      return new BLong( 1 );
×
4466
    }
4467
    else
4468
    {
4469
      return new BError( "No client attached" );
×
4470
    }
4471
  }
4472
  else
4473
  {
4474
    return new BError( "Invalid parameter type" );
×
4475
  }
4476
}
4477

4478
// TODO: a FindSubstance call that does the 'find' portion below, but returns an
4479
// array of the stacks.  All returned items would be marked 'inuse'.
4480
// Then, make ConsumeSubstance able to use such an array of owned items.
4481

4482
BObjectImp* UOExecutorModule::mf_ConsumeSubstance()
×
4483
{
4484
  Item* cont_item;
4485
  unsigned int objtype;
4486
  int amount;
4487
  if ( getItemParam( 0, cont_item ) && getObjtypeParam( 1, objtype ) && getParam( 2, amount ) )
×
4488
  {
4489
    if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
4490
      return new BError( "That is not a container" );
×
4491
    if ( amount < 0 )
×
4492
      return new BError( "Amount cannot be negative" );
×
4493

4494
    UContainer* cont = static_cast<UContainer*>( cont_item );
×
4495
    int amthave = cont->find_sumof_objtype_noninuse( objtype );
×
4496
    if ( amthave < amount )
×
4497
      return new BError( "Not enough of that substance in container" );
×
4498

4499
    cont->consume_sumof_objtype_noninuse( objtype, amount );
×
4500

4501
    return new BLong( 1 );
×
4502
  }
4503
  else
4504
  {
4505
    return new BError( "Invalid parameter type" );
×
4506
  }
4507
}
4508

4509
bool UOExecutorModule::is_reserved_to_me( Item* item )
2✔
4510
{
4511
  for ( unsigned i = 0; i < reserved_items_.size(); ++i )
2✔
4512
  {
4513
    if ( reserved_items_[i].get() == item )
2✔
4514
      return true;
2✔
4515
  }
4516
  return false;
×
4517
}
4518

4519
BObjectImp* UOExecutorModule::mf_Shutdown()
2✔
4520
{
4521
  int exit_code = 0;
2✔
4522

4523
  if ( exec.hasParams( 1 ) )
2✔
4524
  {
4525
    getParam( 0, exit_code );
2✔
4526
  }
4527

4528
  Clib::signal_exit( exit_code );
2✔
4529
#ifndef _WIN32
4530
  // the catch_signals_thread (actually main) sits with sigwait(),
4531
  // so it won't wake up except by being signalled.
4532
  signal_catch_thread();
2✔
4533
#endif
4534
  return new BLong( 1 );
2✔
4535
}
4536

4537

4538
BObjectImp* UOExecutorModule::mf_GetCommandHelp()
×
4539
{
4540
  Character* chr;
4541
  const String* cmd;
4542
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, cmd ) )
×
4543
  {
4544
    std::string help = get_textcmd_help( chr, cmd->value() );
×
4545
    if ( !help.empty() )
×
4546
    {
4547
      return new String( help );
×
4548
    }
4549
    else
4550
    {
4551
      return new BError( "No help for that command found" );
×
4552
    }
4553
  }
×
4554
  else
4555
  {
4556
    return new BError( "Invalid parameter type" );
×
4557
  }
4558
}
4559

4560

4561
BObjectImp* UOExecutorModule::mf_SendStringAsTipWindow()
×
4562
{
4563
  Character* chr;
4564
  const String* str;
4565
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, str ) )
×
4566
  {
4567
    if ( chr->has_active_client() )
×
4568
    {
4569
      send_tip( chr->client, str->value() );
×
4570
      return new BLong( 1 );
×
4571
    }
4572
    else
4573
    {
4574
      return new BError( "No client attached" );
×
4575
    }
4576
  }
4577
  else
4578
  {
4579
    return new BError( "Invalid parameter type" );
×
4580
  }
4581
}
4582

4583
BObjectImp* UOExecutorModule::mf_ListItemsNearLocationWithFlag(
×
4584
    /* x, y, z, range, flags, realm */ )  // DAVE
4585
{
4586
  Pos2d pos;
×
4587
  u16 range;
4588
  int z, flags;
4589
  Realms::Realm* realm;
4590

4591
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4592
       getParam( 3, range ) && getParam( 4, flags ) )
×
4593
  {
4594
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4595
    WorldIterator<ItemFilter>::InRange(
×
4596
        pos, realm, range,
4597
        [&]( Item* item )
×
4598
        {
4599
          if ( ( Plib::tile_uoflags( item->graphic ) & flags ) )
×
4600
          {
4601
            if ( item->in_range( pos, range ) )
×
4602
            {
4603
              if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
4604
                newarr->addElement( new EItemRefObjImp( item ) );
×
4605
            }
4606
          }
4607
        } );
×
4608

4609
    return newarr.release();
×
4610
  }
×
4611

4612
  return new BError( "Invalid parameter" );
×
4613
}
4614

4615
BObjectImp* UOExecutorModule::mf_ListStaticsAtLocation( /* x, y, z, flags, realm */ )
×
4616
{
4617
  Core::Pos2d pos;
×
4618
  int z, flags;
4619
  Realms::Realm* realm;
4620

4621
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4622
       getParam( 3, flags ) )
×
4623
  {
4624
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4625

4626
    if ( !( flags & ITEMS_IGNORE_STATICS ) )
×
4627
    {
4628
      Plib::StaticEntryList slist;
×
4629
      realm->getstatics( slist, pos );
×
4630

4631
      for ( unsigned i = 0; i < slist.size(); ++i )
×
4632
      {
4633
        if ( ( z == LIST_IGNORE_Z ) || ( slist[i].z == z ) )
×
4634
        {
4635
          std::unique_ptr<BStruct> arr( new BStruct );
×
4636
          arr->addMember( "x", new BLong( pos.x() ) );
×
4637
          arr->addMember( "y", new BLong( pos.y() ) );
×
4638
          arr->addMember( "z", new BLong( slist[i].z ) );
×
4639
          arr->addMember( "objtype", new BLong( slist[i].objtype ) );
×
4640
          arr->addMember( "hue", new BLong( slist[i].hue ) );
×
4641
          newarr->addElement( arr.release() );
×
4642
        }
×
4643
      }
4644
    }
×
4645

4646
    if ( !( flags & ITEMS_IGNORE_MULTIS ) )
×
4647
    {
4648
      Plib::StaticList mlist;
×
4649
      realm->readmultis( mlist, pos );
×
4650

4651
      for ( unsigned i = 0; i < mlist.size(); ++i )
×
4652
      {
4653
        if ( ( z == LIST_IGNORE_Z ) || ( mlist[i].z == z ) )
×
4654
        {
4655
          std::unique_ptr<BStruct> arr( new BStruct );
×
4656
          arr->addMember( "x", new BLong( pos.x() ) );
×
4657
          arr->addMember( "y", new BLong( pos.y() ) );
×
4658
          arr->addMember( "z", new BLong( mlist[i].z ) );
×
4659
          arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
×
4660
          newarr->addElement( arr.release() );
×
4661
        }
×
4662
      }
4663
    }
×
4664

4665
    return newarr.release();
×
4666
  }
×
4667
  else
4668
    return new BError( "Invalid parameter" );
×
4669
}
4670

4671
BObjectImp* UOExecutorModule::mf_ListStaticsNearLocation( /* x, y, z, range, flags, realm */ )
×
4672
{
4673
  Core::Pos2d pos;
×
4674
  int z, flags;
4675
  short range;
4676
  Realms::Realm* realm;
4677

4678
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4679
       getParam( 3, range ) && getParam( 4, flags ) )
×
4680
  {
4681
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4682
    Core::Vec2d radius( range, range );
×
4683
    Core::Range2d area( pos - radius, pos + radius, realm );
×
4684
    for ( const auto& tile : area )
×
4685
    {
4686
      if ( !( flags & ITEMS_IGNORE_STATICS ) )
×
4687
      {
4688
        Plib::StaticEntryList slist;
×
4689
        realm->getstatics( slist, tile );
×
4690

4691
        for ( unsigned i = 0; i < slist.size(); ++i )
×
4692
        {
4693
          if ( ( z == LIST_IGNORE_Z ) || ( abs( slist[i].z - z ) < CONST_DEFAULT_ZRANGE ) )
×
4694
          {
4695
            std::unique_ptr<BStruct> arr( new BStruct );
×
4696
            arr->addMember( "x", new BLong( tile.x() ) );
×
4697
            arr->addMember( "y", new BLong( tile.y() ) );
×
4698
            arr->addMember( "z", new BLong( slist[i].z ) );
×
4699
            arr->addMember( "objtype", new BLong( slist[i].objtype ) );
×
4700
            arr->addMember( "hue", new BLong( slist[i].hue ) );
×
4701
            newarr->addElement( arr.release() );
×
4702
          }
×
4703
        }
4704
      }
×
4705

4706
      if ( !( flags & ITEMS_IGNORE_MULTIS ) )
×
4707
      {
4708
        Plib::StaticList mlist;
×
4709
        realm->readmultis( mlist, tile );
×
4710

4711
        for ( unsigned i = 0; i < mlist.size(); ++i )
×
4712
        {
4713
          if ( ( z == LIST_IGNORE_Z ) || ( abs( mlist[i].z - z ) < CONST_DEFAULT_ZRANGE ) )
×
4714
          {
4715
            std::unique_ptr<BStruct> arr( new BStruct );
×
4716
            arr->addMember( "x", new BLong( tile.x() ) );
×
4717
            arr->addMember( "y", new BLong( tile.y() ) );
×
4718
            arr->addMember( "z", new BLong( mlist[i].z ) );
×
4719
            arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
×
4720
            newarr->addElement( arr.release() );
×
4721
          }
×
4722
        }
4723
      }
×
4724
    }
4725

4726
    return newarr.release();
×
4727
  }
×
4728
  else
4729
    return new BError( "Invalid parameter" );
×
4730
}
4731

4732
//  Birdy :  (Notes on Pathfinding)
4733
//
4734
//  This implimentation of pathfinding is actually from a very nice stl based A*
4735
//  implimentation.  It will, within reason, succeed in finding a path in a wide
4736
//  variety of circumstance, including traversing stairs and multiple level
4737
//  structures.
4738
//
4739
//  Unfortunately, it, or my POL support classes implimenting it, seems to at times
4740
//  generate corrupted results in finding a path.  These corruptions take the form
4741
//  of basically looping within the result list, or linking in non-closed list nodes
4742
//  which may be on the open list and generate more looping or which may have even
4743
//  been deleted by the algorithm, generally causing an illegal reference and crash.
4744
//
4745
//  Thus far, the above senerio has only been replicated, though fairly reliably, in
4746
//  cases where there are many many(about 100) npc's trying to pathfind at the same time,
4747
//  in close proximity to one another.
4748
//
4749
//  "Looping" will, of course, cause the shard to hang while the pathfinding routine
4750
//  tries to build the child list for the solution set.  An illegal reference will
4751
//  cause the shard to crash most likely.  Neither is tollerable of course, so I have
4752
//  put in two safeguards against this.
4753
//
4754
//  Safeguard #1) As we go through the nodes in the solution, I make sure each of them
4755
//        is on the Closed List.  If not, the pathfind errors out with a
4756
//        "Solution Corrupt!" error.  This is an attempt to short circuit any
4757
//        solution containing erroneous nodes in it.
4758
//
4759
//  Safeguard #2) I keep a vector of the nodes in the solution as we go through them.
4760
//        Before adding each node to the vector, I search that vector for that
4761
//        node.  If that vector already contains the node, the pathfind errors
4762
//        out with a "Solution Corrupt!" error.  THis is an attempt to catch
4763
//        the cases where Closed List nodes have looped for some reason.
4764
//
4765
//  These two safeguards should not truly be necessary for this algorithm, and take up
4766
//  space and time to guard against.  Thus, finding out why these problems are occurring
4767
//  in the first place would be great, as we could fix that instead and remove these
4768
//  checks.  If necessary to live with long term, then the solution vector should probably
4769
//  be made into a hash table for quick lookups.
4770
//
4771
//  I have also implimented a sort blocking list that is supposed to represent those
4772
//  spots that mobiles occupy and are thus not walkable on.  This is only maintained if
4773
//  the mobilesBlock parameter passed from escript is true.  It would probably be good
4774
//  to make this a hash table.  It may be said that it is a good idea to pre-process all
4775
//  blocking spots on the map, but this would hinder the ability to manage 3d shifts, or
4776
//  probably be prohibitive if an attempt were made to check for traversal at all possible
4777
//  various z's.  So it may be that checking such at the time of the search for the path
4778
//  is ultimately the best means of handling this.
4779
//
4780
//  Other details that may be nice to have here might be a method for interacting with
4781
//  escript, letting escript tell the pathfinder to ignore doors, and then the pathfinder
4782
//  putting in the solution list an indication to escript that at the given node a door
4783
//  was encountered, so that AI can open the door before attempting to traverse that
4784
//  path.  Providing the user the ability to define an array of objects which are to be
4785
//  considered as blocking, or an array of objects which are to be ignored even if they
4786
//  are blocking might be nice as well.  The former could be easily implimented by
4787
//  adding to the mobile's blocking list.  The latter may be more difficult to do
4788
//  considering if the item blocks, the walk function will not let you traverse it, and
4789
//  it is the walk function that we use to accurately replicate the ability of the
4790
//  mobile to traverse from one place to the next.  Additional functionality of some
4791
//  sort may be necessary to do this, or making a copy of the walk function and putting
4792
//  in a special walk function strictly for pathfinding which can take this array into
4793
//  account.
4794
//
4795
//  For now, however, these concerns are all unaddressed.  I wish to see if this
4796
//  function works and is useful, and also would prefer to find some way to track down
4797
//  the corruption of the solution nodes before adding more complexity to the problem.
4798
//
4799
//  Other than the below function, the following files will be added to CVS to support
4800
//  it in the src module.  They are :
4801
//
4802
//  stlastar.h  --  virtually untouched by me really, it is the original author's
4803
//          implimentation, pretty nicely done and documented if you are
4804
//          interested in learning about A*.
4805
//
4806
//  fsa.h     --  a "fixed size allocation" module which I toy with enabling and
4807
//          disabling.  Using it is supposed to get you some speed, but it
4808
//          limits your maps because it will only allocate a certain # of
4809
//          nodes at once, and after that, it will return out of memory.
4810
//          Presently, it is disabled, but will be submitted to CVS for
4811
//          completion in case we wish to use it later.
4812
//
4813
//  uopathnode.h -- this is stricly my work, love it or hate it, it's pretty quick
4814
//          and dirty, and can stand to be cleaned up, optimized, and so forth.
4815
//          I have the name field and function in there to allow for some
4816
//          debugging, though the character array could probably be removed to
4817
//          make the nodes smaller.  I didn't find it mattered particularly, since
4818
//          these puppies are created and destroyed at a pretty good clip.
4819
//          It is this class that encapsulates the necessary functionality to
4820
//          make the otherwise fairly generic stlastar class work.
4821

4822
typedef Plib::AStarSearch<UOPathState> UOSearch;
4823

4824
BObjectImp* UOExecutorModule::mf_FindPath()
7✔
4825
{
4826
  Pos3d pos1, pos2;
7✔
4827
  Realms::Realm* realm;
4828
  const String* movemode_name;
4829

4830
  if ( !getPos3dParam( 0, 1, 2, &pos1 ) || !getPos3dParam( 3, 4, 5, &pos2 ) ||
14✔
4831
       !getRealmParam( 6, &realm ) || !getStringParam( 9, movemode_name ) )
14✔
4832
    return new BError( "Invalid parameter" );
×
4833
  if ( !pos1.in_range( pos2, settingsManager.ssopt.max_pathfind_range ) )
7✔
4834
    return new BError( "Beyond Max Range." );
×
4835

4836
  short theSkirt;
4837
  int flags;
4838

4839
  if ( !getParam( 7, flags ) )
7✔
4840
    flags = FP_IGNORE_MOBILES;
×
4841

4842
  if ( !getParam( 8, theSkirt ) )
7✔
4843
    theSkirt = 5;
×
4844

4845
  Plib::MOVEMODE movemode = Character::decode_movemode( movemode_name->value() );
7✔
4846
  if ( movemode == Plib::MOVEMODE_NONE )
7✔
4847
    return new BError( "Wrong movemode parameter" );
1✔
4848

4849
  if ( theSkirt < 0 )
6✔
4850
    theSkirt = 0;
×
4851

4852
  if ( !realm->valid( pos1.xy() ) )
6✔
4853
    return new BError( "Start Coordinates Invalid for Realm" );
×
4854
  if ( !realm->valid( pos2.xy() ) )
6✔
4855
    return new BError( "End Coordinates Invalid for Realm" );
×
4856

4857
  auto astarsearch = std::make_unique<UOSearch>();
6✔
4858

4859
  Range2d range( pos1.xy().min( pos2.xy() ) - Vec2d( theSkirt, theSkirt ),
6✔
4860
                 pos1.xy().max( pos2.xy() ) + Vec2d( theSkirt, theSkirt ), realm );
12✔
4861

4862
  if ( Plib::systemstate.config.loglevel >= 12 )
6✔
4863
  {
4864
    POLLOGLN( "[FindPath] Calling FindPath({}, {}, {}, {:#x}, {})", pos1, pos2, realm->name(),
×
4865
              flags, theSkirt );
4866
    POLLOGLN( "[FindPath]   search for Blockers inside {}", range );
×
4867
  }
4868

4869
  bool doors_block = ( flags & FP_IGNORE_DOORS ) ? false : true;
6✔
4870
  AStarParams params( range, doors_block, movemode, realm );
6✔
4871

4872
  if ( !( flags & FP_IGNORE_MOBILES ) )
6✔
4873
  {
4874
    WorldIterator<MobileFilter>::InBox( range, realm,
×
4875
                                        [&]( Mobile::Character* chr )
×
4876
                                        {
4877
                                          params.AddBlocker( chr->pos3d() );
×
4878

4879
                                          if ( Plib::systemstate.config.loglevel >= 12 )
×
4880
                                            POLLOGLN( "[FindPath]   add Blocker {} at {}",
×
4881
                                                      chr->name(), chr->pos() );
×
4882
                                        } );
×
4883
  }
4884

4885
  if ( Plib::systemstate.config.loglevel >= 12 )
6✔
4886
  {
4887
    POLLOGLN( "[FindPath]   use StartNode {}", pos1 );
×
4888
    POLLOGLN( "[FindPath]   use EndNode {}", pos2 );
×
4889
  }
4890

4891
  // Create a start state
4892
  UOPathState nodeStart( pos1, &params );
6✔
4893
  // Define the goal state
4894
  UOPathState nodeEnd( pos2, &params );
6✔
4895
  // Set Start and goal states
4896
  astarsearch->SetStartAndGoalStates( nodeStart, nodeEnd );
6✔
4897
  unsigned int SearchState;
4898
  do
4899
  {
4900
    SearchState = astarsearch->SearchStep();
28✔
4901
  } while ( SearchState == UOSearch::SEARCH_STATE_SEARCHING );
28✔
4902
  if ( SearchState == UOSearch::SEARCH_STATE_SUCCEEDED )
6✔
4903
  {
4904
    UOPathState* node = astarsearch->GetSolutionStart();
3✔
4905

4906
    auto nodeArray = std::make_unique<ObjArray>();
3✔
4907
    while ( ( node = astarsearch->GetSolutionNext() ) != nullptr )
12✔
4908
    {
4909
      auto nextStep = std::make_unique<BStruct>();
9✔
4910
      const auto& pos = node->position();
9✔
4911
      nextStep->addMember( "x", new BLong( pos.x() ) );
9✔
4912
      nextStep->addMember( "y", new BLong( pos.y() ) );
9✔
4913
      nextStep->addMember( "z", new BLong( pos.z() ) );
9✔
4914
      nodeArray->addElement( nextStep.release() );
9✔
4915
    }
9✔
4916
    astarsearch->FreeSolutionNodes();
3✔
4917
    return nodeArray.release();
3✔
4918
  }
3✔
4919
  else if ( SearchState == UOSearch::SEARCH_STATE_FAILED )
3✔
4920
  {
4921
    return new BError( "Failed to find a path." );
3✔
4922
  }
4923
  else if ( SearchState == UOSearch::SEARCH_STATE_OUT_OF_MEMORY )
×
4924
  {
4925
    return new BError( "Out of memory." );
×
4926
  }
4927
  else if ( SearchState == UOSearch::SEARCH_STATE_SOLUTION_CORRUPTED )
×
4928
  {
4929
    return new BError( "Solution Corrupted!" );
×
4930
  }
4931

4932
  return new BError( "Pathfind Error." );
×
4933
}
6✔
4934

4935

4936
BObjectImp* UOExecutorModule::mf_UseItem()
×
4937
{
4938
  Item* item;
4939
  Character* chr;
4940

4941
  if ( getItemParam( 0, item ) && getCharacterParam( 1, chr ) )
×
4942
  {
4943
    const ItemDesc& itemdesc = find_itemdesc( item->objtype_ );
×
4944

4945
    if ( itemdesc.requires_attention && ( chr->skill_ex_active() || chr->casting_spell() ) )
×
4946
    {
4947
      if ( chr->client != nullptr )
×
4948
      {
4949
        send_sysmessage( chr->client, "I am already doing something else." );
×
4950
        return new BError( "Character busy." );
×
4951
        ;
4952
      }
4953
    }
4954

4955
    if ( itemdesc.requires_attention && chr->hidden() )
×
4956
      chr->unhide();
×
4957

4958
    ref_ptr<EScriptProgram> prog;
×
4959

4960
    std::string on_use_script = item->get_use_script_name();
×
4961

4962
    if ( !on_use_script.empty() )
×
4963
    {
4964
      ScriptDef sd( on_use_script, nullptr, "" );
×
4965
      prog = find_script2( sd,
×
4966
                           true,  // complain if not found
4967
                           Plib::systemstate.config.cache_interactive_scripts );
×
4968
    }
×
4969
    else if ( !itemdesc.on_use_script.empty() )
×
4970
    {
4971
      prog = find_script2( itemdesc.on_use_script, true,
×
4972
                           Plib::systemstate.config.cache_interactive_scripts );
×
4973
    }
4974

4975
    if ( prog.get() != nullptr )
×
4976
    {
4977
      if ( chr->start_itemuse_script( prog.get(), item, itemdesc.requires_attention ) )
×
4978
        return new BLong( 1 );
×
4979
      else
4980
        return new BError( "Failed to start script!" );
×
4981
      // else log the fact?
4982
    }
4983
    else
4984
    {
4985
      if ( chr->client != nullptr )
×
4986
        item->builtin_on_use( chr->client );
×
4987
      return new BLong( 0 );
×
4988
    }
4989
  }
×
4990
  else
4991
  {
4992
    return new BError( "Invalid parameter" );
×
4993
  }
4994
}
4995

4996
BObjectImp* UOExecutorModule::mf_FindSubstance()
3✔
4997
{
4998
  UContainer::Contents substanceVector;
3✔
4999

5000
  Item* cont_item;
5001
  unsigned int objtype;
5002
  int amount;
5003

5004
  if ( getItemParam( 0, cont_item ) && getObjtypeParam( 1, objtype ) && getParam( 2, amount ) )
3✔
5005
  {
5006
    bool makeInUse;
5007
    int flags;
5008
    if ( !getParam( 3, makeInUse ) )
3✔
5009
      makeInUse = false;
×
5010

5011
    if ( !getParam( 4, flags ) )
3✔
5012
      flags = 0;
×
5013

5014
    if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
3✔
5015
      return new BError( "That is not a container" );
×
5016
    if ( amount < 0 )
3✔
5017
      return new BError( "Amount cannot be negative" );
×
5018

5019
    UContainer* cont = static_cast<UContainer*>( cont_item );
3✔
5020
    int amthave = cont->find_sumof_objtype_noninuse( objtype, amount, substanceVector, flags );
3✔
5021
    if ( ( amthave < amount ) && ( !( flags & FINDSUBSTANCE_FIND_ALL ) ) )
3✔
5022
      return new BError( "Not enough of that substance in container" );
1✔
5023
    else
5024
    {
5025
      std::unique_ptr<ObjArray> theArray( new ObjArray() );
2✔
5026
      Item* item;
5027

5028
      for ( UContainer::Contents::const_iterator itr = substanceVector.begin();
2✔
5029
            itr != substanceVector.end(); ++itr )
4✔
5030
      {
5031
        item = *itr;
2✔
5032
        if ( item != nullptr )
2✔
5033
        {
5034
          if ( ( makeInUse ) && ( !item->inuse() ) )
2✔
5035
          {
5036
            item->inuse( true );
2✔
5037
            reserved_items_.push_back( ItemRef( item ) );
2✔
5038
          }
5039
          theArray->addElement( new EItemRefObjImp( item ) );
2✔
5040
        }
5041
      }
5042
      return theArray.release();
2✔
5043
    }
2✔
5044
  }
5045
  else
5046
  {
5047
    return new BError( "Invalid parameter type" );
×
5048
  }
5049
}
3✔
5050

5051
BObjectImp* UOExecutorModule::mf_IsStackable()
×
5052
{
5053
  Item* item1;
5054
  Item* item2;
5055

5056
  if ( !( getItemParam( 0, item1 ) && getItemParam( 1, item2 ) ) )
×
5057
  {
5058
    return new BError( "Invalid parameter type" );
×
5059
  }
5060

5061
  if ( item1->objtype_ != item2->objtype_ )
×
5062
    return new BError( "Objtypes differs" );
×
5063
  if ( !item1->stackable() )
×
5064
    return new BError( "That item type is not stackable." );
×
5065

5066
  if ( item1->can_add_to_self( *item2, false ) )
×
5067
    return new BLong( 1 );
×
5068
  else
5069
    return new BError( "Failed to stack" );
×
5070
}
5071

5072
BObjectImp* UOExecutorModule::mf_UpdateMobile()
×
5073
{
5074
  Character* chr;
5075
  int flags;
5076

5077
  if ( getCharacterParam( 0, chr ) && getParam( 1, flags ) )
×
5078
  {
5079
    if ( flags == 1 )
×
5080
    {
5081
      if ( ( !chr->isa( UOBJ_CLASS::CLASS_NPC ) ) && ( chr->client ) )  // no npc and active client
×
5082
        send_owncreate( chr->client, chr );                             // inform self
×
5083
      if ( ( chr->isa( UOBJ_CLASS::CLASS_NPC ) ) || ( chr->client ) )   // npc or active client
×
5084
        send_create_mobile_to_nearby_cansee( chr );                     // inform other
×
5085
      else
5086
        return new BError( "Mobile is offline" );
×
5087
    }
5088
    else
5089
    {
5090
      if ( ( !chr->isa( UOBJ_CLASS::CLASS_NPC ) ) && ( chr->client ) )  // no npc and active client
×
5091
        send_move( chr->client, chr );                                  // inform self
×
5092
      if ( ( chr->isa( UOBJ_CLASS::CLASS_NPC ) ) || ( chr->client ) )   // npc or active client
×
5093
        send_move_mobile_to_nearby_cansee( chr );                       // inform other
×
5094
      else
5095
        return new BError( "Mobile is offline" );
×
5096
    }
5097
    return new BLong( 1 );
×
5098
  }
5099
  return new BError( "Invalid parameter type" );
×
5100
}
5101

5102
BObjectImp* UOExecutorModule::mf_UpdateItem()
×
5103
{
5104
  Item* item;
5105

5106
  if ( getItemParam( 0, item ) )
×
5107
  {
5108
    send_item_to_inrange( item );
×
5109
    return new BLong( 1 );
×
5110
  }
5111
  else
5112
  {
5113
    return new BError( "Invalid parameter type" );
×
5114
  }
5115
}
5116

5117

5118
BObjectImp* UOExecutorModule::mf_CanWalk(
×
5119
    /*movemode, x1, y1, z1, x2_or_dir, y2 := -1, realm := DEF*/ )
5120
{
5121
  Pos4d p;
×
5122
  int x2_or_dir, y2_;
5123
  const String* movemode_name;
5124

5125
  if ( ( getStringParam( 0, movemode_name ) ) && ( getPos4dParam( 1, 2, 3, 6, &p ) ) &&
×
5126
       ( getParam( 4, x2_or_dir ) ) && ( getParam( 5, y2_ ) ) )
×
5127
  {
5128
    Plib::MOVEMODE movemode = Character::decode_movemode( movemode_name->value() );
×
5129

5130
    Core::UFACING dir;
5131
    if ( y2_ == -1 )
×
5132
      dir = static_cast<Core::UFACING>( x2_or_dir & 0x7 );
×
5133
    else
5134
    {
5135
      auto p1 = Pos2d( static_cast<u16>( x2_or_dir ), static_cast<u16>( y2_ ) );
×
5136
      if ( !p.realm()->valid( p1 ) )
×
5137
        return new BError( "Invalid coordinates for realm." );
×
5138

5139
      dir = p.xy().direction_toward( p1 );
×
5140
    }
5141

5142
    if ( dir & 1 )  // check if diagonal movement is allowed
×
5143
    {
5144
      short new_z;
5145
      u8 tmp_facing = ( dir + 1 ) & 0x7;
×
5146
      auto tmp_pos = p.move( static_cast<Core::UFACING>( tmp_facing ) );
×
5147

5148
      // needs to save because if only one direction is blocked, it shouldn't block ;)
5149
      bool walk1 = p.realm()->walkheight( tmp_pos.xy(), tmp_pos.z(), &new_z, nullptr, nullptr, true,
×
5150
                                          movemode, nullptr );
5151

5152
      tmp_facing = ( dir - 1 ) & 0x7;
×
5153
      tmp_pos = p.move( static_cast<Core::UFACING>( tmp_facing ) );
×
5154

5155
      if ( !walk1 && !p.realm()->walkheight( tmp_pos.xy(), tmp_pos.z(), &new_z, nullptr, nullptr,
×
5156
                                             true, movemode, nullptr ) )
5157
        return new BError( "Cannot walk there" );
×
5158
    }
5159

5160
    p.move_to( dir );
×
5161
    short newz;
5162

5163
    if ( !p.realm()->walkheight( p.xy(), p.z(), &newz, nullptr, nullptr, true, movemode, nullptr ) )
×
5164
      return new BError( "Cannot walk there" );
×
5165

5166
    return new BLong( newz );
×
5167
  }
5168
  else
5169
    return new BError( "Invalid parameter" );
×
5170
}
5171

5172

5173
BObjectImp* UOExecutorModule::mf_SendCharProfile(
×
5174
    /*chr, of_who, title, uneditable_text := array, editable_text := array*/ )
5175
{
5176
  Character *chr, *of_who;
5177
  const String* title;
5178
  const String* uText;
5179
  const String* eText;
5180

5181
  if ( getCharacterParam( 0, chr ) && getCharacterParam( 1, of_who ) &&
×
5182
       getStringParam( 2, title ) && getUnicodeStringParam( 3, uText ) &&
×
5183
       getUnicodeStringParam( 4, eText ) )
×
5184
  {
5185
    if ( chr->logged_in() && of_who->logged_in() )
×
5186
    {
5187
      if ( uText->length() > SPEECH_MAX_LEN || eText->length() > SPEECH_MAX_LEN )
×
5188
        return new BError( "Text exceeds maximum size." );
×
5189

5190
      sendCharProfile( chr, of_who, title->value(), uText->value(), eText->value() );
×
5191
      return new BLong( 1 );
×
5192
    }
5193
    else
5194
      return new BError( "Mobile must be online." );
×
5195
  }
5196
  else
5197
    return new BError( "Invalid parameter type" );
×
5198
}
5199

5200
BObjectImp* UOExecutorModule::mf_SendOverallSeason( /*season_id, playsound := 1*/ )
×
5201
{
5202
  int season_id, playsound;
5203

5204
  if ( getParam( 0, season_id ) && getParam( 1, playsound ) )
×
5205
  {
5206
    if ( season_id < 0 || season_id > 4 )
×
5207
      return new BError( "Invalid season id" );
×
5208

5209
    Network::PktHelper::PacketOut<Network::PktOut_BC> msg;
×
5210
    msg->Write<u8>( static_cast<u16>( season_id ) );
×
5211
    msg->Write<u8>( static_cast<u16>( playsound ) );
×
5212

5213
    for ( Clients::iterator itr = networkManager.clients.begin(),
×
5214
                            end = networkManager.clients.end();
×
5215
          itr != end; ++itr )
×
5216
    {
5217
      Network::Client* client = *itr;
×
5218
      if ( !client->chr || !client->chr->logged_in() || client->getversiondetail().major < 1 )
×
5219
        continue;
×
5220
      msg.Send( client );
×
5221
    }
5222
    return new BLong( 1 );
×
5223
  }
×
5224
  else
5225
    return new BError( "Invalid parameter" );
×
5226
}
5227

5228
// bresenham circle calculates the coords based on center coords and radius
5229
BObjectImp* UOExecutorModule::mf_GetMidpointCircleCoords( /* xcenter, ycenter, radius */ )
×
5230
{
5231
  int xcenter, ycenter, radius;
5232
  if ( !( getParam( 0, xcenter ) && getParam( 1, ycenter ) && getParam( 2, radius ) ) )
×
5233
  {
5234
    return new BError( "Invalid parameter type" );
×
5235
  }
5236
  std::unique_ptr<ObjArray> coords( new ObjArray );
×
5237

5238
  std::vector<std::tuple<int, int>> points;
×
5239
  auto add_point = [&coords]( int x, int y )
×
5240
  {
5241
    std::unique_ptr<BStruct> point( new BStruct );
×
5242
    point->addMember( "x", new BLong( x ) );
×
5243
    point->addMember( "y", new BLong( y ) );
×
5244
    coords->addElement( point.release() );
×
5245
  };
×
5246

5247
  if ( radius == 0 )
×
5248
  {
5249
    add_point( xcenter, ycenter );
×
5250
    return coords.release();
×
5251
  }
5252

5253
  // inside of each quadrant the points are sorted,
5254
  // store the quadrands in seperated vectors and merge them later
5255
  // -> automatically sorted
5256
  std::vector<std::tuple<int, int>> q1, q2, q3, q4;
×
5257
  int x = -radius, y = 0, err = 2 - 2 * radius; /* II. Quadrant */
×
5258
  do
5259
  {
5260
    q1.emplace_back( xcenter - x, ycenter + y ); /*   I. Quadrant */
×
5261
    q2.emplace_back( xcenter - y, ycenter - x ); /*  II. Quadrant */
×
5262
    q3.emplace_back( xcenter + x, ycenter - y ); /* III. Quadrant */
×
5263
    q4.emplace_back( xcenter + y, ycenter + x ); /*  IV. Quadrant */
×
5264
    radius = err;
×
5265
    if ( radius <= y )
×
5266
      err += ++y * 2 + 1;
×
5267
    if ( radius > x || err > y )
×
5268
      err += ++x * 2 + 1;
×
5269
  } while ( x < 0 );
×
5270

5271
  for ( const auto& p : q1 )
×
5272
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5273
  for ( const auto& p : q2 )
×
5274
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5275
  for ( const auto& p : q3 )
×
5276
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5277
  for ( const auto& p : q4 )
×
5278
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5279

5280
  return coords.release();
×
5281
}
×
5282

5283
size_t UOExecutorModule::sizeEstimate() const
14✔
5284
{
5285
  size_t size = sizeof( *this ) + Clib::memsize( reserved_items_ );
14✔
5286
  return size;
14✔
5287
}
5288
}  // namespace Module
5289
}  // namespace Pol
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc