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

polserver / polserver / 21008991693

14 Jan 2026 08:35PM UTC coverage: 60.508% (+0.001%) from 60.507%
21008991693

push

github

web-flow
ClangTidy "modernize-use-override" (#851)

* let clang-tidy do its thing

* Automated clang-tidy change: modernize-use-override

* compile test

---------

Co-authored-by: Clang Tidy <clang-tidy@users.noreply.github.com>

82 of 153 new or added lines in 40 files covered. (53.59%)

3 existing lines in 3 files now uncovered.

44461 of 73479 relevant lines covered (60.51%)

512457.07 hits per line

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

33.89
/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 ) {}
×
NEW
192
  const char* typeOf() const override { return "MenuRef"; }
×
NEW
193
  u8 typeOfInt() const override { return OTMenuRef; }
×
NEW
194
  BObjectImp* copy() const override { return new EMenuObjImp( value() ); }
×
195
};
196

197

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

219
UOExecutorModule::~UOExecutorModule()
562✔
220
{
221
  auto& uoex = uoexec();
281✔
222
  while ( !reserved_items_.empty() )
281✔
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 )
281✔
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 )
281✔
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 )
281✔
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 )
281✔
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 )
281✔
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 )
281✔
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 )
281✔
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 )
281✔
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 )
281✔
281
  {
282
    passert( attached_chr_->script_ex == &uoex );
×
283
    attached_chr_->script_ex = nullptr;
×
284
    attached_chr_ = nullptr;
×
285
  }
286
  if ( attached_item_.get() )
281✔
287
  {
288
    attached_item_->process( nullptr );
20✔
289
    attached_item_.clear();
20✔
290
  }
291
  if ( registered_for_speech_events )
281✔
292
  {
293
    ListenPoint::deregister_from_speech_events( &uoex );
1✔
294
  }
295
}
562✔
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,161✔
337
{
338
  return ( objtype >= UOBJ_ITEM__LOWEST && objtype <= Plib::systemstate.config.max_objtype ) &&
6,161✔
339
         amount > 0 && amount <= 60000L;
12,322✔
340
}
341

342
BObjectImp* _create_item_in_container( UContainer* cont, const ItemDesc* descriptor,
40✔
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 )
40✔
347
  {
348
    for ( UContainer::const_iterator itr = cont->begin(); itr != cont->end(); ++itr )
33✔
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 )
13✔
407
  {
408
    return new BError( "That item is not stackable.  Create one at a time." );
×
409
  }
410

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

418
    if ( cont->can_add( *item ) )
37✔
419
    {
420
      if ( !descriptor->create_script.empty() )
36✔
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();
36✔
442
      if ( !cont->can_add_to_slot( slotIndex ) )
36✔
443
      {
444
        item->destroy();
×
445
        return new BError( "No slots available in this container" );
×
446
      }
447
      if ( !item->slot_index( slotIndex ) )
36✔
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();
36✔
455
      if ( chr_owner == nullptr )
36✔
456
        if ( uoemod != nullptr )
23✔
457
          chr_owner = uoemod->controller_.get();
23✔
458

459
      if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_CREATED, item ) )
36✔
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
36✔
466
      {
467
        return new BError( "Item was destroyed in CanInsert Script" );
×
468
      }
469

470
      if ( !pos || !cont->is_legal_posn( pos.value() ) )
36✔
471
        pos = cont->get_random_location();
32✔
472
      cont->add( item, pos.value() );
36✔
473

474
      update_item_to_inrange( item );
36✔
475
      // DAVE added this 11/17, refresh owner's weight on item insert
476
      UpdateCharacterWeight( item );
36✔
477

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

484
      return new EItemRefObjImp( item );
36✔
485
    }
486
    else
487
    {
488
      item->destroy();
1✔
489
      return new BError( "That container is full" );
1✔
490
    }
491
  }
37✔
492
  else
493
  {
494
    return new BError( "Failed to create that item type" );
×
495
  }
496
}
497

498
BObjectImp* UOExecutorModule::mf_CreateItemInContainer()
36✔
499
{
500
  Item* item;
501
  const ItemDesc* descriptor;
502
  int amount;
503
  int px;
504
  int py;
505

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

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

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

560

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

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

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

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

619
  return new BLong( 1 );
×
620
}
621

622

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

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

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

646
  return Core::open_trade_window( chr->client, chr2 );
×
647
}
648

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

659
  Core::cancel_trade( chr );
×
660
  return new BLong( 1 );
×
661
}
662

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

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

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

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

712

713
BObjectImp* UOExecutorModule::mf_SendSysMessage()
×
714
{
715
  Character* chr;
716
  const String* ptext;
717
  unsigned short font;
718
  unsigned short color;
719

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

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

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

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

787
// const int TGTOPT_NOCHECK_LOS = 0x0000; // currently unused
788
const int TGTOPT_CHECK_LOS = 0x0001;
789
const int TGTOPT_HARMFUL = 0x0002;
790
const int TGTOPT_HELPFUL = 0x0004;
791
const int TGTOPT_ALLOW_NONLOCAL = 0x0008;
792

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

822

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

839
  if ( !getParam( 1, target_options ) )
3✔
840
    target_options = TGTOPT_CHECK_LOS;
×
841

842
  PKTBI_6C::CURSOR_TYPE crstype;
843

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

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

861
  TargetCursor* tgt_cursor = nullptr;
3✔
862

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

888
  tgt_cursor->send_object_cursor( chr->client, crstype );
3✔
889

890
  chr->client->gd->target_cursor_uoemod = this;
3✔
891
  target_cursor_chr = chr;
3✔
892

893
  return new BLong( 0 );
3✔
894
}
895

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

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

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

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

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

966
      uoex.ValueStack.back().set( new BObject( arr ) );
1✔
967
    }
968

969
    uoex.revive();
1✔
970
    chr->client->gd->target_cursor_uoemod->target_cursor_chr = nullptr;
1✔
971
    chr->client->gd->target_cursor_uoemod = nullptr;
1✔
972
  }
973
}
1✔
974

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

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

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

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

1019

1020
  if ( !chr->has_active_client() )
×
1021
  {
1022
    return new BError( "No client attached" );
×
1023
  }
1024

1025
  if ( chr->target_cursor_busy() )
×
1026
  {
1027
    return new BError( "Client busy with another target cursor" );
×
1028
  }
1029

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

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

1046
  chr->client->gd->target_cursor_uoemod = this;
×
1047
  target_cursor_chr = chr;
×
1048

1049
  gamestate.target_cursors.multi_placement_cursor.send_placemulti(
×
1050
      chr->client, objtype, flags, (s16)xoffset, (s16)yoffset, hue );
1051

1052
  return new BLong( 0 );
×
1053
}
1054

1055
BObjectImp* UOExecutorModule::mf_GetObjType()
×
1056
{
1057
  Item* item;
1058
  Character* chr;
1059

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

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

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

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

1096
BObjectImp* UOExecutorModule::mf_GetAmount()
×
1097
{
1098
  Item* item;
1099

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

1110

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

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

1143
BObjectImp* _complete_create_item_at_location( Item* item, const Core::Pos4d& pos )
6,121✔
1144
{
1145
  item->setposition( pos );
6,121✔
1146

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

1163
  update_item_to_inrange( item );
6,121✔
1164
  add_item_to_world( item );
6,121✔
1165
  register_with_supporting_multi( item );
6,121✔
1166
  return new EItemRefObjImp( item );
6,121✔
1167
}
1168

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

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

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

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

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

1236
  return Multi::UMulti::scripted_create( *descriptor, pos, flags );
34✔
1237
}
1238

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

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

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

1308
  Clib::ConfigElem elem;
74✔
1309
  bool found = FindNpcTemplate( tmplname->data(), elem );
74✔
1310

1311
  if ( !found )
74✔
1312
  {
1313
    return new BError( "NPC template '" + tmplname->value() + "' not found" );
×
1314
  }
1315
  Plib::MOVEMODE movemode = Character::decode_movemode( elem.read_string( "MoveMode", "L" ) );
74✔
1316

1317
  short newz;
1318
  Multi::UMulti* dummy_multi = nullptr;
74✔
1319
  Item* dummy_walkon = nullptr;
74✔
1320
  if ( !pos.realm()->walkheight( pos.xy(), pos.z(), &newz, &dummy_multi, &dummy_walkon, true,
74✔
1321
                                 movemode ) )
1322
  {
1323
    if ( !forceLocation )
2✔
1324
      return new BError( "Not a valid location for an NPC!" );
×
1325
  }
1326
  if ( !forceLocation )
74✔
1327
    pos.z( Clib::clamp_convert<s8>( newz ) );
70✔
1328

1329

1330
  NpcRef npc;
74✔
1331
  // readProperties can throw, if stuff is missing.
1332
  try
1333
  {
1334
    npc.set( new NPC( elem.remove_ushort( "OBJTYPE" ), elem ) );
74✔
1335
    npc->set_dirty();
74✔
1336
    elem.clear_prop( "Serial" );
74✔
1337
    elem.clear_prop( "X" );
74✔
1338
    elem.clear_prop( "Y" );
74✔
1339
    elem.clear_prop( "Z" );
74✔
1340

1341
    elem.add_prop( "Serial", GetNextSerialNumber() );
74✔
1342
    // FIXME sanity check
1343
    elem.add_prop( "X", pos.x() );
74✔
1344
    elem.add_prop( "Y", pos.y() );
74✔
1345
    elem.add_prop( "Z", (s16)pos.z() );
74✔
1346
    elem.add_prop( "Realm", pos.realm()->name() );
74✔
1347
    if ( custom_struct != nullptr )
74✔
1348
      replace_properties( elem, custom_struct );
6✔
1349
    npc->readPropertiesForNewNPC( elem );
74✔
1350
    ////HASH
1351
    objStorageManager.objecthash.Insert( npc.get() );
74✔
1352
    ////
1353

1354

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

1397

1398
BObjectImp* UOExecutorModule::mf_SubtractAmount()
×
1399
{
1400
  Item* item;
1401
  unsigned short amount;
1402

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

1418
BObjectImp* UOExecutorModule::mf_AddAmount()
×
1419
{
1420
  Item* item;
1421
  unsigned short amount;
1422

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

1438
    unsigned short newamount = item->getamount();
×
1439
    newamount += amount;
×
1440
    item->setamount( newamount );
×
1441
    update_item_to_inrange( item );
×
1442

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

1446
    return new EItemRefObjImp( item );
×
1447
  }
1448
  else
1449
  {
1450
    return new BError( "Invalid parameter type" );
×
1451
  }
1452
}
1453

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

1469
    UACTION action = static_cast<UACTION>( actionval );
×
1470
    send_action_to_inrange(
×
1471
        chr, action, framecount, repeatcount, static_cast<DIRECTION_FLAG_OLD>( backward ),
1472
        static_cast<REPEAT_FLAG_OLD>( repeatflag ), static_cast<unsigned char>( delay ) );
1473
    return new BLong( 1 );
×
1474
  }
1475
  else
1476
  {
1477
    return new BError( "Invalid parameter" );
×
1478
  }
1479
}
1480

1481
BObjectImp* UOExecutorModule::mf_PlaySoundEffect()
×
1482
{
1483
  UObject* chr;
1484
  int effect;
1485
  if ( getUObjectParam( 0, chr ) && getParam( 1, effect ) )
×
1486
  {
1487
    play_sound_effect( chr, static_cast<u16>( effect ) );
×
1488
    return new BLong( 1 );
×
1489
  }
1490
  else
1491
  {
1492
    return new BError( "Invalid parameter" );
×
1493
  }
1494
}
1495

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

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

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

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

1569
bool UOExecutorModule::getDynamicMenuParam( unsigned param, Menu*& menu )
×
1570
{
1571
  BApplicObjBase* aob = getApplicObjParam( param, &menu_type );
×
1572
  if ( aob != nullptr )
×
1573
  {
1574
    EMenuObjImp* menu_imp = static_cast<EMenuObjImp*>( aob );
×
1575
    menu = &menu_imp->value();
×
1576
    return true;
×
1577
  }
1578
  else
1579
  {
1580
    return false;
×
1581
  }
1582
}
1583

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

1607
BObjectImp* UOExecutorModule::mf_SelectMenuItem2()
×
1608
{
1609
  Character* chr;
1610
  Menu* menu;
1611

1612
  if ( !getCharacterParam( 0, chr ) || !getStaticOrDynamicMenuParam( 1, menu ) ||
×
1613
       ( chr->client->gd->menu_selection_uoemod != nullptr ) )
×
1614
  {
1615
    return new BError( "Invalid parameter" );
×
1616
  }
1617

1618
  if ( menu == nullptr || !chr->has_active_client() || menu->menuitems_.empty() )
×
1619
  {
1620
    return new BError( "Client is busy, or menu is empty" );
×
1621
  }
1622

1623
  if ( !send_menu( chr->client, menu ) )
×
1624
  {
1625
    return new BError( "Menu too large" );
×
1626
  }
1627

1628
  if ( !uoexec().suspend() )
×
1629
  {
1630
    DEBUGLOGLN(
×
1631
        "Script Error in '{}' PC={}: \n"
1632
        "\tCall to function UO::SelectMenuItem():\n"
1633
        "\tThe execution of this script can't be blocked!",
1634
        scriptname(), exec.PC );
×
1635
    return new Bscript::BError( "Script can't be blocked" );
×
1636
  }
1637

1638
  chr->client->gd->menu = menu->getWeakPtr();
×
1639
  chr->client->gd->on_menu_selection = menu_selection_made;
×
1640
  chr->client->gd->menu_selection_uoemod = this;
×
1641
  menu_selection_chr = chr;
×
1642

1643
  return new BLong( 0 );
×
1644
}
1645

1646
void append_objtypes( ObjArray* objarr, Menu* menu )
×
1647
{
1648
  for ( unsigned i = 0; i < menu->menuitems_.size(); ++i )
×
1649
  {
1650
    MenuItem* mi = &menu->menuitems_[i];
×
1651

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

1668
BObjectImp* UOExecutorModule::mf_GetMenuObjTypes()
×
1669
{
1670
  Menu* menu;
1671
  if ( getStaticOrDynamicMenuParam( 0, menu ) )
×
1672
  {
1673
    std::unique_ptr<ObjArray> objarr( new ObjArray );
×
1674

1675
    append_objtypes( objarr.get(), menu );
×
1676

1677
    return objarr.release();
×
1678
  }
×
1679
  return new BLong( 0 );
×
1680
}
1681

1682
BObjectImp* UOExecutorModule::mf_ApplyConstraint()
×
1683
{
1684
  ObjArray* arr;
1685
  StoredConfigFile* cfile;
1686
  const String* propname_str;
1687
  int amthave;
1688

1689
  if ( getObjArrayParam( 0, arr ) && getStoredConfigFileParam( *this, 1, cfile ) &&
×
1690
       getStringParam( 2, propname_str ) && getParam( 3, amthave ) )
×
1691
  {
1692
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
1693

1694
    for ( unsigned i = 0; i < arr->ref_arr.size(); i++ )
×
1695
    {
1696
      BObjectRef& ref = arr->ref_arr[i];
×
1697

1698
      BObject* bo = ref.get();
×
1699
      if ( bo == nullptr )
×
1700
        continue;
×
1701
      if ( !bo->isa( BObjectImp::OTLong ) )
×
1702
        continue;
×
1703
      BLong* blong = bo->impptr<BLong>();
×
1704
      unsigned int objtype = static_cast<u32>( blong->value() );
×
1705

1706
      ref_ptr<StoredConfigElem> celem = cfile->findelem( objtype );
×
1707
      if ( celem.get() == nullptr )
×
1708
        continue;
×
1709

1710
      BObjectImp* propval = celem->getimp( propname_str->value() );
×
1711
      if ( propval == nullptr )
×
1712
        continue;
×
1713
      if ( !propval->isa( BObjectImp::OTLong ) )
×
1714
        continue;
×
1715
      BLong* amtreq = static_cast<BLong*>( propval );
×
1716
      if ( amtreq->value() > amthave )
×
1717
        continue;
×
1718
      newarr->addElement( new BLong( objtype ) );
×
1719
    }
×
1720

1721
    return newarr.release();
×
1722
  }
×
1723

1724
  return new UninitObject;
×
1725
}
1726

1727
BObjectImp* UOExecutorModule::mf_CreateMenu()
×
1728
{
1729
  const String* title;
1730
  if ( getStringParam( 0, title ) )
×
1731
  {
1732
    Menu temp;
×
1733
    temp.menu_id = 0;
×
1734
    Clib::stracpy( temp.title, title->data(), sizeof temp.title );
×
1735
    return new EMenuObjImp( temp );
×
1736
  }
×
1737
  return new BLong( 0 );
×
1738
}
1739

1740
BObjectImp* UOExecutorModule::mf_AddMenuItem()
×
1741
{
1742
  Menu* menu;
1743
  unsigned int objtype;
1744
  const String* text;
1745
  unsigned short color;
1746

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

1764
BObjectImp* UOExecutorModule::mf_GetObjProperty()
78✔
1765
{
1766
  UObject* uobj;
1767
  const String* propname_str;
1768
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
78✔
1769
  {
1770
    std::string val;
78✔
1771
    if ( uobj->getprop( propname_str->value(), val ) )
78✔
1772
    {
1773
      return BObjectImp::unpack( val.c_str() );
34✔
1774
    }
1775
    else
1776
    {
1777
      return new BError( "Property not found" );
44✔
1778
    }
1779
  }
78✔
1780
  else
1781
  {
1782
    return new BError( "Invalid parameter type" );
×
1783
  }
1784
}
1785

1786
BObjectImp* UOExecutorModule::mf_SetObjProperty()
9✔
1787
{
1788
  UObject* uobj;
1789
  const String* propname_str;
1790
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
9✔
1791
  {
1792
    BObjectImp* propval = getParamImp( 2 );
9✔
1793
    uobj->setprop( propname_str->value(), propval->pack() );
9✔
1794
    return new BLong( 1 );
9✔
1795
  }
1796
  else
1797
  {
1798
    return new BError( "Invalid parameter type" );
×
1799
  }
1800
}
1801

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

1836

1837
BObjectImp* UOExecutorModule::mf_GetGlobalProperty()
49✔
1838
{
1839
  const String* propname_str;
1840
  if ( getStringParam( 0, propname_str ) )
49✔
1841
  {
1842
    std::string val;
49✔
1843
    if ( gamestate.global_properties->getprop( propname_str->value(), val ) )
49✔
1844
    {
1845
      return BObjectImp::unpack( val.c_str() );
43✔
1846
    }
1847
    else
1848
    {
1849
      return new BError( "Property not found" );
6✔
1850
    }
1851
  }
49✔
1852
  else
1853
  {
1854
    return new BError( "Invalid parameter type" );
×
1855
  }
1856
}
1857

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

1873
BObjectImp* UOExecutorModule::mf_EraseGlobalProperty()
×
1874
{
1875
  const String* propname_str;
1876
  if ( getStringParam( 0, propname_str ) )
×
1877
  {
1878
    gamestate.global_properties->eraseprop( propname_str->value() );
×
1879
    return new BLong( 1 );
×
1880
  }
1881
  else
1882
  {
1883
    return new BError( "Invalid parameter type" );
×
1884
  }
1885
}
1886

1887
BObjectImp* UOExecutorModule::mf_GetGlobalPropertyNames()
×
1888
{
1889
  std::vector<std::string> propnames;
×
1890
  gamestate.global_properties->getpropnames( propnames );
×
1891
  std::unique_ptr<ObjArray> arr( new ObjArray );
×
1892
  for ( unsigned i = 0; i < propnames.size(); ++i )
×
1893
  {
1894
    arr->addElement( new String( propnames[i] ) );
×
1895
  }
1896
  return arr.release();
×
1897
}
×
1898

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

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

1947
// FIXME add/test:stationary
1948

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

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

1988

1989
BObjectImp* UOExecutorModule::mf_PlayMovingEffectEx()
×
1990
{
1991
  UObject* src;
1992
  UObject* dst;
1993
  unsigned short effect;
1994
  int speed;
1995
  int duration;
1996
  int hue;
1997
  int render;
1998
  int direction;
1999
  int explode;
2000
  unsigned short effect3d;
2001
  unsigned short effect3dexplode;
2002
  unsigned short effect3dsound;
2003

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

2025
BObjectImp* UOExecutorModule::mf_PlayMovingEffectXYZEx()
×
2026
{
2027
  Pos3d src, dst;
×
2028
  Realms::Realm* realm;
2029

2030
  unsigned short effect;
2031
  int speed;
2032
  int duration;
2033
  int hue;
2034
  int render;
2035
  int direction;
2036
  int explode;
2037
  unsigned short effect3d;
2038
  unsigned short effect3dexplode;
2039
  unsigned short effect3dsound;
2040

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

2062
BObjectImp* UOExecutorModule::mf_PlayObjectCenteredEffectEx()
×
2063
{
2064
  UObject* src;
2065
  unsigned short effect;
2066
  int speed;
2067
  int duration;
2068
  int hue;
2069
  int render;
2070
  int layer;
2071
  unsigned short effect3d;
2072

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

2090
BObjectImp* UOExecutorModule::mf_PlayStationaryEffectEx()
×
2091
{
2092
  Pos4d pos;
×
2093
  unsigned short effect;
2094
  int speed;
2095
  int duration;
2096
  int hue;
2097
  int render;
2098
  unsigned short effect3d;
2099

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

2115
BObjectImp* UOExecutorModule::mf_PlayLightningBoltEffect()
×
2116
{
2117
  UObject* center;
2118
  if ( getUObjectParam( 0, center ) )
×
2119
  {
2120
    play_lightning_bolt_effect( center );
×
2121
    return new BLong( 1 );
×
2122
  }
2123
  else
2124
  {
2125
    return new BLong( 0 );
×
2126
  }
2127
}
2128

2129
BObjectImp* UOExecutorModule::mf_ListItemsNearLocation( /* x, y, z, range, realm */ )
×
2130
{
2131
  Core::Pos2d pos;
×
2132
  int z;
2133
  u16 range;
2134
  Realms::Realm* realm;
2135

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

2151
    return newarr.release();
×
2152
  }
×
2153

2154
  return nullptr;
×
2155
}
2156

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

2171
BObjectImp* UOExecutorModule::mf_ListObjectsInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
×
2172
{
2173
  int z1, z2;
2174
  Pos2d p1, p2;
×
2175
  Realms::Realm* realm;
2176

2177
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
×
2178
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
×
2179
  {
2180
    return new BError( "Invalid parameter" );
×
2181
  }
2182
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
×
2183
  z1 = box.nw_b().z();
×
2184
  z2 = box.se_t().z();
×
2185

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

2204
  return newarr.release();
×
2205
}
×
2206

2207
BObjectImp* UOExecutorModule::mf_ListItemsInBoxOfObjType(
2✔
2208
    /* objtype, x1, y1, z1, x2, y2, z2, realm */ )
2209
{
2210
  unsigned int objtype;
2211
  int z1, z2;
2212
  Pos2d p1, p2;
2✔
2213
  Realms::Realm* realm;
2214

2215
  if ( !( getParam( 0, objtype ) && getPos2dParam( 1, 2, &p1 ) && getParam( 3, z1 ) &&
4✔
2216
          getPos2dParam( 4, 5, &p2 ) && getParam( 6, z2 ) && getRealmParam( 7, &realm ) ) )
2✔
2217
  {
2218
    return new BError( "Invalid parameter" );
×
2219
  }
2220
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
2✔
2221
  z1 = box.nw_b().z();
2✔
2222
  z2 = box.se_t().z();
2✔
2223

2224
  std::unique_ptr<ObjArray> newarr( new ObjArray );
2✔
2225
  WorldIterator<ItemFilter>::InBox(
4✔
2226
      box.range(), realm,
2✔
2227
      [&]( Items::Item* item )
2✔
2228
      {
2229
        if ( item->z() >= z1 && item->z() <= z2 && item->objtype_ == objtype )
40✔
2230
        {
2231
          newarr->addElement( item->make_ref() );
39✔
2232
        }
2233
      } );
40✔
2234

2235
  return newarr.release();
2✔
2236
}
2✔
2237

2238
BObjectImp* UOExecutorModule::mf_ListObjectsInBoxOfClass(
1✔
2239
    /* POL_Class, x1, y1, z1, x2, y2, z2, realm */ )
2240
{
2241
  unsigned int POL_Class;
2242
  int z1, z2;
2243
  Pos2d p1, p2;
1✔
2244
  Realms::Realm* realm;
2245

2246
  if ( !( getParam( 0, POL_Class ) && getPos2dParam( 1, 2, &p1 ) && getParam( 3, z1 ) &&
2✔
2247
          getPos2dParam( 4, 5, &p2 ) && getParam( 6, z2 ) && getRealmParam( 7, &realm ) ) )
1✔
2248
  {
2249
    return new BError( "Invalid parameter" );
×
2250
  }
2251
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
1✔
2252
  z1 = box.nw_b().z();
1✔
2253
  z2 = box.se_t().z();
1✔
2254

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

2275
  return newarr.release();
1✔
2276
}
1✔
2277

2278
BObjectImp* UOExecutorModule::mf_ListMobilesInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
2✔
2279
{
2280
  int z1, z2;
2281
  Pos2d p1, p2;
2✔
2282
  Realms::Realm* realm;
2283

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

2302
  return newarr.release();
2✔
2303
}
2✔
2304

2305
BObjectImp* UOExecutorModule::mf_ListMultisInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
4✔
2306
{
2307
  int z1, z2;
2308
  Pos2d p1, p2;
4✔
2309
  Realms::Realm* realm;
2310

2311
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
8✔
2312
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
4✔
2313
  {
2314
    return new BError( "Invalid parameter" );
×
2315
  }
2316

2317
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
4✔
2318
  z1 = box.nw_b().z();
4✔
2319
  z2 = box.se_t().z();
4✔
2320

2321
  std::unique_ptr<ObjArray> newarr( new ObjArray );
4✔
2322

2323
  // extend the coords to find the center item
2324
  // but only as parameter for the filter function
2325
  Vec2d urange{ Clib::clamp_convert<s16>( gamestate.max_update_range ),
4✔
2326
                Clib::clamp_convert<s16>( gamestate.max_update_range ) };
4✔
2327
  Range2d boxrange( box.nw() - urange, box.se() + urange, realm );
4✔
2328

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

2349
            if ( ( z1 <= absp.z() && absp.z() <= z2 ) ||  // bottom point lies between
8✔
2350
                 ( z1 <= top && top <= z2 ) ||            // top lies between
8✔
2351
                 ( top >= z2 && absp.z() <= z1 ) )        // spans
×
2352
            {
2353
              newarr->addElement( multi->make_ref() );
4✔
2354
              break;  // out of for
4✔
2355
            }
2356
          }
2357
        }
2358
      } );
2359

2360
  return newarr.release();
4✔
2361
}
4✔
2362

2363
BObjectImp* UOExecutorModule::mf_ListStaticsInBox( /* x1, y1, z1, x2, y2, z2, flags, realm */ )
1✔
2364
{
2365
  int z1, z2;
2366
  Pos2d p1, p2;
1✔
2367
  int flags;
2368
  Realms::Realm* realm;
2369

2370
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
2✔
2371
          getParam( 5, z2 ) && getParam( 6, flags ) && getRealmParam( 7, &realm ) ) )
1✔
2372
  {
2373
    return new BError( "Invalid parameter" );
×
2374
  }
2375

2376
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
1✔
2377
  z1 = box.nw_b().z();
1✔
2378
  z2 = box.se_t().z();
1✔
2379

2380
  std::unique_ptr<ObjArray> newarr( new ObjArray );
1✔
2381

2382
  for ( const auto& pos : box.range() )
5✔
2383
  {
2384
    if ( !( flags & ITEMS_IGNORE_STATICS ) )
2✔
2385
    {
2386
      Plib::StaticEntryList slist;
2✔
2387
      realm->getstatics( slist, pos );
2✔
2388

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

2404
    if ( !( flags & ITEMS_IGNORE_MULTIS ) )
2✔
2405
    {
2406
      Plib::StaticList mlist;
2✔
2407
      realm->readmultis( mlist, pos );
2✔
2408

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

2426
BObjectImp* UOExecutorModule::mf_ListItemsNearLocationOfType( /* x, y, z, range, objtype, realm */ )
27✔
2427
{
2428
  Core::Pos2d pos;
27✔
2429
  int z;
2430
  u16 range;
2431
  unsigned int objtype;
2432
  Realms::Realm* realm;
2433

2434
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
54✔
2435
       getParam( 3, range ) && getObjtypeParam( 4, objtype ) )
54✔
2436
  {
2437
    std::unique_ptr<ObjArray> newarr( new ObjArray );
27✔
2438
    WorldIterator<ItemFilter>::InRange(
27✔
2439
        pos, realm, range,
2440
        [&]( Items::Item* item )
27✔
2441
        {
2442
          if ( item->objtype_ == objtype && item->in_range( pos, range ) )
50✔
2443
          {
2444
            if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
37✔
2445
              newarr->addElement( item->make_ref() );
37✔
2446
          }
2447
        } );
50✔
2448

2449
    return newarr.release();
27✔
2450
  }
27✔
2451

2452
  return nullptr;
×
2453
}
2454

2455

2456
BObjectImp* UOExecutorModule::mf_ListItemsAtLocation( /* x, y, z, realm */ )
×
2457
{
2458
  Pos2d pos;
×
2459
  int z;
2460
  Realms::Realm* realm;
2461

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

2475
    return newarr.release();
×
2476
  }
×
2477

2478
  return nullptr;
×
2479
}
2480

2481
BObjectImp* UOExecutorModule::mf_ListGhostsNearLocation()
×
2482
{
2483
  Pos2d pos;
×
2484
  int z;
2485
  u16 range;
2486
  Realms::Realm* realm;
2487

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

2502
    return newarr.release();
×
2503
  }
×
2504
  else
2505
  {
2506
    return new BError( "Invalid parameter" );
×
2507
  }
2508
}
2509

2510
const unsigned LMBLEX_FLAG_NORMAL = 0x01;
2511
const unsigned LMBLEX_FLAG_HIDDEN = 0x02;
2512
const unsigned LMBLEX_FLAG_DEAD = 0x04;
2513
const unsigned LMBLEX_FLAG_CONCEALED = 0x8;
2514
const unsigned LMBLEX_FLAG_PLAYERS_ONLY = 0x10;
2515
const unsigned LMBLEX_FLAG_NPC_ONLY = 0x20;
2516

2517
BObjectImp* UOExecutorModule::mf_ListMobilesNearLocationEx( /* x, y, z, range, flags, realm */ )
×
2518
{
2519
  Pos2d pos;
×
2520
  Realms::Realm* realm;
2521
  int z, flags;
2522
  u16 range;
2523

2524
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2525
       getParam( 3, range ) && getParam( 4, flags ) )
×
2526
  {
2527
    bool inc_normal = ( flags & LMBLEX_FLAG_NORMAL ) ? true : false;
×
2528
    bool inc_hidden = ( flags & LMBLEX_FLAG_HIDDEN ) ? true : false;
×
2529
    bool inc_dead = ( flags & LMBLEX_FLAG_DEAD ) ? true : false;
×
2530
    bool inc_concealed = ( flags & LMBLEX_FLAG_CONCEALED ) ? true : false;
×
2531
    bool inc_players_only = ( flags & LMBLEX_FLAG_PLAYERS_ONLY ) ? true : false;
×
2532
    bool inc_npc_only = ( flags & LMBLEX_FLAG_NPC_ONLY ) ? true : false;
×
2533

2534
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2535

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

2560
    return newarr.release();
×
2561
  }
×
2562
  else
2563
  {
2564
    return new BError( "Invalid parameter" );
×
2565
  }
2566
}
2567

2568
BObjectImp* UOExecutorModule::mf_ListMobilesNearLocation( /* x, y, z, range, realm */ )
×
2569
{
2570
  Pos2d pos;
×
2571
  int z;
2572
  u16 range;
2573
  Realms::Realm* realm;
2574

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

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

2620
    return newarr.release();
×
2621
  }
×
2622
  else
2623
  {
2624
    return new BError( "Invalid parameter" );
×
2625
  }
2626
}
2627

2628
BObjectImp* UOExecutorModule::mf_ListOfflineMobilesInRealm( /*realm*/ )
×
2629
{
2630
  Realms::Realm* realm = nullptr;
×
2631

2632
  if ( getRealmParam( 0, &realm ) )
×
2633
  {
2634
    std::unique_ptr<ObjArray> newarr( new ObjArray() );
×
2635

2636
    for ( const auto& objitr : Pol::Core::objStorageManager.objecthash )
×
2637
    {
2638
      UObject* obj = objitr.second.get();
×
2639
      if ( !obj->ismobile() || obj->isa( UOBJ_CLASS::CLASS_NPC ) )
×
2640
        continue;
×
2641

2642
      Character* chr = static_cast<Character*>( obj );
×
2643
      if ( chr->logged_in() || chr->realm() != realm || chr->orphan() )
×
2644
        continue;
×
2645

2646
      newarr->addElement( new EOfflineCharacterRefObjImp( chr ) );
×
2647
    }
2648
    return newarr.release();
×
2649
  }
×
2650
  else
2651
  {
2652
    return new BError( "Invalid parameter" );
×
2653
  }
2654
}
2655

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

2668
    for ( auto& hostile : chr->hostiles() )
×
2669
    {
2670
      if ( hostile->concealed() )
×
2671
        continue;
×
2672
      if ( ( flags & LH_FLAG_LOS ) && !chr->realm()->has_los( *chr, *hostile ) )
×
2673
        continue;
×
2674
      if ( ( ~flags & LH_FLAG_INCLUDE_HIDDEN ) && hostile->hidden() )
×
2675
        continue;
×
2676
      if ( !chr->in_range( hostile, range ) )
×
2677
        continue;
×
2678
      arr->addElement( hostile->make_ref() );
×
2679
    }
2680

2681
    return arr.release();
×
2682
  }
×
2683
  else
2684
  {
2685
    return new BError( "Invalid parameter" );
×
2686
  }
2687
}
2688

2689
BObjectImp* UOExecutorModule::mf_CheckLineOfSight()
1✔
2690
{
2691
  UObject* src;
2692
  UObject* dst;
2693
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) )
1✔
2694
  {
2695
    return new BLong( src->realm()->has_los( *src, *dst->toplevel_owner() ) );
1✔
2696
  }
2697
  else
2698
  {
2699
    return new BLong( 0 );
×
2700
  }
2701
}
2702

2703
BObjectImp* UOExecutorModule::mf_CheckLosAt()
1✔
2704
{
2705
  UObject* src;
2706
  Core::Pos3d pos;
1✔
2707
  if ( getUObjectParam( 0, src ) && getPos3dParam( 1, 2, 3, &pos, src->realm() ) )
1✔
2708
  {
2709
    LosObj tgt( Core::Pos4d( pos, src->realm() ) );
1✔
2710
    return new BLong( src->realm()->has_los( *src, tgt ) );
1✔
2711
  }
2712
  return nullptr;
×
2713
}
2714

2715
BObjectImp* UOExecutorModule::mf_CheckLosBetween()
2✔
2716
{
2717
  Core::Pos3d p1, p2;
2✔
2718
  Realms::Realm* realm;
2719
  if ( getRealmParam( 6, &realm ) && getPos3dParam( 0, 1, 2, &p1, realm ) &&
4✔
2720
       getPos3dParam( 3, 4, 5, &p2, realm ) )
2✔
2721
  {
2722
    LosObj att( Core::Pos4d( p1, realm ) );
2✔
2723
    LosObj tgt( Core::Pos4d( p2, realm ) );
2✔
2724
    return new BLong( realm->has_los( att, tgt ) );
2✔
2725
  }
2726
  return nullptr;
×
2727
}
2728

2729
BObjectImp* UOExecutorModule::mf_DestroyItem()
6,180✔
2730
{
2731
  Item* item;
2732
  if ( getItemParam( 0, item ) )
6,180✔
2733
  {
2734
    if ( item->has_gotten_by() )
6,176✔
2735
      item->gotten_by()->clear_gotten_item();
×
2736
    else if ( item->inuse() && !is_reserved_to_me( item ) )
6,176✔
2737
      return new BError( "That item is being used." );
×
2738
    else if ( item->script_isa( POLCLASS_MULTI ) )
6,176✔
2739
      return new BError( "That item is a multi. Use uo::DestroyMulti instead." );
×
2740

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

2766
BObjectImp* UOExecutorModule::mf_SetName()
6✔
2767
{
2768
  UObject* obj;
2769
  const String* name_str;
2770

2771
  if ( getUObjectParam( 0, obj ) && getStringParam( 1, name_str ) )
6✔
2772
  {
2773
    obj->setname( name_str->value() );
6✔
2774
    return new BLong( 1 );
6✔
2775
  }
2776
  else
2777
  {
2778
    return new BLong( 0 );
×
2779
  }
2780
}
2781

2782
BObjectImp* UOExecutorModule::mf_GetPosition()
×
2783
{
2784
  UObject* obj;
2785
  if ( getUObjectParam( 0, obj ) )
×
2786
  {
2787
    std::unique_ptr<BStruct> arr( new BStruct );
×
2788
    arr->addMember( "x", new BLong( obj->x() ) );
×
2789
    arr->addMember( "y", new BLong( obj->y() ) );
×
2790
    arr->addMember( "z", new BLong( obj->z() ) );
×
2791
    return arr.release();
×
2792
  }
×
2793
  else
2794
  {
2795
    return new BLong( 0 );
×
2796
  }
2797
}
2798

2799
BObjectImp* UOExecutorModule::mf_EnumerateItemsInContainer()
6✔
2800
{
2801
  Item* item;
2802
  if ( getItemParam( 0, item ) )
6✔
2803
  {
2804
    int flags = 0;
6✔
2805
    if ( exec.fparams.size() >= 2 )
6✔
2806
    {
2807
      if ( !getParam( 1, flags ) )
6✔
2808
        return new BError( "Invalid parameter type" );
×
2809
    }
2810

2811
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
6✔
2812
    {
2813
      std::unique_ptr<ObjArray> newarr( new ObjArray );
6✔
2814

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

2817
      return newarr.release();
6✔
2818
    }
6✔
2819

2820
    return nullptr;
×
2821
  }
2822
  else
2823
    return new BError( "Invalid parameter type" );
×
2824
}
2825

2826
BObjectImp* UOExecutorModule::mf_EnumerateOnlineCharacters()
×
2827
{
2828
  std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2829

2830
  for ( Clients::const_iterator itr = networkManager.clients.begin(),
×
2831
                                end = networkManager.clients.end();
×
2832
        itr != end; ++itr )
×
2833
  {
2834
    if ( ( *itr )->chr != nullptr )
×
2835
    {
2836
      newarr->addElement( new ECharacterRefObjImp( ( *itr )->chr ) );
×
2837
    }
2838
  }
2839

2840
  return newarr.release();
×
2841
}
×
2842

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

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

2893

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

2910
BObjectImp* UOExecutorModule::mf_DisableEvents()
×
2911
{
2912
  int eventmask;
2913
  if ( getParam( 0, eventmask ) )
×
2914
  {
2915
    auto& uoex = uoexec();
×
2916
    uoex.eventmask &= ~eventmask;
×
2917

2918
    return new BLong( uoex.eventmask );
×
2919
  }
2920
  else
2921
  {
2922
    return new BError( "Invalid parameter" );
×
2923
  }
2924
}
2925

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

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

2955
    chr->resurrect();
×
2956
    return new BLong( 1 );
×
2957
  }
2958
  else
2959
  {
2960
    return new BError( "Invalid parameter type" );
×
2961
  }
2962
}
2963

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

2996
      if ( item != nullptr )
17✔
2997
      {
2998
        return item->make_ref();
16✔
2999
      }
3000

3001
      return new BError( "Item not found." );
1✔
3002
    }
3003
  }
3004
  else
3005
  {
3006
    return new BError( "Invalid parameter type" );
×
3007
  }
3008
}
3009

3010
BObjectImp* UOExecutorModule::mf_SaveWorldState()
2✔
3011
{
3012
  update_gameclock();
2✔
3013
  cancel_all_trades();
2✔
3014

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

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

3074

3075
BObjectImp* UOExecutorModule::mf_SetRegionLightLevel()
×
3076
{
3077
  const String* region_name_str;
3078
  int lightlevel;
3079
  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, lightlevel ) ) )
×
3080
  {
3081
    return new BError( "Invalid Parameter type" );
×
3082
  }
3083

3084
  if ( !VALID_LIGHTLEVEL( lightlevel ) )
×
3085
  {
3086
    return new BError( "Light Level is out of range" );
×
3087
  }
3088

3089
  LightRegion* lightregion = gamestate.lightdef->getregion( region_name_str->value() );
×
3090
  if ( lightregion == nullptr )
×
3091
  {
3092
    return new BError( "Light region not found" );
×
3093
  }
3094

3095
  SetRegionLightLevel( lightregion, lightlevel );
×
3096
  return new BLong( 1 );
×
3097
}
3098

3099
BObjectImp* UOExecutorModule::mf_SetRegionWeatherLevel()
×
3100
{
3101
  const String* region_name_str;
3102
  int type;
3103
  int severity;
3104
  int aux;
3105
  int lightoverride;
3106
  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, type ) && getParam( 2, severity ) &&
×
3107
          getParam( 3, aux ) && getParam( 4, lightoverride ) ) )
×
3108
  {
3109
    return new BError( "Invalid Parameter type" );
×
3110
  }
3111

3112
  WeatherRegion* weatherregion = gamestate.weatherdef->getregion( region_name_str->value() );
×
3113
  if ( weatherregion == nullptr )
×
3114
  {
3115
    return new BError( "Weather region not found" );
×
3116
  }
3117

3118
  SetRegionWeatherLevel( weatherregion, type, severity, aux, lightoverride );
×
3119

3120
  return new BLong( 1 );
×
3121
}
3122
BObjectImp* UOExecutorModule::mf_AssignRectToWeatherRegion()
×
3123
{
3124
  const String* region_name_str;
3125
  Pos2d nw, se;
×
3126
  Realms::Realm* realm;
3127

3128
  if ( !( getStringParam( 0, region_name_str ) && getRealmParam( 5, &realm ) &&
×
3129
          getPos2dParam( 1, 2, &nw, realm ) && getPos2dParam( 3, 4, &se, realm ) ) )
×
3130
  {
3131
    return new BError( "Invalid Parameter type" );
×
3132
  }
3133

3134
  bool res = gamestate.weatherdef->assign_zones_to_region( region_name_str->data(),
×
3135
                                                           Range2d( nw, se, nullptr ), realm );
×
3136
  if ( res )
×
3137
    return new BLong( 1 );
×
3138
  else
3139
    return new BError( "Weather region not found" );
×
3140
}
3141

3142
BObjectImp* UOExecutorModule::mf_Distance()
×
3143
{
3144
  UObject* obj1;
3145
  UObject* obj2;
3146
  if ( getUObjectParam( 0, obj1 ) && getUObjectParam( 1, obj2 ) )
×
3147
    return new BLong( obj1->distance_to( obj2->toplevel_pos() ) );
×
3148
  return new BError( "Invalid parameter type" );
×
3149
}
3150

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

3168
BObjectImp* UOExecutorModule::mf_CoordinateDistance()
×
3169
{
3170
  Pos2d pos1, pos2;
×
3171
  if ( !getPos2dParam( 0, 1, &pos1 ) || !getPos2dParam( 2, 3, &pos2 ) )
×
3172
    return new BError( "Invalid parameter type" );
×
3173
  return new BLong( pos1.pol_distance( pos2 ) );
×
3174
}
3175

3176
BObjectImp* UOExecutorModule::mf_CoordinateDistanceEuclidean()
×
3177
{
3178
  unsigned short x1, y1, x2, y2;
3179
  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
×
3180
  {
3181
    return new BError( "Invalid parameter type" );
×
3182
  }
3183
  return new Double( sqrt( pow( (double)( x1 - x2 ), 2 ) + pow( (double)( y1 - y2 ), 2 ) ) );
×
3184
}
3185

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

3203
  double dx = abs( x2 - x1 ) + 0.5;
×
3204
  double dy = abs( y2 - y1 ) + 0.5;
×
3205
  int vx = 0, vy = 0;
×
3206

3207
  if ( x2 > x1 )
×
3208
    vx = 1;
×
3209
  else if ( x2 < x1 )
×
3210
    vx = -1;
×
3211
  if ( y2 > y1 )
×
3212
    vy = 1;
×
3213
  else if ( y2 < y1 )
×
3214
    vy = -1;
×
3215

3216
  std::unique_ptr<ObjArray> coords( new ObjArray );
×
3217
  if ( dx >= dy )
×
3218
  {
3219
    dy = dy / dx;
×
3220

3221
    for ( int c = 0; c <= dx; c++ )
×
3222
    {
3223
      int point_x = x1 + ( c * vx );
×
3224

3225
      double float_y = double( c ) * double( vy ) * dy;
×
3226
      if ( float_y - floor( float_y ) >= 0.5 )
×
3227
        float_y = ceil( float_y );
×
3228
      int point_y = int( float_y ) + y1;
×
3229

3230
      std::unique_ptr<BStruct> point( new BStruct );
×
3231
      point->addMember( "x", new BLong( point_x ) );
×
3232
      point->addMember( "y", new BLong( point_y ) );
×
3233
      coords->addElement( point.release() );
×
3234
    }
×
3235
  }
3236
  else
3237
  {
3238
    dx = dx / dy;
×
3239
    for ( int c = 0; c <= dy; c++ )
×
3240
    {
3241
      int point_y = y1 + ( c * vy );
×
3242

3243
      double float_x = double( c ) * double( vx ) * dx;
×
3244
      if ( float_x - floor( float_x ) >= 0.5 )
×
3245
        float_x = ceil( float_x );
×
3246
      int point_x = int( float_x ) + x1;
×
3247

3248
      std::unique_ptr<BStruct> point( new BStruct );
×
3249
      point->addMember( "x", new BLong( point_x ) );
×
3250
      point->addMember( "y", new BLong( point_y ) );
×
3251
      coords->addElement( point.release() );
×
3252
    }
×
3253
  }
3254
  return coords.release();
×
3255
}
×
3256

3257
BObjectImp* UOExecutorModule::mf_GetFacing()
×
3258
{
3259
  unsigned short from_x, from_y, to_x, to_y;
3260
  if ( !( getParam( 0, from_x ) && getParam( 1, from_y ) && getParam( 2, to_x ) &&
×
3261
          getParam( 3, to_y ) ) )
×
3262
  {
3263
    return new BError( "Invalid parameter type" );
×
3264
  }
3265

3266
  double x = to_x - from_x;
×
3267
  double y = to_y - from_y;
×
3268
  double pi = acos( double( -1 ) );
×
3269
  double r = sqrt( x * x + y * y );
×
3270

3271
  double angle = ( ( acos( x / r ) * 180.0 ) / pi );
×
3272

3273
  double y_check = ( ( asin( y / r ) * 180.0 ) / pi );
×
3274
  if ( y_check < 0 )
×
3275
  {
3276
    angle = 360 - angle;
×
3277
  }
3278

3279
  unsigned short facing = ( ( short( angle / 40 ) + 10 ) % 8 );
×
3280

3281
  return new BLong( facing );
×
3282
}
3283

3284
// FIXME : Should we do an Orphan check here as well? Ugh.
3285
void true_extricate( Item* item )
29✔
3286
{
3287
  send_remove_object_to_inrange( item );
29✔
3288
  if ( item->container != nullptr )
29✔
3289
  {
3290
    item->extricate();
1✔
3291
  }
3292
  else
3293
  {
3294
    remove_item_from_world( item );
28✔
3295
  }
3296
}
29✔
3297

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

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

3323

3324
  if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
7✔
3325
  {
3326
    return new BError( "Non-container selected as target" );
×
3327
  }
3328
  UContainer* cont = static_cast<UContainer*>( cont_item );
7✔
3329

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

3348
  // daved changed order 1/26/3 check canX scripts before onX scripts.
3349
  UContainer* oldcont = item->container;
7✔
3350
  Item* existing_stack = nullptr;
7✔
3351

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

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

3375
  if ( !add_to_existing_stack )
7✔
3376
    if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item ) )
7✔
3377
      return new BError( "Could not insert item into container." );
×
3378

3379
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3380
  {
3381
    return new BError( "Item was destroyed in CanInsert Script" );
×
3382
  }
3383

3384
  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
7✔
3385
    return new BError( "Item cannot be unequipped" );
×
3386
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3387
  {
3388
    return new BError( "Item was destroyed in Equip Script" );
×
3389
  }
3390

3391
  if ( oldcont != nullptr )
7✔
3392
  {
3393
    oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
1✔
3394
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
1✔
3395
    {
3396
      return new BError( "Item was destroyed in OnRemove script" );
×
3397
    }
3398
  }
3399

3400
  if ( !add_to_existing_stack )
7✔
3401
  {
3402
    u8 slotIndex = item->slot_index();
7✔
3403
    if ( !cont->can_add_to_slot( slotIndex ) )
7✔
3404
    {
3405
      item->destroy();
×
3406
      return new BError( "No slots available in new container" );
×
3407
    }
3408
    if ( !item->slot_index( slotIndex ) )
7✔
3409
    {
3410
      item->destroy();
×
3411
      return new BError( "Couldn't set slot index on item" );
×
3412
    }
3413

3414
    Core::Pos2d cntpos;
7✔
3415
    if ( px < 0 || py < 0 )
7✔
3416
      cntpos = cont->get_random_location();
7✔
3417
    else
3418
    {
3419
      cntpos.x( static_cast<u16>( px ) ).y( static_cast<u16>( py ) );
×
3420
      if ( !cont->is_legal_posn( cntpos ) )
×
3421
        cntpos = cont->get_random_location();
×
3422
    }
3423

3424
    true_extricate( item );
7✔
3425

3426
    cont->add( item, cntpos );
7✔
3427
    update_item_to_inrange( item );
7✔
3428
    // DAVE added this 11/17: if in a Character's pack, update weight.
3429
    UpdateCharacterWeight( item );
7✔
3430

3431
    cont->on_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item );
7✔
3432
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3433
    {
3434
      return new BError( "Item was destroyed in OnInsert script" );
×
3435
    }
3436
  }
3437
  else
3438
  {
3439
    u16 amount = item->getamount();
×
3440
    true_extricate( item );
×
3441
    existing_stack->add_to_self( item );
×
3442
    update_item_to_inrange( existing_stack );
×
3443
    UpdateCharacterWeight( existing_stack );
×
3444

3445
    cont->on_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack, amount );
×
3446
  }
3447

3448
  return new BLong( 1 );
7✔
3449
}
7✔
3450

3451

3452
BObjectImp* UOExecutorModule::mf_MoveItemToSecureTradeWin()
×
3453
{
3454
  Item* item;
3455
  Character* chr;
3456
  if ( !( getItemParam( 0, item ) && getCharacterParam( 1, chr ) ) )
×
3457
  {
3458
    return new BError( "Invalid parameter type" );
×
3459
  }
3460

3461
  ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
×
3462
  if ( !item->movable() )
×
3463
  {
3464
    Character* _chr = controller_.get();
×
3465
    if ( _chr == nullptr || !_chr->can_move( item ) )
×
3466
      return new BError( "That is immobile" );
×
3467
  }
3468
  if ( item->inuse() && !is_reserved_to_me( item ) )
×
3469
  {
3470
    return new BError( "That item is being used." );
×
3471
  }
3472

3473
  // daved changed order 1/26/3 check canX scripts before onX scripts.
3474
  UContainer* oldcont = item->container;
×
3475

3476
  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3477
  Character* chr_owner = nullptr;
×
3478
  if ( oldcont != nullptr )
×
3479
    chr_owner = oldcont->GetCharacterOwner();
×
3480
  if ( chr_owner == nullptr )
×
3481
    if ( controller_.get() != nullptr )
×
3482
      chr_owner = controller_.get();
×
3483

3484
  if ( ( oldcont != nullptr ) &&
×
3485
       ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
×
3486
    return new BError( "Could not remove item from its container." );
×
3487
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3488
  {
3489
    return new BError( "Item was destroyed in CanRemove script" );
×
3490
  }
3491

3492
  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
×
3493
    return new BError( "Item cannot be unequipped" );
×
3494
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3495
  {
3496
    return new BError( "Item was destroyed in Equip Script" );
×
3497
  }
3498

3499
  if ( oldcont != nullptr )
×
3500
  {
3501
    oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
×
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 OnRemove script" );
×
3505
    }
3506
  }
3507

3508
  true_extricate( item );
×
3509

3510
  return place_item_in_secure_trade_container( chr->client, item );
×
3511
}
×
3512

3513
BObjectImp* UOExecutorModule::mf_EquipItem()
22✔
3514
{
3515
  Character* chr;
3516
  Item* item;
3517
  if ( getCharacterParam( 0, chr ) && getItemParam( 1, item ) )
22✔
3518
  {
3519
    ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
22✔
3520
    if ( !item->movable() )
22✔
3521
    {
3522
      Character* _chr = controller_.get();
×
3523
      if ( _chr == nullptr || !_chr->can_move( item ) )
×
3524
        return new BError( "That is immobile" );
×
3525
    }
3526

3527
    if ( item->inuse() && !is_reserved_to_me( item ) )
22✔
3528
    {
3529
      return new BError( "That item is being used." );
×
3530
    }
3531

3532
    if ( !chr->equippable( item ) || !item->check_equiptest_scripts( chr ) )
22✔
3533
    {
3534
      return new BError( "That item is not equippable by that character" );
×
3535
    }
3536
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
22✔
3537
    {
3538
      return new BError( "Item was destroyed in EquipTest script" );
×
3539
    }
3540

3541
    item->layer = Plib::tilelayer( item->graphic );
22✔
3542

3543
    if ( item->has_equip_script() )
22✔
3544
    {
3545
      BObjectImp* res = item->run_equip_script( chr, false );
2✔
3546
      if ( !res->isTrue() )
2✔
3547
        return res;
×
3548
      else
3549
        BObject obj( res );
2✔
3550
    }
3551

3552

3553
    true_extricate( item );
22✔
3554

3555
    // at this point, 'item' is free - doesn't belong to the world, or a container.
3556
    chr->equip( item );
22✔
3557
    send_wornitem_to_inrange( chr, item );
22✔
3558

3559
    return new BLong( 1 );
22✔
3560
  }
22✔
3561
  else
3562
  {
3563
    return new BError( "Invalid parameter type" );
×
3564
  }
3565
}
3566

3567
BObjectImp* UOExecutorModule::mf_RestartScript()
1✔
3568
{
3569
  UObject* obj;
3570
  if ( !getUObjectParam( 0, obj ) )
1✔
3571
    return new BError( "Invalid parameter" );
×
3572

3573
  if ( obj->script_isa( POLCLASS_NPC ) )
1✔
3574
  {
3575
    NPC* npc = static_cast<NPC*>( obj );
×
3576
    npc->restart_script();
×
3577
    return new BLong( 1 );
×
3578
  }
3579
  else if ( obj->script_isa( POLCLASS_ITEM ) )
1✔
3580
  {
3581
    Item* item = static_cast<Item*>( obj );
1✔
3582
    item->stop_control_script();
1✔
3583
    return new BLong( item->start_control_script() );
1✔
3584
  }
3585
  else
3586
  {
3587
    return new BError( "RestartScript only operates on NPCs and Items" );
×
3588
  }
3589
}
3590

3591

3592
BObjectImp* UOExecutorModule::mf_GetHarvestDifficulty()
×
3593
{
3594
  const String* resource;
3595
  unsigned short tiletype;
3596
  Pos2d pos;
×
3597
  Realms::Realm* realm;
3598
  if ( getStringParam( 0, resource ) && getRealmParam( 4, &realm ) && getPos2dParam( 1, 2, &pos ) &&
×
3599
       getParam( 3, tiletype ) )
×
3600
  {
3601
    return get_harvest_difficulty( resource->data(), Pos4d( pos, 0, realm ), tiletype );
×
3602
  }
3603
  else
3604
  {
3605
    return new BError( "Invalid parameter" );
×
3606
  }
3607
}
3608

3609
BObjectImp* UOExecutorModule::mf_HarvestResource()
×
3610
{
3611
  Pos2d pos;
×
3612
  Realms::Realm* realm;
3613
  const String* resource;
3614
  int b;
3615
  int n;
3616

3617
  if ( getStringParam( 0, resource ) && getRealmParam( 5, &realm ) &&
×
3618
       getPos2dParam( 1, 2, &pos, realm ) && getParam( 3, b ) && getParam( 4, n ) )
×
3619
  {
3620
    if ( b <= 0 )
×
3621
      return new BError( "b must be >= 0" );
×
3622
    return harvest_resource( resource->data(), Pos4d( pos, 0, realm ), b, n );
×
3623
  }
3624
  else
3625
  {
3626
    return new BError( "Invalid parameter" );
×
3627
  }
3628
}
3629

3630
BObjectImp* UOExecutorModule::mf_GetRegionName( /* objref */ )
×
3631
{
3632
  UObject* obj;
3633

3634
  if ( getUObjectParam( 0, obj ) )
×
3635
  {
3636
    JusticeRegion* justice_region;
3637
    if ( obj->isa( UOBJ_CLASS::CLASS_ITEM ) )
×
3638
      obj = obj->toplevel_owner();
×
3639

3640
    if ( obj->isa( UOBJ_CLASS::CLASS_CHARACTER ) )
×
3641
    {
3642
      Character* chr = static_cast<Character*>( obj );
×
3643

3644
      if ( chr->logged_in() )
×
3645
        justice_region = chr->client->gd->justice_region;
×
3646
      else
3647
        justice_region = gamestate.justicedef->getregion( chr->pos() );
×
3648
    }
3649
    else
3650
      justice_region = gamestate.justicedef->getregion( obj->pos() );
×
3651

3652
    if ( justice_region == nullptr )
×
3653
      return new BError( "No Region defined at this Location" );
×
3654
    else
3655
      return new String( justice_region->region_name() );
×
3656
  }
3657
  else
3658
    return new BError( "Invalid parameter" );
×
3659
}
3660

3661
BObjectImp* UOExecutorModule::mf_GetRegionNameAtLocation( /* x, y, realm */ )
×
3662
{
3663
  Pos2d pos;
×
3664
  Realms::Realm* realm;
3665
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos ) )
×
3666
  {
3667
    JusticeRegion* justice_region = gamestate.justicedef->getregion( Pos4d( pos, 0, realm ) );
×
3668
    if ( justice_region == nullptr )
×
3669
      return new BError( "No Region defined at this Location" );
×
3670
    else
3671
      return new String( justice_region->region_name() );
×
3672
  }
3673
  else
3674
    return new BError( "Invalid parameter" );
×
3675
}
3676

3677
BObjectImp* UOExecutorModule::mf_GetRegionString()
×
3678
{
3679
  const String* resource;
3680
  const String* propname;
3681
  Pos2d pos;
×
3682
  Realms::Realm* realm;
3683
  if ( getStringParam( 0, resource ) && getRealmParam( 4, &realm ) && getPos2dParam( 1, 2, &pos ) &&
×
3684
       getStringParam( 3, propname ) )
×
3685
  {
3686
    return get_region_string( resource->data(), Pos4d( pos, 0, realm ), propname->value() );
×
3687
  }
3688
  else
3689
  {
3690
    return new BError( "Invalid parameter" );
×
3691
  }
3692
}
3693

3694
BObjectImp* UOExecutorModule::mf_GetRegionLightLevelAtLocation( /* x, y, realm */ )
×
3695
{
3696
  Pos2d pos;
×
3697
  Realms::Realm* realm;
3698

3699
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos ) )
×
3700
  {
3701
    LightRegion* light_region = gamestate.lightdef->getregion( Pos4d( pos, 0, realm ) );
×
3702
    int lightlevel;
3703
    if ( light_region != nullptr )
×
3704
      lightlevel = light_region->lightlevel;
×
3705
    else
3706
      lightlevel = settingsManager.ssopt.default_light_level;
×
3707
    return new BLong( lightlevel );
×
3708
  }
3709
  else
3710
  {
3711
    return new BError( "Invalid parameter" );
×
3712
  }
3713
}
3714

3715

3716
BObjectImp* UOExecutorModule::mf_EquipFromTemplate()
1✔
3717
{
3718
  Character* chr;
3719
  const String* template_name;
3720
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, template_name ) )
1✔
3721
  {
3722
    return equip_from_template( chr, template_name->value() );
1✔
3723
  }
3724
  else
3725
  {
3726
    return new BError( "Invalid parameter" );
×
3727
  }
3728
}
3729

3730
// FIXME: Use a PrivUpdater here
3731
BObjectImp* UOExecutorModule::mf_GrantPrivilege()
4✔
3732
{
3733
  Character* chr;
3734
  const String* privstr;
3735
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, privstr ) )
4✔
3736
  {
3737
    chr->grant_privilege( privstr->data() );
4✔
3738
    return new BLong( 1 );
4✔
3739
  }
3740
  else
3741
  {
3742
    return new BError( "Invalid parameter" );
×
3743
  }
3744
}
3745

3746
// FIXME: Use a PrivUpdater here
3747
BObjectImp* UOExecutorModule::mf_RevokePrivilege()
1✔
3748
{
3749
  Character* chr;
3750
  const String* privstr;
3751
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, privstr ) )
1✔
3752
  {
3753
    chr->revoke_privilege( privstr->data() );
1✔
3754
    return new BLong( 1 );
1✔
3755
  }
3756
  else
3757
  {
3758
    return new BError( "Invalid parameter" );
×
3759
  }
3760
}
3761

3762
BObjectImp* UOExecutorModule::mf_ReadGameClock()
14✔
3763
{
3764
  return new BLong( read_gameclock() );
14✔
3765
}
3766

3767
unsigned char decode_xdigit( unsigned char ch )
×
3768
{
3769
  if ( ch >= '0' && ch <= '9' )
×
3770
    ch -= '0';
×
3771
  else if ( ch >= 'A' && ch <= 'F' )
×
3772
    ch = ch - 'A' + 0xa;
×
3773
  else if ( ch >= 'a' && ch <= 'f' )
×
3774
    ch = ch - 'a' + 0xa;
×
3775

3776
  return ch;
×
3777
}
3778

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

3837
BObjectImp* UOExecutorModule::mf_SendQuestArrow()
×
3838
{
3839
  Character* chr;
3840
  int x, y;
3841
  u32 arrowid = 0;
×
3842

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

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

3898
BObjectImp* UOExecutorModule::mf_ConsumeReagents()
×
3899
{
3900
  Character* chr;
3901
  int spellid;
3902
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) )
×
3903
  {
3904
    if ( !VALID_SPELL_ID( spellid ) )
×
3905
    {
3906
      return new BError( "Spell ID out of range" );
×
3907
    }
3908
    USpell* spell = gamestate.spells[spellid];
×
3909
    if ( spell == nullptr )
×
3910
    {
3911
      return new BError( "No such spell" );
×
3912
    }
3913

3914
    return new BLong( spell->consume_reagents( chr ) ? 1 : 0 );
×
3915
  }
3916
  else
3917
  {
3918
    return new BError( "Invalid parameter" );
×
3919
  }
3920
}
3921

3922
BObjectImp* UOExecutorModule::mf_StartSpellEffect()
×
3923
{
3924
  Character* chr;
3925
  int spellid;
3926
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) )
×
3927
  {
3928
    if ( !VALID_SPELL_ID( spellid ) )
×
3929
    {
3930
      return new BError( "Spell ID out of range" );
×
3931
    }
3932
    USpell* spell = gamestate.spells[spellid];
×
3933
    if ( spell == nullptr )
×
3934
    {
3935
      return new BError( "No such spell" );
×
3936
    }
3937

3938
    spell->cast( chr );
×
3939
    return new BLong( 1 );
×
3940
  }
3941
  else
3942
  {
3943
    return new BError( "Invalid parameter" );
×
3944
  }
3945
}
3946
BObjectImp* UOExecutorModule::mf_GetSpellDifficulty()
×
3947
{
3948
  int spellid;
3949
  if ( getParam( 0, spellid ) )
×
3950
  {
3951
    if ( !VALID_SPELL_ID( spellid ) )
×
3952
    {
3953
      return new BError( "Spell ID out of range" );
×
3954
    }
3955
    USpell* spell = gamestate.spells[spellid];
×
3956
    if ( spell == nullptr )
×
3957
    {
3958
      return new BError( "No such spell" );
×
3959
    }
3960

3961
    return new BLong( spell->difficulty() );
×
3962
  }
3963
  else
3964
  {
3965
    return new BError( "Invalid parameter" );
×
3966
  }
3967
}
3968
BObjectImp* UOExecutorModule::mf_SpeakPowerWords()
×
3969
{
3970
  Character* chr;
3971
  int spellid;
3972
  unsigned short font;
3973
  unsigned short color;
3974

3975
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) && getParam( 2, font ) &&
×
3976
       getParam( 3, color ) )
×
3977
  {
3978
    if ( !VALID_SPELL_ID( spellid ) )
×
3979
    {
3980
      return new BError( "Spell ID out of range" );
×
3981
    }
3982
    USpell* spell = gamestate.spells[spellid];
×
3983
    if ( spell == nullptr )
×
3984
    {
3985
      return new BError( "No such spell" );
×
3986
    }
3987

3988
    spell->speak_power_words( chr, font, color );
×
3989

3990
    return new BLong( 1 );
×
3991
  }
3992
  else
3993
  {
3994
    return new BError( "Invalid parameter" );
×
3995
  }
3996
}
3997

3998
BObjectImp* UOExecutorModule::mf_ListEquippedItems()
×
3999
{
4000
  Character* chr;
4001
  if ( getCharacterParam( 0, chr ) )
×
4002
  {
4003
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
4004
    for ( int layer = LAYER_EQUIP__LOWEST; layer <= LAYER_EQUIP__HIGHEST; ++layer )
×
4005
    {
4006
      Item* item = chr->wornitem( layer );
×
4007
      if ( item != nullptr )
×
4008
      {
4009
        arr->addElement( new EItemRefObjImp( item ) );
×
4010
      }
4011
    }
4012
    return arr.release();
×
4013
  }
×
4014
  else
4015
  {
4016
    return new BError( "Invalid parameter" );
×
4017
  }
4018
}
4019

4020
BObjectImp* UOExecutorModule::mf_GetEquipmentByLayer()
3✔
4021
{
4022
  Character* chr;
4023
  int layer;
4024
  if ( getCharacterParam( 0, chr ) && getParam( 1, layer ) )
3✔
4025
  {
4026
    if ( layer < LOWEST_LAYER || layer > HIGHEST_LAYER )
3✔
4027
    {
4028
      return new BError( "Invalid layer" );
×
4029
    }
4030

4031
    Item* item = chr->wornitem( layer );
3✔
4032
    if ( item == nullptr )
3✔
4033
    {
4034
      return new BError( "Nothing equipped on that layer." );
1✔
4035
    }
4036
    else
4037
    {
4038
      return new EItemRefObjImp( item );
2✔
4039
    }
4040
  }
4041
  else
4042
  {
4043
    return new BError( "Invalid parameter" );
×
4044
  }
4045
}
4046

4047
BObjectImp* UOExecutorModule::mf_DisconnectClient()
×
4048
{
4049
  Character* chr;
4050
  Network::Client* client;
4051

4052
  if ( getCharacterOrClientParam( 0, chr, client ) )
×
4053
  {
4054
    if ( chr != nullptr )
×
4055
    {
4056
      if ( !chr->has_active_client() )
×
4057
        return new BError( "No client attached" );
×
4058

4059
      client = chr->client;
×
4060
    }
4061

4062
    if ( client != nullptr )
×
4063
    {
4064
      if ( client->isConnected() )
×
4065
      {
4066
        client->Disconnect();
×
4067
        return new BLong( 1 );
×
4068
      }
4069
      else
4070
        return new BError( "Client is disconnected" );
×
4071
    }
4072
    else
4073
      return new BError( "Invalid parameter type" );
×
4074
  }
4075
  else
4076
  {
4077
    return new BError( "Invalid parameter type" );
×
4078
  }
4079
}
4080

4081
BObjectImp* UOExecutorModule::mf_GetMapInfo()
34✔
4082
{
4083
  Core::Pos2d pos;
34✔
4084
  Realms::Realm* realm;
4085

4086
  // note that this uses WORLD_MAX_X, not WORLD_X,
4087
  // because we can't read the outermost edge of the map
4088
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos, realm ) )
34✔
4089
  {
4090
    Plib::MAPTILE_CELL cell = realm->getmaptile( pos );
34✔
4091
    std::unique_ptr<BStruct> result( new BStruct );
34✔
4092
    result->addMember( "z", new BLong( cell.z ) );
34✔
4093
    result->addMember( "landtile", new BLong( cell.landtile ) );
34✔
4094

4095
    return result.release();
34✔
4096
  }
34✔
4097
  else
4098
  {
4099
    return new BError( "Invalid parameter" );
×
4100
  }
4101
}
4102
BObjectImp* UOExecutorModule::mf_GetWorldHeight()
×
4103
{
4104
  Core::Pos2d pos;
×
4105
  Realms::Realm* realm;
4106
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos, realm ) )
×
4107
  {
4108
    short z = -255;
×
4109
    if ( realm->lowest_standheight( pos, &z ) )
×
4110
      return new BLong( z );
×
4111
    else
4112
      return new BError( "Nowhere" );
×
4113
  }
4114
  else
4115
  {
4116
    return new BError( "Invalid parameter" );
×
4117
  }
4118
}
4119

4120
BObjectImp* UOExecutorModule::mf_GetObjtypeByName()
×
4121
{
4122
  const String* namestr;
4123
  if ( getStringParam( 0, namestr ) )
×
4124
  {
4125
    unsigned int objtype = get_objtype_byname( namestr->data() );
×
4126
    if ( objtype != 0 )
×
4127
      return new BLong( objtype );
×
4128
    else
4129
      return new BError( "No objtype by that name" );
×
4130
  }
4131
  else
4132
  {
4133
    return new BError( "Invalid parameter" );
×
4134
  }
4135
}
4136

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

4167
BObjectImp* UOExecutorModule::mf_DestroyMulti()
29✔
4168
{
4169
  Multi::UMulti* multi;
4170
  if ( getMultiParam( 0, multi ) )
29✔
4171
  {
4172
    const ItemDesc& id = find_itemdesc( multi->objtype_ );
29✔
4173
    if ( !id.destroy_script.empty() )
29✔
4174
    {
4175
      BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( multi ) );
×
4176
      if ( !res->isTrue() )
×
4177
      {  // destruction is okay
4178
        return res;
×
4179
      }
4180
    }
4181

4182
    Multi::UBoat* boat = multi->as_boat();
29✔
4183
    if ( boat != nullptr )
29✔
4184
    {
4185
      return Multi::destroy_boat( boat );
16✔
4186
    }
4187
    Multi::UHouse* house = multi->as_house();
13✔
4188
    if ( house != nullptr )
13✔
4189
    {
4190
      return Multi::destroy_house( house );
13✔
4191
    }
4192
    return new BError( "WTF!? Don't know what kind of multi that is!" );
×
4193
  }
4194
  else
4195
  {
4196
    return new BError( "Invalid parameter type" );
×
4197
  }
4198
}
4199

4200

4201
BObjectImp* UOExecutorModule::mf_GetMultiDimensions()
×
4202
{
4203
  u16 multiid;
4204
  if ( getParam( 0, multiid ) )
×
4205
  {
4206
    if ( !Multi::MultiDefByMultiIDExists( multiid ) )
×
4207
      return new BError( "MultiID not found" );
×
4208

4209
    const Multi::MultiDef& md = *Multi::MultiDefByMultiID( multiid );
×
4210
    std::unique_ptr<BStruct> ret( new BStruct );
×
4211
    ret->addMember( "xmin", new BLong( md.minrxyz.x() ) );
×
4212
    ret->addMember( "xmax", new BLong( md.maxrxyz.x() ) );
×
4213
    ret->addMember( "ymin", new BLong( md.minrxyz.y() ) );
×
4214
    ret->addMember( "ymax", new BLong( md.maxrxyz.y() ) );
×
4215
    return ret.release();
×
4216
  }
×
4217
  else
4218
    return new BError( "Invalid parameter" );
×
4219
}
4220

4221
BObjectImp* UOExecutorModule::mf_SetScriptController()
×
4222
{
4223
  Character* old_controller = controller_.get();
×
4224
  BObjectImp* param0 = getParamImp( 0 );
×
4225
  bool handled = false;
×
4226

4227
  if ( auto* lng = impptrIf<BLong>( param0 ) )
×
4228
  {
4229
    if ( lng->value() == 0 )
×
4230
    {
4231
      controller_.clear();
×
4232
      handled = true;
×
4233
    }
4234
  }
4235

4236
  if ( !handled )
×
4237
  {
4238
    Character* chr;
4239
    if ( getCharacterParam( 0, chr ) )
×
4240
      controller_.set( chr );
×
4241
    else
4242
      controller_.clear();
×
4243
  }
4244

4245
  if ( old_controller )
×
4246
    return new ECharacterRefObjImp( old_controller );
×
4247
  else
4248
    return new BLong( 0 );
×
4249
}
4250

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

4279
BObjectImp* UOExecutorModule::mf_GetStandingLayers( /* x, y, flags, realm, includeitems */ )
×
4280
{
4281
  Core::Pos2d pos;
×
4282
  int flags;
4283
  Realms::Realm* realm;
4284
  int includeitems;
4285

4286
  if ( getRealmParam( 3, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, flags ) &&
×
4287
       getParam( 4, includeitems ) )
×
4288
  {
4289
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4290

4291
    Plib::MapShapeList mlist;
×
4292
    Core::ItemsVector ivec;
×
4293
    realm->readmultis( mlist, pos, flags );
×
4294
    realm->getmapshapes( mlist, pos, flags );
×
4295
    if ( includeitems )
×
4296
    {
4297
      realm->readdynamics( mlist, pos, ivec, false, flags );
×
4298
    }
4299

4300
    for ( unsigned i = 0; i < mlist.size(); ++i )
×
4301
    {
4302
      std::unique_ptr<BStruct> arr( new BStruct );
×
4303

4304
      if ( mlist[i].flags & ( Plib::FLAG::MOVELAND | Plib::FLAG::MOVESEA ) )
×
4305
        arr->addMember( "z", new BLong( mlist[i].z + mlist[i].height ) );
×
4306
      else
4307
        arr->addMember( "z", new BLong( mlist[i].z ) );
×
4308

4309
      arr->addMember( "height", new BLong( mlist[i].height ) );
×
4310
      arr->addMember( "flags", new BLong( mlist[i].flags ) );
×
4311
      newarr->addElement( arr.release() );
×
4312
    }
×
4313

4314
    return newarr.release();
×
4315
  }
×
4316
  else
4317
    return new BError( "Invalid parameter type" );
×
4318
}
4319

4320
BObjectImp*
4321
UOExecutorModule::mf_GetStandingCoordinates() /* x, y, radius, minz, maxz, realm := _DEFAULT_REALM,
1✔
4322
                                                 movemode := "L", doors_block = 0 */
4323
{
4324
  s16 r, minz, maxz;
4325
  int doors_block;
4326
  const String* movemodename;
4327
  Core::Pos2d pos;
1✔
4328
  Realms::Realm* realm;
4329

4330
  if ( !getRealmParam( 5, &realm ) )
1✔
4331
    return new BError( "Realm not found" );
×
4332

4333
  if ( !( getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, r ) && getParam( 3, minz ) &&
2✔
4334
          getParam( 4, maxz ) && getStringParam( 6, movemodename ) && getParam( 7, doors_block ) ) )
1✔
4335
  {
4336
    return new BError( "Invalid parameter type" );
×
4337
  }
4338

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

4341
  std::unique_ptr<ObjArray> result( new ObjArray );
1✔
4342

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

4357
      // Figure out which members to stick in the struct -- we only add multi it exists
4358
      height_struct->addMember( "x", new BLong( tile.x() ) );
33✔
4359
      height_struct->addMember( "y", new BLong( tile.y() ) );
33✔
4360
      height_struct->addMember( "z", new BLong( z ) );
33✔
4361
      if ( multi != nullptr )
33✔
4362
      {
4363
        height_struct->addMember( "multi", new EMultiRefObjImp( multi ) );
×
4364
      }
4365

4366
      // Add struct to the return array
4367
      result->addElement( height_struct.release() );
33✔
4368
    }
33✔
4369
  }
121✔
4370

4371
  return result.release();
1✔
4372
}
1✔
4373

4374
BObjectImp* UOExecutorModule::mf_ReserveItem()
3✔
4375
{
4376
  Item* item;
4377
  if ( getItemParam( 0, item ) )
3✔
4378
  {
4379
    if ( item->inuse() )
3✔
4380
    {
4381
      if ( is_reserved_to_me( item ) )
2✔
4382
        return new BLong( 2 );
2✔
4383
      else
4384
        return new BError( "That item is already being used." );
×
4385
    }
4386
    item->inuse( true );
1✔
4387
    reserved_items_.push_back( ItemRef( item ) );
1✔
4388
    return new BLong( 1 );
1✔
4389
  }
4390
  else
4391
  {
4392
    return new BError( "Invalid parameter" );
×
4393
  }
4394
}
4395

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

4426

4427
BObjectImp* UOExecutorModule::mf_SendSkillWindow()
×
4428
{
4429
  Character *towhom, *forwhom;
4430
  if ( getCharacterParam( 0, towhom ) && getCharacterParam( 1, forwhom ) )
×
4431
  {
4432
    if ( towhom->has_active_client() )
×
4433
    {
4434
      send_skillmsg( towhom->client, forwhom );
×
4435
      return new BLong( 1 );
×
4436
    }
4437
    else
4438
    {
4439
      return new BError( "No client attached" );
×
4440
    }
4441
  }
4442
  else
4443
  {
4444
    return new BError( "Invalid parameter type" );
×
4445
  }
4446
}
4447

4448

4449
BObjectImp* UOExecutorModule::mf_OpenPaperdoll()
×
4450
{
4451
  Character *towhom, *forwhom;
4452
  if ( getCharacterParam( 0, towhom ) && getCharacterParam( 1, forwhom ) )
×
4453
  {
4454
    if ( towhom->has_active_client() )
×
4455
    {
4456
      send_paperdoll( towhom->client, forwhom );
×
4457
      return new BLong( 1 );
×
4458
    }
4459
    else
4460
    {
4461
      return new BError( "No client attached" );
×
4462
    }
4463
  }
4464
  else
4465
  {
4466
    return new BError( "Invalid parameter type" );
×
4467
  }
4468
}
4469

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

4474
BObjectImp* UOExecutorModule::mf_ConsumeSubstance()
×
4475
{
4476
  Item* cont_item;
4477
  unsigned int objtype;
4478
  int amount;
4479
  if ( getItemParam( 0, cont_item ) && getObjtypeParam( 1, objtype ) && getParam( 2, amount ) )
×
4480
  {
4481
    if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
4482
      return new BError( "That is not a container" );
×
4483
    if ( amount < 0 )
×
4484
      return new BError( "Amount cannot be negative" );
×
4485

4486
    UContainer* cont = static_cast<UContainer*>( cont_item );
×
4487
    int amthave = cont->find_sumof_objtype_noninuse( objtype );
×
4488
    if ( amthave < amount )
×
4489
      return new BError( "Not enough of that substance in container" );
×
4490

4491
    cont->consume_sumof_objtype_noninuse( objtype, amount );
×
4492

4493
    return new BLong( 1 );
×
4494
  }
4495
  else
4496
  {
4497
    return new BError( "Invalid parameter type" );
×
4498
  }
4499
}
4500

4501
bool UOExecutorModule::is_reserved_to_me( Item* item )
2✔
4502
{
4503
  for ( unsigned i = 0; i < reserved_items_.size(); ++i )
2✔
4504
  {
4505
    if ( reserved_items_[i].get() == item )
2✔
4506
      return true;
2✔
4507
  }
4508
  return false;
×
4509
}
4510

4511
BObjectImp* UOExecutorModule::mf_Shutdown()
2✔
4512
{
4513
  int exit_code = 0;
2✔
4514

4515
  if ( exec.hasParams( 1 ) )
2✔
4516
  {
4517
    getParam( 0, exit_code );
2✔
4518
  }
4519

4520
  Clib::signal_exit( exit_code );
2✔
4521
#ifndef _WIN32
4522
  // the catch_signals_thread (actually main) sits with sigwait(),
4523
  // so it won't wake up except by being signalled.
4524
  signal_catch_thread();
2✔
4525
#endif
4526
  return new BLong( 1 );
2✔
4527
}
4528

4529

4530
BObjectImp* UOExecutorModule::mf_GetCommandHelp()
×
4531
{
4532
  Character* chr;
4533
  const String* cmd;
4534
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, cmd ) )
×
4535
  {
4536
    std::string help = get_textcmd_help( chr, cmd->value() );
×
4537
    if ( !help.empty() )
×
4538
    {
4539
      return new String( help );
×
4540
    }
4541
    else
4542
    {
4543
      return new BError( "No help for that command found" );
×
4544
    }
4545
  }
×
4546
  else
4547
  {
4548
    return new BError( "Invalid parameter type" );
×
4549
  }
4550
}
4551

4552

4553
BObjectImp* UOExecutorModule::mf_SendStringAsTipWindow()
×
4554
{
4555
  Character* chr;
4556
  const String* str;
4557
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, str ) )
×
4558
  {
4559
    if ( chr->has_active_client() )
×
4560
    {
4561
      send_tip( chr->client, str->value() );
×
4562
      return new BLong( 1 );
×
4563
    }
4564
    else
4565
    {
4566
      return new BError( "No client attached" );
×
4567
    }
4568
  }
4569
  else
4570
  {
4571
    return new BError( "Invalid parameter type" );
×
4572
  }
4573
}
4574

4575
BObjectImp* UOExecutorModule::mf_ListItemsNearLocationWithFlag(
×
4576
    /* x, y, z, range, flags, realm */ )  // DAVE
4577
{
4578
  Pos2d pos;
×
4579
  u16 range;
4580
  int z, flags;
4581
  Realms::Realm* realm;
4582

4583
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4584
       getParam( 3, range ) && getParam( 4, flags ) )
×
4585
  {
4586
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4587
    WorldIterator<ItemFilter>::InRange(
×
4588
        pos, realm, range,
4589
        [&]( Item* item )
×
4590
        {
4591
          if ( ( Plib::tile_uoflags( item->graphic ) & flags ) )
×
4592
          {
4593
            if ( item->in_range( pos, range ) )
×
4594
            {
4595
              if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
4596
                newarr->addElement( new EItemRefObjImp( item ) );
×
4597
            }
4598
          }
4599
        } );
×
4600

4601
    return newarr.release();
×
4602
  }
×
4603

4604
  return new BError( "Invalid parameter" );
×
4605
}
4606

4607
BObjectImp* UOExecutorModule::mf_ListStaticsAtLocation( /* x, y, z, flags, realm */ )
×
4608
{
4609
  Core::Pos2d pos;
×
4610
  int z, flags;
4611
  Realms::Realm* realm;
4612

4613
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4614
       getParam( 3, flags ) )
×
4615
  {
4616
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4617

4618
    if ( !( flags & ITEMS_IGNORE_STATICS ) )
×
4619
    {
4620
      Plib::StaticEntryList slist;
×
4621
      realm->getstatics( slist, pos );
×
4622

4623
      for ( unsigned i = 0; i < slist.size(); ++i )
×
4624
      {
4625
        if ( ( z == LIST_IGNORE_Z ) || ( slist[i].z == z ) )
×
4626
        {
4627
          std::unique_ptr<BStruct> arr( new BStruct );
×
4628
          arr->addMember( "x", new BLong( pos.x() ) );
×
4629
          arr->addMember( "y", new BLong( pos.y() ) );
×
4630
          arr->addMember( "z", new BLong( slist[i].z ) );
×
4631
          arr->addMember( "objtype", new BLong( slist[i].objtype ) );
×
4632
          arr->addMember( "hue", new BLong( slist[i].hue ) );
×
4633
          newarr->addElement( arr.release() );
×
4634
        }
×
4635
      }
4636
    }
×
4637

4638
    if ( !( flags & ITEMS_IGNORE_MULTIS ) )
×
4639
    {
4640
      Plib::StaticList mlist;
×
4641
      realm->readmultis( mlist, pos );
×
4642

4643
      for ( unsigned i = 0; i < mlist.size(); ++i )
×
4644
      {
4645
        if ( ( z == LIST_IGNORE_Z ) || ( mlist[i].z == z ) )
×
4646
        {
4647
          std::unique_ptr<BStruct> arr( new BStruct );
×
4648
          arr->addMember( "x", new BLong( pos.x() ) );
×
4649
          arr->addMember( "y", new BLong( pos.y() ) );
×
4650
          arr->addMember( "z", new BLong( mlist[i].z ) );
×
4651
          arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
×
4652
          newarr->addElement( arr.release() );
×
4653
        }
×
4654
      }
4655
    }
×
4656

4657
    return newarr.release();
×
4658
  }
×
4659
  else
4660
    return new BError( "Invalid parameter" );
×
4661
}
4662

4663
BObjectImp* UOExecutorModule::mf_ListStaticsNearLocation( /* x, y, z, range, flags, realm */ )
×
4664
{
4665
  Core::Pos2d pos;
×
4666
  int z, flags;
4667
  short range;
4668
  Realms::Realm* realm;
4669

4670
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4671
       getParam( 3, range ) && getParam( 4, flags ) )
×
4672
  {
4673
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4674
    Core::Vec2d radius( range, range );
×
4675
    Core::Range2d area( pos - radius, pos + radius, realm );
×
4676
    for ( const auto& tile : area )
×
4677
    {
4678
      if ( !( flags & ITEMS_IGNORE_STATICS ) )
×
4679
      {
4680
        Plib::StaticEntryList slist;
×
4681
        realm->getstatics( slist, tile );
×
4682

4683
        for ( unsigned i = 0; i < slist.size(); ++i )
×
4684
        {
4685
          if ( ( z == LIST_IGNORE_Z ) || ( abs( slist[i].z - z ) < CONST_DEFAULT_ZRANGE ) )
×
4686
          {
4687
            std::unique_ptr<BStruct> arr( new BStruct );
×
4688
            arr->addMember( "x", new BLong( tile.x() ) );
×
4689
            arr->addMember( "y", new BLong( tile.y() ) );
×
4690
            arr->addMember( "z", new BLong( slist[i].z ) );
×
4691
            arr->addMember( "objtype", new BLong( slist[i].objtype ) );
×
4692
            arr->addMember( "hue", new BLong( slist[i].hue ) );
×
4693
            newarr->addElement( arr.release() );
×
4694
          }
×
4695
        }
4696
      }
×
4697

4698
      if ( !( flags & ITEMS_IGNORE_MULTIS ) )
×
4699
      {
4700
        Plib::StaticList mlist;
×
4701
        realm->readmultis( mlist, tile );
×
4702

4703
        for ( unsigned i = 0; i < mlist.size(); ++i )
×
4704
        {
4705
          if ( ( z == LIST_IGNORE_Z ) || ( abs( mlist[i].z - z ) < CONST_DEFAULT_ZRANGE ) )
×
4706
          {
4707
            std::unique_ptr<BStruct> arr( new BStruct );
×
4708
            arr->addMember( "x", new BLong( tile.x() ) );
×
4709
            arr->addMember( "y", new BLong( tile.y() ) );
×
4710
            arr->addMember( "z", new BLong( mlist[i].z ) );
×
4711
            arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
×
4712
            newarr->addElement( arr.release() );
×
4713
          }
×
4714
        }
4715
      }
×
4716
    }
4717

4718
    return newarr.release();
×
4719
  }
×
4720
  else
4721
    return new BError( "Invalid parameter" );
×
4722
}
4723

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

4814
typedef Plib::AStarSearch<UOPathState> UOSearch;
4815

4816
BObjectImp* UOExecutorModule::mf_FindPath()
7✔
4817
{
4818
  Pos3d pos1, pos2;
7✔
4819
  Realms::Realm* realm;
4820
  const String* movemode_name;
4821

4822
  if ( !getPos3dParam( 0, 1, 2, &pos1 ) || !getPos3dParam( 3, 4, 5, &pos2 ) ||
14✔
4823
       !getRealmParam( 6, &realm ) || !getStringParam( 9, movemode_name ) )
14✔
4824
    return new BError( "Invalid parameter" );
×
4825
  if ( !pos1.in_range( pos2, settingsManager.ssopt.max_pathfind_range ) )
7✔
4826
    return new BError( "Beyond Max Range." );
×
4827

4828
  short theSkirt;
4829
  int flags;
4830

4831
  if ( !getParam( 7, flags ) )
7✔
4832
    flags = FP_IGNORE_MOBILES;
×
4833

4834
  if ( !getParam( 8, theSkirt ) )
7✔
4835
    theSkirt = 5;
×
4836

4837
  Plib::MOVEMODE movemode = Character::decode_movemode( movemode_name->value() );
7✔
4838
  if ( movemode == Plib::MOVEMODE_NONE )
7✔
4839
    return new BError( "Wrong movemode parameter" );
1✔
4840

4841
  if ( theSkirt < 0 )
6✔
4842
    theSkirt = 0;
×
4843

4844
  if ( !realm->valid( pos1.xy() ) )
6✔
4845
    return new BError( "Start Coordinates Invalid for Realm" );
×
4846
  if ( !realm->valid( pos2.xy() ) )
6✔
4847
    return new BError( "End Coordinates Invalid for Realm" );
×
4848

4849
  auto astarsearch = std::make_unique<UOSearch>();
6✔
4850

4851
  Range2d range( pos1.xy().min( pos2.xy() ) - Vec2d( theSkirt, theSkirt ),
6✔
4852
                 pos1.xy().max( pos2.xy() ) + Vec2d( theSkirt, theSkirt ), realm );
12✔
4853

4854
  if ( Plib::systemstate.config.loglevel >= 12 )
6✔
4855
  {
4856
    POLLOGLN( "[FindPath] Calling FindPath({}, {}, {}, {:#x}, {})", pos1, pos2, realm->name(),
×
4857
              flags, theSkirt );
4858
    POLLOGLN( "[FindPath]   search for Blockers inside {}", range );
×
4859
  }
4860

4861
  bool doors_block = ( flags & FP_IGNORE_DOORS ) ? false : true;
6✔
4862
  AStarParams params( range, doors_block, movemode, realm );
6✔
4863

4864
  if ( !( flags & FP_IGNORE_MOBILES ) )
6✔
4865
  {
4866
    WorldIterator<MobileFilter>::InBox( range, realm,
×
4867
                                        [&]( Mobile::Character* chr )
×
4868
                                        {
4869
                                          params.AddBlocker( chr->pos3d() );
×
4870

4871
                                          if ( Plib::systemstate.config.loglevel >= 12 )
×
4872
                                            POLLOGLN( "[FindPath]   add Blocker {} at {}",
×
4873
                                                      chr->name(), chr->pos() );
×
4874
                                        } );
×
4875
  }
4876

4877
  if ( Plib::systemstate.config.loglevel >= 12 )
6✔
4878
  {
4879
    POLLOGLN( "[FindPath]   use StartNode {}", pos1 );
×
4880
    POLLOGLN( "[FindPath]   use EndNode {}", pos2 );
×
4881
  }
4882

4883
  // Create a start state
4884
  UOPathState nodeStart( pos1, &params );
6✔
4885
  // Define the goal state
4886
  UOPathState nodeEnd( pos2, &params );
6✔
4887
  // Set Start and goal states
4888
  astarsearch->SetStartAndGoalStates( nodeStart, nodeEnd );
6✔
4889
  unsigned int SearchState;
4890
  do
4891
  {
4892
    SearchState = astarsearch->SearchStep();
28✔
4893
  } while ( SearchState == UOSearch::SEARCH_STATE_SEARCHING );
28✔
4894
  if ( SearchState == UOSearch::SEARCH_STATE_SUCCEEDED )
6✔
4895
  {
4896
    UOPathState* node = astarsearch->GetSolutionStart();
3✔
4897

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

4924
  return new BError( "Pathfind Error." );
×
4925
}
6✔
4926

4927

4928
BObjectImp* UOExecutorModule::mf_UseItem()
×
4929
{
4930
  Item* item;
4931
  Character* chr;
4932

4933
  if ( getItemParam( 0, item ) && getCharacterParam( 1, chr ) )
×
4934
  {
4935
    const ItemDesc& itemdesc = find_itemdesc( item->objtype_ );
×
4936

4937
    if ( itemdesc.requires_attention && ( chr->skill_ex_active() || chr->casting_spell() ) )
×
4938
    {
4939
      if ( chr->client != nullptr )
×
4940
      {
4941
        send_sysmessage( chr->client, "I am already doing something else." );
×
4942
        return new BError( "Character busy." );
×
4943
        ;
4944
      }
4945
    }
4946

4947
    if ( itemdesc.requires_attention && chr->hidden() )
×
4948
      chr->unhide();
×
4949

4950
    ref_ptr<EScriptProgram> prog;
×
4951

4952
    std::string on_use_script = item->get_use_script_name();
×
4953

4954
    if ( !on_use_script.empty() )
×
4955
    {
4956
      ScriptDef sd( on_use_script, nullptr, "" );
×
4957
      prog = find_script2( sd,
×
4958
                           true,  // complain if not found
4959
                           Plib::systemstate.config.cache_interactive_scripts );
×
4960
    }
×
4961
    else if ( !itemdesc.on_use_script.empty() )
×
4962
    {
4963
      prog = find_script2( itemdesc.on_use_script, true,
×
4964
                           Plib::systemstate.config.cache_interactive_scripts );
×
4965
    }
4966

4967
    if ( prog.get() != nullptr )
×
4968
    {
4969
      if ( chr->start_itemuse_script( prog.get(), item, itemdesc.requires_attention ) )
×
4970
        return new BLong( 1 );
×
4971
      else
4972
        return new BError( "Failed to start script!" );
×
4973
      // else log the fact?
4974
    }
4975
    else
4976
    {
4977
      if ( chr->client != nullptr )
×
4978
        item->builtin_on_use( chr->client );
×
4979
      return new BLong( 0 );
×
4980
    }
4981
  }
×
4982
  else
4983
  {
4984
    return new BError( "Invalid parameter" );
×
4985
  }
4986
}
4987

4988
BObjectImp* UOExecutorModule::mf_FindSubstance()
3✔
4989
{
4990
  UContainer::Contents substanceVector;
3✔
4991

4992
  Item* cont_item;
4993
  unsigned int objtype;
4994
  int amount;
4995

4996
  if ( getItemParam( 0, cont_item ) && getObjtypeParam( 1, objtype ) && getParam( 2, amount ) )
3✔
4997
  {
4998
    bool makeInUse;
4999
    int flags;
5000
    if ( !getParam( 3, makeInUse ) )
3✔
5001
      makeInUse = false;
×
5002

5003
    if ( !getParam( 4, flags ) )
3✔
5004
      flags = 0;
×
5005

5006
    if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
3✔
5007
      return new BError( "That is not a container" );
×
5008
    if ( amount < 0 )
3✔
5009
      return new BError( "Amount cannot be negative" );
×
5010

5011
    UContainer* cont = static_cast<UContainer*>( cont_item );
3✔
5012
    int amthave = cont->find_sumof_objtype_noninuse( objtype, amount, substanceVector, flags );
3✔
5013
    if ( ( amthave < amount ) && ( !( flags & FINDSUBSTANCE_FIND_ALL ) ) )
3✔
5014
      return new BError( "Not enough of that substance in container" );
1✔
5015
    else
5016
    {
5017
      std::unique_ptr<ObjArray> theArray( new ObjArray() );
2✔
5018
      Item* item;
5019

5020
      for ( UContainer::Contents::const_iterator itr = substanceVector.begin();
2✔
5021
            itr != substanceVector.end(); ++itr )
4✔
5022
      {
5023
        item = *itr;
2✔
5024
        if ( item != nullptr )
2✔
5025
        {
5026
          if ( ( makeInUse ) && ( !item->inuse() ) )
2✔
5027
          {
5028
            item->inuse( true );
2✔
5029
            reserved_items_.push_back( ItemRef( item ) );
2✔
5030
          }
5031
          theArray->addElement( new EItemRefObjImp( item ) );
2✔
5032
        }
5033
      }
5034
      return theArray.release();
2✔
5035
    }
2✔
5036
  }
5037
  else
5038
  {
5039
    return new BError( "Invalid parameter type" );
×
5040
  }
5041
}
3✔
5042

5043
BObjectImp* UOExecutorModule::mf_IsStackable()
×
5044
{
5045
  Item* item1;
5046
  Item* item2;
5047

5048
  if ( !( getItemParam( 0, item1 ) && getItemParam( 1, item2 ) ) )
×
5049
  {
5050
    return new BError( "Invalid parameter type" );
×
5051
  }
5052

5053
  if ( item1->objtype_ != item2->objtype_ )
×
5054
    return new BError( "Objtypes differs" );
×
5055
  if ( !item1->stackable() )
×
5056
    return new BError( "That item type is not stackable." );
×
5057

5058
  if ( item1->can_add_to_self( *item2, false ) )
×
5059
    return new BLong( 1 );
×
5060
  else
5061
    return new BError( "Failed to stack" );
×
5062
}
5063

5064
BObjectImp* UOExecutorModule::mf_UpdateMobile()
×
5065
{
5066
  Character* chr;
5067
  int flags;
5068

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

5094
BObjectImp* UOExecutorModule::mf_UpdateItem()
×
5095
{
5096
  Item* item;
5097

5098
  if ( getItemParam( 0, item ) )
×
5099
  {
5100
    send_item_to_inrange( item );
×
5101
    return new BLong( 1 );
×
5102
  }
5103
  else
5104
  {
5105
    return new BError( "Invalid parameter type" );
×
5106
  }
5107
}
5108

5109

5110
BObjectImp* UOExecutorModule::mf_CanWalk(
×
5111
    /*movemode, x1, y1, z1, x2_or_dir, y2 := -1, realm := DEF*/ )
5112
{
5113
  Pos4d p;
×
5114
  int x2_or_dir, y2_;
5115
  const String* movemode_name;
5116

5117
  if ( ( getStringParam( 0, movemode_name ) ) && ( getPos4dParam( 1, 2, 3, 6, &p ) ) &&
×
5118
       ( getParam( 4, x2_or_dir ) ) && ( getParam( 5, y2_ ) ) )
×
5119
  {
5120
    Plib::MOVEMODE movemode = Character::decode_movemode( movemode_name->value() );
×
5121

5122
    Core::UFACING dir;
5123
    if ( y2_ == -1 )
×
5124
      dir = static_cast<Core::UFACING>( x2_or_dir & 0x7 );
×
5125
    else
5126
    {
5127
      auto p1 = Pos2d( static_cast<u16>( x2_or_dir ), static_cast<u16>( y2_ ) );
×
5128
      if ( !p.realm()->valid( p1 ) )
×
5129
        return new BError( "Invalid coordinates for realm." );
×
5130

5131
      dir = p.xy().direction_toward( p1 );
×
5132
    }
5133

5134
    if ( dir & 1 )  // check if diagonal movement is allowed
×
5135
    {
5136
      short new_z;
5137
      u8 tmp_facing = ( dir + 1 ) & 0x7;
×
5138
      auto tmp_pos = p.move( static_cast<Core::UFACING>( tmp_facing ) );
×
5139

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

5144
      tmp_facing = ( dir - 1 ) & 0x7;
×
5145
      tmp_pos = p.move( static_cast<Core::UFACING>( tmp_facing ) );
×
5146

5147
      if ( !walk1 && !p.realm()->walkheight( tmp_pos.xy(), tmp_pos.z(), &new_z, nullptr, nullptr,
×
5148
                                             true, movemode, nullptr ) )
5149
        return new BError( "Cannot walk there" );
×
5150
    }
5151

5152
    p.move_to( dir );
×
5153
    short newz;
5154

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

5158
    return new BLong( newz );
×
5159
  }
5160
  else
5161
    return new BError( "Invalid parameter" );
×
5162
}
5163

5164

5165
BObjectImp* UOExecutorModule::mf_SendCharProfile(
×
5166
    /*chr, of_who, title, uneditable_text := array, editable_text := array*/ )
5167
{
5168
  Character *chr, *of_who;
5169
  const String* title;
5170
  const String* uText;
5171
  const String* eText;
5172

5173
  if ( getCharacterParam( 0, chr ) && getCharacterParam( 1, of_who ) &&
×
5174
       getStringParam( 2, title ) && getUnicodeStringParam( 3, uText ) &&
×
5175
       getUnicodeStringParam( 4, eText ) )
×
5176
  {
5177
    if ( chr->logged_in() && of_who->logged_in() )
×
5178
    {
5179
      if ( uText->length() > SPEECH_MAX_LEN || eText->length() > SPEECH_MAX_LEN )
×
5180
        return new BError( "Text exceeds maximum size." );
×
5181

5182
      sendCharProfile( chr, of_who, title->value(), uText->value(), eText->value() );
×
5183
      return new BLong( 1 );
×
5184
    }
5185
    else
5186
      return new BError( "Mobile must be online." );
×
5187
  }
5188
  else
5189
    return new BError( "Invalid parameter type" );
×
5190
}
5191

5192
BObjectImp* UOExecutorModule::mf_SendOverallSeason( /*season_id, playsound := 1*/ )
×
5193
{
5194
  int season_id, playsound;
5195

5196
  if ( getParam( 0, season_id ) && getParam( 1, playsound ) )
×
5197
  {
5198
    if ( season_id < 0 || season_id > 4 )
×
5199
      return new BError( "Invalid season id" );
×
5200

5201
    Network::PktHelper::PacketOut<Network::PktOut_BC> msg;
×
5202
    msg->Write<u8>( static_cast<u16>( season_id ) );
×
5203
    msg->Write<u8>( static_cast<u16>( playsound ) );
×
5204

5205
    for ( Clients::iterator itr = networkManager.clients.begin(),
×
5206
                            end = networkManager.clients.end();
×
5207
          itr != end; ++itr )
×
5208
    {
5209
      Network::Client* client = *itr;
×
5210
      if ( !client->chr || !client->chr->logged_in() || client->getversiondetail().major < 1 )
×
5211
        continue;
×
5212
      msg.Send( client );
×
5213
    }
5214
    return new BLong( 1 );
×
5215
  }
×
5216
  else
5217
    return new BError( "Invalid parameter" );
×
5218
}
5219

5220
// bresenham circle calculates the coords based on center coords and radius
5221
BObjectImp* UOExecutorModule::mf_GetMidpointCircleCoords( /* xcenter, ycenter, radius */ )
×
5222
{
5223
  int xcenter, ycenter, radius;
5224
  if ( !( getParam( 0, xcenter ) && getParam( 1, ycenter ) && getParam( 2, radius ) ) )
×
5225
  {
5226
    return new BError( "Invalid parameter type" );
×
5227
  }
5228
  std::unique_ptr<ObjArray> coords( new ObjArray );
×
5229

5230
  std::vector<std::tuple<int, int>> points;
×
5231
  auto add_point = [&coords]( int x, int y )
×
5232
  {
5233
    std::unique_ptr<BStruct> point( new BStruct );
×
5234
    point->addMember( "x", new BLong( x ) );
×
5235
    point->addMember( "y", new BLong( y ) );
×
5236
    coords->addElement( point.release() );
×
5237
  };
×
5238

5239
  if ( radius == 0 )
×
5240
  {
5241
    add_point( xcenter, ycenter );
×
5242
    return coords.release();
×
5243
  }
5244

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

5263
  for ( const auto& p : q1 )
×
5264
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5265
  for ( const auto& p : q2 )
×
5266
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5267
  for ( const auto& p : q3 )
×
5268
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5269
  for ( const auto& p : q4 )
×
5270
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5271

5272
  return coords.release();
×
5273
}
×
5274

5275
size_t UOExecutorModule::sizeEstimate() const
14✔
5276
{
5277
  size_t size = sizeof( *this ) + Clib::memsize( reserved_items_ );
14✔
5278
  return size;
14✔
5279
}
5280
}  // namespace Module
5281
}  // 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