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

polserver / polserver / 21541532363

31 Jan 2026 08:14AM UTC coverage: 60.532% (+0.03%) from 60.507%
21541532363

push

github

web-flow
Tidy modernize for loops (#862)

* trigger loop convert

* Automated clang-tidy change: modernize-loop-convert

* fixed refactor

* Automated clang-tidy change: modernize-loop-convert

* compile

* first look through

* fixes and start to use a few ranges

* revert autogenerated file

* compilation fix

* second pass

* renamed loop variable

---------

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

164 of 447 new or added lines in 61 files covered. (36.69%)

6 existing lines in 5 files now uncovered.

44377 of 73312 relevant lines covered (60.53%)

499857.83 hits per line

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

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

67

68
#include "pol_global_config.h"
69

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

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

161
#include <module_defs/uo.h>
162

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

186
#define CONST_DEFAULT_ZRANGE 19
187

188
class EMenuObjImp final : public PolApplicObj<Menu>
189
{
190
public:
191
  EMenuObjImp( const Menu& m ) : PolApplicObj<Menu>( &menu_type, m ) {}
×
192
  const char* typeOf() const override { return "MenuRef"; }
×
193
  u8 typeOfInt() const override { return OTMenuRef; }
×
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
      return new BError( "Another script still attached." );
×
312
    }
313
    return new BError( "Another character still attached." );
×
314
  }
315
  return new BError( "Invalid parameter" );
×
316
}
317

318
BObjectImp* UOExecutorModule::mf_Detach()
×
319
{
320
  if ( attached_chr_ != nullptr )
×
321
  {
322
    passert( attached_chr_->script_ex == &uoexec() );
×
323
    attached_chr_->script_ex = nullptr;
×
324
    attached_chr_ = nullptr;
×
325
    return new BLong( 1 );
×
326
  }
327

328
  return new BLong( 0 );
×
329
}
330

331
static bool item_create_params_ok( u32 objtype, int amount )
6,161✔
332
{
333
  return ( objtype >= UOBJ_ITEM__LOWEST && objtype <= Plib::systemstate.config.max_objtype ) &&
6,161✔
334
         amount > 0 && amount <= 60000L;
12,322✔
335
}
336

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

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

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

382
        int newamount = item->getamount();
3✔
383
        newamount += amount;
3✔
384
        item->setamount( static_cast<unsigned short>( newamount ) );
3✔
385

386
        update_item_to_inrange( item );
3✔
387
        UpdateCharacterWeight( item );
3✔
388

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

396

397
        return new EItemRefObjImp( item );
3✔
398
      }
399
    }
9✔
400
  }
401
  else if ( amount != 1 && !force_stacking )
13✔
402
  {
403
    return new BError( "That item is not stackable.  Create one at a time." );
×
404
  }
405

406
  Item* item = Item::create( *descriptor );
37✔
407
  if ( item != nullptr )
37✔
408
  {
409
    ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
37✔
410
    item->setposition( cont->pos() );
37✔
411
    item->setamount( amount );
37✔
412

413
    if ( cont->can_add( *item ) )
37✔
414
    {
415
      if ( !descriptor->create_script.empty() )
36✔
416
      {
417
        BObjectImp* res =
418
            run_script_to_completion( descriptor->create_script, new EItemRefObjImp( item ) );
×
419
        if ( !res->isTrue() )
×
420
        {
421
          item->destroy();
×
422
          return res;
×
423
        }
424
        BObject ob( res );
×
425
        if ( !cont->can_add( *item ) )
×
426
        {  // UNTESTED
427
          item->destroy();
×
428
          return new BError( "Couldn't add item to container after create script ran" );
×
429
        }
430
      }
×
431

432
      // run before owner is found. No need to find owner if not even able to use slots.
433
      u8 slotIndex = item->slot_index();
36✔
434
      if ( !cont->can_add_to_slot( slotIndex ) )
36✔
435
      {
436
        item->destroy();
×
437
        return new BError( "No slots available in this container" );
×
438
      }
439
      if ( !item->slot_index( slotIndex ) )
36✔
440
      {
441
        item->destroy();
×
442
        return new BError( "Couldn't set slot index on item" );
×
443
      }
444

445
      // DAVE added this 11/17, call can/onInsert scripts for this container
446
      Character* chr_owner = cont->GetCharacterOwner();
36✔
447
      if ( chr_owner == nullptr )
36✔
448
        if ( uoemod != nullptr )
23✔
449
          chr_owner = uoemod->controller_.get();
23✔
450

451
      if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_CREATED, item ) )
36✔
452
      {
453
        item->destroy();
×
454
        // FIXME: try to propogate error from returned result of the canInsert script
455
        return new BError( "Could not insert item into container." );
×
456
      }
457
      if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
36✔
458
      {
459
        return new BError( "Item was destroyed in CanInsert Script" );
×
460
      }
461

462
      if ( !pos || !cont->is_legal_posn( pos.value() ) )
36✔
463
        pos = cont->get_random_location();
32✔
464
      cont->add( item, pos.value() );
36✔
465

466
      update_item_to_inrange( item );
36✔
467
      // DAVE added this 11/17, refresh owner's weight on item insert
468
      UpdateCharacterWeight( item );
36✔
469

470
      cont->on_insert_add_item( chr_owner, UContainer::MT_CORE_CREATED, item );
36✔
471
      if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
36✔
472
      {
473
        return new BError( "Item was destroyed in OnInsert Script" );
×
474
      }
475

476
      return new EItemRefObjImp( item );
36✔
477
    }
478

479
    item->destroy();
1✔
480
    return new BError( "That container is full" );
1✔
481
  }
37✔
482

483
  return new BError( "Failed to create that item type" );
×
484
}
485

486
BObjectImp* UOExecutorModule::mf_CreateItemInContainer()
36✔
487
{
488
  Item* item;
489
  const ItemDesc* descriptor;
490
  int amount;
491
  int px;
492
  int py;
493

494
  if ( getItemParam( 0, item ) && getObjtypeParam( 1, descriptor ) && getParam( 2, amount ) &&
72✔
495
       getParam( 3, px, -1, 65535 ) && getParam( 4, py, -1, 65535 ) &&
108✔
496
       item_create_params_ok( descriptor->objtype, amount ) )
36✔
497
  {
498
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
36✔
499
    {
500
      std::optional<Core::Pos2d> pos;
36✔
501
      if ( px >= 0 && py >= 0 )
36✔
502
        pos = Core::Pos2d( static_cast<u16>( px ), static_cast<u16>( py ) );
2✔
503
      return _create_item_in_container( static_cast<UContainer*>( item ), descriptor,
36✔
504
                                        static_cast<unsigned short>( amount ), false, pos, this );
36✔
505
    }
506

507
    return new BError( "That is not a container" );
×
508
  }
509

510
  return new BError( "A parameter was invalid" );
×
511
}
512

513
BObjectImp* UOExecutorModule::mf_CreateItemInInventory()
1✔
514
{
515
  Item* item;
516
  const ItemDesc* descriptor;
517
  int amount;
518
  int px;
519
  int py;
520

521
  if ( getItemParam( 0, item ) && getObjtypeParam( 1, descriptor ) && getParam( 2, amount ) &&
2✔
522
       getParam( 3, px, -1, 65535 ) && getParam( 4, py, -1, 65535 ) &&
3✔
523
       item_create_params_ok( descriptor->objtype, amount ) )
1✔
524
  {
525
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
1✔
526
    {
527
      std::optional<Core::Pos2d> pos;
1✔
528
      if ( px >= 0 && py >= 0 )
1✔
529
        pos = Core::Pos2d( static_cast<u16>( px ), static_cast<u16>( py ) );
1✔
530
      return _create_item_in_container( static_cast<UContainer*>( item ), descriptor,
1✔
531
                                        static_cast<unsigned short>( amount ), true, pos, this );
1✔
532
    }
533

534
    return new BError( "That is not a container" );
×
535
  }
536

537
  return new BError( "A parameter was invalid" );
×
538
}
539

540

541
BObjectImp* UOExecutorModule::mf_Broadcast()
×
542
{
543
  const char* text;
544
  unsigned short font;
545
  unsigned short color;
546
  unsigned short requiredCmdLevel;
547
  text = exec.paramAsString( 0 );
×
548
  if ( text && getParam( 1, font ) &&     // todo: getFontParam
×
549
       getParam( 2, color ) &&            // todo: getColorParam
×
550
       getParam( 3, requiredCmdLevel ) )  // todo: getRequiredCmdLevelParam
×
551
  {
552
    if ( !Bscript::String::hasUTF8Characters( text ) )
×
553
      Core::broadcast( text, font, color, requiredCmdLevel );
×
554
    else
555
      Core::broadcast_unicode( text, "ENU", font, color, requiredCmdLevel );
×
556
    return new BLong( 1 );
×
557
  }
558

559
  return nullptr;
×
560
}
561

562
/* Special containers (specifically, bankboxes, but probably any other
563
   "invisible" accessible container) seem to work thus:
564
   They sit in layer 0x1D.  The player is told to "wear_item" the item,
565
   then the gump and container contents are sent.
566
   We'll put a reference to this item in the character's additional_legal_items
567
   container, which is flushed whenever he moves.
568
   It might be better to actually put it in that layer, because that way
569
   it implicitly is at the same location (location of the wornitems container)
570
   */
571
BObjectImp* UOExecutorModule::mf_SendOpenSpecialContainer()
×
572
{
573
  Character* chr;
574
  Item* item;
575
  if ( !getCharacterParam( 0, chr ) || !getItemParam( 1, item ) )
×
576
  {
577
    return new BError( "Invalid parameter type" );
×
578
  }
579

580
  if ( !chr->has_active_client() )
×
581
  {
582
    return new BError( "No client attached" );
×
583
  }
584
  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
585
  {
586
    return new BError( "That isn't a container" );
×
587
  }
588

589
  u8 save_layer = item->layer;
×
590
  item->layer = LAYER_BANKBOX;
×
591
  send_wornitem( chr->client, chr, item );
×
592
  item->layer = save_layer;
×
593
  item->setposition( chr->pos() );
×
594
  item->double_click( chr->client );  // open the container on the client's screen
×
595
  chr->add_remote_container( item );
×
596

597
  return new BLong( 1 );
×
598
}
599

600

601
BObjectImp* UOExecutorModule::mf_SecureTradeWin()
×
602
{
603
  Character* chr;
604
  Character* chr2;
605
  if ( !getCharacterParam( 0, chr ) || !getCharacterParam( 1, chr2 ) )
×
606
  {
607
    return new BError( "Invalid parameter type." );
×
608
  }
609

610
  if ( chr == chr2 )
×
611
  {
612
    return new BError( "You can't trade with yourself." );
×
613
  }
614

615
  if ( !chr->has_active_client() )
×
616
  {
617
    return new BError( "No client attached." );
×
618
  }
619
  if ( !chr2->has_active_client() )
×
620
  {
621
    return new BError( "No client attached." );
×
622
  }
623

624
  return Core::open_trade_window( chr->client, chr2 );
×
625
}
626

627
BObjectImp* UOExecutorModule::mf_CloseTradeWindow()
×
628
{
629
  Character* chr;
630
  if ( !getCharacterParam( 0, chr ) )
×
631
  {
632
    return new BError( "Invalid parameter type." );
×
633
  }
634
  if ( !chr->trading_with )
×
635
    return new BError( "Mobile is not currently trading with anyone." );
×
636

637
  Core::cancel_trade( chr );
×
638
  return new BLong( 1 );
×
639
}
640

641
BObjectImp* UOExecutorModule::mf_SendViewContainer()
7✔
642
{
643
  Character* chr;
644
  Item* item;
645
  if ( !getCharacterParam( 0, chr ) || !getItemParam( 1, item ) )
7✔
646
  {
647
    return new BError( "Invalid parameter type" );
×
648
  }
649

650
  if ( !chr->has_active_client() )
7✔
651
  {
652
    return new BError( "No client attached" );
×
653
  }
654
  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
7✔
655
  {
656
    return new BError( "That isn't a container" );
×
657
  }
658
  UContainer* cont = static_cast<UContainer*>( item );
7✔
659
  chr->client->pause();
7✔
660
  send_open_gump( chr->client, *cont );
7✔
661
  send_container_contents( chr->client, *cont );
7✔
662
  chr->client->restart();
7✔
663
  return new BLong( 1 );
7✔
664
}
665

666
BObjectImp* UOExecutorModule::mf_FindObjtypeInContainer()
×
667
{
668
  Item* item;
669
  unsigned int objtype;
670
  int flags;
671
  if ( !getItemParam( 0, item ) || !getObjtypeParam( 1, objtype ) )
×
672
  {
673
    return new BError( "Invalid parameter type" );
×
674
  }
675
  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
676
  {
677
    return new BError( "That is not a container" );
×
678
  }
679
  if ( !getParam( 2, flags ) )
×
680
    flags = 0;
×
681

682
  UContainer* cont = static_cast<UContainer*>( item );
×
683
  Item* found = cont->find_objtype( objtype, flags );
×
684
  if ( found == nullptr )
×
685
    return new BError( "No items were found" );
×
686
  return new EItemRefObjImp( found );
×
687
}
688

689

690
BObjectImp* UOExecutorModule::mf_SendSysMessage()
×
691
{
692
  Character* chr;
693
  const String* ptext;
694
  unsigned short font;
695
  unsigned short color;
696

697
  if ( getCharacterParam( 0, chr ) && ( ( ptext = getStringParam( 1 ) ) != nullptr ) &&
×
698
       getParam( 2, font ) && getParam( 3, color ) )
×
699
  {
700
    if ( chr->has_active_client() )
×
701
    {
702
      if ( !ptext->hasUTF8Characters() )
×
703
        send_sysmessage( chr->client, ptext->data(), font, color );
×
704
      else
705
        Core::send_sysmessage_unicode( chr->client, ptext->value(), "ENU", font, color );
×
706
      return new BLong( 1 );
×
707
    }
708

709
    return new BError( "Mobile has no active client" );
×
710
  }
711

712
  return new BError( "Invalid parameter type" );
×
713
}
714

715
BObjectImp* UOExecutorModule::mf_PrintTextAbove()
1✔
716
{
717
  UObject* obj;
718
  const String* ptext;
719
  unsigned short font;
720
  unsigned short color;
721
  int journal_print;
722

723
  if ( getUObjectParam( 0, obj ) && getStringParam( 1, ptext ) && getParam( 2, font ) &&
2✔
724
       getParam( 3, color ) && getParam( 4, journal_print ) )
2✔
725
  {
726
    if ( !ptext->hasUTF8Characters() )
1✔
727
      return new BLong( say_above( obj, ptext->data(), font, color, journal_print ) );
1✔
728
    return new BLong( say_above_unicode( obj, ptext->value(), "ENU", font, color, journal_print ) );
×
729
  }
730

731
  return new BError( "A parameter was invalid" );
×
732
}
733
BObjectImp* UOExecutorModule::mf_PrintTextAbovePrivate()
×
734
{
735
  Character* chr;
736
  UObject* obj;
737
  const String* ptext;
738
  unsigned short font;
739
  unsigned short color;
740
  int journal_print;
741

742
  if ( getUObjectParam( 0, obj ) && getStringParam( 1, ptext ) && getCharacterParam( 2, chr ) &&
×
743
       getParam( 3, font ) && getParam( 4, color ) && getParam( 5, journal_print ) )
×
744
  {
745
    if ( !ptext->hasUTF8Characters() )
×
746
      return new BLong( private_say_above( chr, obj, ptext->data(), font, color, journal_print ) );
×
747
    return new BLong( private_say_above_unicode( chr, obj, ptext->value(), "ENU", font, color ) );
×
748
  }
749

750
  return new BError( "A parameter was invalid" );
×
751
}
752

753
// const int TGTOPT_NOCHECK_LOS = 0x0000; // currently unused
754
const int TGTOPT_CHECK_LOS = 0x0001;
755
const int TGTOPT_HARMFUL = 0x0002;
756
const int TGTOPT_HELPFUL = 0x0004;
757
const int TGTOPT_ALLOW_NONLOCAL = 0x0008;
758

759
void handle_script_cursor( Character* chr, UObject* obj )
3✔
760
{
761
  if ( chr != nullptr && chr->client->gd->target_cursor_uoemod != nullptr )
3✔
762
  {
763
    auto& uoex = chr->client->gd->target_cursor_uoemod->uoexec();
3✔
764
    if ( obj != nullptr )
3✔
765
    {
766
      if ( obj->ismobile() )
2✔
767
      {
768
        Character* targetted_chr = static_cast<Character*>( obj );
1✔
769
        if ( chr->client->gd->target_cursor_uoemod->target_options & TGTOPT_HARMFUL )
1✔
770
        {
771
          targetted_chr->inform_engaged( chr );
1✔
772
          chr->repsys_on_attack( targetted_chr );
1✔
773
        }
774
        else if ( chr->client->gd->target_cursor_uoemod->target_options & TGTOPT_HELPFUL )
×
775
        {
776
          chr->repsys_on_help( targetted_chr );
×
777
        }
778
      }
779
      uoex.ValueStack.back().set( new BObject( obj->make_ref() ) );
2✔
780
    }
781
    // even on cancel, we wake the script up.
782
    uoex.revive();
3✔
783
    chr->client->gd->target_cursor_uoemod->target_cursor_chr = nullptr;
3✔
784
    chr->client->gd->target_cursor_uoemod = nullptr;
3✔
785
  }
786
}
3✔
787

788

789
BObjectImp* UOExecutorModule::mf_Target()
3✔
790
{
791
  Character* chr;
792
  if ( !getCharacterParam( 0, chr ) )
3✔
793
  {
794
    return new BError( "Invalid parameter type" );
×
795
  }
796
  if ( !chr->has_active_client() )
3✔
797
  {
798
    return new BError( "No client connected" );
×
799
  }
800
  if ( chr->target_cursor_busy() )
3✔
801
  {
802
    return new BError( "Client busy with another target cursor" );
×
803
  }
804

805
  if ( !getParam( 1, target_options ) )
3✔
806
    target_options = TGTOPT_CHECK_LOS;
×
807

808
  PKTBI_6C::CURSOR_TYPE crstype;
809

810
  if ( target_options & TGTOPT_HARMFUL )
3✔
811
    crstype = PKTBI_6C::CURSOR_TYPE_HARMFUL;
1✔
812
  else if ( target_options & TGTOPT_HELPFUL )
2✔
813
    crstype = PKTBI_6C::CURSOR_TYPE_HELPFUL;
×
814
  else
815
    crstype = PKTBI_6C::CURSOR_TYPE_NEUTRAL;
2✔
816

817
  if ( !uoexec().suspend() )
3✔
818
  {
819
    DEBUGLOGLN(
×
820
        "Script Error in '{}' PC={}: \n"
821
        "\tCall to function UO::Target():\n"
822
        "\tThe execution of this script can't be blocked!",
823
        scriptname(), exec.PC );
×
824
    return new Bscript::BError( "Script can't be blocked" );
×
825
  }
826

827
  TargetCursor* tgt_cursor = nullptr;
3✔
828

829
  bool is_los_checked = ( target_options & TGTOPT_CHECK_LOS ) && !chr->ignores_line_of_sight();
3✔
830
  bool allow_nonlocal = ( target_options & TGTOPT_ALLOW_NONLOCAL );
3✔
831
  if ( is_los_checked )
3✔
832
  {
833
    if ( allow_nonlocal )
1✔
834
    {
835
      tgt_cursor = &gamestate.target_cursors.los_checked_allow_nonlocal_script_cursor;
1✔
836
    }
837
    else
838
    {
839
      tgt_cursor = &gamestate.target_cursors.los_checked_script_cursor;
×
840
    }
841
  }
842
  else
843
  {
844
    if ( allow_nonlocal )
2✔
845
    {
846
      tgt_cursor = &gamestate.target_cursors.nolos_checked_allow_nonlocal_script_cursor;
1✔
847
    }
848
    else
849
    {
850
      tgt_cursor = &gamestate.target_cursors.nolos_checked_script_cursor;
1✔
851
    }
852
  }
853

854
  tgt_cursor->send_object_cursor( chr->client, crstype );
3✔
855

856
  chr->client->gd->target_cursor_uoemod = this;
3✔
857
  target_cursor_chr = chr;
3✔
858

859
  return new BLong( 0 );
3✔
860
}
861

862
BObjectImp* UOExecutorModule::mf_CancelTarget()
×
863
{
864
  Character* chr;
865
  if ( getCharacterParam( 0, chr ) )
×
866
  {
867
    if ( chr->has_active_client() )
×
868
    {
869
      if ( chr->target_cursor_busy() )
×
870
      {
871
        Network::PktHelper::PacketOut<Network::PktOut_6C> msg;
×
872
        msg->Write<u8>( PKTBI_6C::UNK1_00 );
×
873
        msg->offset += 4;  // u32 target_cursor_serial
×
874
        msg->Write<u8>( 0x3u );
×
875
        // rest 0
876
        msg.Send( chr->client, sizeof msg->buffer );
×
877
        return new BLong( 0 );
×
878
      }
×
879

880
      return new BError( "Client does not have an active target cursor" );
×
881
    }
882

883
    return new BError( "No client connected" );
×
884
  }
885

886
  return new BError( "Invalid parameter type" );
×
887
}
888

889
void handle_coord_cursor( Character* chr, PKTBI_6C* msg )
1✔
890
{
891
  if ( chr != nullptr && chr->client->gd->target_cursor_uoemod != nullptr )
1✔
892
  {
893
    auto& uoex = chr->client->gd->target_cursor_uoemod->uoexec();
1✔
894
    if ( msg != nullptr )
1✔
895
    {
896
      auto pos = Core::Pos3d( cfBEu16( msg->x ), cfBEu16( msg->y ), msg->z );
1✔
897
      BStruct* arr = new BStruct;
1✔
898
      arr->addMember( "x", new BLong( pos.x() ) );
1✔
899
      arr->addMember( "y", new BLong( pos.y() ) );
1✔
900
      arr->addMember( "z", new BLong( pos.z() ) );
1✔
901
      //      FIXME: Objtype CANNOT be trusted! Scripts must validate this, or, we must
902
      //      validate right here. Should we check map/static, or let that reside
903
      //      for scripts to run ListStatics? In theory, using Injection or similar,
904
      //      you could mine where no mineable tiles are by faking objtype in packet
905
      //      and still get the resources?
906
      arr->addMember( "objtype", new BLong( cfBEu16( msg->graphic ) ) );
1✔
907

908
      u32 selected_serial = cfBEu32( msg->selected_serial );
1✔
909
      if ( selected_serial )
1✔
910
      {
911
        UObject* obj = system_find_object( selected_serial );
×
912
        if ( obj )
×
913
        {
914
          arr->addMember( obj->target_tag(), obj->make_ref() );
×
915
        }
916
      }
917

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

922
      Multi::UMulti* multi = chr->realm()->find_supporting_multi( pos );
1✔
923
      if ( multi != nullptr )
1✔
924
        arr->addMember( "multi", multi->make_ref() );
×
925

926
      uoex.ValueStack.back().set( new BObject( arr ) );
1✔
927
    }
928

929
    uoex.revive();
1✔
930
    chr->client->gd->target_cursor_uoemod->target_cursor_chr = nullptr;
1✔
931
    chr->client->gd->target_cursor_uoemod = nullptr;
1✔
932
  }
933
}
1✔
934

935
BObjectImp* UOExecutorModule::mf_TargetCoordinates()
1✔
936
{
937
  Character* chr;
938
  if ( !getCharacterParam( 0, chr ) )
1✔
939
  {
940
    return new BError( "Invalid parameter type" );
×
941
  }
942
  if ( !chr->has_active_client() )
1✔
943
  {
944
    return new BError( "Mobile has no active client" );
×
945
  }
946
  if ( chr->target_cursor_busy() )
1✔
947
  {
948
    return new BError( "Client has an active target cursor" );
×
949
  }
950

951
  if ( !uoexec().suspend() )
1✔
952
  {
953
    DEBUGLOGLN(
×
954
        "Script Error in '{}' PC={}: \n"
955
        "\tCall to function UO::TargetCoordinates():\n"
956
        "\tThe execution of this script can't be blocked!",
957
        scriptname(), exec.PC );
×
958
    return new Bscript::BError( "Script can't be blocked" );
×
959
  }
960

961
  gamestate.target_cursors.script_cursor2.send_coord_cursor( chr->client );
1✔
962
  chr->client->gd->target_cursor_uoemod = this;
1✔
963
  target_cursor_chr = chr;
1✔
964
  return new BLong( 0 );
1✔
965
}
966

967
BObjectImp* UOExecutorModule::mf_TargetMultiPlacement()
×
968
{
969
  Character* chr;
970
  unsigned int objtype, hue;
971
  int flags;
972
  int xoffset, yoffset;
973
  if ( !( getCharacterParam( 0, chr ) && getObjtypeParam( 1, objtype ) && getParam( 2, flags ) &&
×
974
          getParam( 3, xoffset ) && getParam( 4, yoffset ) && getParam( 5, hue ) ) )
×
975
  {
976
    return new BError( "Invalid parameter type" );
×
977
  }
978

979

980
  if ( !chr->has_active_client() )
×
981
  {
982
    return new BError( "No client attached" );
×
983
  }
984

985
  if ( chr->target_cursor_busy() )
×
986
  {
987
    return new BError( "Client busy with another target cursor" );
×
988
  }
989

990
  if ( find_itemdesc( objtype ).type != ItemDesc::BOATDESC &&
×
991
       find_itemdesc( objtype ).type != ItemDesc::HOUSEDESC )
×
992
  {
993
    return new BError( "Object Type is out of range for Multis" );
×
994
  }
995

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

1006
  chr->client->gd->target_cursor_uoemod = this;
×
1007
  target_cursor_chr = chr;
×
1008

1009
  gamestate.target_cursors.multi_placement_cursor.send_placemulti(
×
1010
      chr->client, objtype, flags, (s16)xoffset, (s16)yoffset, hue );
1011

1012
  return new BLong( 0 );
×
1013
}
1014

1015
BObjectImp* UOExecutorModule::mf_GetObjType()
×
1016
{
1017
  Item* item;
1018
  Character* chr;
1019

1020
  if ( getItemParam( 0, item ) )
×
1021
  {
1022
    return new BLong( item->objtype_ );
×
1023
  }
1024
  if ( getCharacterParam( 0, chr ) )
×
1025
  {
1026
    return new BLong( chr->objtype_ );
×
1027
  }
1028

1029
  return new BLong( 0 );
×
1030
}
1031

1032
// FIXME character needs an Accessible that takes an Item*
1033
BObjectImp* UOExecutorModule::mf_Accessible()
×
1034
{
1035
  Character* chr;
1036
  Item* item;
1037
  int range;
1038

1039
  if ( getCharacterParam( 0, chr ) && getItemParam( 1, item ) )
×
1040
  {
1041
    // Range defaults to -1 if it's not defined in the .em file (old scripts)
1042
    // or the user provides a weird object.
1043
    if ( exec.numParams() < 3 || !getParam( 2, range ) )
×
1044
      range = -1;
×
1045

1046
    if ( chr->can_access( item, range ) )
×
1047
      return new BLong( 1 );
×
1048
    return new BLong( 0 );
×
1049
  }
1050
  return new BLong( 0 );
×
1051
}
1052

1053
BObjectImp* UOExecutorModule::mf_GetAmount()
×
1054
{
1055
  Item* item;
1056

1057
  if ( getItemParam( 0, item ) )
×
1058
  {
1059
    return new BLong( item->getamount() );
×
1060
  }
1061

1062
  return new BLong( 0 );
×
1063
}
1064

1065

1066
// FIXME, copy-pasted from NPC, copy-pasted to CreateItemInContainer
1067
BObjectImp* UOExecutorModule::mf_CreateItemInBackpack()
3✔
1068
{
1069
  Character* chr;
1070
  const ItemDesc* descriptor;
1071
  unsigned short amount;
1072
  int px;
1073
  int py;
1074

1075
  if ( getCharacterParam( 0, chr ) && getObjtypeParam( 1, descriptor ) && getParam( 2, amount ) &&
6✔
1076
       getParam( 3, px, -1, 65535 ) && getParam( 4, py, -1, 65535 ) &&
9✔
1077
       item_create_params_ok( descriptor->objtype, amount ) )
3✔
1078
  {
1079
    UContainer* backpack = chr->backpack();
3✔
1080
    if ( backpack != nullptr )
3✔
1081
    {
1082
      std::optional<Core::Pos2d> pos;
3✔
1083
      if ( px >= 0 && py >= 0 )
3✔
1084
        pos = Core::Pos2d( static_cast<u16>( px ), static_cast<u16>( py ) );
1✔
1085
      return _create_item_in_container( backpack, descriptor, amount, false, pos, this );
3✔
1086
    }
1087

1088
    return new BError( "Character has no backpack." );
×
1089
  }
1090

1091
  return new BError( "A parameter was invalid." );
×
1092
}
1093

1094
BObjectImp* _complete_create_item_at_location( Item* item, const Core::Pos4d& pos )
6,121✔
1095
{
1096
  item->setposition( pos );
6,121✔
1097

1098
  // ITEMDESCTODO: use the original descriptor
1099
  const ItemDesc& id = find_itemdesc( item->objtype_ );
6,121✔
1100
  if ( !id.create_script.empty() )
6,121✔
1101
  {
1102
    BObjectImp* res = run_script_to_completion( id.create_script, new EItemRefObjImp( item ) );
×
1103
    if ( !res->isTrue() )
×
1104
    {
1105
      item->destroy();
×
1106
      return res;
×
1107
    }
1108

1109
    BObject ob( res );
×
1110
  }
×
1111

1112
  update_item_to_inrange( item );
6,121✔
1113
  add_item_to_world( item );
6,121✔
1114
  register_with_supporting_multi( item );
6,121✔
1115
  return new EItemRefObjImp( item );
6,121✔
1116
}
1117

1118
BObjectImp* UOExecutorModule::mf_CreateItemAtLocation( /* x,y,z,objtype,amount,realm */ )
6,121✔
1119
{
1120
  Core::Pos4d pos;
6,121✔
1121
  const ItemDesc* itemdesc;
1122
  unsigned short amount;
1123
  if ( getPos4dParam( 0, 1, 2, 5, &pos ) && getObjtypeParam( 3, itemdesc ) &&
12,242✔
1124
       getParam( 4, amount, 1, 60000 ) && item_create_params_ok( itemdesc->objtype, amount ) )
12,242✔
1125
  {
1126
    if ( !( Plib::tile_flags( itemdesc->graphic ) & Plib::FLAG::STACKABLE ) && ( amount != 1 ) )
6,121✔
1127
    {
1128
      return new BError( "That item is not stackable.  Create one at a time." );
×
1129
    }
1130

1131
    Item* item = Item::create( *itemdesc );
6,121✔
1132
    if ( item != nullptr )
6,121✔
1133
    {
1134
      item->setamount( amount );
6,121✔
1135
      return _complete_create_item_at_location( item, pos );
6,121✔
1136
    }
1137

1138
    return new BError( "Unable to create item of objtype " + Clib::hexint( itemdesc->objtype ) );
×
1139
  }
1140
  return new BError( "Invalid parameter type" );
×
1141
}
1142

1143
BObjectImp* UOExecutorModule::mf_CreateItemCopyAtLocation( /* x,y,z,item,realm */ )
×
1144
{
1145
  Core::Pos4d pos;
×
1146
  Item* origitem;
1147
  if ( getPos4dParam( 0, 1, 2, 4, &pos ) && getItemParam( 3, origitem ) )
×
1148
  {
1149
    if ( origitem->script_isa( POLCLASS_MULTI ) )
×
1150
      return new BError( "This function does not work with Multi objects." );
×
1151

1152
    Item* item = origitem->clone();
×
1153
    if ( item != nullptr )
×
1154
    {
1155
      return _complete_create_item_at_location( item, pos );
×
1156
    }
1157

1158
    return new BError( "Unable to clone item" );
×
1159
  }
1160

1161
  return new BError( "Invalid parameter type" );
×
1162
}
1163

1164
BObjectImp* UOExecutorModule::mf_CreateMultiAtLocation( /* x,y,z,objtype,flags,realm */ )
34✔
1165
{
1166
  Core::Pos4d pos;
34✔
1167
  const ItemDesc* descriptor;
1168
  int flags = 0;
34✔
1169
  if ( !( getPos4dParam( 0, 1, 2, 5, &pos ) && getParam( 4, flags ) &&
68✔
1170
          getObjtypeParam( 3, descriptor ) ) )
34✔
1171
  {
1172
    return new BError( "Invalid parameter type" );
×
1173
  }
1174
  if ( descriptor->type != ItemDesc::BOATDESC && descriptor->type != ItemDesc::HOUSEDESC )
34✔
1175
  {
1176
    return new BError( "That objtype is not a Multi" );
×
1177
  }
1178

1179
  return Multi::UMulti::scripted_create( *descriptor, pos, flags );
34✔
1180
}
1181

1182
void replace_properties( Clib::ConfigElem& elem, BStruct* custom )
6✔
1183
{
1184
  for ( const auto& [name, refobj] : custom->contents() )
46✔
1185
  {
1186
    BObjectImp* ref = refobj->impptr();
40✔
1187

1188
    if ( name == "CProps" )
40✔
1189
    {
1190
      if ( auto* cpropdict = impptrIf<BDictionary>( ref ) )
5✔
1191
      {
1192
        const BDictionary::Contents& cprop_cont = cpropdict->contents();
5✔
1193
        for ( const auto& [key, valueobj] : cprop_cont )
10✔
1194
        {
1195
          elem.add_prop( "cprop", ( key->getStringRep() + "\t" + valueobj->impptr()->pack() ) );
5✔
1196
        }
1197
      }
1198
      else
1199
      {
1200
        throw std::runtime_error( "NPC override_properties: CProps must be a dictionary, but is: " +
×
1201
                                  std::string( ref->typeOf() ) );
×
1202
      }
1203
    }
1204
    else
1205
    {
1206
      elem.clear_prop( name.c_str() );
35✔
1207
      elem.add_prop( name, ref->getStringRep() );
35✔
1208
    }
1209
  }
1210
}
6✔
1211

1212
BObjectImp* UOExecutorModule::mf_CreateNpcFromTemplate()
74✔
1213
{
1214
  const String* tmplname;
1215
  Core::Pos4d pos;
74✔
1216
  int forceInt;
1217
  bool forceLocation;
1218
  if ( !( getStringParam( 0, tmplname ) && getPos4dParam( 1, 2, 3, 5, &pos ) ) )
74✔
1219
  {
1220
    return new BError( "Invalid parameter type" );
×
1221
  }
1222
  BObjectImp* imp = getParamImp( 4 );
74✔
1223
  BStruct* custom_struct = nullptr;
74✔
1224
  if ( imp->isa( BObjectImp::OTLong ) )
74✔
1225
  {
1226
    custom_struct = nullptr;
68✔
1227
  }
1228
  else if ( imp->isa( BObjectImp::OTStruct ) )
6✔
1229
  {
1230
    custom_struct = static_cast<BStruct*>( imp );
6✔
1231
  }
1232
  else
1233
  {
1234
    return new BError( std::string( "Parameter 4 must be a Struct or Integer(0), got " ) +
×
1235
                       BObjectImp::typestr( imp->type() ) );
×
1236
  }
1237
  if ( !getParam( 6, forceInt ) )
74✔
1238
  {
1239
    forceLocation = false;
×
1240
  }
1241
  else
1242
  {
1243
    forceLocation = forceInt ? true : false;
74✔
1244
  }
1245

1246
  Clib::ConfigElem elem;
74✔
1247
  bool found = FindNpcTemplate( tmplname->data(), elem );
74✔
1248

1249
  if ( !found )
74✔
1250
  {
1251
    return new BError( "NPC template '" + tmplname->value() + "' not found" );
×
1252
  }
1253
  Plib::MOVEMODE movemode = Character::decode_movemode( elem.read_string( "MoveMode", "L" ) );
74✔
1254

1255
  short newz;
1256
  Multi::UMulti* dummy_multi = nullptr;
74✔
1257
  Item* dummy_walkon = nullptr;
74✔
1258
  if ( !pos.realm()->walkheight( pos.xy(), pos.z(), &newz, &dummy_multi, &dummy_walkon, true,
74✔
1259
                                 movemode ) )
1260
  {
1261
    if ( !forceLocation )
2✔
1262
      return new BError( "Not a valid location for an NPC!" );
×
1263
  }
1264
  if ( !forceLocation )
74✔
1265
    pos.z( Clib::clamp_convert<s8>( newz ) );
70✔
1266

1267

1268
  NpcRef npc;
74✔
1269
  // readProperties can throw, if stuff is missing.
1270
  try
1271
  {
1272
    npc.set( new NPC( elem.remove_ushort( "OBJTYPE" ), elem ) );
74✔
1273
    npc->set_dirty();
74✔
1274
    elem.clear_prop( "Serial" );
74✔
1275
    elem.clear_prop( "X" );
74✔
1276
    elem.clear_prop( "Y" );
74✔
1277
    elem.clear_prop( "Z" );
74✔
1278

1279
    elem.add_prop( "Serial", GetNextSerialNumber() );
74✔
1280
    // FIXME sanity check
1281
    elem.add_prop( "X", pos.x() );
74✔
1282
    elem.add_prop( "Y", pos.y() );
74✔
1283
    elem.add_prop( "Z", (s16)pos.z() );
74✔
1284
    elem.add_prop( "Realm", pos.realm()->name() );
74✔
1285
    if ( custom_struct != nullptr )
74✔
1286
      replace_properties( elem, custom_struct );
6✔
1287
    npc->readPropertiesForNewNPC( elem );
74✔
1288
    ////HASH
1289
    objStorageManager.objecthash.Insert( npc.get() );
74✔
1290
    ////
1291

1292

1293
    // characters.push_back( npc.get() );
1294
    SetCharacterWorldPosition( npc.get(), Realms::WorldChangeReason::NpcCreate );
74✔
1295
    WorldIterator<OnlinePlayerFilter>::InMaxVisualRange(
74✔
1296
        npc.get(),
74✔
1297
        [&]( Character* zonechr )
74✔
1298
        {
1299
          if ( zonechr->in_visual_range( npc.get() ) )
23✔
1300
            send_char_data( zonechr->client, npc.get() );
21✔
1301
        } );
23✔
1302
    pos.realm()->notify_entered( *npc );
74✔
1303
    if ( dummy_multi )
74✔
1304
    {
1305
      dummy_multi->register_object( npc.get() );
9✔
1306
      if ( npc->registered_multi == 0 )
9✔
1307
      {
1308
        npc->registered_multi = dummy_multi->serial;
9✔
1309
        dummy_multi->walk_on( npc.get() );
9✔
1310
      }
1311
    }
1312
    else
1313
    {
1314
      if ( npc->registered_multi > 0 )
65✔
1315
      {
1316
        Multi::UMulti* multi = system_find_multi( npc->registered_multi );
×
1317
        if ( multi != nullptr )
×
1318
        {
1319
          multi->unregister_object( npc.get() );
×
1320
        }
1321
        npc->registered_multi = 0;
×
1322
      }
1323
    }
1324
    return new ECharacterRefObjImp( npc.get() );
74✔
1325
  }
1326
  catch ( std::exception& ex )
×
1327
  {
1328
    if ( npc.get() != nullptr )
×
1329
      npc->destroy();
×
1330
    return new BError( "Exception detected trying to create npc from template '" +
×
1331
                       tmplname->value() + "': " + ex.what() );
×
1332
  }
×
1333
}
74✔
1334

1335

1336
BObjectImp* UOExecutorModule::mf_SubtractAmount()
×
1337
{
1338
  Item* item;
1339
  unsigned short amount;
1340

1341
  if ( getItemParam( 0, item ) && getParam( 1, amount, 1, item->itemdesc().stack_limit ) )
×
1342
  {
1343
    if ( item->has_gotten_by() )
×
1344
      item->gotten_by()->clear_gotten_item();
×
1345
    else if ( item->inuse() && !is_reserved_to_me( item ) )
×
1346
      return new BError( "That item is being used." );
×
1347
    subtract_amount_from_item( item, amount );
×
1348
    return new BLong( 1 );
×
1349
  }
1350

1351
  return new BError( "Invalid parameter type" );
×
1352
}
1353

1354
BObjectImp* UOExecutorModule::mf_AddAmount()
×
1355
{
1356
  Item* item;
1357
  unsigned short amount;
1358

1359
  if ( getItemParam( 0, item ) && getParam( 1, amount, 1, MAX_STACK_ITEMS ) )
×
1360
  {
1361
    if ( item->inuse() && !is_reserved_to_me( item ) )
×
1362
    {
1363
      return new BError( "That item is being used." );
×
1364
    }
1365
    if ( ~Plib::tile_flags( item->graphic ) & Plib::FLAG::STACKABLE )
×
1366
    {
1367
      return new BError( "That item type is not stackable." );
×
1368
    }
1369
    if ( !item->can_add_to_self( amount, false ) )
×
1370
    {
1371
      return new BError( "Can't add that much to that stack" );
×
1372
    }
1373

1374
    unsigned short newamount = item->getamount();
×
1375
    newamount += amount;
×
1376
    item->setamount( newamount );
×
1377
    update_item_to_inrange( item );
×
1378

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

1382
    return new EItemRefObjImp( item );
×
1383
  }
1384

1385
  return new BError( "Invalid parameter type" );
×
1386
}
1387

1388
BObjectImp* UOExecutorModule::mf_PerformAction()
×
1389
{
1390
  Character* chr;
1391
  unsigned short actionval;
1392
  unsigned short framecount, repeatcount;
1393
  unsigned short backward, repeatflag, delay;
1394
  if ( getCharacterParam( 0, chr ) && getParam( 1, actionval ) && getParam( 2, framecount ) &&
×
1395
       getParam( 3, repeatcount ) && getParam( 4, backward ) && getParam( 5, repeatflag ) &&
×
1396
       getParam( 6, delay ) )
×
1397
  {
1398
    if ( !UACTION_IS_VALID( actionval ) )
×
1399
    {
1400
      return new BError( "Invalid parameter 'action'" );
×
1401
    }
1402

1403
    UACTION action = static_cast<UACTION>( actionval );
×
1404
    send_action_to_inrange(
×
1405
        chr, action, framecount, repeatcount, static_cast<DIRECTION_FLAG_OLD>( backward ),
1406
        static_cast<REPEAT_FLAG_OLD>( repeatflag ), static_cast<unsigned char>( delay ) );
1407
    return new BLong( 1 );
×
1408
  }
1409

1410
  return new BError( "Invalid parameter" );
×
1411
}
1412

1413
BObjectImp* UOExecutorModule::mf_PlaySoundEffect()
×
1414
{
1415
  UObject* chr;
1416
  int effect;
1417
  if ( getUObjectParam( 0, chr ) && getParam( 1, effect ) )
×
1418
  {
1419
    play_sound_effect( chr, static_cast<u16>( effect ) );
×
1420
    return new BLong( 1 );
×
1421
  }
1422

1423
  return new BError( "Invalid parameter" );
×
1424
}
1425

1426
BObjectImp* UOExecutorModule::mf_PlaySoundEffectPrivate()
×
1427
{
1428
  UObject* center;
1429
  int effect;
1430
  Character* forchr;
1431
  if ( getUObjectParam( 0, center ) && getParam( 1, effect ) && getCharacterParam( 2, forchr ) )
×
1432
  {
1433
    play_sound_effect_private( center, static_cast<u16>( effect ), forchr );
×
1434
    return new BLong( 1 );
×
1435
  }
1436

1437
  return new BError( "Invalid parameter" );
×
1438
}
1439

1440
BObjectImp* UOExecutorModule::mf_PlaySoundEffectXYZ()
×
1441
{
1442
  Core::Pos4d center;
×
1443
  int effect;
1444
  if ( getPos4dParam( 0, 1, 2, 4, &center ) && getParam( 3, effect ) )
×
1445
  {
1446
    play_sound_effect_xyz( center, static_cast<u16>( effect ) );
×
1447
    return new BLong( 1 );
×
1448
  }
1449

1450
  return new BError( "Invalid parameter" );
×
1451
}
1452

1453
BObjectImp* UOExecutorModule::mf_PlayMusic( /* char, music_id */ )
×
1454
{
1455
  Character* chr;
1456
  int musicid;
1457
  if ( getCharacterParam( 0, chr ) && getParam( 1, musicid ) )
×
1458
  {
1459
    send_midi( chr->client, static_cast<u16>( musicid ) );
×
1460
    return new BLong( 1 );
×
1461
  }
1462

1463
  return new BError( "Invalid parameter" );
×
1464
}
1465

1466
void menu_selection_made( Network::Client* client, MenuItem* mi, PKTIN_7D* msg )
×
1467
{
1468
  if ( client != nullptr )
×
1469
  {
1470
    Character* chr = client->chr;
×
1471
    if ( chr != nullptr && chr->client->gd->menu_selection_uoemod != nullptr )
×
1472
    {
1473
      auto& uoex = chr->client->gd->menu_selection_uoemod->uoexec();
×
1474
      if ( mi != nullptr && msg != nullptr )
×
1475
      {
1476
        BStruct* selection = new BStruct;
×
1477
        // FIXME should make sure objtype and choice are within valid range.
1478
        selection->addMember( "objtype", new BLong( mi->objtype_ ) );
×
1479
        selection->addMember( "graphic", new BLong( mi->graphic_ ) );
×
1480
        selection->addMember( "index",
×
1481
                              new BLong( cfBEu16( msg->choice ) ) );  // this has been validated
×
1482
        selection->addMember( "color", new BLong( mi->color_ ) );
×
1483
        uoex.ValueStack.back().set( new BObject( selection ) );
×
1484
      }
1485
      // 0 is already on the value stack, for the case of cancellation.
1486
      uoex.revive();
×
1487
      chr->client->gd->menu_selection_uoemod->menu_selection_chr = nullptr;
×
1488
      chr->client->gd->menu_selection_uoemod = nullptr;
×
1489
    }
1490
  }
1491
}
×
1492

1493
bool UOExecutorModule::getDynamicMenuParam( unsigned param, Menu*& menu )
×
1494
{
1495
  BApplicObjBase* aob = getApplicObjParam( param, &menu_type );
×
1496
  if ( aob != nullptr )
×
1497
  {
1498
    EMenuObjImp* menu_imp = static_cast<EMenuObjImp*>( aob );
×
1499
    menu = &menu_imp->value();
×
1500
    return true;
×
1501
  }
1502

1503
  return false;
×
1504
}
1505

1506
bool UOExecutorModule::getStaticOrDynamicMenuParam( unsigned param, Menu*& menu )
×
1507
{
1508
  BObjectImp* imp = getParamImp( param );
×
1509
  if ( imp->isa( BObjectImp::OTString ) )
×
1510
  {
1511
    String* pmenuname = static_cast<String*>( imp );
×
1512
    menu = Menu::find_menu( pmenuname->data() );
×
1513
    return ( menu != nullptr );
×
1514
  }
1515
  if ( imp->isa( BObjectImp::OTApplicObj ) )
×
1516
  {
1517
    BApplicObjBase* aob = static_cast<BApplicObjBase*>( imp );
×
1518
    if ( aob->object_type() == &menu_type )
×
1519
    {
1520
      EMenuObjImp* menu_imp = static_cast<EMenuObjImp*>( aob );
×
1521
      menu = &menu_imp->value();
×
1522
      return true;
×
1523
    }
1524
  }
1525
  DEBUGLOGLN( "SelectMenuItem: expected a menu name (static menu) or a CreateMenu() dynamic menu" );
×
1526
  return false;
×
1527
}
1528

1529
BObjectImp* UOExecutorModule::mf_SelectMenuItem2()
×
1530
{
1531
  Character* chr;
1532
  Menu* menu;
1533

1534
  if ( !getCharacterParam( 0, chr ) || !getStaticOrDynamicMenuParam( 1, menu ) ||
×
1535
       ( chr->client->gd->menu_selection_uoemod != nullptr ) )
×
1536
  {
1537
    return new BError( "Invalid parameter" );
×
1538
  }
1539

1540
  if ( menu == nullptr || !chr->has_active_client() || menu->menuitems_.empty() )
×
1541
  {
1542
    return new BError( "Client is busy, or menu is empty" );
×
1543
  }
1544

1545
  if ( !send_menu( chr->client, menu ) )
×
1546
  {
1547
    return new BError( "Menu too large" );
×
1548
  }
1549

1550
  if ( !uoexec().suspend() )
×
1551
  {
1552
    DEBUGLOGLN(
×
1553
        "Script Error in '{}' PC={}: \n"
1554
        "\tCall to function UO::SelectMenuItem():\n"
1555
        "\tThe execution of this script can't be blocked!",
1556
        scriptname(), exec.PC );
×
1557
    return new Bscript::BError( "Script can't be blocked" );
×
1558
  }
1559

1560
  chr->client->gd->menu = menu->getWeakPtr();
×
1561
  chr->client->gd->on_menu_selection = menu_selection_made;
×
1562
  chr->client->gd->menu_selection_uoemod = this;
×
1563
  menu_selection_chr = chr;
×
1564

1565
  return new BLong( 0 );
×
1566
}
1567

1568
void append_objtypes( ObjArray* objarr, Menu* menu )
×
1569
{
NEW
1570
  for ( const auto& menuitem : menu->menuitems_ )
×
1571
  {
NEW
1572
    if ( menuitem.submenu_id )
×
1573
    {
1574
      // Code Analyze: Commented out and replaced with tmp_menu due to hiding
1575
      // menu passed to function.
1576
      //      Menu* menu = find_menu( mi->submenu_id );
NEW
1577
      Menu* tmp_menu = Menu::find_menu( menuitem.submenu_id );
×
1578
      if ( tmp_menu != nullptr )
×
1579
        append_objtypes( objarr, tmp_menu );
×
1580
    }
1581
    else
1582
    {
NEW
1583
      objarr->addElement( new BLong( menuitem.objtype_ ) );
×
1584
    }
1585
  }
1586
}
×
1587

1588
BObjectImp* UOExecutorModule::mf_GetMenuObjTypes()
×
1589
{
1590
  Menu* menu;
1591
  if ( getStaticOrDynamicMenuParam( 0, menu ) )
×
1592
  {
1593
    std::unique_ptr<ObjArray> objarr( new ObjArray );
×
1594

1595
    append_objtypes( objarr.get(), menu );
×
1596

1597
    return objarr.release();
×
1598
  }
×
1599
  return new BLong( 0 );
×
1600
}
1601

1602
BObjectImp* UOExecutorModule::mf_ApplyConstraint()
×
1603
{
1604
  ObjArray* arr;
1605
  StoredConfigFile* cfile;
1606
  const String* propname_str;
1607
  int amthave;
1608

1609
  if ( getObjArrayParam( 0, arr ) && getStoredConfigFileParam( *this, 1, cfile ) &&
×
1610
       getStringParam( 2, propname_str ) && getParam( 3, amthave ) )
×
1611
  {
1612
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
1613

NEW
1614
    for ( auto& ref : arr->ref_arr )
×
1615
    {
1616
      BObject* bo = ref.get();
×
UNCOV
1617
      if ( bo == nullptr )
×
1618
        continue;
×
1619
      if ( !bo->isa( BObjectImp::OTLong ) )
×
1620
        continue;
×
1621
      BLong* blong = bo->impptr<BLong>();
×
1622
      unsigned int objtype = static_cast<u32>( blong->value() );
×
1623

1624
      ref_ptr<StoredConfigElem> celem = cfile->findelem( objtype );
×
1625
      if ( celem.get() == nullptr )
×
1626
        continue;
×
1627

1628
      BObjectImp* propval = celem->getimp( propname_str->value() );
×
1629
      if ( propval == nullptr )
×
1630
        continue;
×
1631
      if ( !propval->isa( BObjectImp::OTLong ) )
×
1632
        continue;
×
1633
      BLong* amtreq = static_cast<BLong*>( propval );
×
1634
      if ( amtreq->value() > amthave )
×
1635
        continue;
×
1636
      newarr->addElement( new BLong( objtype ) );
×
1637
    }
×
1638

1639
    return newarr.release();
×
1640
  }
×
1641

1642
  return new UninitObject;
×
1643
}
1644

1645
BObjectImp* UOExecutorModule::mf_CreateMenu()
×
1646
{
1647
  const String* title;
1648
  if ( getStringParam( 0, title ) )
×
1649
  {
1650
    Menu temp;
×
1651
    temp.menu_id = 0;
×
1652
    Clib::stracpy( temp.title, title->data(), sizeof temp.title );
×
1653
    return new EMenuObjImp( temp );
×
1654
  }
×
1655
  return new BLong( 0 );
×
1656
}
1657

1658
BObjectImp* UOExecutorModule::mf_AddMenuItem()
×
1659
{
1660
  Menu* menu;
1661
  unsigned int objtype;
1662
  const String* text;
1663
  unsigned short color;
1664

1665
  if ( getDynamicMenuParam( 0, menu ) && getObjtypeParam( 1, objtype ) &&
×
1666
       getStringParam( 2, text ) && getParam( 3, color ) )
×
1667
  {
1668
    menu->menuitems_.emplace_back();
×
1669
    MenuItem* mi = &menu->menuitems_.back();
×
1670
    mi->objtype_ = objtype;
×
1671
    mi->graphic_ = getgraphic( objtype );
×
1672
    Clib::stracpy( mi->title, text->data(), sizeof mi->title );
×
1673
    mi->color_ = color & settingsManager.ssopt.item_color_mask;
×
1674
    return new BLong( 1 );
×
1675
  }
1676

1677
  return new BLong( 0 );
×
1678
}
1679

1680
BObjectImp* UOExecutorModule::mf_GetObjProperty()
78✔
1681
{
1682
  UObject* uobj;
1683
  const String* propname_str;
1684
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
78✔
1685
  {
1686
    std::string val;
78✔
1687
    if ( uobj->getprop( propname_str->value(), val ) )
78✔
1688
    {
1689
      return BObjectImp::unpack( val.c_str() );
34✔
1690
    }
1691

1692
    return new BError( "Property not found" );
44✔
1693
  }
78✔
1694

1695
  return new BError( "Invalid parameter type" );
×
1696
}
1697

1698
BObjectImp* UOExecutorModule::mf_SetObjProperty()
9✔
1699
{
1700
  UObject* uobj;
1701
  const String* propname_str;
1702
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
9✔
1703
  {
1704
    BObjectImp* propval = getParamImp( 2 );
9✔
1705
    uobj->setprop( propname_str->value(), propval->pack() );
9✔
1706
    return new BLong( 1 );
9✔
1707
  }
1708

1709
  return new BError( "Invalid parameter type" );
×
1710
}
1711

1712
BObjectImp* UOExecutorModule::mf_EraseObjProperty()
×
1713
{
1714
  UObject* uobj;
1715
  const String* propname_str;
1716
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
×
1717
  {
1718
    uobj->eraseprop( propname_str->value() );
×
1719
    return new BLong( 1 );
×
1720
  }
1721

1722
  return new BError( "Invalid parameter type" );
×
1723
}
1724
BObjectImp* UOExecutorModule::mf_GetObjPropertyNames()
×
1725
{
1726
  UObject* uobj;
1727
  if ( getUObjectParam( 0, uobj ) )
×
1728
  {
1729
    std::vector<std::string> propnames;
×
1730
    uobj->getpropnames( propnames );
×
1731
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
NEW
1732
    for ( const auto& propname : propnames )
×
1733
    {
NEW
1734
      arr->addElement( new String( propname ) );
×
1735
    }
1736
    return arr.release();
×
1737
  }
×
1738

1739
  return new BError( "Invalid parameter type" );
×
1740
}
1741

1742

1743
BObjectImp* UOExecutorModule::mf_GetGlobalProperty()
49✔
1744
{
1745
  const String* propname_str;
1746
  if ( getStringParam( 0, propname_str ) )
49✔
1747
  {
1748
    std::string val;
49✔
1749
    if ( gamestate.global_properties->getprop( propname_str->value(), val ) )
49✔
1750
    {
1751
      return BObjectImp::unpack( val.c_str() );
43✔
1752
    }
1753

1754
    return new BError( "Property not found" );
6✔
1755
  }
49✔
1756

1757
  return new BError( "Invalid parameter type" );
×
1758
}
1759

1760
BObjectImp* UOExecutorModule::mf_SetGlobalProperty()
24✔
1761
{
1762
  const String* propname_str;
1763
  if ( exec.getStringParam( 0, propname_str ) )
24✔
1764
  {
1765
    BObjectImp* propval = exec.getParamImp( 1 );
24✔
1766
    gamestate.global_properties->setprop( propname_str->value(), propval->pack() );
24✔
1767
    return new BLong( 1 );
24✔
1768
  }
1769

1770
  return new BError( "Invalid parameter type" );
×
1771
}
1772

1773
BObjectImp* UOExecutorModule::mf_EraseGlobalProperty()
×
1774
{
1775
  const String* propname_str;
1776
  if ( getStringParam( 0, propname_str ) )
×
1777
  {
1778
    gamestate.global_properties->eraseprop( propname_str->value() );
×
1779
    return new BLong( 1 );
×
1780
  }
1781

1782
  return new BError( "Invalid parameter type" );
×
1783
}
1784

1785
BObjectImp* UOExecutorModule::mf_GetGlobalPropertyNames()
×
1786
{
1787
  std::vector<std::string> propnames;
×
1788
  gamestate.global_properties->getpropnames( propnames );
×
1789
  std::unique_ptr<ObjArray> arr( new ObjArray );
×
NEW
1790
  for ( const auto& propname : propnames )
×
1791
  {
NEW
1792
    arr->addElement( new String( propname ) );
×
1793
  }
1794
  return arr.release();
×
1795
}
×
1796

1797
BObjectImp* UOExecutorModule::mf_PlayMovingEffect()
×
1798
{
1799
  UObject* src;
1800
  UObject* dst;
1801
  unsigned short effect;
1802
  int speed;
1803
  int loop;
1804
  int explode;
1805
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) && getParam( 2, effect ) &&
×
1806
       getParam( 3, speed, UCHAR_MAX ) && getParam( 4, loop, UCHAR_MAX ) &&
×
1807
       getParam( 5, explode, UCHAR_MAX ) )
×
1808
  {
1809
    if ( src->realm() != dst->realm() )
×
1810
      return new BError( "Realms must match" );
×
1811
    play_moving_effect( src, dst, effect, static_cast<unsigned char>( speed ),
×
1812
                        static_cast<unsigned char>( loop ), static_cast<unsigned char>( explode ) );
1813
    return new BLong( 1 );
×
1814
  }
1815

1816
  return new BLong( 0 );
×
1817
}
1818

1819
BObjectImp* UOExecutorModule::mf_PlayMovingEffectXYZ()
×
1820
{
1821
  unsigned short effect;
1822
  int speed;
1823
  int loop;
1824
  int explode;
1825
  Realms::Realm* realm;
1826
  Pos3d src, dst;
×
1827
  if ( getRealmParam( 10, &realm ) && getPos3dParam( 0, 1, 2, &src, realm ) &&
×
1828
       getPos3dParam( 3, 4, 5, &dst, realm ) && getParam( 6, effect ) &&
×
1829
       getParam( 7, speed, UCHAR_MAX ) && getParam( 8, loop, UCHAR_MAX ) &&
×
1830
       getParam( 9, explode, UCHAR_MAX ) )
×
1831
  {
1832
    play_moving_effect2( src, dst, effect, static_cast<signed char>( speed ),
×
1833
                         static_cast<signed char>( loop ), static_cast<signed char>( explode ),
×
1834
                         realm );
1835
    return new BLong( 1 );
×
1836
  }
1837

1838
  return nullptr;
×
1839
}
1840

1841
// FIXME add/test:stationary
1842

1843
BObjectImp* UOExecutorModule::mf_PlayObjectCenteredEffect()
×
1844
{
1845
  UObject* src;
1846
  unsigned short effect;
1847
  int speed;
1848
  int loop;
1849
  if ( getUObjectParam( 0, src ) && getParam( 1, effect ) && getParam( 2, speed, UCHAR_MAX ) &&
×
1850
       getParam( 3, loop, UCHAR_MAX ) )
×
1851
  {
1852
    play_object_centered_effect( src, effect, static_cast<unsigned char>( speed ),
×
1853
                                 static_cast<unsigned char>( loop ) );
1854
    return new BLong( 1 );
×
1855
  }
1856

1857
  return nullptr;
×
1858
}
1859

1860
BObjectImp* UOExecutorModule::mf_PlayStationaryEffect()
×
1861
{
1862
  Pos4d pos;
×
1863
  unsigned short effect;
1864
  int speed, loop, explode;
1865
  if ( getPos4dParam( 0, 1, 2, 7, &pos ) && getParam( 3, effect ) &&
×
1866
       getParam( 4, speed, UCHAR_MAX ) && getParam( 5, loop, UCHAR_MAX ) &&
×
1867
       getParam( 6, explode, UCHAR_MAX ) )
×
1868
  {
1869
    play_stationary_effect( pos, effect, static_cast<unsigned char>( speed ),
×
1870
                            static_cast<unsigned char>( loop ),
1871
                            static_cast<unsigned char>( explode ) );
1872
    return new BLong( 1 );
×
1873
  }
1874

1875
  return new BError( "Invalid parameter" );
×
1876
}
1877

1878

1879
BObjectImp* UOExecutorModule::mf_PlayMovingEffectEx()
×
1880
{
1881
  UObject* src;
1882
  UObject* dst;
1883
  unsigned short effect;
1884
  int speed;
1885
  int duration;
1886
  int hue;
1887
  int render;
1888
  int direction;
1889
  int explode;
1890
  unsigned short effect3d;
1891
  unsigned short effect3dexplode;
1892
  unsigned short effect3dsound;
1893

1894
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) && getParam( 2, effect ) &&
×
1895
       getParam( 3, speed, UCHAR_MAX ) && getParam( 4, duration, UCHAR_MAX ) &&
×
1896
       getParam( 5, hue, INT_MAX ) && getParam( 6, render, INT_MAX ) &&
×
1897
       getParam( 7, direction, UCHAR_MAX ) && getParam( 8, explode, UCHAR_MAX ) &&
×
1898
       getParam( 9, effect3d ) && getParam( 10, effect3dexplode ) && getParam( 11, effect3dsound ) )
×
1899
  {
1900
    if ( src->realm() != dst->realm() )
×
1901
      return new BError( "Realms must match" );
×
1902
    play_moving_effect_ex(
×
1903
        src, dst, effect, static_cast<unsigned char>( speed ),
1904
        static_cast<unsigned char>( duration ), static_cast<unsigned int>( hue ),
1905
        static_cast<unsigned int>( render ), static_cast<unsigned char>( direction ),
1906
        static_cast<unsigned char>( explode ), effect3d, effect3dexplode, effect3dsound );
1907
    return new BLong( 1 );
×
1908
  }
1909

1910
  return new BLong( 0 );
×
1911
}
1912

1913
BObjectImp* UOExecutorModule::mf_PlayMovingEffectXYZEx()
×
1914
{
1915
  Pos3d src, dst;
×
1916
  Realms::Realm* realm;
1917

1918
  unsigned short effect;
1919
  int speed;
1920
  int duration;
1921
  int hue;
1922
  int render;
1923
  int direction;
1924
  int explode;
1925
  unsigned short effect3d;
1926
  unsigned short effect3dexplode;
1927
  unsigned short effect3dsound;
1928

1929
  if ( getRealmParam( 6, &realm ) && getPos3dParam( 0, 1, 2, &src, realm ) &&
×
1930
       getPos3dParam( 3, 4, 5, &dst, realm ) && getParam( 7, effect ) &&
×
1931
       getParam( 8, speed, UCHAR_MAX ) && getParam( 9, duration, UCHAR_MAX ) &&
×
1932
       getParam( 10, hue, INT_MAX ) && getParam( 11, render, INT_MAX ) &&
×
1933
       getParam( 12, direction, UCHAR_MAX ) && getParam( 13, explode, UCHAR_MAX ) &&
×
1934
       getParam( 14, effect3d ) && getParam( 15, effect3dexplode ) &&
×
1935
       getParam( 16, effect3dsound ) )
×
1936
  {
1937
    play_moving_effect2_ex(
×
1938
        src, dst, realm, effect, static_cast<unsigned char>( speed ),
1939
        static_cast<unsigned char>( duration ), static_cast<unsigned int>( hue ),
1940
        static_cast<unsigned int>( render ), static_cast<unsigned char>( direction ),
1941
        static_cast<unsigned char>( explode ), effect3d, effect3dexplode, effect3dsound );
1942
    return new BLong( 1 );
×
1943
  }
1944

1945
  return nullptr;
×
1946
}
1947

1948
BObjectImp* UOExecutorModule::mf_PlayObjectCenteredEffectEx()
×
1949
{
1950
  UObject* src;
1951
  unsigned short effect;
1952
  int speed;
1953
  int duration;
1954
  int hue;
1955
  int render;
1956
  int layer;
1957
  unsigned short effect3d;
1958

1959
  if ( getUObjectParam( 0, src ) && getParam( 1, effect ) && getParam( 2, speed, UCHAR_MAX ) &&
×
1960
       getParam( 3, duration, UCHAR_MAX ) && getParam( 4, hue, INT_MAX ) &&
×
1961
       getParam( 5, render, INT_MAX ) && getParam( 6, layer, UCHAR_MAX ) &&
×
1962
       getParam( 7, effect3d ) )
×
1963
  {
1964
    play_object_centered_effect_ex(
×
1965
        src, effect, static_cast<unsigned char>( speed ), static_cast<unsigned char>( duration ),
1966
        static_cast<unsigned int>( hue ), static_cast<unsigned int>( render ),
1967
        static_cast<unsigned char>( layer ), effect3d );
1968
    return new BLong( 1 );
×
1969
  }
1970

1971
  return nullptr;
×
1972
}
1973

1974
BObjectImp* UOExecutorModule::mf_PlayStationaryEffectEx()
×
1975
{
1976
  Pos4d pos;
×
1977
  unsigned short effect;
1978
  int speed;
1979
  int duration;
1980
  int hue;
1981
  int render;
1982
  unsigned short effect3d;
1983

1984
  if ( getPos4dParam( 0, 1, 2, 3, &pos ) && getParam( 4, effect ) &&
×
1985
       getParam( 5, speed, UCHAR_MAX ) && getParam( 6, duration, UCHAR_MAX ) &&
×
1986
       getParam( 7, hue, INT_MAX ) && getParam( 8, render, INT_MAX ) && getParam( 9, effect3d ) )
×
1987
  {
1988
    play_stationary_effect_ex(
×
1989
        pos, effect, static_cast<unsigned char>( speed ), static_cast<unsigned char>( duration ),
1990
        static_cast<unsigned int>( hue ), static_cast<unsigned int>( render ), effect3d );
1991
    return new BLong( 1 );
×
1992
  }
1993

1994
  return new BError( "Invalid parameter" );
×
1995
}
1996

1997
BObjectImp* UOExecutorModule::mf_PlayLightningBoltEffect()
×
1998
{
1999
  UObject* center;
2000
  if ( getUObjectParam( 0, center ) )
×
2001
  {
2002
    play_lightning_bolt_effect( center );
×
2003
    return new BLong( 1 );
×
2004
  }
2005

2006
  return new BLong( 0 );
×
2007
}
2008

2009
BObjectImp* UOExecutorModule::mf_ListItemsNearLocation( /* x, y, z, range, realm */ )
×
2010
{
2011
  Core::Pos2d pos;
×
2012
  int z;
2013
  u16 range;
2014
  Realms::Realm* realm;
2015

2016
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2017
       getParam( 3, range ) )
×
2018
  {
2019
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2020
    WorldIterator<ItemFilter>::InRange(
×
2021
        pos, realm, range,
2022
        [&]( Item* item )
×
2023
        {
2024
          if ( item->in_range( pos, range ) )
×
2025
          {
2026
            if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2027
              newarr->addElement( item->make_ref() );
×
2028
          }
2029
        } );
×
2030

2031
    return newarr.release();
×
2032
  }
×
2033

2034
  return nullptr;
×
2035
}
2036

2037
Range3d UOExecutorModule::internal_InBoxAreaChecks( const Pos2d& p1, int z1, const Pos2d& p2,
10✔
2038
                                                    int z2, Realms::Realm* realm )
2039
{
2040
  // no error checks for out of realm range on purpose
2041
  if ( ( z1 > z2 ) && z1 != LIST_IGNORE_Z && z2 != LIST_IGNORE_Z )
10✔
2042
    std::swap( z1, z2 );
×
2043
  if ( z1 == LIST_IGNORE_Z )
10✔
2044
    z1 = ZCOORD_MIN;
×
2045
  if ( z2 == LIST_IGNORE_Z )
10✔
2046
    z2 = ZCOORD_MAX;
×
2047
  return Range3d( Pos3d( p1, Clib::clamp_convert<s8>( z1 ) ),
10✔
2048
                  Pos3d( p2, Clib::clamp_convert<s8>( z2 ) ), realm );
20✔
2049
}
2050

2051
BObjectImp* UOExecutorModule::mf_ListObjectsInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
×
2052
{
2053
  int z1, z2;
2054
  Pos2d p1, p2;
×
2055
  Realms::Realm* realm;
2056

2057
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
×
2058
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
×
2059
  {
2060
    return new BError( "Invalid parameter" );
×
2061
  }
2062
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
×
2063
  z1 = box.nw_b().z();
×
2064
  z2 = box.se_t().z();
×
2065

2066
  std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2067
  WorldIterator<MobileFilter>::InBox( box.range(), realm,
×
2068
                                      [&]( Mobile::Character* chr )
×
2069
                                      {
2070
                                        if ( chr->z() >= z1 && chr->z() <= z2 )
×
2071
                                        {
2072
                                          newarr->addElement( chr->make_ref() );
×
2073
                                        }
2074
                                      } );
×
2075
  WorldIterator<ItemFilter>::InBox( box.range(), realm,
×
2076
                                    [&]( Items::Item* item )
×
2077
                                    {
2078
                                      if ( item->z() >= z1 && item->z() <= z2 )
×
2079
                                      {
2080
                                        newarr->addElement( item->make_ref() );
×
2081
                                      }
2082
                                    } );
×
2083

2084
  return newarr.release();
×
2085
}
×
2086

2087
BObjectImp* UOExecutorModule::mf_ListItemsInBoxOfObjType(
2✔
2088
    /* objtype, x1, y1, z1, x2, y2, z2, realm */ )
2089
{
2090
  unsigned int objtype;
2091
  int z1, z2;
2092
  Pos2d p1, p2;
2✔
2093
  Realms::Realm* realm;
2094

2095
  if ( !( getParam( 0, objtype ) && getPos2dParam( 1, 2, &p1 ) && getParam( 3, z1 ) &&
4✔
2096
          getPos2dParam( 4, 5, &p2 ) && getParam( 6, z2 ) && getRealmParam( 7, &realm ) ) )
2✔
2097
  {
2098
    return new BError( "Invalid parameter" );
×
2099
  }
2100
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
2✔
2101
  z1 = box.nw_b().z();
2✔
2102
  z2 = box.se_t().z();
2✔
2103

2104
  std::unique_ptr<ObjArray> newarr( new ObjArray );
2✔
2105
  WorldIterator<ItemFilter>::InBox(
4✔
2106
      box.range(), realm,
2✔
2107
      [&]( Items::Item* item )
2✔
2108
      {
2109
        if ( item->z() >= z1 && item->z() <= z2 && item->objtype_ == objtype )
40✔
2110
        {
2111
          newarr->addElement( item->make_ref() );
39✔
2112
        }
2113
      } );
40✔
2114

2115
  return newarr.release();
2✔
2116
}
2✔
2117

2118
BObjectImp* UOExecutorModule::mf_ListObjectsInBoxOfClass(
1✔
2119
    /* POL_Class, x1, y1, z1, x2, y2, z2, realm */ )
2120
{
2121
  unsigned int POL_Class;
2122
  int z1, z2;
2123
  Pos2d p1, p2;
1✔
2124
  Realms::Realm* realm;
2125

2126
  if ( !( getParam( 0, POL_Class ) && getPos2dParam( 1, 2, &p1 ) && getParam( 3, z1 ) &&
2✔
2127
          getPos2dParam( 4, 5, &p2 ) && getParam( 6, z2 ) && getRealmParam( 7, &realm ) ) )
1✔
2128
  {
2129
    return new BError( "Invalid parameter" );
×
2130
  }
2131
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
1✔
2132
  z1 = box.nw_b().z();
1✔
2133
  z2 = box.se_t().z();
1✔
2134

2135
  std::unique_ptr<ObjArray> newarr( new ObjArray );
1✔
2136
  WorldIterator<MobileFilter>::InBox(
2✔
2137
      box.range(), realm,
1✔
2138
      [&]( Mobile::Character* chr )
1✔
2139
      {
2140
        if ( chr->z() >= z1 && chr->z() <= z2 && chr->script_isa( POL_Class ) )
×
2141
        {
2142
          newarr->addElement( chr->make_ref() );
×
2143
        }
2144
      } );
×
2145
  WorldIterator<ItemFilter>::InBox(
2✔
2146
      box.range(), realm,
1✔
2147
      [&]( Items::Item* item )
1✔
2148
      {
2149
        if ( item->z() >= z1 && item->z() <= z2 && item->script_isa( POL_Class ) )
1✔
2150
        {
2151
          newarr->addElement( item->make_ref() );
1✔
2152
        }
2153
      } );
1✔
2154

2155
  return newarr.release();
1✔
2156
}
1✔
2157

2158
BObjectImp* UOExecutorModule::mf_ListMobilesInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
2✔
2159
{
2160
  int z1, z2;
2161
  Pos2d p1, p2;
2✔
2162
  Realms::Realm* realm;
2163

2164
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
4✔
2165
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
2✔
2166
  {
2167
    return new BError( "Invalid parameter" );
×
2168
  }
2169
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
2✔
2170
  z1 = box.nw_b().z();
2✔
2171
  z2 = box.se_t().z();
2✔
2172
  std::unique_ptr<ObjArray> newarr( new ObjArray );
2✔
2173
  WorldIterator<MobileFilter>::InBox( box.range(), realm,
2✔
2174
                                      [&]( Mobile::Character* chr )
2✔
2175
                                      {
2176
                                        if ( chr->z() >= z1 && chr->z() <= z2 )
2✔
2177
                                        {
2178
                                          newarr->addElement( chr->make_ref() );
2✔
2179
                                        }
2180
                                      } );
2✔
2181

2182
  return newarr.release();
2✔
2183
}
2✔
2184

2185
BObjectImp* UOExecutorModule::mf_ListMultisInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
4✔
2186
{
2187
  int z1, z2;
2188
  Pos2d p1, p2;
4✔
2189
  Realms::Realm* realm;
2190

2191
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
8✔
2192
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
4✔
2193
  {
2194
    return new BError( "Invalid parameter" );
×
2195
  }
2196

2197
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
4✔
2198
  z1 = box.nw_b().z();
4✔
2199
  z2 = box.se_t().z();
4✔
2200

2201
  std::unique_ptr<ObjArray> newarr( new ObjArray );
4✔
2202

2203
  // extend the coords to find the center item
2204
  // but only as parameter for the filter function
2205
  Vec2d urange{ Clib::clamp_convert<s16>( gamestate.max_update_range ),
4✔
2206
                Clib::clamp_convert<s16>( gamestate.max_update_range ) };
4✔
2207
  Range2d boxrange( box.nw() - urange, box.se() + urange, realm );
4✔
2208

2209
  // search for multis.  this is tricky, since the center might lie outside the box
2210
  WorldIterator<MultiFilter>::InBox(
4✔
2211
      boxrange, realm,
2212
      [&]( Multi::UMulti* multi )
4✔
2213
      {
2214
        if ( !box.intersect( multi->current_box() ) )
4✔
2215
          return;
×
2216
        const Multi::MultiDef& md = multi->multidef();
4✔
2217
        // some part of it is contained in the box.  Look at the
2218
        // individual statics, to see if any of them lie within.
2219
        for ( const auto& citr : md.components )
34✔
2220
        {
2221
          const Multi::MULTI_ELEM* elem = citr.second;
34✔
2222
          Pos3d absp = multi->pos3d() + elem->relpos;
34✔
2223
          if ( box.contains( absp.xy() ) )
34✔
2224
          {
2225
            // do Z checking
2226
            int height = Plib::tileheight( getgraphic( elem->objtype ) );
4✔
2227
            int top = absp.z() + height;
4✔
2228

2229
            if ( ( z1 <= absp.z() && absp.z() <= z2 ) ||  // bottom point lies between
8✔
2230
                 ( z1 <= top && top <= z2 ) ||            // top lies between
8✔
2231
                 ( top >= z2 && absp.z() <= z1 ) )        // spans
×
2232
            {
2233
              newarr->addElement( multi->make_ref() );
4✔
2234
              break;  // out of for
4✔
2235
            }
2236
          }
2237
        }
2238
      } );
2239

2240
  return newarr.release();
4✔
2241
}
4✔
2242

2243
BObjectImp* UOExecutorModule::mf_ListStaticsInBox( /* x1, y1, z1, x2, y2, z2, flags, realm */ )
1✔
2244
{
2245
  int z1, z2;
2246
  Pos2d p1, p2;
1✔
2247
  int flags;
2248
  Realms::Realm* realm;
2249

2250
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
2✔
2251
          getParam( 5, z2 ) && getParam( 6, flags ) && getRealmParam( 7, &realm ) ) )
1✔
2252
  {
2253
    return new BError( "Invalid parameter" );
×
2254
  }
2255

2256
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
1✔
2257
  z1 = box.nw_b().z();
1✔
2258
  z2 = box.se_t().z();
1✔
2259

2260
  std::unique_ptr<ObjArray> newarr( new ObjArray );
1✔
2261

2262
  for ( const auto& pos : box.range() )
5✔
2263
  {
2264
    if ( !( flags & ITEMS_IGNORE_STATICS ) )
2✔
2265
    {
2266
      Plib::StaticEntryList slist;
2✔
2267
      realm->getstatics( slist, pos );
2✔
2268

2269
      for ( const auto& entry : slist )
4✔
2270
      {
2271
        if ( ( z1 <= entry.z ) && ( entry.z <= z2 ) )
2✔
2272
        {
2273
          std::unique_ptr<BStruct> arr( new BStruct );
2✔
2274
          arr->addMember( "x", new BLong( pos.x() ) );
2✔
2275
          arr->addMember( "y", new BLong( pos.y() ) );
2✔
2276
          arr->addMember( "z", new BLong( entry.z ) );
2✔
2277
          arr->addMember( "objtype", new BLong( entry.objtype ) );
2✔
2278
          arr->addMember( "hue", new BLong( entry.hue ) );
2✔
2279
          newarr->addElement( arr.release() );
2✔
2280
        }
2✔
2281
      }
2282
    }
2✔
2283

2284
    if ( !( flags & ITEMS_IGNORE_MULTIS ) )
2✔
2285
    {
2286
      Plib::StaticList mlist;
2✔
2287
      realm->readmultis( mlist, pos );
2✔
2288

2289
      for ( const auto& entry : mlist )
2✔
2290
      {
NEW
2291
        if ( ( z1 <= entry.z ) && ( entry.z <= z2 ) )
×
2292
        {
2293
          std::unique_ptr<BStruct> arr( new BStruct );
×
2294
          arr->addMember( "x", new BLong( pos.x() ) );
×
2295
          arr->addMember( "y", new BLong( pos.y() ) );
×
NEW
2296
          arr->addMember( "z", new BLong( entry.z ) );
×
NEW
2297
          arr->addMember( "objtype", new BLong( entry.graphic ) );
×
2298
          newarr->addElement( arr.release() );
×
2299
        }
×
2300
      }
2301
    }
2✔
2302
  }
2303
  return newarr.release();
1✔
2304
}
1✔
2305

2306
BObjectImp* UOExecutorModule::mf_ListItemsNearLocationOfType( /* x, y, z, range, objtype, realm */ )
27✔
2307
{
2308
  Core::Pos2d pos;
27✔
2309
  int z;
2310
  u16 range;
2311
  unsigned int objtype;
2312
  Realms::Realm* realm;
2313

2314
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
54✔
2315
       getParam( 3, range ) && getObjtypeParam( 4, objtype ) )
54✔
2316
  {
2317
    std::unique_ptr<ObjArray> newarr( new ObjArray );
27✔
2318
    WorldIterator<ItemFilter>::InRange(
27✔
2319
        pos, realm, range,
2320
        [&]( Items::Item* item )
27✔
2321
        {
2322
          if ( item->objtype_ == objtype && item->in_range( pos, range ) )
50✔
2323
          {
2324
            if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
37✔
2325
              newarr->addElement( item->make_ref() );
37✔
2326
          }
2327
        } );
50✔
2328

2329
    return newarr.release();
27✔
2330
  }
27✔
2331

2332
  return nullptr;
×
2333
}
2334

2335

2336
BObjectImp* UOExecutorModule::mf_ListItemsAtLocation( /* x, y, z, realm */ )
×
2337
{
2338
  Pos2d pos;
×
2339
  int z;
2340
  Realms::Realm* realm;
2341

2342
  if ( getRealmParam( 3, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) )
×
2343
  {
2344
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2345
    WorldIterator<ItemFilter>::InRange( pos, realm, 0,
×
2346
                                        [&]( Items::Item* item )
×
2347
                                        {
2348
                                          if ( item->pos2d() == pos )
×
2349
                                          {
2350
                                            if ( ( z == LIST_IGNORE_Z ) || ( item->z() == z ) )
×
2351
                                              newarr->addElement( item->make_ref() );
×
2352
                                          }
2353
                                        } );
×
2354

2355
    return newarr.release();
×
2356
  }
×
2357

2358
  return nullptr;
×
2359
}
2360

2361
BObjectImp* UOExecutorModule::mf_ListGhostsNearLocation()
×
2362
{
2363
  Pos2d pos;
×
2364
  int z;
2365
  u16 range;
2366
  Realms::Realm* realm;
2367

2368
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2369
       getParam( 3, range ) )
×
2370
  {
2371
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2372
    WorldIterator<PlayerFilter>::InRange(
×
2373
        pos, realm, range,
2374
        [&]( Mobile::Character* chr )
×
2375
        {
2376
          if ( chr->dead() && ( abs( chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2377
          {
2378
            newarr->addElement( chr->make_ref() );
×
2379
          }
2380
        } );
×
2381

2382
    return newarr.release();
×
2383
  }
×
2384

2385
  return new BError( "Invalid parameter" );
×
2386
}
2387

2388
const unsigned LMBLEX_FLAG_NORMAL = 0x01;
2389
const unsigned LMBLEX_FLAG_HIDDEN = 0x02;
2390
const unsigned LMBLEX_FLAG_DEAD = 0x04;
2391
const unsigned LMBLEX_FLAG_CONCEALED = 0x8;
2392
const unsigned LMBLEX_FLAG_PLAYERS_ONLY = 0x10;
2393
const unsigned LMBLEX_FLAG_NPC_ONLY = 0x20;
2394

2395
BObjectImp* UOExecutorModule::mf_ListMobilesNearLocationEx( /* x, y, z, range, flags, realm */ )
×
2396
{
2397
  Pos2d pos;
×
2398
  Realms::Realm* realm;
2399
  int z, flags;
2400
  u16 range;
2401

2402
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2403
       getParam( 3, range ) && getParam( 4, flags ) )
×
2404
  {
2405
    bool inc_normal = ( flags & LMBLEX_FLAG_NORMAL ) ? true : false;
×
2406
    bool inc_hidden = ( flags & LMBLEX_FLAG_HIDDEN ) ? true : false;
×
2407
    bool inc_dead = ( flags & LMBLEX_FLAG_DEAD ) ? true : false;
×
2408
    bool inc_concealed = ( flags & LMBLEX_FLAG_CONCEALED ) ? true : false;
×
2409
    bool inc_players_only = ( flags & LMBLEX_FLAG_PLAYERS_ONLY ) ? true : false;
×
2410
    bool inc_npc_only = ( flags & LMBLEX_FLAG_NPC_ONLY ) ? true : false;
×
2411

2412
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2413

2414
    auto fill_mobs = [&]( Mobile::Character* _chr )
×
2415
    {
2416
      if ( ( inc_hidden && _chr->hidden() ) || ( inc_dead && _chr->dead() ) ||
×
2417
           ( inc_concealed && _chr->concealed() ) ||
×
2418
           ( inc_normal && !( _chr->hidden() || _chr->dead() || _chr->concealed() ) ) )
×
2419
      {
2420
        if ( ( z == LIST_IGNORE_Z ) || ( abs( _chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2421
        {
2422
          if ( !inc_players_only && !inc_npc_only )
×
2423
            newarr->addElement( _chr->make_ref() );
×
2424
          else if ( inc_players_only && _chr->client )
×
2425
            newarr->addElement( _chr->make_ref() );
×
2426
          else if ( inc_npc_only && _chr->isa( UOBJ_CLASS::CLASS_NPC ) )
×
2427
            newarr->addElement( _chr->make_ref() );
×
2428
        }
2429
      }
2430
    };
×
2431
    if ( inc_players_only )
×
2432
      WorldIterator<PlayerFilter>::InRange( pos, realm, range, fill_mobs );
×
2433
    else if ( inc_npc_only )
×
2434
      WorldIterator<NPCFilter>::InRange( pos, realm, range, fill_mobs );
×
2435
    else
2436
      WorldIterator<MobileFilter>::InRange( pos, realm, range, fill_mobs );
×
2437

2438
    return newarr.release();
×
2439
  }
×
2440

2441
  return new BError( "Invalid parameter" );
×
2442
}
2443

2444
BObjectImp* UOExecutorModule::mf_ListMobilesNearLocation( /* x, y, z, range, realm */ )
×
2445
{
2446
  Pos2d pos;
×
2447
  int z;
2448
  u16 range;
2449
  Realms::Realm* realm;
2450

2451
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2452
       getParam( 3, range ) )
×
2453
  {
2454
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2455
    WorldIterator<MobileFilter>::InRange(
×
2456
        pos, realm, range,
2457
        [&]( Mobile::Character* chr )
×
2458
        {
2459
          if ( ( !chr->concealed() ) && ( !chr->hidden() ) && ( !chr->dead() ) )
×
2460
            if ( ( z == LIST_IGNORE_Z ) || ( abs( chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2461
              newarr->addElement( chr->make_ref() );
×
2462
        } );
×
2463
    return newarr.release();
×
2464
  }
×
2465

2466
  return new BError( "Invalid parameter" );
×
2467
}
2468

2469
BObjectImp* UOExecutorModule::mf_ListMobilesInLineOfSight()
×
2470
{
2471
  UObject* obj;
2472
  int range;
2473
  if ( getUObjectParam( 0, obj ) && getParam( 1, range ) )
×
2474
  {
2475
    obj = obj->toplevel_owner();
×
2476
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2477
    WorldIterator<MobileFilter>::InRange(
×
2478
        obj, range,
2479
        [&]( Mobile::Character* chr )
×
2480
        {
2481
          if ( chr->dead() || chr->hidden() || chr->concealed() )
×
2482
            return;
×
2483
          if ( chr == obj )
×
2484
            return;
×
2485
          if ( ( abs( chr->z() - obj->z() ) < CONST_DEFAULT_ZRANGE ) )
×
2486
          {
2487
            if ( obj->realm()->has_los( *obj, *chr ) )
×
2488
            {
2489
              newarr->addElement( chr->make_ref() );
×
2490
            }
2491
          }
2492
        } );
2493

2494
    return newarr.release();
×
2495
  }
×
2496

2497
  return new BError( "Invalid parameter" );
×
2498
}
2499

2500
BObjectImp* UOExecutorModule::mf_ListOfflineMobilesInRealm( /*realm*/ )
×
2501
{
2502
  Realms::Realm* realm = nullptr;
×
2503

2504
  if ( getRealmParam( 0, &realm ) )
×
2505
  {
2506
    std::unique_ptr<ObjArray> newarr( new ObjArray() );
×
2507

2508
    for ( const auto& objitr : Pol::Core::objStorageManager.objecthash )
×
2509
    {
2510
      UObject* obj = objitr.second.get();
×
2511
      if ( !obj->ismobile() || obj->isa( UOBJ_CLASS::CLASS_NPC ) )
×
2512
        continue;
×
2513

2514
      Character* chr = static_cast<Character*>( obj );
×
2515
      if ( chr->logged_in() || chr->realm() != realm || chr->orphan() )
×
2516
        continue;
×
2517

2518
      newarr->addElement( new EOfflineCharacterRefObjImp( chr ) );
×
2519
    }
2520
    return newarr.release();
×
2521
  }
×
2522

2523
  return new BError( "Invalid parameter" );
×
2524
}
2525

2526
// keep this in sync with UO.EM
2527
const int LH_FLAG_LOS = 1;             // only include those in LOS
2528
const int LH_FLAG_INCLUDE_HIDDEN = 2;  // include hidden characters
2529
BObjectImp* UOExecutorModule::mf_ListHostiles()
×
2530
{
2531
  Character* chr;
2532
  u16 range;
2533
  int flags;
2534
  if ( getCharacterParam( 0, chr ) && getParam( 1, range ) && getParam( 2, flags ) )
×
2535
  {
2536
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
2537

2538
    for ( auto& hostile : chr->hostiles() )
×
2539
    {
2540
      if ( hostile->concealed() )
×
2541
        continue;
×
2542
      if ( ( flags & LH_FLAG_LOS ) && !chr->realm()->has_los( *chr, *hostile ) )
×
2543
        continue;
×
2544
      if ( ( ~flags & LH_FLAG_INCLUDE_HIDDEN ) && hostile->hidden() )
×
2545
        continue;
×
2546
      if ( !chr->in_range( hostile, range ) )
×
2547
        continue;
×
2548
      arr->addElement( hostile->make_ref() );
×
2549
    }
2550

2551
    return arr.release();
×
2552
  }
×
2553

2554
  return new BError( "Invalid parameter" );
×
2555
}
2556

2557
BObjectImp* UOExecutorModule::mf_CheckLineOfSight()
1✔
2558
{
2559
  UObject* src;
2560
  UObject* dst;
2561
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) )
1✔
2562
  {
2563
    return new BLong( src->realm()->has_los( *src, *dst->toplevel_owner() ) );
1✔
2564
  }
2565

2566
  return new BLong( 0 );
×
2567
}
2568

2569
BObjectImp* UOExecutorModule::mf_CheckLosAt()
1✔
2570
{
2571
  UObject* src;
2572
  Core::Pos3d pos;
1✔
2573
  if ( getUObjectParam( 0, src ) && getPos3dParam( 1, 2, 3, &pos, src->realm() ) )
1✔
2574
  {
2575
    LosObj tgt( Core::Pos4d( pos, src->realm() ) );
1✔
2576
    return new BLong( src->realm()->has_los( *src, tgt ) );
1✔
2577
  }
2578
  return nullptr;
×
2579
}
2580

2581
BObjectImp* UOExecutorModule::mf_CheckLosBetween()
2✔
2582
{
2583
  Core::Pos3d p1, p2;
2✔
2584
  Realms::Realm* realm;
2585
  if ( getRealmParam( 6, &realm ) && getPos3dParam( 0, 1, 2, &p1, realm ) &&
4✔
2586
       getPos3dParam( 3, 4, 5, &p2, realm ) )
2✔
2587
  {
2588
    LosObj att( Core::Pos4d( p1, realm ) );
2✔
2589
    LosObj tgt( Core::Pos4d( p2, realm ) );
2✔
2590
    return new BLong( realm->has_los( att, tgt ) );
2✔
2591
  }
2592
  return nullptr;
×
2593
}
2594

2595
BObjectImp* UOExecutorModule::mf_DestroyItem()
6,180✔
2596
{
2597
  Item* item;
2598
  if ( getItemParam( 0, item ) )
6,180✔
2599
  {
2600
    if ( item->has_gotten_by() )
6,176✔
2601
      item->gotten_by()->clear_gotten_item();
×
2602
    else if ( item->inuse() && !is_reserved_to_me( item ) )
6,176✔
2603
      return new BError( "That item is being used." );
×
2604
    else if ( item->script_isa( POLCLASS_MULTI ) )
6,176✔
2605
      return new BError( "That item is a multi. Use uo::DestroyMulti instead." );
×
2606

2607
    const ItemDesc& id = find_itemdesc( item->objtype_ );
6,176✔
2608
    if ( !id.destroy_script.empty() )
6,176✔
2609
    {
2610
      BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( item ) );
×
2611
      if ( res->isTrue() )
×
2612
      {  // destruction is okay
2613
        BObject ob( res );
×
2614
      }
×
2615
      else
2616
      {
2617
        // destruction isn't okay!
2618
        return res;
×
2619
      }
2620
    }
2621
    UpdateCharacterOnDestroyItem( item );
6,176✔
2622
    UpdateCharacterWeight( item );
6,176✔
2623
    destroy_item( item );
6,176✔
2624
    return new BLong( 1 );
6,176✔
2625
  }
2626

2627
  return new BError( "Invalid parameter type" );
4✔
2628
}
2629

2630
BObjectImp* UOExecutorModule::mf_SetName()
6✔
2631
{
2632
  UObject* obj;
2633
  const String* name_str;
2634

2635
  if ( getUObjectParam( 0, obj ) && getStringParam( 1, name_str ) )
6✔
2636
  {
2637
    obj->setname( name_str->value() );
6✔
2638
    return new BLong( 1 );
6✔
2639
  }
2640

2641
  return new BLong( 0 );
×
2642
}
2643

2644
BObjectImp* UOExecutorModule::mf_GetPosition()
×
2645
{
2646
  UObject* obj;
2647
  if ( getUObjectParam( 0, obj ) )
×
2648
  {
2649
    std::unique_ptr<BStruct> arr( new BStruct );
×
2650
    arr->addMember( "x", new BLong( obj->x() ) );
×
2651
    arr->addMember( "y", new BLong( obj->y() ) );
×
2652
    arr->addMember( "z", new BLong( obj->z() ) );
×
2653
    return arr.release();
×
2654
  }
×
2655

2656
  return new BLong( 0 );
×
2657
}
2658

2659
BObjectImp* UOExecutorModule::mf_EnumerateItemsInContainer()
6✔
2660
{
2661
  Item* item;
2662
  if ( getItemParam( 0, item ) )
6✔
2663
  {
2664
    int flags = 0;
6✔
2665
    if ( exec.fparams.size() >= 2 )
6✔
2666
    {
2667
      if ( !getParam( 1, flags ) )
6✔
2668
        return new BError( "Invalid parameter type" );
×
2669
    }
2670

2671
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
6✔
2672
    {
2673
      std::unique_ptr<ObjArray> newarr( new ObjArray );
6✔
2674

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

2677
      return newarr.release();
6✔
2678
    }
6✔
2679

2680
    return nullptr;
×
2681
  }
2682
  return new BError( "Invalid parameter type" );
×
2683
}
2684

2685
BObjectImp* UOExecutorModule::mf_EnumerateOnlineCharacters()
×
2686
{
2687
  std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2688

NEW
2689
  for ( auto client : networkManager.clients )
×
2690
  {
NEW
2691
    if ( client->chr != nullptr )
×
2692
    {
NEW
2693
      newarr->addElement( new ECharacterRefObjImp( client->chr ) );
×
2694
    }
2695
  }
2696

2697
  return newarr.release();
×
2698
}
×
2699

2700
BObjectImp* UOExecutorModule::mf_RegisterForSpeechEvents()
1✔
2701
{
2702
  UObject* center;
2703
  int range;
2704
  if ( getUObjectParam( 0, center ) && getParam( 1, range ) )
1✔
2705
  {
2706
    int flags = 0;
1✔
2707
    if ( exec.hasParams( 3 ) )
1✔
2708
    {
2709
      if ( !getParam( 2, flags ) )
1✔
2710
        return new BError( "Invalid parameter type" );
×
2711
    }
2712
    if ( !registered_for_speech_events )
1✔
2713
    {
2714
      ListenPoint::register_for_speech_events( center, &uoexec(), range, flags );
1✔
2715
      registered_for_speech_events = true;
1✔
2716
      return new BLong( 1 );
1✔
2717
    }
2718

2719
    return new BError( "Already registered for speech events" );
×
2720
  }
2721

2722
  return new BError( "Invalid parameter type" );
×
2723
}
2724

2725
BObjectImp* UOExecutorModule::mf_EnableEvents()
5✔
2726
{
2727
  int eventmask;
2728
  if ( getParam( 0, eventmask ) )
5✔
2729
  {
2730
    auto& uoex = uoexec();
5✔
2731
    if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA | EVID_SPOKE ) )
5✔
2732
    {
2733
      unsigned short range;
2734
      if ( getParam( 1, range, 0, 32 ) )
5✔
2735
      {
2736
        if ( eventmask & ( EVID_SPOKE ) )
5✔
2737
          uoex.speech_size = range;
2✔
2738
        if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA ) )
5✔
2739
          uoex.area_size = range;
3✔
2740
      }
2741
      else
2742
      {
2743
        return nullptr;
×
2744
      }
2745

2746

2747
      unsigned short evopts = 0;
5✔
2748
      if ( exec.hasParams( 2 ) && getParam( 2, evopts ) )
5✔
2749
      {
2750
        if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA ) )
5✔
2751
          uoex.area_mask = static_cast<unsigned char>( evopts & 255 );
3✔
2752
      }
2753
    }
2754
    uoex.eventmask |= eventmask;
5✔
2755
    return new BLong( uoex.eventmask );
5✔
2756
  }
2757

2758
  return new BError( "Invalid parameter" );
×
2759
}
2760

2761
BObjectImp* UOExecutorModule::mf_DisableEvents()
×
2762
{
2763
  int eventmask;
2764
  if ( getParam( 0, eventmask ) )
×
2765
  {
2766
    auto& uoex = uoexec();
×
2767
    uoex.eventmask &= ~eventmask;
×
2768

2769
    return new BLong( uoex.eventmask );
×
2770
  }
2771

2772
  return new BError( "Invalid parameter" );
×
2773
}
2774

2775
BObjectImp* UOExecutorModule::mf_Resurrect()
×
2776
{
2777
  Character* chr;
2778
  if ( getCharacterParam( 0, chr ) )
×
2779
  {
2780
    if ( !chr->dead() )
×
2781
      return new BError( "That is not dead" );
×
2782
    int flags = 0;
×
2783
    if ( exec.hasParams( 2 ) )
×
2784
    {
2785
      if ( !getParam( 1, flags ) )
×
2786
        return new BError( "Invalid parameter type" );
×
2787
    }
2788

2789
    if ( ~flags & RESURRECT_FORCELOCATION )
×
2790
    {
2791
      // we want doors to block ghosts in this case.
2792
      bool doors_block = !( chr->graphic == UOBJ_GAMEMASTER ||
×
2793
                            chr->cached_settings.get( PRIV_FLAGS::IGNORE_DOORS ) );
×
2794
      short newz;
2795
      Multi::UMulti* supporting_multi;
2796
      Item* walkon_item;
2797
      if ( !chr->realm()->walkheight( chr->pos2d(), chr->z(), &newz, &supporting_multi,
×
2798
                                      &walkon_item, doors_block, chr->movemode ) )
×
2799
      {
2800
        return new BError( "That location is blocked" );
×
2801
      }
2802
    }
2803

2804
    chr->resurrect();
×
2805
    return new BLong( 1 );
×
2806
  }
2807

2808
  return new BError( "Invalid parameter type" );
×
2809
}
2810

2811
BObjectImp* UOExecutorModule::mf_SystemFindObjectBySerial()
20✔
2812
{
2813
  int serial;
2814
  if ( getParam( 0, serial ) )
20✔
2815
  {
2816
    int sysfind_flags = 0;
20✔
2817
    if ( exec.hasParams( 2 ) )
20✔
2818
    {
2819
      if ( !getParam( 1, sysfind_flags ) )
20✔
2820
        return new BError( "Invalid parameter type" );
×
2821
    }
2822
    if ( IsCharacter( serial ) )
20✔
2823
    {
2824
      Character* chr = system_find_mobile( serial );
3✔
2825
      if ( chr != nullptr )
3✔
2826
      {
2827
        if ( sysfind_flags & SYSFIND_SEARCH_OFFLINE_MOBILES )
2✔
2828
          return new EOfflineCharacterRefObjImp( chr );
×
2829
        return new ECharacterRefObjImp( chr );
2✔
2830
      }
2831

2832
      return new BError( "Character not found" );
1✔
2833
    }
2834

2835
    // dave changed this 3/8/3: objecthash (via system_find_item) will find any kind of item, so
2836
    // don't need system_find_multi here.
2837
    Item* item = system_find_item( serial );
17✔
2838

2839
    if ( item != nullptr )
17✔
2840
    {
2841
      return item->make_ref();
16✔
2842
    }
2843

2844
    return new BError( "Item not found." );
1✔
2845
  }
2846

2847
  return new BError( "Invalid parameter type" );
×
2848
}
2849

2850
BObjectImp* UOExecutorModule::mf_SaveWorldState()
2✔
2851
{
2852
  update_gameclock();
2✔
2853
  cancel_all_trades();
2✔
2854

2855
  PolClockPauser pauser;
2✔
2856
  // do not suspend when critical, to keep defined state of a critical block
2857
  if ( !uoexec().critical() && uoexec().suspend() )
2✔
2858
  {
2859
    Tools::Timer<> total_timer;
1✔
2860
    auto res = write_data(
1✔
2861
        [uoexec = uoexec().weakptr.non_owning(), total_timer = std::move( total_timer )](
2✔
2862
            bool result, u32 clean_writes, u32 dirty_writes, s64 ellapsed ) mutable
2863
        {
2864
          Core::PolLock lck;
1✔
2865
          if ( !uoexec.exists() )
1✔
2866
            return;
×
2867
          if ( result )
1✔
2868
          {
2869
            auto* ret = new Bscript::BStruct();
1✔
2870
            ret->addMember( "DirtyObjects", new Bscript::BLong( dirty_writes ) );
1✔
2871
            ret->addMember( "CleanObjects", new Bscript::BLong( clean_writes ) );
1✔
2872
            ret->addMember( "ElapsedMilliseconds",
1✔
2873
                            new Bscript::BLong( Clib::clamp_convert<int>( ellapsed ) ) );
1✔
2874
            ret->addMember(
1✔
2875
                "ElapsedMillisecondsTotal",
2876
                new Bscript::BLong( Clib::clamp_convert<int>( total_timer.ellapsed() ) ) );
1✔
2877
            uoexec.get_weakptr()->ValueStack.back().set( new Bscript::BObject( ret ) );
1✔
2878
          }
2879
          else
2880
          {
2881
            uoexec.get_weakptr()->ValueStack.back().set(
×
2882
                new Bscript::BObject( new Bscript::BError( "Failed to save world" ) ) );
×
2883
          }
2884
          uoexec.get_weakptr()->revive();
1✔
2885
        } );
1✔
2886
    if ( !res )
1✔
2887
    {
2888
      uoexec().revive();
×
2889
      return new BError( "pol.cfg has InhibitSaves=1" );
×
2890
    }
2891
    if ( *res )
1✔
2892
      return new BLong( 0 );  // callback will be called
1✔
2893
    uoexec().revive();
×
2894
    return new BError( "Failed to save world" );
×
2895
  }
1✔
2896

2897
  // non waiting version
2898
  u32 dirty, clean;
2899
  s64 elapsed_ms;
2900
  auto res = write_data( {}, &dirty, &clean, &elapsed_ms );
1✔
2901
  if ( !res )
1✔
2902
    return new BError( "pol.cfg has InhibitSaves=1" );
×
2903
  if ( *res )
1✔
2904
  {
2905
    BStruct* ret = new BStruct();
1✔
2906
    ret->addMember( "DirtyObjects", new BLong( dirty ) );
1✔
2907
    ret->addMember( "CleanObjects", new BLong( clean ) );
1✔
2908
    ret->addMember( "ElapsedMilliseconds", new BLong( Clib::clamp_convert<int>( elapsed_ms ) ) );
1✔
2909
    return ret;
1✔
2910
  }
2911
  return new BError( "Failed to save world" );
×
2912
}
2✔
2913

2914

2915
BObjectImp* UOExecutorModule::mf_SetRegionLightLevel()
×
2916
{
2917
  const String* region_name_str;
2918
  int lightlevel;
2919
  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, lightlevel ) ) )
×
2920
  {
2921
    return new BError( "Invalid Parameter type" );
×
2922
  }
2923

2924
  if ( !VALID_LIGHTLEVEL( lightlevel ) )
×
2925
  {
2926
    return new BError( "Light Level is out of range" );
×
2927
  }
2928

2929
  LightRegion* lightregion = gamestate.lightdef->getregion( region_name_str->value() );
×
2930
  if ( lightregion == nullptr )
×
2931
  {
2932
    return new BError( "Light region not found" );
×
2933
  }
2934

2935
  SetRegionLightLevel( lightregion, lightlevel );
×
2936
  return new BLong( 1 );
×
2937
}
2938

2939
BObjectImp* UOExecutorModule::mf_SetRegionWeatherLevel()
×
2940
{
2941
  const String* region_name_str;
2942
  int type;
2943
  int severity;
2944
  int aux;
2945
  int lightoverride;
2946
  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, type ) && getParam( 2, severity ) &&
×
2947
          getParam( 3, aux ) && getParam( 4, lightoverride ) ) )
×
2948
  {
2949
    return new BError( "Invalid Parameter type" );
×
2950
  }
2951

2952
  WeatherRegion* weatherregion = gamestate.weatherdef->getregion( region_name_str->value() );
×
2953
  if ( weatherregion == nullptr )
×
2954
  {
2955
    return new BError( "Weather region not found" );
×
2956
  }
2957

2958
  SetRegionWeatherLevel( weatherregion, type, severity, aux, lightoverride );
×
2959

2960
  return new BLong( 1 );
×
2961
}
2962
BObjectImp* UOExecutorModule::mf_AssignRectToWeatherRegion()
×
2963
{
2964
  const String* region_name_str;
2965
  Pos2d nw, se;
×
2966
  Realms::Realm* realm;
2967

2968
  if ( !( getStringParam( 0, region_name_str ) && getRealmParam( 5, &realm ) &&
×
2969
          getPos2dParam( 1, 2, &nw, realm ) && getPos2dParam( 3, 4, &se, realm ) ) )
×
2970
  {
2971
    return new BError( "Invalid Parameter type" );
×
2972
  }
2973

2974
  bool res = gamestate.weatherdef->assign_zones_to_region( region_name_str->data(),
×
2975
                                                           Range2d( nw, se, nullptr ), realm );
×
2976
  if ( res )
×
2977
    return new BLong( 1 );
×
2978
  return new BError( "Weather region not found" );
×
2979
}
2980

2981
BObjectImp* UOExecutorModule::mf_Distance()
×
2982
{
2983
  UObject* obj1;
2984
  UObject* obj2;
2985
  if ( getUObjectParam( 0, obj1 ) && getUObjectParam( 1, obj2 ) )
×
2986
    return new BLong( obj1->distance_to( obj2->toplevel_pos() ) );
×
2987
  return new BError( "Invalid parameter type" );
×
2988
}
2989

2990
BObjectImp* UOExecutorModule::mf_DistanceEuclidean()
×
2991
{
2992
  UObject* obj1;
2993
  UObject* obj2;
2994
  if ( getUObjectParam( 0, obj1 ) && getUObjectParam( 1, obj2 ) )
×
2995
  {
2996
    const UObject* tobj1 = obj1->toplevel_owner();
×
2997
    const UObject* tobj2 = obj2->toplevel_owner();
×
2998
    return new Double( sqrt( pow( (double)( tobj1->x() - tobj2->x() ), 2 ) +
×
2999
                             pow( (double)( tobj1->y() - tobj2->y() ), 2 ) ) );
×
3000
  }
3001

3002
  return new BError( "Invalid parameter type" );
×
3003
}
3004

3005
BObjectImp* UOExecutorModule::mf_CoordinateDistance()
×
3006
{
3007
  Pos2d pos1, pos2;
×
3008
  if ( !getPos2dParam( 0, 1, &pos1 ) || !getPos2dParam( 2, 3, &pos2 ) )
×
3009
    return new BError( "Invalid parameter type" );
×
3010
  return new BLong( pos1.pol_distance( pos2 ) );
×
3011
}
3012

3013
BObjectImp* UOExecutorModule::mf_CoordinateDistanceEuclidean()
×
3014
{
3015
  unsigned short x1, y1, x2, y2;
3016
  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
×
3017
  {
3018
    return new BError( "Invalid parameter type" );
×
3019
  }
3020
  return new Double( sqrt( pow( (double)( x1 - x2 ), 2 ) + pow( (double)( y1 - y2 ), 2 ) ) );
×
3021
}
3022

3023
BObjectImp* UOExecutorModule::mf_GetCoordsInLine()
×
3024
{
3025
  int x1, y1, x2, y2;
3026
  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
×
3027
  {
3028
    return new BError( "Invalid parameter type" );
×
3029
  }
3030
  if ( x1 == x2 && y1 == y2 )
×
3031
  {  // Same exact coordinates ... just give them the coordinate back!
3032
    ObjArray* coords = new ObjArray;
×
3033
    BStruct* point = new BStruct;
×
3034
    point->addMember( "x", new BLong( x1 ) );
×
3035
    point->addMember( "y", new BLong( y1 ) );
×
3036
    coords->addElement( point );
×
3037
    return coords;
×
3038
  }
3039

3040
  double dx = abs( x2 - x1 ) + 0.5;
×
3041
  double dy = abs( y2 - y1 ) + 0.5;
×
3042
  int vx = 0, vy = 0;
×
3043

3044
  if ( x2 > x1 )
×
3045
    vx = 1;
×
3046
  else if ( x2 < x1 )
×
3047
    vx = -1;
×
3048
  if ( y2 > y1 )
×
3049
    vy = 1;
×
3050
  else if ( y2 < y1 )
×
3051
    vy = -1;
×
3052

3053
  std::unique_ptr<ObjArray> coords( new ObjArray );
×
3054
  if ( dx >= dy )
×
3055
  {
3056
    dy = dy / dx;
×
3057

3058
    for ( int c = 0; c <= dx; c++ )
×
3059
    {
3060
      int point_x = x1 + ( c * vx );
×
3061

3062
      double float_y = double( c ) * double( vy ) * dy;
×
3063
      if ( float_y - floor( float_y ) >= 0.5 )
×
3064
        float_y = ceil( float_y );
×
3065
      int point_y = int( float_y ) + y1;
×
3066

3067
      std::unique_ptr<BStruct> point( new BStruct );
×
3068
      point->addMember( "x", new BLong( point_x ) );
×
3069
      point->addMember( "y", new BLong( point_y ) );
×
3070
      coords->addElement( point.release() );
×
3071
    }
×
3072
  }
3073
  else
3074
  {
3075
    dx = dx / dy;
×
3076
    for ( int c = 0; c <= dy; c++ )
×
3077
    {
3078
      int point_y = y1 + ( c * vy );
×
3079

3080
      double float_x = double( c ) * double( vx ) * dx;
×
3081
      if ( float_x - floor( float_x ) >= 0.5 )
×
3082
        float_x = ceil( float_x );
×
3083
      int point_x = int( float_x ) + x1;
×
3084

3085
      std::unique_ptr<BStruct> point( new BStruct );
×
3086
      point->addMember( "x", new BLong( point_x ) );
×
3087
      point->addMember( "y", new BLong( point_y ) );
×
3088
      coords->addElement( point.release() );
×
3089
    }
×
3090
  }
3091
  return coords.release();
×
3092
}
×
3093

3094
BObjectImp* UOExecutorModule::mf_GetFacing()
×
3095
{
3096
  unsigned short from_x, from_y, to_x, to_y;
3097
  if ( !( getParam( 0, from_x ) && getParam( 1, from_y ) && getParam( 2, to_x ) &&
×
3098
          getParam( 3, to_y ) ) )
×
3099
  {
3100
    return new BError( "Invalid parameter type" );
×
3101
  }
3102

3103
  double x = to_x - from_x;
×
3104
  double y = to_y - from_y;
×
3105
  double pi = acos( double( -1 ) );
×
3106
  double r = sqrt( x * x + y * y );
×
3107

3108
  double angle = ( ( acos( x / r ) * 180.0 ) / pi );
×
3109

3110
  double y_check = ( ( asin( y / r ) * 180.0 ) / pi );
×
3111
  if ( y_check < 0 )
×
3112
  {
3113
    angle = 360 - angle;
×
3114
  }
3115

3116
  unsigned short facing = ( ( short( angle / 40 ) + 10 ) % 8 );
×
3117

3118
  return new BLong( facing );
×
3119
}
3120

3121
// FIXME : Should we do an Orphan check here as well? Ugh.
3122
void true_extricate( Item* item )
29✔
3123
{
3124
  send_remove_object_to_inrange( item );
29✔
3125
  if ( item->container != nullptr )
29✔
3126
  {
3127
    item->extricate();
1✔
3128
  }
3129
  else
3130
  {
3131
    remove_item_from_world( item );
28✔
3132
  }
3133
}
29✔
3134

3135
BObjectImp* UOExecutorModule::mf_MoveItemToContainer()
7✔
3136
{
3137
  Item* item;
3138
  Item* cont_item;
3139
  int px;
3140
  int py;
3141
  int add_to_existing_stack;
3142
  if ( !( getItemParam( 0, item ) && getItemParam( 1, cont_item ) && getParam( 2, px, -1, 65535 ) &&
14✔
3143
          getParam( 3, py, -1, 65535 ) && getParam( 4, add_to_existing_stack, 0, 2 ) ) )
7✔
3144
  {
3145
    return new BError( "Invalid parameter type" );
×
3146
  }
3147

3148
  ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
7✔
3149
  if ( !item->movable() )
7✔
3150
  {
3151
    Character* chr = controller_.get();
×
3152
    if ( chr == nullptr || !chr->can_move( item ) )
×
3153
      return new BError( "That is immobile" );
×
3154
  }
3155
  if ( item->inuse() && !is_reserved_to_me( item ) )
7✔
3156
  {
3157
    return new BError( "That item is being used." );
×
3158
  }
3159

3160

3161
  if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
7✔
3162
  {
3163
    return new BError( "Non-container selected as target" );
×
3164
  }
3165
  UContainer* cont = static_cast<UContainer*>( cont_item );
7✔
3166

3167
  if ( cont->serial == item->serial )
7✔
3168
  {
3169
    return new BError( "Can't put a container into itself" );
×
3170
  }
3171
  if ( is_a_parent( cont, item->serial ) )
7✔
3172
  {
3173
    return new BError( "Can't put a container into an item in itself" );
×
3174
  }
3175
  if ( !cont->can_add( *item ) )
7✔
3176
  {
3177
    return new BError( "Container is too full to add that" );
×
3178
  }
3179
  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3180
  Character* chr_owner = cont->GetCharacterOwner();
7✔
3181
  if ( chr_owner == nullptr )
7✔
3182
    if ( controller_.get() != nullptr )
6✔
3183
      chr_owner = controller_.get();
×
3184

3185
  // daved changed order 1/26/3 check canX scripts before onX scripts.
3186
  UContainer* oldcont = item->container;
7✔
3187
  Item* existing_stack = nullptr;
7✔
3188

3189
  if ( ( oldcont != nullptr ) &&
8✔
3190
       ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
1✔
3191
    return new BError( "Could not remove item from its container." );
×
3192
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3193
  {
3194
    return new BError( "Item was destroyed in CanRemove script" );
×
3195
  }
3196

3197
  if ( add_to_existing_stack )
7✔
3198
  {
3199
    existing_stack = cont->find_addable_stack( item );
×
3200
    if ( existing_stack != nullptr )
×
3201
    {
3202
      if ( !cont->can_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack,
×
3203
                                             item->getamount(), item ) )
×
3204
        return new BError( "Could not add to existing stack" );
×
3205
    }
3206
    else if ( add_to_existing_stack == 2 )
×
3207
      add_to_existing_stack = 0;
×
3208
    else
3209
      return new BError( "There is no existing stack" );
×
3210
  }
3211

3212
  if ( !add_to_existing_stack )
7✔
3213
    if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item ) )
7✔
3214
      return new BError( "Could not insert item into container." );
×
3215

3216
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3217
  {
3218
    return new BError( "Item was destroyed in CanInsert Script" );
×
3219
  }
3220

3221
  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
7✔
3222
    return new BError( "Item cannot be unequipped" );
×
3223
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3224
  {
3225
    return new BError( "Item was destroyed in Equip Script" );
×
3226
  }
3227

3228
  if ( oldcont != nullptr )
7✔
3229
  {
3230
    oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
1✔
3231
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
1✔
3232
    {
3233
      return new BError( "Item was destroyed in OnRemove script" );
×
3234
    }
3235
  }
3236

3237
  if ( !add_to_existing_stack )
7✔
3238
  {
3239
    u8 slotIndex = item->slot_index();
7✔
3240
    if ( !cont->can_add_to_slot( slotIndex ) )
7✔
3241
    {
3242
      item->destroy();
×
3243
      return new BError( "No slots available in new container" );
×
3244
    }
3245
    if ( !item->slot_index( slotIndex ) )
7✔
3246
    {
3247
      item->destroy();
×
3248
      return new BError( "Couldn't set slot index on item" );
×
3249
    }
3250

3251
    Core::Pos2d cntpos;
7✔
3252
    if ( px < 0 || py < 0 )
7✔
3253
      cntpos = cont->get_random_location();
7✔
3254
    else
3255
    {
3256
      cntpos.x( static_cast<u16>( px ) ).y( static_cast<u16>( py ) );
×
3257
      if ( !cont->is_legal_posn( cntpos ) )
×
3258
        cntpos = cont->get_random_location();
×
3259
    }
3260

3261
    true_extricate( item );
7✔
3262

3263
    cont->add( item, cntpos );
7✔
3264
    update_item_to_inrange( item );
7✔
3265
    // DAVE added this 11/17: if in a Character's pack, update weight.
3266
    UpdateCharacterWeight( item );
7✔
3267

3268
    cont->on_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item );
7✔
3269
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3270
    {
3271
      return new BError( "Item was destroyed in OnInsert script" );
×
3272
    }
3273
  }
3274
  else
3275
  {
3276
    u16 amount = item->getamount();
×
3277
    true_extricate( item );
×
3278
    existing_stack->add_to_self( item );
×
3279
    update_item_to_inrange( existing_stack );
×
3280
    UpdateCharacterWeight( existing_stack );
×
3281

3282
    cont->on_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack, amount );
×
3283
  }
3284

3285
  return new BLong( 1 );
7✔
3286
}
7✔
3287

3288

3289
BObjectImp* UOExecutorModule::mf_MoveItemToSecureTradeWin()
×
3290
{
3291
  Item* item;
3292
  Character* chr;
3293
  if ( !( getItemParam( 0, item ) && getCharacterParam( 1, chr ) ) )
×
3294
  {
3295
    return new BError( "Invalid parameter type" );
×
3296
  }
3297

3298
  ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
×
3299
  if ( !item->movable() )
×
3300
  {
3301
    Character* _chr = controller_.get();
×
3302
    if ( _chr == nullptr || !_chr->can_move( item ) )
×
3303
      return new BError( "That is immobile" );
×
3304
  }
3305
  if ( item->inuse() && !is_reserved_to_me( item ) )
×
3306
  {
3307
    return new BError( "That item is being used." );
×
3308
  }
3309

3310
  // daved changed order 1/26/3 check canX scripts before onX scripts.
3311
  UContainer* oldcont = item->container;
×
3312

3313
  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3314
  Character* chr_owner = nullptr;
×
3315
  if ( oldcont != nullptr )
×
3316
    chr_owner = oldcont->GetCharacterOwner();
×
3317
  if ( chr_owner == nullptr )
×
3318
    if ( controller_.get() != nullptr )
×
3319
      chr_owner = controller_.get();
×
3320

3321
  if ( ( oldcont != nullptr ) &&
×
3322
       ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
×
3323
    return new BError( "Could not remove item from its container." );
×
3324
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3325
  {
3326
    return new BError( "Item was destroyed in CanRemove script" );
×
3327
  }
3328

3329
  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
×
3330
    return new BError( "Item cannot be unequipped" );
×
3331
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3332
  {
3333
    return new BError( "Item was destroyed in Equip Script" );
×
3334
  }
3335

3336
  if ( oldcont != nullptr )
×
3337
  {
3338
    oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
×
3339
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3340
    {
3341
      return new BError( "Item was destroyed in OnRemove script" );
×
3342
    }
3343
  }
3344

3345
  true_extricate( item );
×
3346

3347
  return place_item_in_secure_trade_container( chr->client, item );
×
3348
}
×
3349

3350
BObjectImp* UOExecutorModule::mf_EquipItem()
22✔
3351
{
3352
  Character* chr;
3353
  Item* item;
3354
  if ( getCharacterParam( 0, chr ) && getItemParam( 1, item ) )
22✔
3355
  {
3356
    ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
22✔
3357
    if ( !item->movable() )
22✔
3358
    {
3359
      Character* _chr = controller_.get();
×
3360
      if ( _chr == nullptr || !_chr->can_move( item ) )
×
3361
        return new BError( "That is immobile" );
×
3362
    }
3363

3364
    if ( item->inuse() && !is_reserved_to_me( item ) )
22✔
3365
    {
3366
      return new BError( "That item is being used." );
×
3367
    }
3368

3369
    if ( !chr->equippable( item ) || !item->check_equiptest_scripts( chr ) )
22✔
3370
    {
3371
      return new BError( "That item is not equippable by that character" );
×
3372
    }
3373
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
22✔
3374
    {
3375
      return new BError( "Item was destroyed in EquipTest script" );
×
3376
    }
3377

3378
    item->layer = Plib::tilelayer( item->graphic );
22✔
3379

3380
    if ( item->has_equip_script() )
22✔
3381
    {
3382
      BObjectImp* res = item->run_equip_script( chr, false );
2✔
3383
      if ( !res->isTrue() )
2✔
3384
        return res;
×
3385
      BObject obj( res );
2✔
3386
    }
2✔
3387

3388

3389
    true_extricate( item );
22✔
3390

3391
    // at this point, 'item' is free - doesn't belong to the world, or a container.
3392
    chr->equip( item );
22✔
3393
    send_wornitem_to_inrange( chr, item );
22✔
3394

3395
    return new BLong( 1 );
22✔
3396
  }
22✔
3397

3398
  return new BError( "Invalid parameter type" );
×
3399
}
3400

3401
BObjectImp* UOExecutorModule::mf_RestartScript()
1✔
3402
{
3403
  UObject* obj;
3404
  if ( !getUObjectParam( 0, obj ) )
1✔
3405
    return new BError( "Invalid parameter" );
×
3406

3407
  if ( obj->script_isa( POLCLASS_NPC ) )
1✔
3408
  {
3409
    NPC* npc = static_cast<NPC*>( obj );
×
3410
    npc->restart_script();
×
3411
    return new BLong( 1 );
×
3412
  }
3413
  if ( obj->script_isa( POLCLASS_ITEM ) )
1✔
3414
  {
3415
    Item* item = static_cast<Item*>( obj );
1✔
3416
    item->stop_control_script();
1✔
3417
    return new BLong( item->start_control_script() );
1✔
3418
  }
3419

3420
  return new BError( "RestartScript only operates on NPCs and Items" );
×
3421
}
3422

3423

3424
BObjectImp* UOExecutorModule::mf_GetHarvestDifficulty()
×
3425
{
3426
  const String* resource;
3427
  unsigned short tiletype;
3428
  Pos2d pos;
×
3429
  Realms::Realm* realm;
3430
  if ( getStringParam( 0, resource ) && getRealmParam( 4, &realm ) && getPos2dParam( 1, 2, &pos ) &&
×
3431
       getParam( 3, tiletype ) )
×
3432
  {
3433
    return get_harvest_difficulty( resource->data(), Pos4d( pos, 0, realm ), tiletype );
×
3434
  }
3435

3436
  return new BError( "Invalid parameter" );
×
3437
}
3438

3439
BObjectImp* UOExecutorModule::mf_HarvestResource()
×
3440
{
3441
  Pos2d pos;
×
3442
  Realms::Realm* realm;
3443
  const String* resource;
3444
  int b;
3445
  int n;
3446

3447
  if ( getStringParam( 0, resource ) && getRealmParam( 5, &realm ) &&
×
3448
       getPos2dParam( 1, 2, &pos, realm ) && getParam( 3, b ) && getParam( 4, n ) )
×
3449
  {
3450
    if ( b <= 0 )
×
3451
      return new BError( "b must be >= 0" );
×
3452
    return harvest_resource( resource->data(), Pos4d( pos, 0, realm ), b, n );
×
3453
  }
3454

3455
  return new BError( "Invalid parameter" );
×
3456
}
3457

3458
BObjectImp* UOExecutorModule::mf_GetRegionName( /* objref */ )
×
3459
{
3460
  UObject* obj;
3461

3462
  if ( getUObjectParam( 0, obj ) )
×
3463
  {
3464
    JusticeRegion* justice_region;
3465
    if ( obj->isa( UOBJ_CLASS::CLASS_ITEM ) )
×
3466
      obj = obj->toplevel_owner();
×
3467

3468
    if ( obj->isa( UOBJ_CLASS::CLASS_CHARACTER ) )
×
3469
    {
3470
      Character* chr = static_cast<Character*>( obj );
×
3471

3472
      if ( chr->logged_in() )
×
3473
        justice_region = chr->client->gd->justice_region;
×
3474
      else
3475
        justice_region = gamestate.justicedef->getregion( chr->pos() );
×
3476
    }
3477
    else
3478
      justice_region = gamestate.justicedef->getregion( obj->pos() );
×
3479

3480
    if ( justice_region == nullptr )
×
3481
      return new BError( "No Region defined at this Location" );
×
3482
    return new String( justice_region->region_name() );
×
3483
  }
3484
  return new BError( "Invalid parameter" );
×
3485
}
3486

3487
BObjectImp* UOExecutorModule::mf_GetRegionNameAtLocation( /* x, y, realm */ )
×
3488
{
3489
  Pos2d pos;
×
3490
  Realms::Realm* realm;
3491
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos ) )
×
3492
  {
3493
    JusticeRegion* justice_region = gamestate.justicedef->getregion( Pos4d( pos, 0, realm ) );
×
3494
    if ( justice_region == nullptr )
×
3495
      return new BError( "No Region defined at this Location" );
×
3496
    return new String( justice_region->region_name() );
×
3497
  }
3498
  return new BError( "Invalid parameter" );
×
3499
}
3500

3501
BObjectImp* UOExecutorModule::mf_GetRegionString()
×
3502
{
3503
  const String* resource;
3504
  const String* propname;
3505
  Pos2d pos;
×
3506
  Realms::Realm* realm;
3507
  if ( getStringParam( 0, resource ) && getRealmParam( 4, &realm ) && getPos2dParam( 1, 2, &pos ) &&
×
3508
       getStringParam( 3, propname ) )
×
3509
  {
3510
    return get_region_string( resource->data(), Pos4d( pos, 0, realm ), propname->value() );
×
3511
  }
3512

3513
  return new BError( "Invalid parameter" );
×
3514
}
3515

3516
BObjectImp* UOExecutorModule::mf_GetRegionLightLevelAtLocation( /* x, y, realm */ )
×
3517
{
3518
  Pos2d pos;
×
3519
  Realms::Realm* realm;
3520

3521
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos ) )
×
3522
  {
3523
    LightRegion* light_region = gamestate.lightdef->getregion( Pos4d( pos, 0, realm ) );
×
3524
    int lightlevel;
3525
    if ( light_region != nullptr )
×
3526
      lightlevel = light_region->lightlevel;
×
3527
    else
3528
      lightlevel = settingsManager.ssopt.default_light_level;
×
3529
    return new BLong( lightlevel );
×
3530
  }
3531

3532
  return new BError( "Invalid parameter" );
×
3533
}
3534

3535

3536
BObjectImp* UOExecutorModule::mf_EquipFromTemplate()
1✔
3537
{
3538
  Character* chr;
3539
  const String* template_name;
3540
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, template_name ) )
1✔
3541
  {
3542
    return equip_from_template( chr, template_name->value() );
1✔
3543
  }
3544

3545
  return new BError( "Invalid parameter" );
×
3546
}
3547

3548
// FIXME: Use a PrivUpdater here
3549
BObjectImp* UOExecutorModule::mf_GrantPrivilege()
4✔
3550
{
3551
  Character* chr;
3552
  const String* privstr;
3553
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, privstr ) )
4✔
3554
  {
3555
    chr->grant_privilege( privstr->data() );
4✔
3556
    return new BLong( 1 );
4✔
3557
  }
3558

3559
  return new BError( "Invalid parameter" );
×
3560
}
3561

3562
// FIXME: Use a PrivUpdater here
3563
BObjectImp* UOExecutorModule::mf_RevokePrivilege()
1✔
3564
{
3565
  Character* chr;
3566
  const String* privstr;
3567
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, privstr ) )
1✔
3568
  {
3569
    chr->revoke_privilege( privstr->data() );
1✔
3570
    return new BLong( 1 );
1✔
3571
  }
3572

3573
  return new BError( "Invalid parameter" );
×
3574
}
3575

3576
BObjectImp* UOExecutorModule::mf_ReadGameClock()
14✔
3577
{
3578
  return new BLong( read_gameclock() );
14✔
3579
}
3580

3581
unsigned char decode_xdigit( unsigned char ch )
×
3582
{
3583
  if ( ch >= '0' && ch <= '9' )
×
3584
    ch -= '0';
×
3585
  else if ( ch >= 'A' && ch <= 'F' )
×
3586
    ch = ch - 'A' + 0xa;
×
3587
  else if ( ch >= 'a' && ch <= 'f' )
×
3588
    ch = ch - 'a' + 0xa;
×
3589

3590
  return ch;
×
3591
}
3592

3593
BObjectImp* UOExecutorModule::mf_SendPacket()
×
3594
{
3595
  Character* chr;
3596
  Network::Client* client;
3597
  const String* str;
3598
  if ( getCharacterOrClientParam( 0, chr, client ) && getStringParam( 1, str ) )
×
3599
  {
3600
    if ( str->length() % 2 > 0 )
×
3601
    {
3602
      return new BError( "Invalid packet string length." );
×
3603
    }
3604
    Network::PktHelper::PacketOut<Network::EncryptedPktBuffer>
3605
        buffer;  // encryptedbuffer is the only one without getID buffer[0]
×
3606
    unsigned char* buf = reinterpret_cast<unsigned char*>( buffer->getBuffer() );
×
3607
    const char* s = str->data();
×
3608
    while ( buffer->offset < 2000 && isxdigit( s[0] ) && isxdigit( s[1] ) )
×
3609
    {
3610
      unsigned char ch;
3611
      ch = ( decode_xdigit( s[0] ) << 4 ) | decode_xdigit( s[1] );
×
3612
      *( buf++ ) = ch;
×
3613
      buffer->offset++;
×
3614
      s += 2;
×
3615
    }
3616
    if ( chr != nullptr )
×
3617
    {
3618
      if ( chr->has_active_client() )
×
3619
      {
3620
        buffer.Send( chr->client );
×
3621
        return new BLong( 1 );
×
3622
      }
3623

3624
      return new BError( "No client attached" );
×
3625
    }
3626
    if ( client != nullptr )
×
3627
    {
3628
      if ( client->isConnected() )
×
3629
      {
3630
        buffer.Send( client );
×
3631
        return new BLong( 1 );
×
3632
      }
3633

3634
      return new BError( "Client is disconnected" );
×
3635
    }
3636

3637
    return new BError( "Invalid parameter type" );
×
3638
  }
×
3639

3640
  return new BError( "Invalid parameter type" );
×
3641
}
3642

3643
BObjectImp* UOExecutorModule::mf_SendQuestArrow()
×
3644
{
3645
  Character* chr;
3646
  int x, y;
3647
  u32 arrowid = 0;
×
3648

3649
  if ( getCharacterParam( 0, chr ) && getParam( 1, x, -1, 1000000 ) &&
×
3650
       getParam( 2, y, -1, 1000000 ) )  // max values checked below
×
3651
  {
3652
    if ( !chr->has_active_client() )
×
3653
      return new BError( "No client attached" );
×
3654
    {
3655
      int arrow_id;
3656
      if ( exec.getParam( 3, arrow_id ) )
×
3657
      {
3658
        if ( arrow_id < 1 )
×
3659
          return new BError( "ArrowID out of range" );
×
3660
        arrowid = static_cast<u32>( arrow_id );
×
3661
      }
3662
      else
3663
        arrowid = uoexec().pid();
×
3664
    }
3665
    bool usesNewPktSize = ( chr->client->ClientType & Network::CLIENTTYPE_7090 ) > 0;
×
3666

3667
    Network::PktHelper::PacketOut<Network::PktOut_BA> msg;
×
3668
    if ( x == -1 && y == -1 )
×
3669
    {
3670
      msg->Write<u8>( PKTOUT_BA_ARROW_OFF );
×
3671
      msg->offset += 4;  // u16 x_tgt,y_tgt
×
3672
      if ( usesNewPktSize )
×
3673
      {
3674
        if ( !arrowid )
×
3675
        {
3676
          return new BError( "ArrowID must be supplied for cancelation." );
×
3677
        }
3678

3679
        msg->Write<u32>( static_cast<u32>( arrowid & 0xFFFFFFFF ) );
×
3680
      }
3681
    }
3682
    else
3683
    {
3684
      auto pos = Core::Pos2d( Clib::clamp_convert<u16>( x ), Clib::clamp_convert<u16>( y ) );
×
3685
      if ( !chr->realm()->valid( pos ) )
×
3686
        return new BError( "Invalid Coordinates for Realm" );
×
3687
      msg->Write<u8>( PKTOUT_BA_ARROW_ON );
×
3688
      msg->WriteFlipped<u16>( pos.x() );
×
3689
      msg->WriteFlipped<u16>( pos.y() );
×
3690
      if ( usesNewPktSize )
×
3691
        msg->Write<u32>( static_cast<u32>( arrowid & 0xFFFFFFFF ) );
×
3692
    }
3693
    msg.Send( chr->client );
×
3694
    return new BLong( arrowid );
×
3695
  }
×
3696

3697
  return new BError( "Invalid parameter" );
×
3698
}
3699

3700
BObjectImp* UOExecutorModule::mf_ConsumeReagents()
×
3701
{
3702
  Character* chr;
3703
  int spellid;
3704
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) )
×
3705
  {
3706
    if ( !VALID_SPELL_ID( spellid ) )
×
3707
    {
3708
      return new BError( "Spell ID out of range" );
×
3709
    }
3710
    USpell* spell = gamestate.spells[spellid];
×
3711
    if ( spell == nullptr )
×
3712
    {
3713
      return new BError( "No such spell" );
×
3714
    }
3715

3716
    return new BLong( spell->consume_reagents( chr ) ? 1 : 0 );
×
3717
  }
3718

3719
  return new BError( "Invalid parameter" );
×
3720
}
3721

3722
BObjectImp* UOExecutorModule::mf_StartSpellEffect()
×
3723
{
3724
  Character* chr;
3725
  int spellid;
3726
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) )
×
3727
  {
3728
    if ( !VALID_SPELL_ID( spellid ) )
×
3729
    {
3730
      return new BError( "Spell ID out of range" );
×
3731
    }
3732
    USpell* spell = gamestate.spells[spellid];
×
3733
    if ( spell == nullptr )
×
3734
    {
3735
      return new BError( "No such spell" );
×
3736
    }
3737

3738
    spell->cast( chr );
×
3739
    return new BLong( 1 );
×
3740
  }
3741

3742
  return new BError( "Invalid parameter" );
×
3743
}
3744
BObjectImp* UOExecutorModule::mf_GetSpellDifficulty()
×
3745
{
3746
  int spellid;
3747
  if ( getParam( 0, spellid ) )
×
3748
  {
3749
    if ( !VALID_SPELL_ID( spellid ) )
×
3750
    {
3751
      return new BError( "Spell ID out of range" );
×
3752
    }
3753
    USpell* spell = gamestate.spells[spellid];
×
3754
    if ( spell == nullptr )
×
3755
    {
3756
      return new BError( "No such spell" );
×
3757
    }
3758

3759
    return new BLong( spell->difficulty() );
×
3760
  }
3761

3762
  return new BError( "Invalid parameter" );
×
3763
}
3764
BObjectImp* UOExecutorModule::mf_SpeakPowerWords()
×
3765
{
3766
  Character* chr;
3767
  int spellid;
3768
  unsigned short font;
3769
  unsigned short color;
3770

3771
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) && getParam( 2, font ) &&
×
3772
       getParam( 3, color ) )
×
3773
  {
3774
    if ( !VALID_SPELL_ID( spellid ) )
×
3775
    {
3776
      return new BError( "Spell ID out of range" );
×
3777
    }
3778
    USpell* spell = gamestate.spells[spellid];
×
3779
    if ( spell == nullptr )
×
3780
    {
3781
      return new BError( "No such spell" );
×
3782
    }
3783

3784
    spell->speak_power_words( chr, font, color );
×
3785

3786
    return new BLong( 1 );
×
3787
  }
3788

3789
  return new BError( "Invalid parameter" );
×
3790
}
3791

3792
BObjectImp* UOExecutorModule::mf_ListEquippedItems()
×
3793
{
3794
  Character* chr;
3795
  if ( getCharacterParam( 0, chr ) )
×
3796
  {
3797
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
3798
    for ( int layer = LAYER_EQUIP__LOWEST; layer <= LAYER_EQUIP__HIGHEST; ++layer )
×
3799
    {
3800
      Item* item = chr->wornitem( layer );
×
3801
      if ( item != nullptr )
×
3802
      {
3803
        arr->addElement( new EItemRefObjImp( item ) );
×
3804
      }
3805
    }
3806
    return arr.release();
×
3807
  }
×
3808

3809
  return new BError( "Invalid parameter" );
×
3810
}
3811

3812
BObjectImp* UOExecutorModule::mf_GetEquipmentByLayer()
3✔
3813
{
3814
  Character* chr;
3815
  int layer;
3816
  if ( getCharacterParam( 0, chr ) && getParam( 1, layer ) )
3✔
3817
  {
3818
    if ( layer < LOWEST_LAYER || layer > HIGHEST_LAYER )
3✔
3819
    {
3820
      return new BError( "Invalid layer" );
×
3821
    }
3822

3823
    Item* item = chr->wornitem( layer );
3✔
3824
    if ( item == nullptr )
3✔
3825
    {
3826
      return new BError( "Nothing equipped on that layer." );
1✔
3827
    }
3828

3829
    return new EItemRefObjImp( item );
2✔
3830
  }
3831

3832
  return new BError( "Invalid parameter" );
×
3833
}
3834

3835
BObjectImp* UOExecutorModule::mf_DisconnectClient()
×
3836
{
3837
  Character* chr;
3838
  Network::Client* client;
3839

3840
  if ( getCharacterOrClientParam( 0, chr, client ) )
×
3841
  {
3842
    if ( chr != nullptr )
×
3843
    {
3844
      if ( !chr->has_active_client() )
×
3845
        return new BError( "No client attached" );
×
3846

3847
      client = chr->client;
×
3848
    }
3849

3850
    if ( client != nullptr )
×
3851
    {
3852
      if ( client->isConnected() )
×
3853
      {
3854
        client->Disconnect();
×
3855
        return new BLong( 1 );
×
3856
      }
3857
      return new BError( "Client is disconnected" );
×
3858
    }
3859
    return new BError( "Invalid parameter type" );
×
3860
  }
3861

3862
  return new BError( "Invalid parameter type" );
×
3863
}
3864

3865
BObjectImp* UOExecutorModule::mf_GetMapInfo()
34✔
3866
{
3867
  Core::Pos2d pos;
34✔
3868
  Realms::Realm* realm;
3869

3870
  // note that this uses WORLD_MAX_X, not WORLD_X,
3871
  // because we can't read the outermost edge of the map
3872
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos, realm ) )
34✔
3873
  {
3874
    Plib::MAPTILE_CELL cell = realm->getmaptile( pos );
34✔
3875
    std::unique_ptr<BStruct> result( new BStruct );
34✔
3876
    result->addMember( "z", new BLong( cell.z ) );
34✔
3877
    result->addMember( "landtile", new BLong( cell.landtile ) );
34✔
3878

3879
    return result.release();
34✔
3880
  }
34✔
3881

3882
  return new BError( "Invalid parameter" );
×
3883
}
3884
BObjectImp* UOExecutorModule::mf_GetWorldHeight()
×
3885
{
3886
  Core::Pos2d pos;
×
3887
  Realms::Realm* realm;
3888
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos, realm ) )
×
3889
  {
3890
    short z = -255;
×
3891
    if ( realm->lowest_standheight( pos, &z ) )
×
3892
      return new BLong( z );
×
3893
    return new BError( "Nowhere" );
×
3894
  }
3895

3896
  return new BError( "Invalid parameter" );
×
3897
}
3898

3899
BObjectImp* UOExecutorModule::mf_GetObjtypeByName()
×
3900
{
3901
  const String* namestr;
3902
  if ( getStringParam( 0, namestr ) )
×
3903
  {
3904
    unsigned int objtype = get_objtype_byname( namestr->data() );
×
3905
    if ( objtype != 0 )
×
3906
      return new BLong( objtype );
×
3907
    return new BError( "No objtype by that name" );
×
3908
  }
3909

3910
  return new BError( "Invalid parameter" );
×
3911
}
3912

3913
BObjectImp* UOExecutorModule::mf_SendEvent()
×
3914
{
3915
  Character* chr;
3916
  if ( getCharacterParam( 0, chr ) )
×
3917
  {
3918
    BObjectImp* event = exec.getParamImp( 1 );
×
3919
    if ( event != nullptr )
×
3920
    {
3921
      if ( chr->isa( UOBJ_CLASS::CLASS_NPC ) )
×
3922
      {
3923
        NPC* npc = static_cast<NPC*>( chr );
×
3924
        // event->add_ref(); // UNTESTED
3925
        return npc->send_event_script( event->copy() );
×
3926
      }
3927

3928
      return new BError( "That mobile is not an NPC" );
×
3929
    }
3930

3931
    return new BError( "Huh?  Not enough parameters" );
×
3932
  }
3933

3934
  return new BError( "Invalid parameter" );
×
3935
}
3936

3937
BObjectImp* UOExecutorModule::mf_DestroyMulti()
29✔
3938
{
3939
  Multi::UMulti* multi;
3940
  if ( getMultiParam( 0, multi ) )
29✔
3941
  {
3942
    const ItemDesc& id = find_itemdesc( multi->objtype_ );
29✔
3943
    if ( !id.destroy_script.empty() )
29✔
3944
    {
3945
      BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( multi ) );
×
3946
      if ( !res->isTrue() )
×
3947
      {  // destruction is okay
3948
        return res;
×
3949
      }
3950
    }
3951

3952
    Multi::UBoat* boat = multi->as_boat();
29✔
3953
    if ( boat != nullptr )
29✔
3954
    {
3955
      return Multi::destroy_boat( boat );
16✔
3956
    }
3957
    Multi::UHouse* house = multi->as_house();
13✔
3958
    if ( house != nullptr )
13✔
3959
    {
3960
      return Multi::destroy_house( house );
13✔
3961
    }
3962
    return new BError( "WTF!? Don't know what kind of multi that is!" );
×
3963
  }
3964

3965
  return new BError( "Invalid parameter type" );
×
3966
}
3967

3968

3969
BObjectImp* UOExecutorModule::mf_GetMultiDimensions()
×
3970
{
3971
  u16 multiid;
3972
  if ( getParam( 0, multiid ) )
×
3973
  {
3974
    if ( !Multi::MultiDefByMultiIDExists( multiid ) )
×
3975
      return new BError( "MultiID not found" );
×
3976

3977
    const Multi::MultiDef& md = *Multi::MultiDefByMultiID( multiid );
×
3978
    std::unique_ptr<BStruct> ret( new BStruct );
×
3979
    ret->addMember( "xmin", new BLong( md.minrxyz.x() ) );
×
3980
    ret->addMember( "xmax", new BLong( md.maxrxyz.x() ) );
×
3981
    ret->addMember( "ymin", new BLong( md.minrxyz.y() ) );
×
3982
    ret->addMember( "ymax", new BLong( md.maxrxyz.y() ) );
×
3983
    return ret.release();
×
3984
  }
×
3985
  return new BError( "Invalid parameter" );
×
3986
}
3987

3988
BObjectImp* UOExecutorModule::mf_SetScriptController()
×
3989
{
3990
  Character* old_controller = controller_.get();
×
3991
  BObjectImp* param0 = getParamImp( 0 );
×
3992
  bool handled = false;
×
3993

3994
  if ( auto* lng = impptrIf<BLong>( param0 ) )
×
3995
  {
3996
    if ( lng->value() == 0 )
×
3997
    {
3998
      controller_.clear();
×
3999
      handled = true;
×
4000
    }
4001
  }
4002

4003
  if ( !handled )
×
4004
  {
4005
    Character* chr;
4006
    if ( getCharacterParam( 0, chr ) )
×
4007
      controller_.set( chr );
×
4008
    else
4009
      controller_.clear();
×
4010
  }
4011

4012
  if ( old_controller )
×
4013
    return new ECharacterRefObjImp( old_controller );
×
4014
  return new BLong( 0 );
×
4015
}
4016

4017
BObjectImp* UOExecutorModule::mf_GetStandingHeight()
6✔
4018
{
4019
  Core::Pos4d pos;
6✔
4020
  if ( getPos4dParam( 0, 1, 2, 3, &pos ) )
6✔
4021
  {
4022
    short newz;
4023
    Multi::UMulti* multi;
4024
    Item* walkon;
4025
    if ( pos.realm()->lowest_walkheight( pos.xy(), pos.z(), &newz, &multi, &walkon, true,
6✔
4026
                                         Plib::MOVEMODE_LAND ) )
4027
    {
4028
      std::unique_ptr<BStruct> arr( new BStruct );
6✔
4029
      arr->addMember( "z", new BLong( newz ) );
6✔
4030
      if ( multi != nullptr )
6✔
4031
        arr->addMember( "multi", new EMultiRefObjImp( multi ) );
6✔
4032
      return arr.release();
6✔
4033
    }
6✔
4034

4035
    return new BError( "Can't stand there" );
×
4036
  }
4037

4038
  return new BError( "Invalid parameter type" );
×
4039
}
4040

4041
BObjectImp* UOExecutorModule::mf_GetStandingLayers( /* x, y, flags, realm, includeitems */ )
×
4042
{
4043
  Core::Pos2d pos;
×
4044
  int flags;
4045
  Realms::Realm* realm;
4046
  int includeitems;
4047

4048
  if ( getRealmParam( 3, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, flags ) &&
×
4049
       getParam( 4, includeitems ) )
×
4050
  {
4051
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4052

4053
    Plib::MapShapeList mlist;
×
4054
    Core::ItemsVector ivec;
×
4055
    realm->readmultis( mlist, pos, flags );
×
4056
    realm->getmapshapes( mlist, pos, flags );
×
4057
    if ( includeitems )
×
4058
    {
4059
      realm->readdynamics( mlist, pos, ivec, false, flags );
×
4060
    }
4061

NEW
4062
    for ( const auto& entry : mlist )
×
4063
    {
4064
      std::unique_ptr<BStruct> arr( new BStruct );
×
4065

NEW
4066
      if ( entry.flags & ( Plib::FLAG::MOVELAND | Plib::FLAG::MOVESEA ) )
×
NEW
4067
        arr->addMember( "z", new BLong( entry.z + entry.height ) );
×
4068
      else
NEW
4069
        arr->addMember( "z", new BLong( entry.z ) );
×
4070

NEW
4071
      arr->addMember( "height", new BLong( entry.height ) );
×
NEW
4072
      arr->addMember( "flags", new BLong( entry.flags ) );
×
4073
      newarr->addElement( arr.release() );
×
4074
    }
×
4075

4076
    return newarr.release();
×
4077
  }
×
4078
  return new BError( "Invalid parameter type" );
×
4079
}
4080

4081
BObjectImp*
4082
UOExecutorModule::mf_GetStandingCoordinates() /* x, y, radius, minz, maxz, realm := _DEFAULT_REALM,
1✔
4083
                                                 movemode := "L", doors_block = 0 */
4084
{
4085
  s16 r, minz, maxz;
4086
  int doors_block;
4087
  const String* movemodename;
4088
  Core::Pos2d pos;
1✔
4089
  Realms::Realm* realm;
4090

4091
  if ( !getRealmParam( 5, &realm ) )
1✔
4092
    return new BError( "Realm not found" );
×
4093

4094
  if ( !( getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, r ) && getParam( 3, minz ) &&
2✔
4095
          getParam( 4, maxz ) && getStringParam( 6, movemodename ) && getParam( 7, doors_block ) ) )
1✔
4096
  {
4097
    return new BError( "Invalid parameter type" );
×
4098
  }
4099

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

4102
  std::unique_ptr<ObjArray> result( new ObjArray );
1✔
4103

4104
  // Iterate through all tiles in range and populate the return array with valid standing locations
4105
  Core::Vec2d radius( r, r );
1✔
4106
  Core::Pos2d tl = pos - radius;
1✔
4107
  Core::Pos2d br = pos + radius;
1✔
4108
  Core::Range2d range( tl, br, realm );
1✔
4109
  for ( const auto& tile : range )
243✔
4110
  {
4111
    auto layers = realm->get_walkheights( tile, minz, maxz, movemode, doors_block );
121✔
4112
    for ( const auto& layer : layers )
154✔
4113
    {
4114
      std::unique_ptr<BStruct> height_struct( new BStruct );
33✔
4115
      auto z = std::get<0>( layer );
33✔
4116
      auto multi = std::get<1>( layer );
33✔
4117

4118
      // Figure out which members to stick in the struct -- we only add multi it exists
4119
      height_struct->addMember( "x", new BLong( tile.x() ) );
33✔
4120
      height_struct->addMember( "y", new BLong( tile.y() ) );
33✔
4121
      height_struct->addMember( "z", new BLong( z ) );
33✔
4122
      if ( multi != nullptr )
33✔
4123
      {
4124
        height_struct->addMember( "multi", new EMultiRefObjImp( multi ) );
×
4125
      }
4126

4127
      // Add struct to the return array
4128
      result->addElement( height_struct.release() );
33✔
4129
    }
33✔
4130
  }
121✔
4131

4132
  return result.release();
1✔
4133
}
1✔
4134

4135
BObjectImp* UOExecutorModule::mf_ReserveItem()
3✔
4136
{
4137
  Item* item;
4138
  if ( getItemParam( 0, item ) )
3✔
4139
  {
4140
    if ( item->inuse() )
3✔
4141
    {
4142
      if ( is_reserved_to_me( item ) )
2✔
4143
        return new BLong( 2 );
2✔
4144
      return new BError( "That item is already being used." );
×
4145
    }
4146
    item->inuse( true );
1✔
4147
    reserved_items_.emplace_back( item );
1✔
4148
    return new BLong( 1 );
1✔
4149
  }
4150

4151
  return new BError( "Invalid parameter" );
×
4152
}
4153

4154
BObjectImp* UOExecutorModule::mf_ReleaseItem()
3✔
4155
{
4156
  Item* item;
4157
  if ( getItemParam( 0, item ) )
3✔
4158
  {
4159
    if ( item->inuse() )
3✔
4160
    {
4161
      for ( unsigned i = 0; i < reserved_items_.size(); ++i )
3✔
4162
      {
4163
        if ( reserved_items_[i].get() == item )
3✔
4164
        {
4165
          item->inuse( false );
3✔
4166
          reserved_items_[i] = reserved_items_.back();
3✔
4167
          reserved_items_.pop_back();
3✔
4168
          return new BLong( 1 );
3✔
4169
        }
4170
      }
4171
      return new BError( "That item is not reserved by this script." );
×
4172
    }
4173

4174
    return new BError( "That item is not reserved." );
×
4175
  }
4176

4177
  return new BError( "Invalid parameter" );
×
4178
}
4179

4180

4181
BObjectImp* UOExecutorModule::mf_SendSkillWindow()
×
4182
{
4183
  Character *towhom, *forwhom;
4184
  if ( getCharacterParam( 0, towhom ) && getCharacterParam( 1, forwhom ) )
×
4185
  {
4186
    if ( towhom->has_active_client() )
×
4187
    {
4188
      send_skillmsg( towhom->client, forwhom );
×
4189
      return new BLong( 1 );
×
4190
    }
4191

4192
    return new BError( "No client attached" );
×
4193
  }
4194

4195
  return new BError( "Invalid parameter type" );
×
4196
}
4197

4198

4199
BObjectImp* UOExecutorModule::mf_OpenPaperdoll()
×
4200
{
4201
  Character *towhom, *forwhom;
4202
  if ( getCharacterParam( 0, towhom ) && getCharacterParam( 1, forwhom ) )
×
4203
  {
4204
    if ( towhom->has_active_client() )
×
4205
    {
4206
      send_paperdoll( towhom->client, forwhom );
×
4207
      return new BLong( 1 );
×
4208
    }
4209

4210
    return new BError( "No client attached" );
×
4211
  }
4212

4213
  return new BError( "Invalid parameter type" );
×
4214
}
4215

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

4220
BObjectImp* UOExecutorModule::mf_ConsumeSubstance()
×
4221
{
4222
  Item* cont_item;
4223
  unsigned int objtype;
4224
  int amount;
4225
  if ( getItemParam( 0, cont_item ) && getObjtypeParam( 1, objtype ) && getParam( 2, amount ) )
×
4226
  {
4227
    if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
4228
      return new BError( "That is not a container" );
×
4229
    if ( amount < 0 )
×
4230
      return new BError( "Amount cannot be negative" );
×
4231

4232
    UContainer* cont = static_cast<UContainer*>( cont_item );
×
4233
    int amthave = cont->find_sumof_objtype_noninuse( objtype );
×
4234
    if ( amthave < amount )
×
4235
      return new BError( "Not enough of that substance in container" );
×
4236

4237
    cont->consume_sumof_objtype_noninuse( objtype, amount );
×
4238

4239
    return new BLong( 1 );
×
4240
  }
4241

4242
  return new BError( "Invalid parameter type" );
×
4243
}
4244

4245
bool UOExecutorModule::is_reserved_to_me( Item* item )
2✔
4246
{
4247
  for ( const auto& reserved_item : reserved_items_ )
2✔
4248
  {
4249
    if ( reserved_item.get() == item )
2✔
4250
      return true;
2✔
4251
  }
4252
  return false;
×
4253
}
4254

4255
BObjectImp* UOExecutorModule::mf_Shutdown()
2✔
4256
{
4257
  int exit_code = 0;
2✔
4258

4259
  if ( exec.hasParams( 1 ) )
2✔
4260
  {
4261
    getParam( 0, exit_code );
2✔
4262
  }
4263

4264
  Clib::signal_exit( exit_code );
2✔
4265
#ifndef _WIN32
4266
  // the catch_signals_thread (actually main) sits with sigwait(),
4267
  // so it won't wake up except by being signalled.
4268
  signal_catch_thread();
2✔
4269
#endif
4270
  return new BLong( 1 );
2✔
4271
}
4272

4273

4274
BObjectImp* UOExecutorModule::mf_GetCommandHelp()
×
4275
{
4276
  Character* chr;
4277
  const String* cmd;
4278
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, cmd ) )
×
4279
  {
4280
    std::string help = get_textcmd_help( chr, cmd->value() );
×
4281
    if ( !help.empty() )
×
4282
    {
4283
      return new String( help );
×
4284
    }
4285

4286
    return new BError( "No help for that command found" );
×
4287
  }
×
4288

4289
  return new BError( "Invalid parameter type" );
×
4290
}
4291

4292

4293
BObjectImp* UOExecutorModule::mf_SendStringAsTipWindow()
×
4294
{
4295
  Character* chr;
4296
  const String* str;
4297
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, str ) )
×
4298
  {
4299
    if ( chr->has_active_client() )
×
4300
    {
4301
      send_tip( chr->client, str->value() );
×
4302
      return new BLong( 1 );
×
4303
    }
4304

4305
    return new BError( "No client attached" );
×
4306
  }
4307

4308
  return new BError( "Invalid parameter type" );
×
4309
}
4310

4311
BObjectImp* UOExecutorModule::mf_ListItemsNearLocationWithFlag(
×
4312
    /* x, y, z, range, flags, realm */ )  // DAVE
4313
{
4314
  Pos2d pos;
×
4315
  u16 range;
4316
  int z, flags;
4317
  Realms::Realm* realm;
4318

4319
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4320
       getParam( 3, range ) && getParam( 4, flags ) )
×
4321
  {
4322
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4323
    WorldIterator<ItemFilter>::InRange(
×
4324
        pos, realm, range,
4325
        [&]( Item* item )
×
4326
        {
4327
          if ( ( Plib::tile_uoflags( item->graphic ) & flags ) )
×
4328
          {
4329
            if ( item->in_range( pos, range ) )
×
4330
            {
4331
              if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
4332
                newarr->addElement( new EItemRefObjImp( item ) );
×
4333
            }
4334
          }
4335
        } );
×
4336

4337
    return newarr.release();
×
4338
  }
×
4339

4340
  return new BError( "Invalid parameter" );
×
4341
}
4342

4343
BObjectImp* UOExecutorModule::mf_ListStaticsAtLocation( /* x, y, z, flags, realm */ )
×
4344
{
4345
  Core::Pos2d pos;
×
4346
  int z, flags;
4347
  Realms::Realm* realm;
4348

4349
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4350
       getParam( 3, flags ) )
×
4351
  {
4352
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4353

4354
    if ( !( flags & ITEMS_IGNORE_STATICS ) )
×
4355
    {
4356
      Plib::StaticEntryList slist;
×
4357
      realm->getstatics( slist, pos );
×
4358

NEW
4359
      for ( const auto& entry : slist )
×
4360
      {
NEW
4361
        if ( ( z == LIST_IGNORE_Z ) || ( entry.z == z ) )
×
4362
        {
4363
          std::unique_ptr<BStruct> arr( new BStruct );
×
4364
          arr->addMember( "x", new BLong( pos.x() ) );
×
4365
          arr->addMember( "y", new BLong( pos.y() ) );
×
NEW
4366
          arr->addMember( "z", new BLong( entry.z ) );
×
NEW
4367
          arr->addMember( "objtype", new BLong( entry.objtype ) );
×
NEW
4368
          arr->addMember( "hue", new BLong( entry.hue ) );
×
4369
          newarr->addElement( arr.release() );
×
4370
        }
×
4371
      }
4372
    }
×
4373

4374
    if ( !( flags & ITEMS_IGNORE_MULTIS ) )
×
4375
    {
4376
      Plib::StaticList mlist;
×
4377
      realm->readmultis( mlist, pos );
×
4378

NEW
4379
      for ( const auto& entry : mlist )
×
4380
      {
NEW
4381
        if ( ( z == LIST_IGNORE_Z ) || ( entry.z == z ) )
×
4382
        {
4383
          std::unique_ptr<BStruct> arr( new BStruct );
×
4384
          arr->addMember( "x", new BLong( pos.x() ) );
×
4385
          arr->addMember( "y", new BLong( pos.y() ) );
×
NEW
4386
          arr->addMember( "z", new BLong( entry.z ) );
×
NEW
4387
          arr->addMember( "objtype", new BLong( entry.graphic ) );
×
4388
          newarr->addElement( arr.release() );
×
4389
        }
×
4390
      }
4391
    }
×
4392

4393
    return newarr.release();
×
4394
  }
×
4395
  return new BError( "Invalid parameter" );
×
4396
}
4397

4398
BObjectImp* UOExecutorModule::mf_ListStaticsNearLocation( /* x, y, z, range, flags, realm */ )
×
4399
{
4400
  Core::Pos2d pos;
×
4401
  int z, flags;
4402
  short range;
4403
  Realms::Realm* realm;
4404

4405
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4406
       getParam( 3, range ) && getParam( 4, flags ) )
×
4407
  {
4408
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4409
    Core::Vec2d radius( range, range );
×
4410
    Core::Range2d area( pos - radius, pos + radius, realm );
×
4411
    for ( const auto& tile : area )
×
4412
    {
4413
      if ( !( flags & ITEMS_IGNORE_STATICS ) )
×
4414
      {
4415
        Plib::StaticEntryList slist;
×
4416
        realm->getstatics( slist, tile );
×
4417

NEW
4418
        for ( const auto& entry : slist )
×
4419
        {
NEW
4420
          if ( ( z == LIST_IGNORE_Z ) || ( abs( entry.z - z ) < CONST_DEFAULT_ZRANGE ) )
×
4421
          {
4422
            std::unique_ptr<BStruct> arr( new BStruct );
×
4423
            arr->addMember( "x", new BLong( tile.x() ) );
×
4424
            arr->addMember( "y", new BLong( tile.y() ) );
×
NEW
4425
            arr->addMember( "z", new BLong( entry.z ) );
×
NEW
4426
            arr->addMember( "objtype", new BLong( entry.objtype ) );
×
NEW
4427
            arr->addMember( "hue", new BLong( entry.hue ) );
×
4428
            newarr->addElement( arr.release() );
×
4429
          }
×
4430
        }
4431
      }
×
4432

4433
      if ( !( flags & ITEMS_IGNORE_MULTIS ) )
×
4434
      {
4435
        Plib::StaticList mlist;
×
4436
        realm->readmultis( mlist, tile );
×
4437

NEW
4438
        for ( const auto& entry : mlist )
×
4439
        {
NEW
4440
          if ( ( z == LIST_IGNORE_Z ) || ( abs( entry.z - z ) < CONST_DEFAULT_ZRANGE ) )
×
4441
          {
4442
            std::unique_ptr<BStruct> arr( new BStruct );
×
4443
            arr->addMember( "x", new BLong( tile.x() ) );
×
4444
            arr->addMember( "y", new BLong( tile.y() ) );
×
NEW
4445
            arr->addMember( "z", new BLong( entry.z ) );
×
NEW
4446
            arr->addMember( "objtype", new BLong( entry.graphic ) );
×
4447
            newarr->addElement( arr.release() );
×
4448
          }
×
4449
        }
4450
      }
×
4451
    }
4452

4453
    return newarr.release();
×
4454
  }
×
4455
  return new BError( "Invalid parameter" );
×
4456
}
4457

4458
//  Birdy :  (Notes on Pathfinding)
4459
//
4460
//  This implimentation of pathfinding is actually from a very nice stl based A*
4461
//  implimentation.  It will, within reason, succeed in finding a path in a wide
4462
//  variety of circumstance, including traversing stairs and multiple level
4463
//  structures.
4464
//
4465
//  Unfortunately, it, or my POL support classes implimenting it, seems to at times
4466
//  generate corrupted results in finding a path.  These corruptions take the form
4467
//  of basically looping within the result list, or linking in non-closed list nodes
4468
//  which may be on the open list and generate more looping or which may have even
4469
//  been deleted by the algorithm, generally causing an illegal reference and crash.
4470
//
4471
//  Thus far, the above senerio has only been replicated, though fairly reliably, in
4472
//  cases where there are many many(about 100) npc's trying to pathfind at the same time,
4473
//  in close proximity to one another.
4474
//
4475
//  "Looping" will, of course, cause the shard to hang while the pathfinding routine
4476
//  tries to build the child list for the solution set.  An illegal reference will
4477
//  cause the shard to crash most likely.  Neither is tollerable of course, so I have
4478
//  put in two safeguards against this.
4479
//
4480
//  Safeguard #1) As we go through the nodes in the solution, I make sure each of them
4481
//        is on the Closed List.  If not, the pathfind errors out with a
4482
//        "Solution Corrupt!" error.  This is an attempt to short circuit any
4483
//        solution containing erroneous nodes in it.
4484
//
4485
//  Safeguard #2) I keep a vector of the nodes in the solution as we go through them.
4486
//        Before adding each node to the vector, I search that vector for that
4487
//        node.  If that vector already contains the node, the pathfind errors
4488
//        out with a "Solution Corrupt!" error.  THis is an attempt to catch
4489
//        the cases where Closed List nodes have looped for some reason.
4490
//
4491
//  These two safeguards should not truly be necessary for this algorithm, and take up
4492
//  space and time to guard against.  Thus, finding out why these problems are occurring
4493
//  in the first place would be great, as we could fix that instead and remove these
4494
//  checks.  If necessary to live with long term, then the solution vector should probably
4495
//  be made into a hash table for quick lookups.
4496
//
4497
//  I have also implimented a sort blocking list that is supposed to represent those
4498
//  spots that mobiles occupy and are thus not walkable on.  This is only maintained if
4499
//  the mobilesBlock parameter passed from escript is true.  It would probably be good
4500
//  to make this a hash table.  It may be said that it is a good idea to pre-process all
4501
//  blocking spots on the map, but this would hinder the ability to manage 3d shifts, or
4502
//  probably be prohibitive if an attempt were made to check for traversal at all possible
4503
//  various z's.  So it may be that checking such at the time of the search for the path
4504
//  is ultimately the best means of handling this.
4505
//
4506
//  Other details that may be nice to have here might be a method for interacting with
4507
//  escript, letting escript tell the pathfinder to ignore doors, and then the pathfinder
4508
//  putting in the solution list an indication to escript that at the given node a door
4509
//  was encountered, so that AI can open the door before attempting to traverse that
4510
//  path.  Providing the user the ability to define an array of objects which are to be
4511
//  considered as blocking, or an array of objects which are to be ignored even if they
4512
//  are blocking might be nice as well.  The former could be easily implimented by
4513
//  adding to the mobile's blocking list.  The latter may be more difficult to do
4514
//  considering if the item blocks, the walk function will not let you traverse it, and
4515
//  it is the walk function that we use to accurately replicate the ability of the
4516
//  mobile to traverse from one place to the next.  Additional functionality of some
4517
//  sort may be necessary to do this, or making a copy of the walk function and putting
4518
//  in a special walk function strictly for pathfinding which can take this array into
4519
//  account.
4520
//
4521
//  For now, however, these concerns are all unaddressed.  I wish to see if this
4522
//  function works and is useful, and also would prefer to find some way to track down
4523
//  the corruption of the solution nodes before adding more complexity to the problem.
4524
//
4525
//  Other than the below function, the following files will be added to CVS to support
4526
//  it in the src module.  They are :
4527
//
4528
//  stlastar.h  --  virtually untouched by me really, it is the original author's
4529
//          implimentation, pretty nicely done and documented if you are
4530
//          interested in learning about A*.
4531
//
4532
//  fsa.h     --  a "fixed size allocation" module which I toy with enabling and
4533
//          disabling.  Using it is supposed to get you some speed, but it
4534
//          limits your maps because it will only allocate a certain # of
4535
//          nodes at once, and after that, it will return out of memory.
4536
//          Presently, it is disabled, but will be submitted to CVS for
4537
//          completion in case we wish to use it later.
4538
//
4539
//  uopathnode.h -- this is stricly my work, love it or hate it, it's pretty quick
4540
//          and dirty, and can stand to be cleaned up, optimized, and so forth.
4541
//          I have the name field and function in there to allow for some
4542
//          debugging, though the character array could probably be removed to
4543
//          make the nodes smaller.  I didn't find it mattered particularly, since
4544
//          these puppies are created and destroyed at a pretty good clip.
4545
//          It is this class that encapsulates the necessary functionality to
4546
//          make the otherwise fairly generic stlastar class work.
4547

4548
using UOSearch = Plib::AStarSearch<UOPathState>;
4549

4550
BObjectImp* UOExecutorModule::mf_FindPath()
7✔
4551
{
4552
  Pos3d pos1, pos2;
7✔
4553
  Realms::Realm* realm;
4554
  const String* movemode_name;
4555

4556
  if ( !getPos3dParam( 0, 1, 2, &pos1 ) || !getPos3dParam( 3, 4, 5, &pos2 ) ||
14✔
4557
       !getRealmParam( 6, &realm ) || !getStringParam( 9, movemode_name ) )
14✔
4558
    return new BError( "Invalid parameter" );
×
4559
  if ( !pos1.in_range( pos2, settingsManager.ssopt.max_pathfind_range ) )
7✔
4560
    return new BError( "Beyond Max Range." );
×
4561

4562
  short theSkirt;
4563
  int flags;
4564

4565
  if ( !getParam( 7, flags ) )
7✔
4566
    flags = FP_IGNORE_MOBILES;
×
4567

4568
  if ( !getParam( 8, theSkirt ) )
7✔
4569
    theSkirt = 5;
×
4570

4571
  Plib::MOVEMODE movemode = Character::decode_movemode( movemode_name->value() );
7✔
4572
  if ( movemode == Plib::MOVEMODE_NONE )
7✔
4573
    return new BError( "Wrong movemode parameter" );
1✔
4574

4575
  if ( theSkirt < 0 )
6✔
4576
    theSkirt = 0;
×
4577

4578
  if ( !realm->valid( pos1.xy() ) )
6✔
4579
    return new BError( "Start Coordinates Invalid for Realm" );
×
4580
  if ( !realm->valid( pos2.xy() ) )
6✔
4581
    return new BError( "End Coordinates Invalid for Realm" );
×
4582

4583
  auto astarsearch = std::make_unique<UOSearch>();
6✔
4584

4585
  Range2d range( pos1.xy().min( pos2.xy() ) - Vec2d( theSkirt, theSkirt ),
6✔
4586
                 pos1.xy().max( pos2.xy() ) + Vec2d( theSkirt, theSkirt ), realm );
12✔
4587

4588
  if ( Plib::systemstate.config.loglevel >= 12 )
6✔
4589
  {
4590
    POLLOGLN( "[FindPath] Calling FindPath({}, {}, {}, {:#x}, {})", pos1, pos2, realm->name(),
×
4591
              flags, theSkirt );
4592
    POLLOGLN( "[FindPath]   search for Blockers inside {}", range );
×
4593
  }
4594

4595
  bool doors_block = ( flags & FP_IGNORE_DOORS ) ? false : true;
6✔
4596
  AStarParams params( range, doors_block, movemode, realm );
6✔
4597

4598
  if ( !( flags & FP_IGNORE_MOBILES ) )
6✔
4599
  {
4600
    WorldIterator<MobileFilter>::InBox( range, realm,
×
4601
                                        [&]( Mobile::Character* chr )
×
4602
                                        {
4603
                                          params.AddBlocker( chr->pos3d() );
×
4604

4605
                                          if ( Plib::systemstate.config.loglevel >= 12 )
×
4606
                                            POLLOGLN( "[FindPath]   add Blocker {} at {}",
×
4607
                                                      chr->name(), chr->pos() );
×
4608
                                        } );
×
4609
  }
4610

4611
  if ( Plib::systemstate.config.loglevel >= 12 )
6✔
4612
  {
4613
    POLLOGLN( "[FindPath]   use StartNode {}", pos1 );
×
4614
    POLLOGLN( "[FindPath]   use EndNode {}", pos2 );
×
4615
  }
4616

4617
  // Create a start state
4618
  UOPathState nodeStart( pos1, &params );
6✔
4619
  // Define the goal state
4620
  UOPathState nodeEnd( pos2, &params );
6✔
4621
  // Set Start and goal states
4622
  astarsearch->SetStartAndGoalStates( nodeStart, nodeEnd );
6✔
4623
  unsigned int SearchState;
4624
  do
4625
  {
4626
    SearchState = astarsearch->SearchStep();
28✔
4627
  } while ( SearchState == UOSearch::SEARCH_STATE_SEARCHING );
28✔
4628
  if ( SearchState == UOSearch::SEARCH_STATE_SUCCEEDED )
6✔
4629
  {
4630
    UOPathState* node = astarsearch->GetSolutionStart();
3✔
4631

4632
    auto nodeArray = std::make_unique<ObjArray>();
3✔
4633
    while ( ( node = astarsearch->GetSolutionNext() ) != nullptr )
12✔
4634
    {
4635
      auto nextStep = std::make_unique<BStruct>();
9✔
4636
      const auto& pos = node->position();
9✔
4637
      nextStep->addMember( "x", new BLong( pos.x() ) );
9✔
4638
      nextStep->addMember( "y", new BLong( pos.y() ) );
9✔
4639
      nextStep->addMember( "z", new BLong( pos.z() ) );
9✔
4640
      nodeArray->addElement( nextStep.release() );
9✔
4641
    }
9✔
4642
    astarsearch->FreeSolutionNodes();
3✔
4643
    return nodeArray.release();
3✔
4644
  }
3✔
4645
  if ( SearchState == UOSearch::SEARCH_STATE_FAILED )
3✔
4646
  {
4647
    return new BError( "Failed to find a path." );
3✔
4648
  }
4649
  if ( SearchState == UOSearch::SEARCH_STATE_OUT_OF_MEMORY )
×
4650
  {
4651
    return new BError( "Out of memory." );
×
4652
  }
4653
  if ( SearchState == UOSearch::SEARCH_STATE_SOLUTION_CORRUPTED )
×
4654
  {
4655
    return new BError( "Solution Corrupted!" );
×
4656
  }
4657

4658
  return new BError( "Pathfind Error." );
×
4659
}
6✔
4660

4661

4662
BObjectImp* UOExecutorModule::mf_UseItem()
×
4663
{
4664
  Item* item;
4665
  Character* chr;
4666

4667
  if ( getItemParam( 0, item ) && getCharacterParam( 1, chr ) )
×
4668
  {
4669
    const ItemDesc& itemdesc = find_itemdesc( item->objtype_ );
×
4670

4671
    if ( itemdesc.requires_attention && ( chr->skill_ex_active() || chr->casting_spell() ) )
×
4672
    {
4673
      if ( chr->client != nullptr )
×
4674
      {
4675
        send_sysmessage( chr->client, "I am already doing something else." );
×
4676
        return new BError( "Character busy." );
×
4677
        ;
4678
      }
4679
    }
4680

4681
    if ( itemdesc.requires_attention && chr->hidden() )
×
4682
      chr->unhide();
×
4683

4684
    ref_ptr<EScriptProgram> prog;
×
4685

4686
    std::string on_use_script = item->get_use_script_name();
×
4687

4688
    if ( !on_use_script.empty() )
×
4689
    {
4690
      ScriptDef sd( on_use_script, nullptr, "" );
×
4691
      prog = find_script2( sd,
×
4692
                           true,  // complain if not found
4693
                           Plib::systemstate.config.cache_interactive_scripts );
×
4694
    }
×
4695
    else if ( !itemdesc.on_use_script.empty() )
×
4696
    {
4697
      prog = find_script2( itemdesc.on_use_script, true,
×
4698
                           Plib::systemstate.config.cache_interactive_scripts );
×
4699
    }
4700

4701
    if ( prog.get() != nullptr )
×
4702
    {
4703
      if ( chr->start_itemuse_script( prog.get(), item, itemdesc.requires_attention ) )
×
4704
        return new BLong( 1 );
×
4705
      return new BError( "Failed to start script!" );
×
4706
      // else log the fact?
4707
    }
4708

4709
    if ( chr->client != nullptr )
×
4710
      item->builtin_on_use( chr->client );
×
4711
    return new BLong( 0 );
×
4712
  }
×
4713

4714
  return new BError( "Invalid parameter" );
×
4715
}
4716

4717
BObjectImp* UOExecutorModule::mf_FindSubstance()
3✔
4718
{
4719
  UContainer::Contents substanceVector;
3✔
4720

4721
  Item* cont_item;
4722
  unsigned int objtype;
4723
  int amount;
4724

4725
  if ( getItemParam( 0, cont_item ) && getObjtypeParam( 1, objtype ) && getParam( 2, amount ) )
3✔
4726
  {
4727
    bool makeInUse;
4728
    int flags;
4729
    if ( !getParam( 3, makeInUse ) )
3✔
4730
      makeInUse = false;
×
4731

4732
    if ( !getParam( 4, flags ) )
3✔
4733
      flags = 0;
×
4734

4735
    if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
3✔
4736
      return new BError( "That is not a container" );
×
4737
    if ( amount < 0 )
3✔
4738
      return new BError( "Amount cannot be negative" );
×
4739

4740
    UContainer* cont = static_cast<UContainer*>( cont_item );
3✔
4741
    int amthave = cont->find_sumof_objtype_noninuse( objtype, amount, substanceVector, flags );
3✔
4742
    if ( ( amthave < amount ) && ( !( flags & FINDSUBSTANCE_FIND_ALL ) ) )
3✔
4743
      return new BError( "Not enough of that substance in container" );
1✔
4744

4745
    std::unique_ptr<ObjArray> theArray( new ObjArray() );
2✔
4746
    for ( auto item : substanceVector )
4✔
4747
    {
4748
      if ( item != nullptr )
2✔
4749
      {
4750
        if ( ( makeInUse ) && ( !item->inuse() ) )
2✔
4751
        {
4752
          item->inuse( true );
2✔
4753
          reserved_items_.emplace_back( item );
2✔
4754
        }
4755
        theArray->addElement( new EItemRefObjImp( item ) );
2✔
4756
      }
4757
    }
4758
    return theArray.release();
2✔
4759
  }
2✔
4760

4761
  return new BError( "Invalid parameter type" );
×
4762
}
3✔
4763

4764
BObjectImp* UOExecutorModule::mf_IsStackable()
×
4765
{
4766
  Item* item1;
4767
  Item* item2;
4768

4769
  if ( !( getItemParam( 0, item1 ) && getItemParam( 1, item2 ) ) )
×
4770
  {
4771
    return new BError( "Invalid parameter type" );
×
4772
  }
4773

4774
  if ( item1->objtype_ != item2->objtype_ )
×
4775
    return new BError( "Objtypes differs" );
×
4776
  if ( !item1->stackable() )
×
4777
    return new BError( "That item type is not stackable." );
×
4778

4779
  if ( item1->can_add_to_self( *item2, false ) )
×
4780
    return new BLong( 1 );
×
4781
  return new BError( "Failed to stack" );
×
4782
}
4783

4784
BObjectImp* UOExecutorModule::mf_UpdateMobile()
×
4785
{
4786
  Character* chr;
4787
  int flags;
4788

4789
  if ( getCharacterParam( 0, chr ) && getParam( 1, flags ) )
×
4790
  {
4791
    if ( flags == 1 )
×
4792
    {
4793
      if ( ( !chr->isa( UOBJ_CLASS::CLASS_NPC ) ) && ( chr->client ) )  // no npc and active client
×
4794
        send_owncreate( chr->client, chr );                             // inform self
×
4795
      if ( ( chr->isa( UOBJ_CLASS::CLASS_NPC ) ) || ( chr->client ) )   // npc or active client
×
4796
        send_create_mobile_to_nearby_cansee( chr );                     // inform other
×
4797
      else
4798
        return new BError( "Mobile is offline" );
×
4799
    }
4800
    else
4801
    {
4802
      if ( ( !chr->isa( UOBJ_CLASS::CLASS_NPC ) ) && ( chr->client ) )  // no npc and active client
×
4803
        send_move( chr->client, chr );                                  // inform self
×
4804
      if ( ( chr->isa( UOBJ_CLASS::CLASS_NPC ) ) || ( chr->client ) )   // npc or active client
×
4805
        send_move_mobile_to_nearby_cansee( chr );                       // inform other
×
4806
      else
4807
        return new BError( "Mobile is offline" );
×
4808
    }
4809
    return new BLong( 1 );
×
4810
  }
4811
  return new BError( "Invalid parameter type" );
×
4812
}
4813

4814
BObjectImp* UOExecutorModule::mf_UpdateItem()
×
4815
{
4816
  Item* item;
4817

4818
  if ( getItemParam( 0, item ) )
×
4819
  {
4820
    send_item_to_inrange( item );
×
4821
    return new BLong( 1 );
×
4822
  }
4823

4824
  return new BError( "Invalid parameter type" );
×
4825
}
4826

4827

4828
BObjectImp* UOExecutorModule::mf_CanWalk(
×
4829
    /*movemode, x1, y1, z1, x2_or_dir, y2 := -1, realm := DEF*/ )
4830
{
4831
  Pos4d p;
×
4832
  int x2_or_dir, y2_;
4833
  const String* movemode_name;
4834

4835
  if ( ( getStringParam( 0, movemode_name ) ) && ( getPos4dParam( 1, 2, 3, 6, &p ) ) &&
×
4836
       ( getParam( 4, x2_or_dir ) ) && ( getParam( 5, y2_ ) ) )
×
4837
  {
4838
    Plib::MOVEMODE movemode = Character::decode_movemode( movemode_name->value() );
×
4839

4840
    Core::UFACING dir;
4841
    if ( y2_ == -1 )
×
4842
      dir = static_cast<Core::UFACING>( x2_or_dir & 0x7 );
×
4843
    else
4844
    {
4845
      auto p1 = Pos2d( static_cast<u16>( x2_or_dir ), static_cast<u16>( y2_ ) );
×
4846
      if ( !p.realm()->valid( p1 ) )
×
4847
        return new BError( "Invalid coordinates for realm." );
×
4848

4849
      dir = p.xy().direction_toward( p1 );
×
4850
    }
4851

4852
    if ( dir & 1 )  // check if diagonal movement is allowed
×
4853
    {
4854
      short new_z;
4855
      u8 tmp_facing = ( dir + 1 ) & 0x7;
×
4856
      auto tmp_pos = p.move( static_cast<Core::UFACING>( tmp_facing ) );
×
4857

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

4862
      tmp_facing = ( dir - 1 ) & 0x7;
×
4863
      tmp_pos = p.move( static_cast<Core::UFACING>( tmp_facing ) );
×
4864

4865
      if ( !walk1 && !p.realm()->walkheight( tmp_pos.xy(), tmp_pos.z(), &new_z, nullptr, nullptr,
×
4866
                                             true, movemode, nullptr ) )
4867
        return new BError( "Cannot walk there" );
×
4868
    }
4869

4870
    p.move_to( dir );
×
4871
    short newz;
4872

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

4876
    return new BLong( newz );
×
4877
  }
4878
  return new BError( "Invalid parameter" );
×
4879
}
4880

4881

4882
BObjectImp* UOExecutorModule::mf_SendCharProfile(
×
4883
    /*chr, of_who, title, uneditable_text := array, editable_text := array*/ )
4884
{
4885
  Character *chr, *of_who;
4886
  const String* title;
4887
  const String* uText;
4888
  const String* eText;
4889

4890
  if ( getCharacterParam( 0, chr ) && getCharacterParam( 1, of_who ) &&
×
4891
       getStringParam( 2, title ) && getUnicodeStringParam( 3, uText ) &&
×
4892
       getUnicodeStringParam( 4, eText ) )
×
4893
  {
4894
    if ( chr->logged_in() && of_who->logged_in() )
×
4895
    {
4896
      if ( uText->length() > SPEECH_MAX_LEN || eText->length() > SPEECH_MAX_LEN )
×
4897
        return new BError( "Text exceeds maximum size." );
×
4898

4899
      sendCharProfile( chr, of_who, title->value(), uText->value(), eText->value() );
×
4900
      return new BLong( 1 );
×
4901
    }
4902
    return new BError( "Mobile must be online." );
×
4903
  }
4904
  return new BError( "Invalid parameter type" );
×
4905
}
4906

4907
BObjectImp* UOExecutorModule::mf_SendOverallSeason( /*season_id, playsound := 1*/ )
×
4908
{
4909
  int season_id, playsound;
4910

4911
  if ( getParam( 0, season_id ) && getParam( 1, playsound ) )
×
4912
  {
4913
    if ( season_id < 0 || season_id > 4 )
×
4914
      return new BError( "Invalid season id" );
×
4915

4916
    Network::PktHelper::PacketOut<Network::PktOut_BC> msg;
×
4917
    msg->Write<u8>( static_cast<u16>( season_id ) );
×
4918
    msg->Write<u8>( static_cast<u16>( playsound ) );
×
4919

NEW
4920
    for ( auto client : networkManager.clients )
×
4921
    {
4922
      if ( !client->chr || !client->chr->logged_in() || client->getversiondetail().major < 1 )
×
4923
        continue;
×
4924
      msg.Send( client );
×
4925
    }
4926
    return new BLong( 1 );
×
4927
  }
×
4928
  return new BError( "Invalid parameter" );
×
4929
}
4930

4931
// bresenham circle calculates the coords based on center coords and radius
4932
BObjectImp* UOExecutorModule::mf_GetMidpointCircleCoords( /* xcenter, ycenter, radius */ )
×
4933
{
4934
  int xcenter, ycenter, radius;
4935
  if ( !( getParam( 0, xcenter ) && getParam( 1, ycenter ) && getParam( 2, radius ) ) )
×
4936
  {
4937
    return new BError( "Invalid parameter type" );
×
4938
  }
4939
  std::unique_ptr<ObjArray> coords( new ObjArray );
×
4940

4941
  std::vector<std::tuple<int, int>> points;
×
4942
  auto add_point = [&coords]( int x, int y )
×
4943
  {
4944
    std::unique_ptr<BStruct> point( new BStruct );
×
4945
    point->addMember( "x", new BLong( x ) );
×
4946
    point->addMember( "y", new BLong( y ) );
×
4947
    coords->addElement( point.release() );
×
4948
  };
×
4949

4950
  if ( radius == 0 )
×
4951
  {
4952
    add_point( xcenter, ycenter );
×
4953
    return coords.release();
×
4954
  }
4955

4956
  // inside of each quadrant the points are sorted,
4957
  // store the quadrands in seperated vectors and merge them later
4958
  // -> automatically sorted
4959
  std::vector<std::tuple<int, int>> q1, q2, q3, q4;
×
4960
  int x = -radius, y = 0, err = 2 - 2 * radius; /* II. Quadrant */
×
4961
  do
4962
  {
4963
    q1.emplace_back( xcenter - x, ycenter + y ); /*   I. Quadrant */
×
4964
    q2.emplace_back( xcenter - y, ycenter - x ); /*  II. Quadrant */
×
4965
    q3.emplace_back( xcenter + x, ycenter - y ); /* III. Quadrant */
×
4966
    q4.emplace_back( xcenter + y, ycenter + x ); /*  IV. Quadrant */
×
4967
    radius = err;
×
4968
    if ( radius <= y )
×
4969
      err += ++y * 2 + 1;
×
4970
    if ( radius > x || err > y )
×
4971
      err += ++x * 2 + 1;
×
4972
  } while ( x < 0 );
×
4973

4974
  for ( const auto& p : q1 )
×
4975
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
4976
  for ( const auto& p : q2 )
×
4977
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
4978
  for ( const auto& p : q3 )
×
4979
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
4980
  for ( const auto& p : q4 )
×
4981
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
4982

4983
  return coords.release();
×
4984
}
×
4985

4986
size_t UOExecutorModule::sizeEstimate() const
14✔
4987
{
4988
  size_t size = sizeof( *this ) + Clib::memsize( reserved_items_ );
14✔
4989
  return size;
14✔
4990
}
4991
}  // namespace Module
4992
}  // 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