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

polserver / polserver / 21108840797

18 Jan 2026 08:35AM UTC coverage: 60.508% (+0.02%) from 60.492%
21108840797

push

github

web-flow
ClangTidy readability-else-after-return (#857)

* trigger tidy

* Automated clang-tidy change: readability-else-after-return

* compile test

* rerun

* Automated clang-tidy change: readability-else-after-return

* trigger..

* Automated clang-tidy change: readability-else-after-return

* manually removed a few

* Automated clang-tidy change: readability-else-after-return

* removed duplicate code

* fix remaining warnings

* fixed scope

---------

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

837 of 1874 new or added lines in 151 files covered. (44.66%)

46 existing lines in 25 files now uncovered.

44448 of 73458 relevant lines covered (60.51%)

525066.38 hits per line

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

33.92
/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
      }
NEW
311
      return new BError( "Another script still attached." );
×
312
    }
NEW
313
    return new BError( "Another character still attached." );
×
314
  }
NEW
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

NEW
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
        }
NEW
424
        BObject ob( res );
×
UNCOV
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
        }
UNCOV
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

NEW
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

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

NEW
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

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

NEW
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

NEW
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" );
×
NEW
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

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

NEW
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✔
NEW
728
    return new BLong( say_above_unicode( obj, ptext->value(), "ENU", font, color, journal_print ) );
×
729
  }
730

NEW
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 ) );
×
NEW
747
    return new BLong( private_say_above_unicode( chr, obj, ptext->value(), "ENU", font, color ) );
×
748
  }
749

NEW
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

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

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

NEW
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
  }
NEW
1024
  if ( getCharacterParam( 0, chr ) )
×
1025
  {
1026
    return new BLong( chr->objtype_ );
×
1027
  }
1028

NEW
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 );
×
NEW
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

NEW
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

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

NEW
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

NEW
1109
    BObject ob( res );
×
UNCOV
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

NEW
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

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

NEW
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 ( BStruct::Contents::const_iterator citr = custom->contents().begin(),
12✔
1185
                                          end = custom->contents().end();
6✔
1186
        citr != end; ++citr )
46✔
1187
  {
1188
    const std::string& name = ( *citr ).first;
40✔
1189
    BObjectImp* ref = ( *citr ).second->impptr();
40✔
1190

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

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

1251
  Clib::ConfigElem elem;
74✔
1252
  bool found = FindNpcTemplate( tmplname->data(), elem );
74✔
1253

1254
  if ( !found )
74✔
1255
  {
1256
    return new BError( "NPC template '" + tmplname->value() + "' not found" );
×
1257
  }
1258
  Plib::MOVEMODE movemode = Character::decode_movemode( elem.read_string( "MoveMode", "L" ) );
74✔
1259

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

1272

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

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

1297

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

1340

1341
BObjectImp* UOExecutorModule::mf_SubtractAmount()
×
1342
{
1343
  Item* item;
1344
  unsigned short amount;
1345

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

NEW
1356
  return new BError( "Invalid parameter type" );
×
1357
}
1358

1359
BObjectImp* UOExecutorModule::mf_AddAmount()
×
1360
{
1361
  Item* item;
1362
  unsigned short amount;
1363

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

1379
    unsigned short newamount = item->getamount();
×
1380
    newamount += amount;
×
1381
    item->setamount( newamount );
×
1382
    update_item_to_inrange( item );
×
1383

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

1387
    return new EItemRefObjImp( item );
×
1388
  }
1389

NEW
1390
  return new BError( "Invalid parameter type" );
×
1391
}
1392

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

1408
    UACTION action = static_cast<UACTION>( actionval );
×
1409
    send_action_to_inrange(
×
1410
        chr, action, framecount, repeatcount, static_cast<DIRECTION_FLAG_OLD>( backward ),
1411
        static_cast<REPEAT_FLAG_OLD>( repeatflag ), static_cast<unsigned char>( delay ) );
1412
    return new BLong( 1 );
×
1413
  }
1414

NEW
1415
  return new BError( "Invalid parameter" );
×
1416
}
1417

1418
BObjectImp* UOExecutorModule::mf_PlaySoundEffect()
×
1419
{
1420
  UObject* chr;
1421
  int effect;
1422
  if ( getUObjectParam( 0, chr ) && getParam( 1, effect ) )
×
1423
  {
1424
    play_sound_effect( chr, static_cast<u16>( effect ) );
×
1425
    return new BLong( 1 );
×
1426
  }
1427

NEW
1428
  return new BError( "Invalid parameter" );
×
1429
}
1430

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

NEW
1442
  return new BError( "Invalid parameter" );
×
1443
}
1444

1445
BObjectImp* UOExecutorModule::mf_PlaySoundEffectXYZ()
×
1446
{
1447
  Core::Pos4d center;
×
1448
  int effect;
1449
  if ( getPos4dParam( 0, 1, 2, 4, &center ) && getParam( 3, effect ) )
×
1450
  {
1451
    play_sound_effect_xyz( center, static_cast<u16>( effect ) );
×
1452
    return new BLong( 1 );
×
1453
  }
1454

NEW
1455
  return new BError( "Invalid parameter" );
×
1456
}
1457

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

NEW
1468
  return new BError( "Invalid parameter" );
×
1469
}
1470

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

1498
bool UOExecutorModule::getDynamicMenuParam( unsigned param, Menu*& menu )
×
1499
{
1500
  BApplicObjBase* aob = getApplicObjParam( param, &menu_type );
×
1501
  if ( aob != nullptr )
×
1502
  {
1503
    EMenuObjImp* menu_imp = static_cast<EMenuObjImp*>( aob );
×
1504
    menu = &menu_imp->value();
×
1505
    return true;
×
1506
  }
1507

NEW
1508
  return false;
×
1509
}
1510

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

1534
BObjectImp* UOExecutorModule::mf_SelectMenuItem2()
×
1535
{
1536
  Character* chr;
1537
  Menu* menu;
1538

1539
  if ( !getCharacterParam( 0, chr ) || !getStaticOrDynamicMenuParam( 1, menu ) ||
×
1540
       ( chr->client->gd->menu_selection_uoemod != nullptr ) )
×
1541
  {
1542
    return new BError( "Invalid parameter" );
×
1543
  }
1544

1545
  if ( menu == nullptr || !chr->has_active_client() || menu->menuitems_.empty() )
×
1546
  {
1547
    return new BError( "Client is busy, or menu is empty" );
×
1548
  }
1549

1550
  if ( !send_menu( chr->client, menu ) )
×
1551
  {
1552
    return new BError( "Menu too large" );
×
1553
  }
1554

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

1565
  chr->client->gd->menu = menu->getWeakPtr();
×
1566
  chr->client->gd->on_menu_selection = menu_selection_made;
×
1567
  chr->client->gd->menu_selection_uoemod = this;
×
1568
  menu_selection_chr = chr;
×
1569

1570
  return new BLong( 0 );
×
1571
}
1572

1573
void append_objtypes( ObjArray* objarr, Menu* menu )
×
1574
{
1575
  for ( unsigned i = 0; i < menu->menuitems_.size(); ++i )
×
1576
  {
1577
    MenuItem* mi = &menu->menuitems_[i];
×
1578

1579
    if ( mi->submenu_id )
×
1580
    {
1581
      // Code Analyze: Commented out and replaced with tmp_menu due to hiding
1582
      // menu passed to function.
1583
      //      Menu* menu = find_menu( mi->submenu_id );
1584
      Menu* tmp_menu = Menu::find_menu( mi->submenu_id );
×
1585
      if ( tmp_menu != nullptr )
×
1586
        append_objtypes( objarr, tmp_menu );
×
1587
    }
1588
    else
1589
    {
1590
      objarr->addElement( new BLong( mi->objtype_ ) );
×
1591
    }
1592
  }
1593
}
×
1594

1595
BObjectImp* UOExecutorModule::mf_GetMenuObjTypes()
×
1596
{
1597
  Menu* menu;
1598
  if ( getStaticOrDynamicMenuParam( 0, menu ) )
×
1599
  {
1600
    std::unique_ptr<ObjArray> objarr( new ObjArray );
×
1601

1602
    append_objtypes( objarr.get(), menu );
×
1603

1604
    return objarr.release();
×
1605
  }
×
1606
  return new BLong( 0 );
×
1607
}
1608

1609
BObjectImp* UOExecutorModule::mf_ApplyConstraint()
×
1610
{
1611
  ObjArray* arr;
1612
  StoredConfigFile* cfile;
1613
  const String* propname_str;
1614
  int amthave;
1615

1616
  if ( getObjArrayParam( 0, arr ) && getStoredConfigFileParam( *this, 1, cfile ) &&
×
1617
       getStringParam( 2, propname_str ) && getParam( 3, amthave ) )
×
1618
  {
1619
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
1620

1621
    for ( unsigned i = 0; i < arr->ref_arr.size(); i++ )
×
1622
    {
1623
      BObjectRef& ref = arr->ref_arr[i];
×
1624

1625
      BObject* bo = ref.get();
×
1626
      if ( bo == nullptr )
×
1627
        continue;
×
1628
      if ( !bo->isa( BObjectImp::OTLong ) )
×
1629
        continue;
×
1630
      BLong* blong = bo->impptr<BLong>();
×
1631
      unsigned int objtype = static_cast<u32>( blong->value() );
×
1632

1633
      ref_ptr<StoredConfigElem> celem = cfile->findelem( objtype );
×
1634
      if ( celem.get() == nullptr )
×
1635
        continue;
×
1636

1637
      BObjectImp* propval = celem->getimp( propname_str->value() );
×
1638
      if ( propval == nullptr )
×
1639
        continue;
×
1640
      if ( !propval->isa( BObjectImp::OTLong ) )
×
1641
        continue;
×
1642
      BLong* amtreq = static_cast<BLong*>( propval );
×
1643
      if ( amtreq->value() > amthave )
×
1644
        continue;
×
1645
      newarr->addElement( new BLong( objtype ) );
×
1646
    }
×
1647

1648
    return newarr.release();
×
1649
  }
×
1650

1651
  return new UninitObject;
×
1652
}
1653

1654
BObjectImp* UOExecutorModule::mf_CreateMenu()
×
1655
{
1656
  const String* title;
1657
  if ( getStringParam( 0, title ) )
×
1658
  {
1659
    Menu temp;
×
1660
    temp.menu_id = 0;
×
1661
    Clib::stracpy( temp.title, title->data(), sizeof temp.title );
×
1662
    return new EMenuObjImp( temp );
×
1663
  }
×
1664
  return new BLong( 0 );
×
1665
}
1666

1667
BObjectImp* UOExecutorModule::mf_AddMenuItem()
×
1668
{
1669
  Menu* menu;
1670
  unsigned int objtype;
1671
  const String* text;
1672
  unsigned short color;
1673

1674
  if ( getDynamicMenuParam( 0, menu ) && getObjtypeParam( 1, objtype ) &&
×
1675
       getStringParam( 2, text ) && getParam( 3, color ) )
×
1676
  {
1677
    menu->menuitems_.emplace_back();
×
1678
    MenuItem* mi = &menu->menuitems_.back();
×
1679
    mi->objtype_ = objtype;
×
1680
    mi->graphic_ = getgraphic( objtype );
×
1681
    Clib::stracpy( mi->title, text->data(), sizeof mi->title );
×
1682
    mi->color_ = color & settingsManager.ssopt.item_color_mask;
×
1683
    return new BLong( 1 );
×
1684
  }
1685

NEW
1686
  return new BLong( 0 );
×
1687
}
1688

1689
BObjectImp* UOExecutorModule::mf_GetObjProperty()
78✔
1690
{
1691
  UObject* uobj;
1692
  const String* propname_str;
1693
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
78✔
1694
  {
1695
    std::string val;
78✔
1696
    if ( uobj->getprop( propname_str->value(), val ) )
78✔
1697
    {
1698
      return BObjectImp::unpack( val.c_str() );
34✔
1699
    }
1700

1701
    return new BError( "Property not found" );
44✔
1702
  }
78✔
1703

NEW
1704
  return new BError( "Invalid parameter type" );
×
1705
}
1706

1707
BObjectImp* UOExecutorModule::mf_SetObjProperty()
9✔
1708
{
1709
  UObject* uobj;
1710
  const String* propname_str;
1711
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
9✔
1712
  {
1713
    BObjectImp* propval = getParamImp( 2 );
9✔
1714
    uobj->setprop( propname_str->value(), propval->pack() );
9✔
1715
    return new BLong( 1 );
9✔
1716
  }
1717

NEW
1718
  return new BError( "Invalid parameter type" );
×
1719
}
1720

1721
BObjectImp* UOExecutorModule::mf_EraseObjProperty()
×
1722
{
1723
  UObject* uobj;
1724
  const String* propname_str;
1725
  if ( getUObjectParam( 0, uobj ) && getStringParam( 1, propname_str ) )
×
1726
  {
1727
    uobj->eraseprop( propname_str->value() );
×
1728
    return new BLong( 1 );
×
1729
  }
1730

NEW
1731
  return new BError( "Invalid parameter type" );
×
1732
}
1733
BObjectImp* UOExecutorModule::mf_GetObjPropertyNames()
×
1734
{
1735
  UObject* uobj;
1736
  if ( getUObjectParam( 0, uobj ) )
×
1737
  {
1738
    std::vector<std::string> propnames;
×
1739
    uobj->getpropnames( propnames );
×
1740
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
1741
    for ( unsigned i = 0; i < propnames.size(); ++i )
×
1742
    {
1743
      arr->addElement( new String( propnames[i] ) );
×
1744
    }
1745
    return arr.release();
×
1746
  }
×
1747

NEW
1748
  return new BError( "Invalid parameter type" );
×
1749
}
1750

1751

1752
BObjectImp* UOExecutorModule::mf_GetGlobalProperty()
49✔
1753
{
1754
  const String* propname_str;
1755
  if ( getStringParam( 0, propname_str ) )
49✔
1756
  {
1757
    std::string val;
49✔
1758
    if ( gamestate.global_properties->getprop( propname_str->value(), val ) )
49✔
1759
    {
1760
      return BObjectImp::unpack( val.c_str() );
43✔
1761
    }
1762

1763
    return new BError( "Property not found" );
6✔
1764
  }
49✔
1765

NEW
1766
  return new BError( "Invalid parameter type" );
×
1767
}
1768

1769
BObjectImp* UOExecutorModule::mf_SetGlobalProperty()
24✔
1770
{
1771
  const String* propname_str;
1772
  if ( exec.getStringParam( 0, propname_str ) )
24✔
1773
  {
1774
    BObjectImp* propval = exec.getParamImp( 1 );
24✔
1775
    gamestate.global_properties->setprop( propname_str->value(), propval->pack() );
24✔
1776
    return new BLong( 1 );
24✔
1777
  }
1778

NEW
1779
  return new BError( "Invalid parameter type" );
×
1780
}
1781

1782
BObjectImp* UOExecutorModule::mf_EraseGlobalProperty()
×
1783
{
1784
  const String* propname_str;
1785
  if ( getStringParam( 0, propname_str ) )
×
1786
  {
1787
    gamestate.global_properties->eraseprop( propname_str->value() );
×
1788
    return new BLong( 1 );
×
1789
  }
1790

NEW
1791
  return new BError( "Invalid parameter type" );
×
1792
}
1793

1794
BObjectImp* UOExecutorModule::mf_GetGlobalPropertyNames()
×
1795
{
1796
  std::vector<std::string> propnames;
×
1797
  gamestate.global_properties->getpropnames( propnames );
×
1798
  std::unique_ptr<ObjArray> arr( new ObjArray );
×
1799
  for ( unsigned i = 0; i < propnames.size(); ++i )
×
1800
  {
1801
    arr->addElement( new String( propnames[i] ) );
×
1802
  }
1803
  return arr.release();
×
1804
}
×
1805

1806
BObjectImp* UOExecutorModule::mf_PlayMovingEffect()
×
1807
{
1808
  UObject* src;
1809
  UObject* dst;
1810
  unsigned short effect;
1811
  int speed;
1812
  int loop;
1813
  int explode;
1814
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) && getParam( 2, effect ) &&
×
1815
       getParam( 3, speed, UCHAR_MAX ) && getParam( 4, loop, UCHAR_MAX ) &&
×
1816
       getParam( 5, explode, UCHAR_MAX ) )
×
1817
  {
1818
    if ( src->realm() != dst->realm() )
×
1819
      return new BError( "Realms must match" );
×
1820
    play_moving_effect( src, dst, effect, static_cast<unsigned char>( speed ),
×
1821
                        static_cast<unsigned char>( loop ), static_cast<unsigned char>( explode ) );
1822
    return new BLong( 1 );
×
1823
  }
1824

NEW
1825
  return new BLong( 0 );
×
1826
}
1827

1828
BObjectImp* UOExecutorModule::mf_PlayMovingEffectXYZ()
×
1829
{
1830
  unsigned short effect;
1831
  int speed;
1832
  int loop;
1833
  int explode;
1834
  Realms::Realm* realm;
1835
  Pos3d src, dst;
×
1836
  if ( getRealmParam( 10, &realm ) && getPos3dParam( 0, 1, 2, &src, realm ) &&
×
1837
       getPos3dParam( 3, 4, 5, &dst, realm ) && getParam( 6, effect ) &&
×
1838
       getParam( 7, speed, UCHAR_MAX ) && getParam( 8, loop, UCHAR_MAX ) &&
×
1839
       getParam( 9, explode, UCHAR_MAX ) )
×
1840
  {
1841
    play_moving_effect2( src, dst, effect, static_cast<signed char>( speed ),
×
1842
                         static_cast<signed char>( loop ), static_cast<signed char>( explode ),
×
1843
                         realm );
1844
    return new BLong( 1 );
×
1845
  }
1846

NEW
1847
  return nullptr;
×
1848
}
1849

1850
// FIXME add/test:stationary
1851

1852
BObjectImp* UOExecutorModule::mf_PlayObjectCenteredEffect()
×
1853
{
1854
  UObject* src;
1855
  unsigned short effect;
1856
  int speed;
1857
  int loop;
1858
  if ( getUObjectParam( 0, src ) && getParam( 1, effect ) && getParam( 2, speed, UCHAR_MAX ) &&
×
1859
       getParam( 3, loop, UCHAR_MAX ) )
×
1860
  {
1861
    play_object_centered_effect( src, effect, static_cast<unsigned char>( speed ),
×
1862
                                 static_cast<unsigned char>( loop ) );
1863
    return new BLong( 1 );
×
1864
  }
1865

NEW
1866
  return nullptr;
×
1867
}
1868

1869
BObjectImp* UOExecutorModule::mf_PlayStationaryEffect()
×
1870
{
1871
  Pos4d pos;
×
1872
  unsigned short effect;
1873
  int speed, loop, explode;
1874
  if ( getPos4dParam( 0, 1, 2, 7, &pos ) && getParam( 3, effect ) &&
×
1875
       getParam( 4, speed, UCHAR_MAX ) && getParam( 5, loop, UCHAR_MAX ) &&
×
1876
       getParam( 6, explode, UCHAR_MAX ) )
×
1877
  {
1878
    play_stationary_effect( pos, effect, static_cast<unsigned char>( speed ),
×
1879
                            static_cast<unsigned char>( loop ),
1880
                            static_cast<unsigned char>( explode ) );
1881
    return new BLong( 1 );
×
1882
  }
1883

NEW
1884
  return new BError( "Invalid parameter" );
×
1885
}
1886

1887

1888
BObjectImp* UOExecutorModule::mf_PlayMovingEffectEx()
×
1889
{
1890
  UObject* src;
1891
  UObject* dst;
1892
  unsigned short effect;
1893
  int speed;
1894
  int duration;
1895
  int hue;
1896
  int render;
1897
  int direction;
1898
  int explode;
1899
  unsigned short effect3d;
1900
  unsigned short effect3dexplode;
1901
  unsigned short effect3dsound;
1902

1903
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) && getParam( 2, effect ) &&
×
1904
       getParam( 3, speed, UCHAR_MAX ) && getParam( 4, duration, UCHAR_MAX ) &&
×
1905
       getParam( 5, hue, INT_MAX ) && getParam( 6, render, INT_MAX ) &&
×
1906
       getParam( 7, direction, UCHAR_MAX ) && getParam( 8, explode, UCHAR_MAX ) &&
×
1907
       getParam( 9, effect3d ) && getParam( 10, effect3dexplode ) && getParam( 11, effect3dsound ) )
×
1908
  {
1909
    if ( src->realm() != dst->realm() )
×
1910
      return new BError( "Realms must match" );
×
1911
    play_moving_effect_ex(
×
1912
        src, dst, effect, static_cast<unsigned char>( speed ),
1913
        static_cast<unsigned char>( duration ), static_cast<unsigned int>( hue ),
1914
        static_cast<unsigned int>( render ), static_cast<unsigned char>( direction ),
1915
        static_cast<unsigned char>( explode ), effect3d, effect3dexplode, effect3dsound );
1916
    return new BLong( 1 );
×
1917
  }
1918

NEW
1919
  return new BLong( 0 );
×
1920
}
1921

1922
BObjectImp* UOExecutorModule::mf_PlayMovingEffectXYZEx()
×
1923
{
1924
  Pos3d src, dst;
×
1925
  Realms::Realm* realm;
1926

1927
  unsigned short effect;
1928
  int speed;
1929
  int duration;
1930
  int hue;
1931
  int render;
1932
  int direction;
1933
  int explode;
1934
  unsigned short effect3d;
1935
  unsigned short effect3dexplode;
1936
  unsigned short effect3dsound;
1937

1938
  if ( getRealmParam( 6, &realm ) && getPos3dParam( 0, 1, 2, &src, realm ) &&
×
1939
       getPos3dParam( 3, 4, 5, &dst, realm ) && getParam( 7, effect ) &&
×
1940
       getParam( 8, speed, UCHAR_MAX ) && getParam( 9, duration, UCHAR_MAX ) &&
×
1941
       getParam( 10, hue, INT_MAX ) && getParam( 11, render, INT_MAX ) &&
×
1942
       getParam( 12, direction, UCHAR_MAX ) && getParam( 13, explode, UCHAR_MAX ) &&
×
1943
       getParam( 14, effect3d ) && getParam( 15, effect3dexplode ) &&
×
1944
       getParam( 16, effect3dsound ) )
×
1945
  {
1946
    play_moving_effect2_ex(
×
1947
        src, dst, realm, effect, static_cast<unsigned char>( speed ),
1948
        static_cast<unsigned char>( duration ), static_cast<unsigned int>( hue ),
1949
        static_cast<unsigned int>( render ), static_cast<unsigned char>( direction ),
1950
        static_cast<unsigned char>( explode ), effect3d, effect3dexplode, effect3dsound );
1951
    return new BLong( 1 );
×
1952
  }
1953

NEW
1954
  return nullptr;
×
1955
}
1956

1957
BObjectImp* UOExecutorModule::mf_PlayObjectCenteredEffectEx()
×
1958
{
1959
  UObject* src;
1960
  unsigned short effect;
1961
  int speed;
1962
  int duration;
1963
  int hue;
1964
  int render;
1965
  int layer;
1966
  unsigned short effect3d;
1967

1968
  if ( getUObjectParam( 0, src ) && getParam( 1, effect ) && getParam( 2, speed, UCHAR_MAX ) &&
×
1969
       getParam( 3, duration, UCHAR_MAX ) && getParam( 4, hue, INT_MAX ) &&
×
1970
       getParam( 5, render, INT_MAX ) && getParam( 6, layer, UCHAR_MAX ) &&
×
1971
       getParam( 7, effect3d ) )
×
1972
  {
1973
    play_object_centered_effect_ex(
×
1974
        src, effect, static_cast<unsigned char>( speed ), static_cast<unsigned char>( duration ),
1975
        static_cast<unsigned int>( hue ), static_cast<unsigned int>( render ),
1976
        static_cast<unsigned char>( layer ), effect3d );
1977
    return new BLong( 1 );
×
1978
  }
1979

NEW
1980
  return nullptr;
×
1981
}
1982

1983
BObjectImp* UOExecutorModule::mf_PlayStationaryEffectEx()
×
1984
{
1985
  Pos4d pos;
×
1986
  unsigned short effect;
1987
  int speed;
1988
  int duration;
1989
  int hue;
1990
  int render;
1991
  unsigned short effect3d;
1992

1993
  if ( getPos4dParam( 0, 1, 2, 3, &pos ) && getParam( 4, effect ) &&
×
1994
       getParam( 5, speed, UCHAR_MAX ) && getParam( 6, duration, UCHAR_MAX ) &&
×
1995
       getParam( 7, hue, INT_MAX ) && getParam( 8, render, INT_MAX ) && getParam( 9, effect3d ) )
×
1996
  {
1997
    play_stationary_effect_ex(
×
1998
        pos, effect, static_cast<unsigned char>( speed ), static_cast<unsigned char>( duration ),
1999
        static_cast<unsigned int>( hue ), static_cast<unsigned int>( render ), effect3d );
2000
    return new BLong( 1 );
×
2001
  }
2002

NEW
2003
  return new BError( "Invalid parameter" );
×
2004
}
2005

2006
BObjectImp* UOExecutorModule::mf_PlayLightningBoltEffect()
×
2007
{
2008
  UObject* center;
2009
  if ( getUObjectParam( 0, center ) )
×
2010
  {
2011
    play_lightning_bolt_effect( center );
×
2012
    return new BLong( 1 );
×
2013
  }
2014

NEW
2015
  return new BLong( 0 );
×
2016
}
2017

2018
BObjectImp* UOExecutorModule::mf_ListItemsNearLocation( /* x, y, z, range, realm */ )
×
2019
{
2020
  Core::Pos2d pos;
×
2021
  int z;
2022
  u16 range;
2023
  Realms::Realm* realm;
2024

2025
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2026
       getParam( 3, range ) )
×
2027
  {
2028
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2029
    WorldIterator<ItemFilter>::InRange(
×
2030
        pos, realm, range,
2031
        [&]( Item* item )
×
2032
        {
2033
          if ( item->in_range( pos, range ) )
×
2034
          {
2035
            if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2036
              newarr->addElement( item->make_ref() );
×
2037
          }
2038
        } );
×
2039

2040
    return newarr.release();
×
2041
  }
×
2042

2043
  return nullptr;
×
2044
}
2045

2046
Range3d UOExecutorModule::internal_InBoxAreaChecks( const Pos2d& p1, int z1, const Pos2d& p2,
10✔
2047
                                                    int z2, Realms::Realm* realm )
2048
{
2049
  // no error checks for out of realm range on purpose
2050
  if ( ( z1 > z2 ) && z1 != LIST_IGNORE_Z && z2 != LIST_IGNORE_Z )
10✔
2051
    std::swap( z1, z2 );
×
2052
  if ( z1 == LIST_IGNORE_Z )
10✔
2053
    z1 = ZCOORD_MIN;
×
2054
  if ( z2 == LIST_IGNORE_Z )
10✔
2055
    z2 = ZCOORD_MAX;
×
2056
  return Range3d( Pos3d( p1, Clib::clamp_convert<s8>( z1 ) ),
10✔
2057
                  Pos3d( p2, Clib::clamp_convert<s8>( z2 ) ), realm );
20✔
2058
}
2059

2060
BObjectImp* UOExecutorModule::mf_ListObjectsInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
×
2061
{
2062
  int z1, z2;
2063
  Pos2d p1, p2;
×
2064
  Realms::Realm* realm;
2065

2066
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
×
2067
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
×
2068
  {
2069
    return new BError( "Invalid parameter" );
×
2070
  }
2071
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
×
2072
  z1 = box.nw_b().z();
×
2073
  z2 = box.se_t().z();
×
2074

2075
  std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2076
  WorldIterator<MobileFilter>::InBox( box.range(), realm,
×
2077
                                      [&]( Mobile::Character* chr )
×
2078
                                      {
2079
                                        if ( chr->z() >= z1 && chr->z() <= z2 )
×
2080
                                        {
2081
                                          newarr->addElement( chr->make_ref() );
×
2082
                                        }
2083
                                      } );
×
2084
  WorldIterator<ItemFilter>::InBox( box.range(), realm,
×
2085
                                    [&]( Items::Item* item )
×
2086
                                    {
2087
                                      if ( item->z() >= z1 && item->z() <= z2 )
×
2088
                                      {
2089
                                        newarr->addElement( item->make_ref() );
×
2090
                                      }
2091
                                    } );
×
2092

2093
  return newarr.release();
×
2094
}
×
2095

2096
BObjectImp* UOExecutorModule::mf_ListItemsInBoxOfObjType(
2✔
2097
    /* objtype, x1, y1, z1, x2, y2, z2, realm */ )
2098
{
2099
  unsigned int objtype;
2100
  int z1, z2;
2101
  Pos2d p1, p2;
2✔
2102
  Realms::Realm* realm;
2103

2104
  if ( !( getParam( 0, objtype ) && getPos2dParam( 1, 2, &p1 ) && getParam( 3, z1 ) &&
4✔
2105
          getPos2dParam( 4, 5, &p2 ) && getParam( 6, z2 ) && getRealmParam( 7, &realm ) ) )
2✔
2106
  {
2107
    return new BError( "Invalid parameter" );
×
2108
  }
2109
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
2✔
2110
  z1 = box.nw_b().z();
2✔
2111
  z2 = box.se_t().z();
2✔
2112

2113
  std::unique_ptr<ObjArray> newarr( new ObjArray );
2✔
2114
  WorldIterator<ItemFilter>::InBox(
4✔
2115
      box.range(), realm,
2✔
2116
      [&]( Items::Item* item )
2✔
2117
      {
2118
        if ( item->z() >= z1 && item->z() <= z2 && item->objtype_ == objtype )
40✔
2119
        {
2120
          newarr->addElement( item->make_ref() );
39✔
2121
        }
2122
      } );
40✔
2123

2124
  return newarr.release();
2✔
2125
}
2✔
2126

2127
BObjectImp* UOExecutorModule::mf_ListObjectsInBoxOfClass(
1✔
2128
    /* POL_Class, x1, y1, z1, x2, y2, z2, realm */ )
2129
{
2130
  unsigned int POL_Class;
2131
  int z1, z2;
2132
  Pos2d p1, p2;
1✔
2133
  Realms::Realm* realm;
2134

2135
  if ( !( getParam( 0, POL_Class ) && getPos2dParam( 1, 2, &p1 ) && getParam( 3, z1 ) &&
2✔
2136
          getPos2dParam( 4, 5, &p2 ) && getParam( 6, z2 ) && getRealmParam( 7, &realm ) ) )
1✔
2137
  {
2138
    return new BError( "Invalid parameter" );
×
2139
  }
2140
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
1✔
2141
  z1 = box.nw_b().z();
1✔
2142
  z2 = box.se_t().z();
1✔
2143

2144
  std::unique_ptr<ObjArray> newarr( new ObjArray );
1✔
2145
  WorldIterator<MobileFilter>::InBox(
2✔
2146
      box.range(), realm,
1✔
2147
      [&]( Mobile::Character* chr )
1✔
2148
      {
2149
        if ( chr->z() >= z1 && chr->z() <= z2 && chr->script_isa( POL_Class ) )
×
2150
        {
2151
          newarr->addElement( chr->make_ref() );
×
2152
        }
2153
      } );
×
2154
  WorldIterator<ItemFilter>::InBox(
2✔
2155
      box.range(), realm,
1✔
2156
      [&]( Items::Item* item )
1✔
2157
      {
2158
        if ( item->z() >= z1 && item->z() <= z2 && item->script_isa( POL_Class ) )
1✔
2159
        {
2160
          newarr->addElement( item->make_ref() );
1✔
2161
        }
2162
      } );
1✔
2163

2164
  return newarr.release();
1✔
2165
}
1✔
2166

2167
BObjectImp* UOExecutorModule::mf_ListMobilesInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
2✔
2168
{
2169
  int z1, z2;
2170
  Pos2d p1, p2;
2✔
2171
  Realms::Realm* realm;
2172

2173
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
4✔
2174
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
2✔
2175
  {
2176
    return new BError( "Invalid parameter" );
×
2177
  }
2178
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
2✔
2179
  z1 = box.nw_b().z();
2✔
2180
  z2 = box.se_t().z();
2✔
2181
  std::unique_ptr<ObjArray> newarr( new ObjArray );
2✔
2182
  WorldIterator<MobileFilter>::InBox( box.range(), realm,
2✔
2183
                                      [&]( Mobile::Character* chr )
2✔
2184
                                      {
2185
                                        if ( chr->z() >= z1 && chr->z() <= z2 )
2✔
2186
                                        {
2187
                                          newarr->addElement( chr->make_ref() );
2✔
2188
                                        }
2189
                                      } );
2✔
2190

2191
  return newarr.release();
2✔
2192
}
2✔
2193

2194
BObjectImp* UOExecutorModule::mf_ListMultisInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
4✔
2195
{
2196
  int z1, z2;
2197
  Pos2d p1, p2;
4✔
2198
  Realms::Realm* realm;
2199

2200
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
8✔
2201
          getParam( 5, z2 ) && getRealmParam( 6, &realm ) ) )
4✔
2202
  {
2203
    return new BError( "Invalid parameter" );
×
2204
  }
2205

2206
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
4✔
2207
  z1 = box.nw_b().z();
4✔
2208
  z2 = box.se_t().z();
4✔
2209

2210
  std::unique_ptr<ObjArray> newarr( new ObjArray );
4✔
2211

2212
  // extend the coords to find the center item
2213
  // but only as parameter for the filter function
2214
  Vec2d urange{ Clib::clamp_convert<s16>( gamestate.max_update_range ),
4✔
2215
                Clib::clamp_convert<s16>( gamestate.max_update_range ) };
4✔
2216
  Range2d boxrange( box.nw() - urange, box.se() + urange, realm );
4✔
2217

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

2238
            if ( ( z1 <= absp.z() && absp.z() <= z2 ) ||  // bottom point lies between
8✔
2239
                 ( z1 <= top && top <= z2 ) ||            // top lies between
8✔
2240
                 ( top >= z2 && absp.z() <= z1 ) )        // spans
×
2241
            {
2242
              newarr->addElement( multi->make_ref() );
4✔
2243
              break;  // out of for
4✔
2244
            }
2245
          }
2246
        }
2247
      } );
2248

2249
  return newarr.release();
4✔
2250
}
4✔
2251

2252
BObjectImp* UOExecutorModule::mf_ListStaticsInBox( /* x1, y1, z1, x2, y2, z2, flags, realm */ )
1✔
2253
{
2254
  int z1, z2;
2255
  Pos2d p1, p2;
1✔
2256
  int flags;
2257
  Realms::Realm* realm;
2258

2259
  if ( !( getPos2dParam( 0, 1, &p1 ) && getParam( 2, z1 ) && getPos2dParam( 3, 4, &p2 ) &&
2✔
2260
          getParam( 5, z2 ) && getParam( 6, flags ) && getRealmParam( 7, &realm ) ) )
1✔
2261
  {
2262
    return new BError( "Invalid parameter" );
×
2263
  }
2264

2265
  Range3d box = internal_InBoxAreaChecks( p1, z1, p2, z2, realm );
1✔
2266
  z1 = box.nw_b().z();
1✔
2267
  z2 = box.se_t().z();
1✔
2268

2269
  std::unique_ptr<ObjArray> newarr( new ObjArray );
1✔
2270

2271
  for ( const auto& pos : box.range() )
5✔
2272
  {
2273
    if ( !( flags & ITEMS_IGNORE_STATICS ) )
2✔
2274
    {
2275
      Plib::StaticEntryList slist;
2✔
2276
      realm->getstatics( slist, pos );
2✔
2277

2278
      for ( unsigned i = 0; i < slist.size(); ++i )
4✔
2279
      {
2280
        if ( ( z1 <= slist[i].z ) && ( slist[i].z <= z2 ) )
2✔
2281
        {
2282
          std::unique_ptr<BStruct> arr( new BStruct );
2✔
2283
          arr->addMember( "x", new BLong( pos.x() ) );
2✔
2284
          arr->addMember( "y", new BLong( pos.y() ) );
2✔
2285
          arr->addMember( "z", new BLong( slist[i].z ) );
2✔
2286
          arr->addMember( "objtype", new BLong( slist[i].objtype ) );
2✔
2287
          arr->addMember( "hue", new BLong( slist[i].hue ) );
2✔
2288
          newarr->addElement( arr.release() );
2✔
2289
        }
2✔
2290
      }
2291
    }
2✔
2292

2293
    if ( !( flags & ITEMS_IGNORE_MULTIS ) )
2✔
2294
    {
2295
      Plib::StaticList mlist;
2✔
2296
      realm->readmultis( mlist, pos );
2✔
2297

2298
      for ( unsigned i = 0; i < mlist.size(); ++i )
2✔
2299
      {
2300
        if ( ( z1 <= mlist[i].z ) && ( mlist[i].z <= z2 ) )
×
2301
        {
2302
          std::unique_ptr<BStruct> arr( new BStruct );
×
2303
          arr->addMember( "x", new BLong( pos.x() ) );
×
2304
          arr->addMember( "y", new BLong( pos.y() ) );
×
2305
          arr->addMember( "z", new BLong( mlist[i].z ) );
×
2306
          arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
×
2307
          newarr->addElement( arr.release() );
×
2308
        }
×
2309
      }
2310
    }
2✔
2311
  }
2312
  return newarr.release();
1✔
2313
}
1✔
2314

2315
BObjectImp* UOExecutorModule::mf_ListItemsNearLocationOfType( /* x, y, z, range, objtype, realm */ )
27✔
2316
{
2317
  Core::Pos2d pos;
27✔
2318
  int z;
2319
  u16 range;
2320
  unsigned int objtype;
2321
  Realms::Realm* realm;
2322

2323
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
54✔
2324
       getParam( 3, range ) && getObjtypeParam( 4, objtype ) )
54✔
2325
  {
2326
    std::unique_ptr<ObjArray> newarr( new ObjArray );
27✔
2327
    WorldIterator<ItemFilter>::InRange(
27✔
2328
        pos, realm, range,
2329
        [&]( Items::Item* item )
27✔
2330
        {
2331
          if ( item->objtype_ == objtype && item->in_range( pos, range ) )
50✔
2332
          {
2333
            if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
37✔
2334
              newarr->addElement( item->make_ref() );
37✔
2335
          }
2336
        } );
50✔
2337

2338
    return newarr.release();
27✔
2339
  }
27✔
2340

2341
  return nullptr;
×
2342
}
2343

2344

2345
BObjectImp* UOExecutorModule::mf_ListItemsAtLocation( /* x, y, z, realm */ )
×
2346
{
2347
  Pos2d pos;
×
2348
  int z;
2349
  Realms::Realm* realm;
2350

2351
  if ( getRealmParam( 3, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) )
×
2352
  {
2353
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2354
    WorldIterator<ItemFilter>::InRange( pos, realm, 0,
×
2355
                                        [&]( Items::Item* item )
×
2356
                                        {
2357
                                          if ( item->pos2d() == pos )
×
2358
                                          {
2359
                                            if ( ( z == LIST_IGNORE_Z ) || ( item->z() == z ) )
×
2360
                                              newarr->addElement( item->make_ref() );
×
2361
                                          }
2362
                                        } );
×
2363

2364
    return newarr.release();
×
2365
  }
×
2366

2367
  return nullptr;
×
2368
}
2369

2370
BObjectImp* UOExecutorModule::mf_ListGhostsNearLocation()
×
2371
{
2372
  Pos2d pos;
×
2373
  int z;
2374
  u16 range;
2375
  Realms::Realm* realm;
2376

2377
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2378
       getParam( 3, range ) )
×
2379
  {
2380
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2381
    WorldIterator<PlayerFilter>::InRange(
×
2382
        pos, realm, range,
2383
        [&]( Mobile::Character* chr )
×
2384
        {
2385
          if ( chr->dead() && ( abs( chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2386
          {
2387
            newarr->addElement( chr->make_ref() );
×
2388
          }
2389
        } );
×
2390

2391
    return newarr.release();
×
2392
  }
×
2393

NEW
2394
  return new BError( "Invalid parameter" );
×
2395
}
2396

2397
const unsigned LMBLEX_FLAG_NORMAL = 0x01;
2398
const unsigned LMBLEX_FLAG_HIDDEN = 0x02;
2399
const unsigned LMBLEX_FLAG_DEAD = 0x04;
2400
const unsigned LMBLEX_FLAG_CONCEALED = 0x8;
2401
const unsigned LMBLEX_FLAG_PLAYERS_ONLY = 0x10;
2402
const unsigned LMBLEX_FLAG_NPC_ONLY = 0x20;
2403

2404
BObjectImp* UOExecutorModule::mf_ListMobilesNearLocationEx( /* x, y, z, range, flags, realm */ )
×
2405
{
2406
  Pos2d pos;
×
2407
  Realms::Realm* realm;
2408
  int z, flags;
2409
  u16 range;
2410

2411
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2412
       getParam( 3, range ) && getParam( 4, flags ) )
×
2413
  {
2414
    bool inc_normal = ( flags & LMBLEX_FLAG_NORMAL ) ? true : false;
×
2415
    bool inc_hidden = ( flags & LMBLEX_FLAG_HIDDEN ) ? true : false;
×
2416
    bool inc_dead = ( flags & LMBLEX_FLAG_DEAD ) ? true : false;
×
2417
    bool inc_concealed = ( flags & LMBLEX_FLAG_CONCEALED ) ? true : false;
×
2418
    bool inc_players_only = ( flags & LMBLEX_FLAG_PLAYERS_ONLY ) ? true : false;
×
2419
    bool inc_npc_only = ( flags & LMBLEX_FLAG_NPC_ONLY ) ? true : false;
×
2420

2421
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2422

2423
    auto fill_mobs = [&]( Mobile::Character* _chr )
×
2424
    {
2425
      if ( ( inc_hidden && _chr->hidden() ) || ( inc_dead && _chr->dead() ) ||
×
2426
           ( inc_concealed && _chr->concealed() ) ||
×
2427
           ( inc_normal && !( _chr->hidden() || _chr->dead() || _chr->concealed() ) ) )
×
2428
      {
2429
        if ( ( z == LIST_IGNORE_Z ) || ( abs( _chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2430
        {
2431
          if ( !inc_players_only && !inc_npc_only )
×
2432
            newarr->addElement( _chr->make_ref() );
×
2433
          else if ( inc_players_only && _chr->client )
×
2434
            newarr->addElement( _chr->make_ref() );
×
2435
          else if ( inc_npc_only && _chr->isa( UOBJ_CLASS::CLASS_NPC ) )
×
2436
            newarr->addElement( _chr->make_ref() );
×
2437
        }
2438
      }
2439
    };
×
2440
    if ( inc_players_only )
×
2441
      WorldIterator<PlayerFilter>::InRange( pos, realm, range, fill_mobs );
×
2442
    else if ( inc_npc_only )
×
2443
      WorldIterator<NPCFilter>::InRange( pos, realm, range, fill_mobs );
×
2444
    else
2445
      WorldIterator<MobileFilter>::InRange( pos, realm, range, fill_mobs );
×
2446

2447
    return newarr.release();
×
2448
  }
×
2449

NEW
2450
  return new BError( "Invalid parameter" );
×
2451
}
2452

2453
BObjectImp* UOExecutorModule::mf_ListMobilesNearLocation( /* x, y, z, range, realm */ )
×
2454
{
2455
  Pos2d pos;
×
2456
  int z;
2457
  u16 range;
2458
  Realms::Realm* realm;
2459

2460
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
2461
       getParam( 3, range ) )
×
2462
  {
2463
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2464
    WorldIterator<MobileFilter>::InRange(
×
2465
        pos, realm, range,
2466
        [&]( Mobile::Character* chr )
×
2467
        {
2468
          if ( ( !chr->concealed() ) && ( !chr->hidden() ) && ( !chr->dead() ) )
×
2469
            if ( ( z == LIST_IGNORE_Z ) || ( abs( chr->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
2470
              newarr->addElement( chr->make_ref() );
×
2471
        } );
×
2472
    return newarr.release();
×
2473
  }
×
2474

NEW
2475
  return new BError( "Invalid parameter" );
×
2476
}
2477

2478
BObjectImp* UOExecutorModule::mf_ListMobilesInLineOfSight()
×
2479
{
2480
  UObject* obj;
2481
  int range;
2482
  if ( getUObjectParam( 0, obj ) && getParam( 1, range ) )
×
2483
  {
2484
    obj = obj->toplevel_owner();
×
2485
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2486
    WorldIterator<MobileFilter>::InRange(
×
2487
        obj, range,
2488
        [&]( Mobile::Character* chr )
×
2489
        {
2490
          if ( chr->dead() || chr->hidden() || chr->concealed() )
×
2491
            return;
×
2492
          if ( chr == obj )
×
2493
            return;
×
2494
          if ( ( abs( chr->z() - obj->z() ) < CONST_DEFAULT_ZRANGE ) )
×
2495
          {
2496
            if ( obj->realm()->has_los( *obj, *chr ) )
×
2497
            {
2498
              newarr->addElement( chr->make_ref() );
×
2499
            }
2500
          }
2501
        } );
2502

2503
    return newarr.release();
×
2504
  }
×
2505

NEW
2506
  return new BError( "Invalid parameter" );
×
2507
}
2508

2509
BObjectImp* UOExecutorModule::mf_ListOfflineMobilesInRealm( /*realm*/ )
×
2510
{
2511
  Realms::Realm* realm = nullptr;
×
2512

2513
  if ( getRealmParam( 0, &realm ) )
×
2514
  {
2515
    std::unique_ptr<ObjArray> newarr( new ObjArray() );
×
2516

2517
    for ( const auto& objitr : Pol::Core::objStorageManager.objecthash )
×
2518
    {
2519
      UObject* obj = objitr.second.get();
×
2520
      if ( !obj->ismobile() || obj->isa( UOBJ_CLASS::CLASS_NPC ) )
×
2521
        continue;
×
2522

2523
      Character* chr = static_cast<Character*>( obj );
×
2524
      if ( chr->logged_in() || chr->realm() != realm || chr->orphan() )
×
2525
        continue;
×
2526

2527
      newarr->addElement( new EOfflineCharacterRefObjImp( chr ) );
×
2528
    }
2529
    return newarr.release();
×
2530
  }
×
2531

NEW
2532
  return new BError( "Invalid parameter" );
×
2533
}
2534

2535
// keep this in sync with UO.EM
2536
const int LH_FLAG_LOS = 1;             // only include those in LOS
2537
const int LH_FLAG_INCLUDE_HIDDEN = 2;  // include hidden characters
2538
BObjectImp* UOExecutorModule::mf_ListHostiles()
×
2539
{
2540
  Character* chr;
2541
  u16 range;
2542
  int flags;
2543
  if ( getCharacterParam( 0, chr ) && getParam( 1, range ) && getParam( 2, flags ) )
×
2544
  {
2545
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
2546

2547
    for ( auto& hostile : chr->hostiles() )
×
2548
    {
2549
      if ( hostile->concealed() )
×
2550
        continue;
×
2551
      if ( ( flags & LH_FLAG_LOS ) && !chr->realm()->has_los( *chr, *hostile ) )
×
2552
        continue;
×
2553
      if ( ( ~flags & LH_FLAG_INCLUDE_HIDDEN ) && hostile->hidden() )
×
2554
        continue;
×
2555
      if ( !chr->in_range( hostile, range ) )
×
2556
        continue;
×
2557
      arr->addElement( hostile->make_ref() );
×
2558
    }
2559

2560
    return arr.release();
×
2561
  }
×
2562

NEW
2563
  return new BError( "Invalid parameter" );
×
2564
}
2565

2566
BObjectImp* UOExecutorModule::mf_CheckLineOfSight()
1✔
2567
{
2568
  UObject* src;
2569
  UObject* dst;
2570
  if ( getUObjectParam( 0, src ) && getUObjectParam( 1, dst ) )
1✔
2571
  {
2572
    return new BLong( src->realm()->has_los( *src, *dst->toplevel_owner() ) );
1✔
2573
  }
2574

NEW
2575
  return new BLong( 0 );
×
2576
}
2577

2578
BObjectImp* UOExecutorModule::mf_CheckLosAt()
1✔
2579
{
2580
  UObject* src;
2581
  Core::Pos3d pos;
1✔
2582
  if ( getUObjectParam( 0, src ) && getPos3dParam( 1, 2, 3, &pos, src->realm() ) )
1✔
2583
  {
2584
    LosObj tgt( Core::Pos4d( pos, src->realm() ) );
1✔
2585
    return new BLong( src->realm()->has_los( *src, tgt ) );
1✔
2586
  }
2587
  return nullptr;
×
2588
}
2589

2590
BObjectImp* UOExecutorModule::mf_CheckLosBetween()
2✔
2591
{
2592
  Core::Pos3d p1, p2;
2✔
2593
  Realms::Realm* realm;
2594
  if ( getRealmParam( 6, &realm ) && getPos3dParam( 0, 1, 2, &p1, realm ) &&
4✔
2595
       getPos3dParam( 3, 4, 5, &p2, realm ) )
2✔
2596
  {
2597
    LosObj att( Core::Pos4d( p1, realm ) );
2✔
2598
    LosObj tgt( Core::Pos4d( p2, realm ) );
2✔
2599
    return new BLong( realm->has_los( att, tgt ) );
2✔
2600
  }
2601
  return nullptr;
×
2602
}
2603

2604
BObjectImp* UOExecutorModule::mf_DestroyItem()
6,180✔
2605
{
2606
  Item* item;
2607
  if ( getItemParam( 0, item ) )
6,180✔
2608
  {
2609
    if ( item->has_gotten_by() )
6,176✔
2610
      item->gotten_by()->clear_gotten_item();
×
2611
    else if ( item->inuse() && !is_reserved_to_me( item ) )
6,176✔
2612
      return new BError( "That item is being used." );
×
2613
    else if ( item->script_isa( POLCLASS_MULTI ) )
6,176✔
2614
      return new BError( "That item is a multi. Use uo::DestroyMulti instead." );
×
2615

2616
    const ItemDesc& id = find_itemdesc( item->objtype_ );
6,176✔
2617
    if ( !id.destroy_script.empty() )
6,176✔
2618
    {
2619
      BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( item ) );
×
2620
      if ( res->isTrue() )
×
2621
      {  // destruction is okay
2622
        BObject ob( res );
×
2623
      }
×
2624
      else
2625
      {
2626
        // destruction isn't okay!
2627
        return res;
×
2628
      }
2629
    }
2630
    UpdateCharacterOnDestroyItem( item );
6,176✔
2631
    UpdateCharacterWeight( item );
6,176✔
2632
    destroy_item( item );
6,176✔
2633
    return new BLong( 1 );
6,176✔
2634
  }
2635

2636
  return new BError( "Invalid parameter type" );
4✔
2637
}
2638

2639
BObjectImp* UOExecutorModule::mf_SetName()
6✔
2640
{
2641
  UObject* obj;
2642
  const String* name_str;
2643

2644
  if ( getUObjectParam( 0, obj ) && getStringParam( 1, name_str ) )
6✔
2645
  {
2646
    obj->setname( name_str->value() );
6✔
2647
    return new BLong( 1 );
6✔
2648
  }
2649

NEW
2650
  return new BLong( 0 );
×
2651
}
2652

2653
BObjectImp* UOExecutorModule::mf_GetPosition()
×
2654
{
2655
  UObject* obj;
2656
  if ( getUObjectParam( 0, obj ) )
×
2657
  {
2658
    std::unique_ptr<BStruct> arr( new BStruct );
×
2659
    arr->addMember( "x", new BLong( obj->x() ) );
×
2660
    arr->addMember( "y", new BLong( obj->y() ) );
×
2661
    arr->addMember( "z", new BLong( obj->z() ) );
×
2662
    return arr.release();
×
2663
  }
×
2664

NEW
2665
  return new BLong( 0 );
×
2666
}
2667

2668
BObjectImp* UOExecutorModule::mf_EnumerateItemsInContainer()
6✔
2669
{
2670
  Item* item;
2671
  if ( getItemParam( 0, item ) )
6✔
2672
  {
2673
    int flags = 0;
6✔
2674
    if ( exec.fparams.size() >= 2 )
6✔
2675
    {
2676
      if ( !getParam( 1, flags ) )
6✔
2677
        return new BError( "Invalid parameter type" );
×
2678
    }
2679

2680
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
6✔
2681
    {
2682
      std::unique_ptr<ObjArray> newarr( new ObjArray );
6✔
2683

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

2686
      return newarr.release();
6✔
2687
    }
6✔
2688

2689
    return nullptr;
×
2690
  }
NEW
2691
  return new BError( "Invalid parameter type" );
×
2692
}
2693

2694
BObjectImp* UOExecutorModule::mf_EnumerateOnlineCharacters()
×
2695
{
2696
  std::unique_ptr<ObjArray> newarr( new ObjArray );
×
2697

2698
  for ( Clients::const_iterator itr = networkManager.clients.begin(),
×
2699
                                end = networkManager.clients.end();
×
2700
        itr != end; ++itr )
×
2701
  {
2702
    if ( ( *itr )->chr != nullptr )
×
2703
    {
2704
      newarr->addElement( new ECharacterRefObjImp( ( *itr )->chr ) );
×
2705
    }
2706
  }
2707

2708
  return newarr.release();
×
2709
}
×
2710

2711
BObjectImp* UOExecutorModule::mf_RegisterForSpeechEvents()
1✔
2712
{
2713
  UObject* center;
2714
  int range;
2715
  if ( getUObjectParam( 0, center ) && getParam( 1, range ) )
1✔
2716
  {
2717
    int flags = 0;
1✔
2718
    if ( exec.hasParams( 3 ) )
1✔
2719
    {
2720
      if ( !getParam( 2, flags ) )
1✔
2721
        return new BError( "Invalid parameter type" );
×
2722
    }
2723
    if ( !registered_for_speech_events )
1✔
2724
    {
2725
      ListenPoint::register_for_speech_events( center, &uoexec(), range, flags );
1✔
2726
      registered_for_speech_events = true;
1✔
2727
      return new BLong( 1 );
1✔
2728
    }
2729

NEW
2730
    return new BError( "Already registered for speech events" );
×
2731
  }
2732

NEW
2733
  return new BError( "Invalid parameter type" );
×
2734
}
2735

2736
BObjectImp* UOExecutorModule::mf_EnableEvents()
5✔
2737
{
2738
  int eventmask;
2739
  if ( getParam( 0, eventmask ) )
5✔
2740
  {
2741
    auto& uoex = uoexec();
5✔
2742
    if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA | EVID_SPOKE ) )
5✔
2743
    {
2744
      unsigned short range;
2745
      if ( getParam( 1, range, 0, 32 ) )
5✔
2746
      {
2747
        if ( eventmask & ( EVID_SPOKE ) )
5✔
2748
          uoex.speech_size = range;
2✔
2749
        if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA ) )
5✔
2750
          uoex.area_size = range;
3✔
2751
      }
2752
      else
2753
      {
2754
        return nullptr;
×
2755
      }
2756

2757

2758
      unsigned short evopts = 0;
5✔
2759
      if ( exec.hasParams( 2 ) && getParam( 2, evopts ) )
5✔
2760
      {
2761
        if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA ) )
5✔
2762
          uoex.area_mask = static_cast<unsigned char>( evopts & 255 );
3✔
2763
      }
2764
    }
2765
    uoex.eventmask |= eventmask;
5✔
2766
    return new BLong( uoex.eventmask );
5✔
2767
  }
2768

NEW
2769
  return new BError( "Invalid parameter" );
×
2770
}
2771

2772
BObjectImp* UOExecutorModule::mf_DisableEvents()
×
2773
{
2774
  int eventmask;
2775
  if ( getParam( 0, eventmask ) )
×
2776
  {
2777
    auto& uoex = uoexec();
×
2778
    uoex.eventmask &= ~eventmask;
×
2779

2780
    return new BLong( uoex.eventmask );
×
2781
  }
2782

NEW
2783
  return new BError( "Invalid parameter" );
×
2784
}
2785

2786
BObjectImp* UOExecutorModule::mf_Resurrect()
×
2787
{
2788
  Character* chr;
2789
  if ( getCharacterParam( 0, chr ) )
×
2790
  {
2791
    if ( !chr->dead() )
×
2792
      return new BError( "That is not dead" );
×
2793
    int flags = 0;
×
2794
    if ( exec.hasParams( 2 ) )
×
2795
    {
2796
      if ( !getParam( 1, flags ) )
×
2797
        return new BError( "Invalid parameter type" );
×
2798
    }
2799

2800
    if ( ~flags & RESURRECT_FORCELOCATION )
×
2801
    {
2802
      // we want doors to block ghosts in this case.
2803
      bool doors_block = !( chr->graphic == UOBJ_GAMEMASTER ||
×
2804
                            chr->cached_settings.get( PRIV_FLAGS::IGNORE_DOORS ) );
×
2805
      short newz;
2806
      Multi::UMulti* supporting_multi;
2807
      Item* walkon_item;
2808
      if ( !chr->realm()->walkheight( chr->pos2d(), chr->z(), &newz, &supporting_multi,
×
2809
                                      &walkon_item, doors_block, chr->movemode ) )
×
2810
      {
2811
        return new BError( "That location is blocked" );
×
2812
      }
2813
    }
2814

2815
    chr->resurrect();
×
2816
    return new BLong( 1 );
×
2817
  }
2818

NEW
2819
  return new BError( "Invalid parameter type" );
×
2820
}
2821

2822
BObjectImp* UOExecutorModule::mf_SystemFindObjectBySerial()
20✔
2823
{
2824
  int serial;
2825
  if ( getParam( 0, serial ) )
20✔
2826
  {
2827
    int sysfind_flags = 0;
20✔
2828
    if ( exec.hasParams( 2 ) )
20✔
2829
    {
2830
      if ( !getParam( 1, sysfind_flags ) )
20✔
2831
        return new BError( "Invalid parameter type" );
×
2832
    }
2833
    if ( IsCharacter( serial ) )
20✔
2834
    {
2835
      Character* chr = system_find_mobile( serial );
3✔
2836
      if ( chr != nullptr )
3✔
2837
      {
2838
        if ( sysfind_flags & SYSFIND_SEARCH_OFFLINE_MOBILES )
2✔
2839
          return new EOfflineCharacterRefObjImp( chr );
×
2840
        return new ECharacterRefObjImp( chr );
2✔
2841
      }
2842

2843
      return new BError( "Character not found" );
1✔
2844
    }
2845

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

2850
    if ( item != nullptr )
17✔
2851
    {
2852
      return item->make_ref();
16✔
2853
    }
2854

2855
    return new BError( "Item not found." );
1✔
2856
  }
2857

NEW
2858
  return new BError( "Invalid parameter type" );
×
2859
}
2860

2861
BObjectImp* UOExecutorModule::mf_SaveWorldState()
2✔
2862
{
2863
  update_gameclock();
2✔
2864
  cancel_all_trades();
2✔
2865

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

2908
  // non waiting version
2909
  u32 dirty, clean;
2910
  s64 elapsed_ms;
2911
  auto res = write_data( {}, &dirty, &clean, &elapsed_ms );
1✔
2912
  if ( !res )
1✔
2913
    return new BError( "pol.cfg has InhibitSaves=1" );
×
2914
  if ( *res )
1✔
2915
  {
2916
    BStruct* ret = new BStruct();
1✔
2917
    ret->addMember( "DirtyObjects", new BLong( dirty ) );
1✔
2918
    ret->addMember( "CleanObjects", new BLong( clean ) );
1✔
2919
    ret->addMember( "ElapsedMilliseconds", new BLong( Clib::clamp_convert<int>( elapsed_ms ) ) );
1✔
2920
    return ret;
1✔
2921
  }
2922
  return new BError( "Failed to save world" );
×
2923
}
2✔
2924

2925

2926
BObjectImp* UOExecutorModule::mf_SetRegionLightLevel()
×
2927
{
2928
  const String* region_name_str;
2929
  int lightlevel;
2930
  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, lightlevel ) ) )
×
2931
  {
2932
    return new BError( "Invalid Parameter type" );
×
2933
  }
2934

2935
  if ( !VALID_LIGHTLEVEL( lightlevel ) )
×
2936
  {
2937
    return new BError( "Light Level is out of range" );
×
2938
  }
2939

2940
  LightRegion* lightregion = gamestate.lightdef->getregion( region_name_str->value() );
×
2941
  if ( lightregion == nullptr )
×
2942
  {
2943
    return new BError( "Light region not found" );
×
2944
  }
2945

2946
  SetRegionLightLevel( lightregion, lightlevel );
×
2947
  return new BLong( 1 );
×
2948
}
2949

2950
BObjectImp* UOExecutorModule::mf_SetRegionWeatherLevel()
×
2951
{
2952
  const String* region_name_str;
2953
  int type;
2954
  int severity;
2955
  int aux;
2956
  int lightoverride;
2957
  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, type ) && getParam( 2, severity ) &&
×
2958
          getParam( 3, aux ) && getParam( 4, lightoverride ) ) )
×
2959
  {
2960
    return new BError( "Invalid Parameter type" );
×
2961
  }
2962

2963
  WeatherRegion* weatherregion = gamestate.weatherdef->getregion( region_name_str->value() );
×
2964
  if ( weatherregion == nullptr )
×
2965
  {
2966
    return new BError( "Weather region not found" );
×
2967
  }
2968

2969
  SetRegionWeatherLevel( weatherregion, type, severity, aux, lightoverride );
×
2970

2971
  return new BLong( 1 );
×
2972
}
2973
BObjectImp* UOExecutorModule::mf_AssignRectToWeatherRegion()
×
2974
{
2975
  const String* region_name_str;
2976
  Pos2d nw, se;
×
2977
  Realms::Realm* realm;
2978

2979
  if ( !( getStringParam( 0, region_name_str ) && getRealmParam( 5, &realm ) &&
×
2980
          getPos2dParam( 1, 2, &nw, realm ) && getPos2dParam( 3, 4, &se, realm ) ) )
×
2981
  {
2982
    return new BError( "Invalid Parameter type" );
×
2983
  }
2984

2985
  bool res = gamestate.weatherdef->assign_zones_to_region( region_name_str->data(),
×
2986
                                                           Range2d( nw, se, nullptr ), realm );
×
2987
  if ( res )
×
2988
    return new BLong( 1 );
×
NEW
2989
  return new BError( "Weather region not found" );
×
2990
}
2991

2992
BObjectImp* UOExecutorModule::mf_Distance()
×
2993
{
2994
  UObject* obj1;
2995
  UObject* obj2;
2996
  if ( getUObjectParam( 0, obj1 ) && getUObjectParam( 1, obj2 ) )
×
2997
    return new BLong( obj1->distance_to( obj2->toplevel_pos() ) );
×
2998
  return new BError( "Invalid parameter type" );
×
2999
}
3000

3001
BObjectImp* UOExecutorModule::mf_DistanceEuclidean()
×
3002
{
3003
  UObject* obj1;
3004
  UObject* obj2;
3005
  if ( getUObjectParam( 0, obj1 ) && getUObjectParam( 1, obj2 ) )
×
3006
  {
3007
    const UObject* tobj1 = obj1->toplevel_owner();
×
3008
    const UObject* tobj2 = obj2->toplevel_owner();
×
3009
    return new Double( sqrt( pow( (double)( tobj1->x() - tobj2->x() ), 2 ) +
×
3010
                             pow( (double)( tobj1->y() - tobj2->y() ), 2 ) ) );
×
3011
  }
3012

NEW
3013
  return new BError( "Invalid parameter type" );
×
3014
}
3015

3016
BObjectImp* UOExecutorModule::mf_CoordinateDistance()
×
3017
{
3018
  Pos2d pos1, pos2;
×
3019
  if ( !getPos2dParam( 0, 1, &pos1 ) || !getPos2dParam( 2, 3, &pos2 ) )
×
3020
    return new BError( "Invalid parameter type" );
×
3021
  return new BLong( pos1.pol_distance( pos2 ) );
×
3022
}
3023

3024
BObjectImp* UOExecutorModule::mf_CoordinateDistanceEuclidean()
×
3025
{
3026
  unsigned short x1, y1, x2, y2;
3027
  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
×
3028
  {
3029
    return new BError( "Invalid parameter type" );
×
3030
  }
3031
  return new Double( sqrt( pow( (double)( x1 - x2 ), 2 ) + pow( (double)( y1 - y2 ), 2 ) ) );
×
3032
}
3033

3034
BObjectImp* UOExecutorModule::mf_GetCoordsInLine()
×
3035
{
3036
  int x1, y1, x2, y2;
3037
  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
×
3038
  {
3039
    return new BError( "Invalid parameter type" );
×
3040
  }
NEW
3041
  if ( x1 == x2 && y1 == y2 )
×
3042
  {  // Same exact coordinates ... just give them the coordinate back!
3043
    ObjArray* coords = new ObjArray;
×
3044
    BStruct* point = new BStruct;
×
3045
    point->addMember( "x", new BLong( x1 ) );
×
3046
    point->addMember( "y", new BLong( y1 ) );
×
3047
    coords->addElement( point );
×
3048
    return coords;
×
3049
  }
3050

3051
  double dx = abs( x2 - x1 ) + 0.5;
×
3052
  double dy = abs( y2 - y1 ) + 0.5;
×
3053
  int vx = 0, vy = 0;
×
3054

3055
  if ( x2 > x1 )
×
3056
    vx = 1;
×
3057
  else if ( x2 < x1 )
×
3058
    vx = -1;
×
3059
  if ( y2 > y1 )
×
3060
    vy = 1;
×
3061
  else if ( y2 < y1 )
×
3062
    vy = -1;
×
3063

3064
  std::unique_ptr<ObjArray> coords( new ObjArray );
×
3065
  if ( dx >= dy )
×
3066
  {
3067
    dy = dy / dx;
×
3068

3069
    for ( int c = 0; c <= dx; c++ )
×
3070
    {
3071
      int point_x = x1 + ( c * vx );
×
3072

3073
      double float_y = double( c ) * double( vy ) * dy;
×
3074
      if ( float_y - floor( float_y ) >= 0.5 )
×
3075
        float_y = ceil( float_y );
×
3076
      int point_y = int( float_y ) + y1;
×
3077

3078
      std::unique_ptr<BStruct> point( new BStruct );
×
3079
      point->addMember( "x", new BLong( point_x ) );
×
3080
      point->addMember( "y", new BLong( point_y ) );
×
3081
      coords->addElement( point.release() );
×
3082
    }
×
3083
  }
3084
  else
3085
  {
3086
    dx = dx / dy;
×
3087
    for ( int c = 0; c <= dy; c++ )
×
3088
    {
3089
      int point_y = y1 + ( c * vy );
×
3090

3091
      double float_x = double( c ) * double( vx ) * dx;
×
3092
      if ( float_x - floor( float_x ) >= 0.5 )
×
3093
        float_x = ceil( float_x );
×
3094
      int point_x = int( float_x ) + x1;
×
3095

3096
      std::unique_ptr<BStruct> point( new BStruct );
×
3097
      point->addMember( "x", new BLong( point_x ) );
×
3098
      point->addMember( "y", new BLong( point_y ) );
×
3099
      coords->addElement( point.release() );
×
3100
    }
×
3101
  }
3102
  return coords.release();
×
3103
}
×
3104

3105
BObjectImp* UOExecutorModule::mf_GetFacing()
×
3106
{
3107
  unsigned short from_x, from_y, to_x, to_y;
3108
  if ( !( getParam( 0, from_x ) && getParam( 1, from_y ) && getParam( 2, to_x ) &&
×
3109
          getParam( 3, to_y ) ) )
×
3110
  {
3111
    return new BError( "Invalid parameter type" );
×
3112
  }
3113

3114
  double x = to_x - from_x;
×
3115
  double y = to_y - from_y;
×
3116
  double pi = acos( double( -1 ) );
×
3117
  double r = sqrt( x * x + y * y );
×
3118

3119
  double angle = ( ( acos( x / r ) * 180.0 ) / pi );
×
3120

3121
  double y_check = ( ( asin( y / r ) * 180.0 ) / pi );
×
3122
  if ( y_check < 0 )
×
3123
  {
3124
    angle = 360 - angle;
×
3125
  }
3126

3127
  unsigned short facing = ( ( short( angle / 40 ) + 10 ) % 8 );
×
3128

3129
  return new BLong( facing );
×
3130
}
3131

3132
// FIXME : Should we do an Orphan check here as well? Ugh.
3133
void true_extricate( Item* item )
29✔
3134
{
3135
  send_remove_object_to_inrange( item );
29✔
3136
  if ( item->container != nullptr )
29✔
3137
  {
3138
    item->extricate();
1✔
3139
  }
3140
  else
3141
  {
3142
    remove_item_from_world( item );
28✔
3143
  }
3144
}
29✔
3145

3146
BObjectImp* UOExecutorModule::mf_MoveItemToContainer()
7✔
3147
{
3148
  Item* item;
3149
  Item* cont_item;
3150
  int px;
3151
  int py;
3152
  int add_to_existing_stack;
3153
  if ( !( getItemParam( 0, item ) && getItemParam( 1, cont_item ) && getParam( 2, px, -1, 65535 ) &&
14✔
3154
          getParam( 3, py, -1, 65535 ) && getParam( 4, add_to_existing_stack, 0, 2 ) ) )
7✔
3155
  {
3156
    return new BError( "Invalid parameter type" );
×
3157
  }
3158

3159
  ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
7✔
3160
  if ( !item->movable() )
7✔
3161
  {
3162
    Character* chr = controller_.get();
×
3163
    if ( chr == nullptr || !chr->can_move( item ) )
×
3164
      return new BError( "That is immobile" );
×
3165
  }
3166
  if ( item->inuse() && !is_reserved_to_me( item ) )
7✔
3167
  {
3168
    return new BError( "That item is being used." );
×
3169
  }
3170

3171

3172
  if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
7✔
3173
  {
3174
    return new BError( "Non-container selected as target" );
×
3175
  }
3176
  UContainer* cont = static_cast<UContainer*>( cont_item );
7✔
3177

3178
  if ( cont->serial == item->serial )
7✔
3179
  {
3180
    return new BError( "Can't put a container into itself" );
×
3181
  }
3182
  if ( is_a_parent( cont, item->serial ) )
7✔
3183
  {
3184
    return new BError( "Can't put a container into an item in itself" );
×
3185
  }
3186
  if ( !cont->can_add( *item ) )
7✔
3187
  {
3188
    return new BError( "Container is too full to add that" );
×
3189
  }
3190
  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3191
  Character* chr_owner = cont->GetCharacterOwner();
7✔
3192
  if ( chr_owner == nullptr )
7✔
3193
    if ( controller_.get() != nullptr )
6✔
3194
      chr_owner = controller_.get();
×
3195

3196
  // daved changed order 1/26/3 check canX scripts before onX scripts.
3197
  UContainer* oldcont = item->container;
7✔
3198
  Item* existing_stack = nullptr;
7✔
3199

3200
  if ( ( oldcont != nullptr ) &&
8✔
3201
       ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
1✔
3202
    return new BError( "Could not remove item from its container." );
×
3203
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3204
  {
3205
    return new BError( "Item was destroyed in CanRemove script" );
×
3206
  }
3207

3208
  if ( add_to_existing_stack )
7✔
3209
  {
3210
    existing_stack = cont->find_addable_stack( item );
×
3211
    if ( existing_stack != nullptr )
×
3212
    {
3213
      if ( !cont->can_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack,
×
3214
                                             item->getamount(), item ) )
×
3215
        return new BError( "Could not add to existing stack" );
×
3216
    }
3217
    else if ( add_to_existing_stack == 2 )
×
3218
      add_to_existing_stack = 0;
×
3219
    else
3220
      return new BError( "There is no existing stack" );
×
3221
  }
3222

3223
  if ( !add_to_existing_stack )
7✔
3224
    if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item ) )
7✔
3225
      return new BError( "Could not insert item into container." );
×
3226

3227
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3228
  {
3229
    return new BError( "Item was destroyed in CanInsert Script" );
×
3230
  }
3231

3232
  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
7✔
3233
    return new BError( "Item cannot be unequipped" );
×
3234
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3235
  {
3236
    return new BError( "Item was destroyed in Equip Script" );
×
3237
  }
3238

3239
  if ( oldcont != nullptr )
7✔
3240
  {
3241
    oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
1✔
3242
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
1✔
3243
    {
3244
      return new BError( "Item was destroyed in OnRemove script" );
×
3245
    }
3246
  }
3247

3248
  if ( !add_to_existing_stack )
7✔
3249
  {
3250
    u8 slotIndex = item->slot_index();
7✔
3251
    if ( !cont->can_add_to_slot( slotIndex ) )
7✔
3252
    {
3253
      item->destroy();
×
3254
      return new BError( "No slots available in new container" );
×
3255
    }
3256
    if ( !item->slot_index( slotIndex ) )
7✔
3257
    {
3258
      item->destroy();
×
3259
      return new BError( "Couldn't set slot index on item" );
×
3260
    }
3261

3262
    Core::Pos2d cntpos;
7✔
3263
    if ( px < 0 || py < 0 )
7✔
3264
      cntpos = cont->get_random_location();
7✔
3265
    else
3266
    {
3267
      cntpos.x( static_cast<u16>( px ) ).y( static_cast<u16>( py ) );
×
3268
      if ( !cont->is_legal_posn( cntpos ) )
×
3269
        cntpos = cont->get_random_location();
×
3270
    }
3271

3272
    true_extricate( item );
7✔
3273

3274
    cont->add( item, cntpos );
7✔
3275
    update_item_to_inrange( item );
7✔
3276
    // DAVE added this 11/17: if in a Character's pack, update weight.
3277
    UpdateCharacterWeight( item );
7✔
3278

3279
    cont->on_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item );
7✔
3280
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
7✔
3281
    {
3282
      return new BError( "Item was destroyed in OnInsert script" );
×
3283
    }
3284
  }
3285
  else
3286
  {
3287
    u16 amount = item->getamount();
×
3288
    true_extricate( item );
×
3289
    existing_stack->add_to_self( item );
×
3290
    update_item_to_inrange( existing_stack );
×
3291
    UpdateCharacterWeight( existing_stack );
×
3292

3293
    cont->on_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack, amount );
×
3294
  }
3295

3296
  return new BLong( 1 );
7✔
3297
}
7✔
3298

3299

3300
BObjectImp* UOExecutorModule::mf_MoveItemToSecureTradeWin()
×
3301
{
3302
  Item* item;
3303
  Character* chr;
3304
  if ( !( getItemParam( 0, item ) && getCharacterParam( 1, chr ) ) )
×
3305
  {
3306
    return new BError( "Invalid parameter type" );
×
3307
  }
3308

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

3321
  // daved changed order 1/26/3 check canX scripts before onX scripts.
3322
  UContainer* oldcont = item->container;
×
3323

3324
  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3325
  Character* chr_owner = nullptr;
×
3326
  if ( oldcont != nullptr )
×
3327
    chr_owner = oldcont->GetCharacterOwner();
×
3328
  if ( chr_owner == nullptr )
×
3329
    if ( controller_.get() != nullptr )
×
3330
      chr_owner = controller_.get();
×
3331

3332
  if ( ( oldcont != nullptr ) &&
×
3333
       ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
×
3334
    return new BError( "Could not remove item from its container." );
×
3335
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3336
  {
3337
    return new BError( "Item was destroyed in CanRemove script" );
×
3338
  }
3339

3340
  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
×
3341
    return new BError( "Item cannot be unequipped" );
×
3342
  if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3343
  {
3344
    return new BError( "Item was destroyed in Equip Script" );
×
3345
  }
3346

3347
  if ( oldcont != nullptr )
×
3348
  {
3349
    oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
×
3350
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
×
3351
    {
3352
      return new BError( "Item was destroyed in OnRemove script" );
×
3353
    }
3354
  }
3355

3356
  true_extricate( item );
×
3357

3358
  return place_item_in_secure_trade_container( chr->client, item );
×
3359
}
×
3360

3361
BObjectImp* UOExecutorModule::mf_EquipItem()
22✔
3362
{
3363
  Character* chr;
3364
  Item* item;
3365
  if ( getCharacterParam( 0, chr ) && getItemParam( 1, item ) )
22✔
3366
  {
3367
    ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
22✔
3368
    if ( !item->movable() )
22✔
3369
    {
3370
      Character* _chr = controller_.get();
×
3371
      if ( _chr == nullptr || !_chr->can_move( item ) )
×
3372
        return new BError( "That is immobile" );
×
3373
    }
3374

3375
    if ( item->inuse() && !is_reserved_to_me( item ) )
22✔
3376
    {
3377
      return new BError( "That item is being used." );
×
3378
    }
3379

3380
    if ( !chr->equippable( item ) || !item->check_equiptest_scripts( chr ) )
22✔
3381
    {
3382
      return new BError( "That item is not equippable by that character" );
×
3383
    }
3384
    if ( item->orphan() )  // dave added 1/28/3, item might be destroyed in RTC script
22✔
3385
    {
3386
      return new BError( "Item was destroyed in EquipTest script" );
×
3387
    }
3388

3389
    item->layer = Plib::tilelayer( item->graphic );
22✔
3390

3391
    if ( item->has_equip_script() )
22✔
3392
    {
3393
      BObjectImp* res = item->run_equip_script( chr, false );
2✔
3394
      if ( !res->isTrue() )
2✔
3395
        return res;
×
3396
      BObject obj( res );
2✔
3397
    }
2✔
3398

3399

3400
    true_extricate( item );
22✔
3401

3402
    // at this point, 'item' is free - doesn't belong to the world, or a container.
3403
    chr->equip( item );
22✔
3404
    send_wornitem_to_inrange( chr, item );
22✔
3405

3406
    return new BLong( 1 );
22✔
3407
  }
22✔
3408

NEW
3409
  return new BError( "Invalid parameter type" );
×
3410
}
3411

3412
BObjectImp* UOExecutorModule::mf_RestartScript()
1✔
3413
{
3414
  UObject* obj;
3415
  if ( !getUObjectParam( 0, obj ) )
1✔
3416
    return new BError( "Invalid parameter" );
×
3417

3418
  if ( obj->script_isa( POLCLASS_NPC ) )
1✔
3419
  {
3420
    NPC* npc = static_cast<NPC*>( obj );
×
3421
    npc->restart_script();
×
3422
    return new BLong( 1 );
×
3423
  }
3424
  if ( obj->script_isa( POLCLASS_ITEM ) )
1✔
3425
  {
3426
    Item* item = static_cast<Item*>( obj );
1✔
3427
    item->stop_control_script();
1✔
3428
    return new BLong( item->start_control_script() );
1✔
3429
  }
3430

NEW
3431
  return new BError( "RestartScript only operates on NPCs and Items" );
×
3432
}
3433

3434

3435
BObjectImp* UOExecutorModule::mf_GetHarvestDifficulty()
×
3436
{
3437
  const String* resource;
3438
  unsigned short tiletype;
3439
  Pos2d pos;
×
3440
  Realms::Realm* realm;
3441
  if ( getStringParam( 0, resource ) && getRealmParam( 4, &realm ) && getPos2dParam( 1, 2, &pos ) &&
×
3442
       getParam( 3, tiletype ) )
×
3443
  {
3444
    return get_harvest_difficulty( resource->data(), Pos4d( pos, 0, realm ), tiletype );
×
3445
  }
3446

NEW
3447
  return new BError( "Invalid parameter" );
×
3448
}
3449

3450
BObjectImp* UOExecutorModule::mf_HarvestResource()
×
3451
{
3452
  Pos2d pos;
×
3453
  Realms::Realm* realm;
3454
  const String* resource;
3455
  int b;
3456
  int n;
3457

3458
  if ( getStringParam( 0, resource ) && getRealmParam( 5, &realm ) &&
×
3459
       getPos2dParam( 1, 2, &pos, realm ) && getParam( 3, b ) && getParam( 4, n ) )
×
3460
  {
3461
    if ( b <= 0 )
×
3462
      return new BError( "b must be >= 0" );
×
3463
    return harvest_resource( resource->data(), Pos4d( pos, 0, realm ), b, n );
×
3464
  }
3465

NEW
3466
  return new BError( "Invalid parameter" );
×
3467
}
3468

3469
BObjectImp* UOExecutorModule::mf_GetRegionName( /* objref */ )
×
3470
{
3471
  UObject* obj;
3472

3473
  if ( getUObjectParam( 0, obj ) )
×
3474
  {
3475
    JusticeRegion* justice_region;
3476
    if ( obj->isa( UOBJ_CLASS::CLASS_ITEM ) )
×
3477
      obj = obj->toplevel_owner();
×
3478

3479
    if ( obj->isa( UOBJ_CLASS::CLASS_CHARACTER ) )
×
3480
    {
3481
      Character* chr = static_cast<Character*>( obj );
×
3482

3483
      if ( chr->logged_in() )
×
3484
        justice_region = chr->client->gd->justice_region;
×
3485
      else
3486
        justice_region = gamestate.justicedef->getregion( chr->pos() );
×
3487
    }
3488
    else
3489
      justice_region = gamestate.justicedef->getregion( obj->pos() );
×
3490

3491
    if ( justice_region == nullptr )
×
3492
      return new BError( "No Region defined at this Location" );
×
NEW
3493
    return new String( justice_region->region_name() );
×
3494
  }
NEW
3495
  return new BError( "Invalid parameter" );
×
3496
}
3497

3498
BObjectImp* UOExecutorModule::mf_GetRegionNameAtLocation( /* x, y, realm */ )
×
3499
{
3500
  Pos2d pos;
×
3501
  Realms::Realm* realm;
3502
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos ) )
×
3503
  {
3504
    JusticeRegion* justice_region = gamestate.justicedef->getregion( Pos4d( pos, 0, realm ) );
×
3505
    if ( justice_region == nullptr )
×
3506
      return new BError( "No Region defined at this Location" );
×
NEW
3507
    return new String( justice_region->region_name() );
×
3508
  }
NEW
3509
  return new BError( "Invalid parameter" );
×
3510
}
3511

3512
BObjectImp* UOExecutorModule::mf_GetRegionString()
×
3513
{
3514
  const String* resource;
3515
  const String* propname;
3516
  Pos2d pos;
×
3517
  Realms::Realm* realm;
3518
  if ( getStringParam( 0, resource ) && getRealmParam( 4, &realm ) && getPos2dParam( 1, 2, &pos ) &&
×
3519
       getStringParam( 3, propname ) )
×
3520
  {
3521
    return get_region_string( resource->data(), Pos4d( pos, 0, realm ), propname->value() );
×
3522
  }
3523

NEW
3524
  return new BError( "Invalid parameter" );
×
3525
}
3526

3527
BObjectImp* UOExecutorModule::mf_GetRegionLightLevelAtLocation( /* x, y, realm */ )
×
3528
{
3529
  Pos2d pos;
×
3530
  Realms::Realm* realm;
3531

3532
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos ) )
×
3533
  {
3534
    LightRegion* light_region = gamestate.lightdef->getregion( Pos4d( pos, 0, realm ) );
×
3535
    int lightlevel;
3536
    if ( light_region != nullptr )
×
3537
      lightlevel = light_region->lightlevel;
×
3538
    else
3539
      lightlevel = settingsManager.ssopt.default_light_level;
×
3540
    return new BLong( lightlevel );
×
3541
  }
3542

NEW
3543
  return new BError( "Invalid parameter" );
×
3544
}
3545

3546

3547
BObjectImp* UOExecutorModule::mf_EquipFromTemplate()
1✔
3548
{
3549
  Character* chr;
3550
  const String* template_name;
3551
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, template_name ) )
1✔
3552
  {
3553
    return equip_from_template( chr, template_name->value() );
1✔
3554
  }
3555

NEW
3556
  return new BError( "Invalid parameter" );
×
3557
}
3558

3559
// FIXME: Use a PrivUpdater here
3560
BObjectImp* UOExecutorModule::mf_GrantPrivilege()
4✔
3561
{
3562
  Character* chr;
3563
  const String* privstr;
3564
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, privstr ) )
4✔
3565
  {
3566
    chr->grant_privilege( privstr->data() );
4✔
3567
    return new BLong( 1 );
4✔
3568
  }
3569

NEW
3570
  return new BError( "Invalid parameter" );
×
3571
}
3572

3573
// FIXME: Use a PrivUpdater here
3574
BObjectImp* UOExecutorModule::mf_RevokePrivilege()
1✔
3575
{
3576
  Character* chr;
3577
  const String* privstr;
3578
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, privstr ) )
1✔
3579
  {
3580
    chr->revoke_privilege( privstr->data() );
1✔
3581
    return new BLong( 1 );
1✔
3582
  }
3583

NEW
3584
  return new BError( "Invalid parameter" );
×
3585
}
3586

3587
BObjectImp* UOExecutorModule::mf_ReadGameClock()
14✔
3588
{
3589
  return new BLong( read_gameclock() );
14✔
3590
}
3591

3592
unsigned char decode_xdigit( unsigned char ch )
×
3593
{
3594
  if ( ch >= '0' && ch <= '9' )
×
3595
    ch -= '0';
×
3596
  else if ( ch >= 'A' && ch <= 'F' )
×
3597
    ch = ch - 'A' + 0xa;
×
3598
  else if ( ch >= 'a' && ch <= 'f' )
×
3599
    ch = ch - 'a' + 0xa;
×
3600

3601
  return ch;
×
3602
}
3603

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

NEW
3635
      return new BError( "No client attached" );
×
3636
    }
NEW
3637
    if ( client != nullptr )
×
3638
    {
3639
      if ( client->isConnected() )
×
3640
      {
3641
        buffer.Send( client );
×
3642
        return new BLong( 1 );
×
3643
      }
3644

NEW
3645
      return new BError( "Client is disconnected" );
×
3646
    }
3647

UNCOV
3648
    return new BError( "Invalid parameter type" );
×
UNCOV
3649
  }
×
3650

NEW
3651
  return new BError( "Invalid parameter type" );
×
3652
}
3653

3654
BObjectImp* UOExecutorModule::mf_SendQuestArrow()
×
3655
{
3656
  Character* chr;
3657
  int x, y;
3658
  u32 arrowid = 0;
×
3659

3660
  if ( getCharacterParam( 0, chr ) && getParam( 1, x, -1, 1000000 ) &&
×
3661
       getParam( 2, y, -1, 1000000 ) )  // max values checked below
×
3662
  {
3663
    if ( !chr->has_active_client() )
×
3664
      return new BError( "No client attached" );
×
3665
    {
3666
      int arrow_id;
3667
      if ( exec.getParam( 3, arrow_id ) )
×
3668
      {
3669
        if ( arrow_id < 1 )
×
3670
          return new BError( "ArrowID out of range" );
×
3671
        arrowid = static_cast<u32>( arrow_id );
×
3672
      }
3673
      else
3674
        arrowid = uoexec().pid();
×
3675
    }
3676
    bool usesNewPktSize = ( chr->client->ClientType & Network::CLIENTTYPE_7090 ) > 0;
×
3677

3678
    Network::PktHelper::PacketOut<Network::PktOut_BA> msg;
×
3679
    if ( x == -1 && y == -1 )
×
3680
    {
3681
      msg->Write<u8>( PKTOUT_BA_ARROW_OFF );
×
3682
      msg->offset += 4;  // u16 x_tgt,y_tgt
×
3683
      if ( usesNewPktSize )
×
3684
      {
3685
        if ( !arrowid )
×
3686
        {
3687
          return new BError( "ArrowID must be supplied for cancelation." );
×
3688
        }
3689

NEW
3690
        msg->Write<u32>( static_cast<u32>( arrowid & 0xFFFFFFFF ) );
×
3691
      }
3692
    }
3693
    else
3694
    {
3695
      auto pos = Core::Pos2d( Clib::clamp_convert<u16>( x ), Clib::clamp_convert<u16>( y ) );
×
3696
      if ( !chr->realm()->valid( pos ) )
×
3697
        return new BError( "Invalid Coordinates for Realm" );
×
3698
      msg->Write<u8>( PKTOUT_BA_ARROW_ON );
×
3699
      msg->WriteFlipped<u16>( pos.x() );
×
3700
      msg->WriteFlipped<u16>( pos.y() );
×
3701
      if ( usesNewPktSize )
×
3702
        msg->Write<u32>( static_cast<u32>( arrowid & 0xFFFFFFFF ) );
×
3703
    }
3704
    msg.Send( chr->client );
×
3705
    return new BLong( arrowid );
×
3706
  }
×
3707

NEW
3708
  return new BError( "Invalid parameter" );
×
3709
}
3710

3711
BObjectImp* UOExecutorModule::mf_ConsumeReagents()
×
3712
{
3713
  Character* chr;
3714
  int spellid;
3715
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) )
×
3716
  {
3717
    if ( !VALID_SPELL_ID( spellid ) )
×
3718
    {
3719
      return new BError( "Spell ID out of range" );
×
3720
    }
3721
    USpell* spell = gamestate.spells[spellid];
×
3722
    if ( spell == nullptr )
×
3723
    {
3724
      return new BError( "No such spell" );
×
3725
    }
3726

3727
    return new BLong( spell->consume_reagents( chr ) ? 1 : 0 );
×
3728
  }
3729

NEW
3730
  return new BError( "Invalid parameter" );
×
3731
}
3732

3733
BObjectImp* UOExecutorModule::mf_StartSpellEffect()
×
3734
{
3735
  Character* chr;
3736
  int spellid;
3737
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) )
×
3738
  {
3739
    if ( !VALID_SPELL_ID( spellid ) )
×
3740
    {
3741
      return new BError( "Spell ID out of range" );
×
3742
    }
3743
    USpell* spell = gamestate.spells[spellid];
×
3744
    if ( spell == nullptr )
×
3745
    {
3746
      return new BError( "No such spell" );
×
3747
    }
3748

3749
    spell->cast( chr );
×
3750
    return new BLong( 1 );
×
3751
  }
3752

NEW
3753
  return new BError( "Invalid parameter" );
×
3754
}
3755
BObjectImp* UOExecutorModule::mf_GetSpellDifficulty()
×
3756
{
3757
  int spellid;
3758
  if ( getParam( 0, spellid ) )
×
3759
  {
3760
    if ( !VALID_SPELL_ID( spellid ) )
×
3761
    {
3762
      return new BError( "Spell ID out of range" );
×
3763
    }
3764
    USpell* spell = gamestate.spells[spellid];
×
3765
    if ( spell == nullptr )
×
3766
    {
3767
      return new BError( "No such spell" );
×
3768
    }
3769

3770
    return new BLong( spell->difficulty() );
×
3771
  }
3772

NEW
3773
  return new BError( "Invalid parameter" );
×
3774
}
3775
BObjectImp* UOExecutorModule::mf_SpeakPowerWords()
×
3776
{
3777
  Character* chr;
3778
  int spellid;
3779
  unsigned short font;
3780
  unsigned short color;
3781

3782
  if ( getCharacterParam( 0, chr ) && getParam( 1, spellid ) && getParam( 2, font ) &&
×
3783
       getParam( 3, color ) )
×
3784
  {
3785
    if ( !VALID_SPELL_ID( spellid ) )
×
3786
    {
3787
      return new BError( "Spell ID out of range" );
×
3788
    }
3789
    USpell* spell = gamestate.spells[spellid];
×
3790
    if ( spell == nullptr )
×
3791
    {
3792
      return new BError( "No such spell" );
×
3793
    }
3794

3795
    spell->speak_power_words( chr, font, color );
×
3796

3797
    return new BLong( 1 );
×
3798
  }
3799

NEW
3800
  return new BError( "Invalid parameter" );
×
3801
}
3802

3803
BObjectImp* UOExecutorModule::mf_ListEquippedItems()
×
3804
{
3805
  Character* chr;
3806
  if ( getCharacterParam( 0, chr ) )
×
3807
  {
3808
    std::unique_ptr<ObjArray> arr( new ObjArray );
×
3809
    for ( int layer = LAYER_EQUIP__LOWEST; layer <= LAYER_EQUIP__HIGHEST; ++layer )
×
3810
    {
3811
      Item* item = chr->wornitem( layer );
×
3812
      if ( item != nullptr )
×
3813
      {
3814
        arr->addElement( new EItemRefObjImp( item ) );
×
3815
      }
3816
    }
3817
    return arr.release();
×
3818
  }
×
3819

NEW
3820
  return new BError( "Invalid parameter" );
×
3821
}
3822

3823
BObjectImp* UOExecutorModule::mf_GetEquipmentByLayer()
3✔
3824
{
3825
  Character* chr;
3826
  int layer;
3827
  if ( getCharacterParam( 0, chr ) && getParam( 1, layer ) )
3✔
3828
  {
3829
    if ( layer < LOWEST_LAYER || layer > HIGHEST_LAYER )
3✔
3830
    {
3831
      return new BError( "Invalid layer" );
×
3832
    }
3833

3834
    Item* item = chr->wornitem( layer );
3✔
3835
    if ( item == nullptr )
3✔
3836
    {
3837
      return new BError( "Nothing equipped on that layer." );
1✔
3838
    }
3839

3840
    return new EItemRefObjImp( item );
2✔
3841
  }
3842

NEW
3843
  return new BError( "Invalid parameter" );
×
3844
}
3845

3846
BObjectImp* UOExecutorModule::mf_DisconnectClient()
×
3847
{
3848
  Character* chr;
3849
  Network::Client* client;
3850

3851
  if ( getCharacterOrClientParam( 0, chr, client ) )
×
3852
  {
3853
    if ( chr != nullptr )
×
3854
    {
3855
      if ( !chr->has_active_client() )
×
3856
        return new BError( "No client attached" );
×
3857

3858
      client = chr->client;
×
3859
    }
3860

3861
    if ( client != nullptr )
×
3862
    {
3863
      if ( client->isConnected() )
×
3864
      {
3865
        client->Disconnect();
×
3866
        return new BLong( 1 );
×
3867
      }
NEW
3868
      return new BError( "Client is disconnected" );
×
3869
    }
UNCOV
3870
    return new BError( "Invalid parameter type" );
×
3871
  }
3872

NEW
3873
  return new BError( "Invalid parameter type" );
×
3874
}
3875

3876
BObjectImp* UOExecutorModule::mf_GetMapInfo()
34✔
3877
{
3878
  Core::Pos2d pos;
34✔
3879
  Realms::Realm* realm;
3880

3881
  // note that this uses WORLD_MAX_X, not WORLD_X,
3882
  // because we can't read the outermost edge of the map
3883
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos, realm ) )
34✔
3884
  {
3885
    Plib::MAPTILE_CELL cell = realm->getmaptile( pos );
34✔
3886
    std::unique_ptr<BStruct> result( new BStruct );
34✔
3887
    result->addMember( "z", new BLong( cell.z ) );
34✔
3888
    result->addMember( "landtile", new BLong( cell.landtile ) );
34✔
3889

3890
    return result.release();
34✔
3891
  }
34✔
3892

NEW
3893
  return new BError( "Invalid parameter" );
×
3894
}
3895
BObjectImp* UOExecutorModule::mf_GetWorldHeight()
×
3896
{
3897
  Core::Pos2d pos;
×
3898
  Realms::Realm* realm;
3899
  if ( getRealmParam( 2, &realm ) && getPos2dParam( 0, 1, &pos, realm ) )
×
3900
  {
3901
    short z = -255;
×
3902
    if ( realm->lowest_standheight( pos, &z ) )
×
3903
      return new BLong( z );
×
NEW
3904
    return new BError( "Nowhere" );
×
3905
  }
3906

NEW
3907
  return new BError( "Invalid parameter" );
×
3908
}
3909

3910
BObjectImp* UOExecutorModule::mf_GetObjtypeByName()
×
3911
{
3912
  const String* namestr;
3913
  if ( getStringParam( 0, namestr ) )
×
3914
  {
3915
    unsigned int objtype = get_objtype_byname( namestr->data() );
×
3916
    if ( objtype != 0 )
×
3917
      return new BLong( objtype );
×
NEW
3918
    return new BError( "No objtype by that name" );
×
3919
  }
3920

NEW
3921
  return new BError( "Invalid parameter" );
×
3922
}
3923

3924
BObjectImp* UOExecutorModule::mf_SendEvent()
×
3925
{
3926
  Character* chr;
3927
  if ( getCharacterParam( 0, chr ) )
×
3928
  {
3929
    BObjectImp* event = exec.getParamImp( 1 );
×
3930
    if ( event != nullptr )
×
3931
    {
3932
      if ( chr->isa( UOBJ_CLASS::CLASS_NPC ) )
×
3933
      {
3934
        NPC* npc = static_cast<NPC*>( chr );
×
3935
        // event->add_ref(); // UNTESTED
3936
        return npc->send_event_script( event->copy() );
×
3937
      }
3938

NEW
3939
      return new BError( "That mobile is not an NPC" );
×
3940
    }
3941

NEW
3942
    return new BError( "Huh?  Not enough parameters" );
×
3943
  }
3944

NEW
3945
  return new BError( "Invalid parameter" );
×
3946
}
3947

3948
BObjectImp* UOExecutorModule::mf_DestroyMulti()
29✔
3949
{
3950
  Multi::UMulti* multi;
3951
  if ( getMultiParam( 0, multi ) )
29✔
3952
  {
3953
    const ItemDesc& id = find_itemdesc( multi->objtype_ );
29✔
3954
    if ( !id.destroy_script.empty() )
29✔
3955
    {
3956
      BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( multi ) );
×
3957
      if ( !res->isTrue() )
×
3958
      {  // destruction is okay
3959
        return res;
×
3960
      }
3961
    }
3962

3963
    Multi::UBoat* boat = multi->as_boat();
29✔
3964
    if ( boat != nullptr )
29✔
3965
    {
3966
      return Multi::destroy_boat( boat );
16✔
3967
    }
3968
    Multi::UHouse* house = multi->as_house();
13✔
3969
    if ( house != nullptr )
13✔
3970
    {
3971
      return Multi::destroy_house( house );
13✔
3972
    }
3973
    return new BError( "WTF!? Don't know what kind of multi that is!" );
×
3974
  }
3975

NEW
3976
  return new BError( "Invalid parameter type" );
×
3977
}
3978

3979

3980
BObjectImp* UOExecutorModule::mf_GetMultiDimensions()
×
3981
{
3982
  u16 multiid;
3983
  if ( getParam( 0, multiid ) )
×
3984
  {
3985
    if ( !Multi::MultiDefByMultiIDExists( multiid ) )
×
3986
      return new BError( "MultiID not found" );
×
3987

3988
    const Multi::MultiDef& md = *Multi::MultiDefByMultiID( multiid );
×
3989
    std::unique_ptr<BStruct> ret( new BStruct );
×
3990
    ret->addMember( "xmin", new BLong( md.minrxyz.x() ) );
×
3991
    ret->addMember( "xmax", new BLong( md.maxrxyz.x() ) );
×
3992
    ret->addMember( "ymin", new BLong( md.minrxyz.y() ) );
×
3993
    ret->addMember( "ymax", new BLong( md.maxrxyz.y() ) );
×
3994
    return ret.release();
×
3995
  }
×
NEW
3996
  return new BError( "Invalid parameter" );
×
3997
}
3998

3999
BObjectImp* UOExecutorModule::mf_SetScriptController()
×
4000
{
4001
  Character* old_controller = controller_.get();
×
4002
  BObjectImp* param0 = getParamImp( 0 );
×
4003
  bool handled = false;
×
4004

4005
  if ( auto* lng = impptrIf<BLong>( param0 ) )
×
4006
  {
4007
    if ( lng->value() == 0 )
×
4008
    {
4009
      controller_.clear();
×
4010
      handled = true;
×
4011
    }
4012
  }
4013

4014
  if ( !handled )
×
4015
  {
4016
    Character* chr;
4017
    if ( getCharacterParam( 0, chr ) )
×
4018
      controller_.set( chr );
×
4019
    else
4020
      controller_.clear();
×
4021
  }
4022

4023
  if ( old_controller )
×
4024
    return new ECharacterRefObjImp( old_controller );
×
NEW
4025
  return new BLong( 0 );
×
4026
}
4027

4028
BObjectImp* UOExecutorModule::mf_GetStandingHeight()
6✔
4029
{
4030
  Core::Pos4d pos;
6✔
4031
  if ( getPos4dParam( 0, 1, 2, 3, &pos ) )
6✔
4032
  {
4033
    short newz;
4034
    Multi::UMulti* multi;
4035
    Item* walkon;
4036
    if ( pos.realm()->lowest_walkheight( pos.xy(), pos.z(), &newz, &multi, &walkon, true,
6✔
4037
                                         Plib::MOVEMODE_LAND ) )
4038
    {
4039
      std::unique_ptr<BStruct> arr( new BStruct );
6✔
4040
      arr->addMember( "z", new BLong( newz ) );
6✔
4041
      if ( multi != nullptr )
6✔
4042
        arr->addMember( "multi", new EMultiRefObjImp( multi ) );
6✔
4043
      return arr.release();
6✔
4044
    }
6✔
4045

NEW
4046
    return new BError( "Can't stand there" );
×
4047
  }
4048

NEW
4049
  return new BError( "Invalid parameter type" );
×
4050
}
4051

4052
BObjectImp* UOExecutorModule::mf_GetStandingLayers( /* x, y, flags, realm, includeitems */ )
×
4053
{
4054
  Core::Pos2d pos;
×
4055
  int flags;
4056
  Realms::Realm* realm;
4057
  int includeitems;
4058

4059
  if ( getRealmParam( 3, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, flags ) &&
×
4060
       getParam( 4, includeitems ) )
×
4061
  {
4062
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4063

4064
    Plib::MapShapeList mlist;
×
4065
    Core::ItemsVector ivec;
×
4066
    realm->readmultis( mlist, pos, flags );
×
4067
    realm->getmapshapes( mlist, pos, flags );
×
4068
    if ( includeitems )
×
4069
    {
4070
      realm->readdynamics( mlist, pos, ivec, false, flags );
×
4071
    }
4072

4073
    for ( unsigned i = 0; i < mlist.size(); ++i )
×
4074
    {
4075
      std::unique_ptr<BStruct> arr( new BStruct );
×
4076

4077
      if ( mlist[i].flags & ( Plib::FLAG::MOVELAND | Plib::FLAG::MOVESEA ) )
×
4078
        arr->addMember( "z", new BLong( mlist[i].z + mlist[i].height ) );
×
4079
      else
4080
        arr->addMember( "z", new BLong( mlist[i].z ) );
×
4081

4082
      arr->addMember( "height", new BLong( mlist[i].height ) );
×
4083
      arr->addMember( "flags", new BLong( mlist[i].flags ) );
×
4084
      newarr->addElement( arr.release() );
×
4085
    }
×
4086

4087
    return newarr.release();
×
4088
  }
×
NEW
4089
  return new BError( "Invalid parameter type" );
×
4090
}
4091

4092
BObjectImp*
4093
UOExecutorModule::mf_GetStandingCoordinates() /* x, y, radius, minz, maxz, realm := _DEFAULT_REALM,
1✔
4094
                                                 movemode := "L", doors_block = 0 */
4095
{
4096
  s16 r, minz, maxz;
4097
  int doors_block;
4098
  const String* movemodename;
4099
  Core::Pos2d pos;
1✔
4100
  Realms::Realm* realm;
4101

4102
  if ( !getRealmParam( 5, &realm ) )
1✔
4103
    return new BError( "Realm not found" );
×
4104

4105
  if ( !( getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, r ) && getParam( 3, minz ) &&
2✔
4106
          getParam( 4, maxz ) && getStringParam( 6, movemodename ) && getParam( 7, doors_block ) ) )
1✔
4107
  {
4108
    return new BError( "Invalid parameter type" );
×
4109
  }
4110

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

4113
  std::unique_ptr<ObjArray> result( new ObjArray );
1✔
4114

4115
  // Iterate through all tiles in range and populate the return array with valid standing locations
4116
  Core::Vec2d radius( r, r );
1✔
4117
  Core::Pos2d tl = pos - radius;
1✔
4118
  Core::Pos2d br = pos + radius;
1✔
4119
  Core::Range2d range( tl, br, realm );
1✔
4120
  for ( const auto& tile : range )
243✔
4121
  {
4122
    auto layers = realm->get_walkheights( tile, minz, maxz, movemode, doors_block );
121✔
4123
    for ( const auto& layer : layers )
154✔
4124
    {
4125
      std::unique_ptr<BStruct> height_struct( new BStruct );
33✔
4126
      auto z = std::get<0>( layer );
33✔
4127
      auto multi = std::get<1>( layer );
33✔
4128

4129
      // Figure out which members to stick in the struct -- we only add multi it exists
4130
      height_struct->addMember( "x", new BLong( tile.x() ) );
33✔
4131
      height_struct->addMember( "y", new BLong( tile.y() ) );
33✔
4132
      height_struct->addMember( "z", new BLong( z ) );
33✔
4133
      if ( multi != nullptr )
33✔
4134
      {
4135
        height_struct->addMember( "multi", new EMultiRefObjImp( multi ) );
×
4136
      }
4137

4138
      // Add struct to the return array
4139
      result->addElement( height_struct.release() );
33✔
4140
    }
33✔
4141
  }
121✔
4142

4143
  return result.release();
1✔
4144
}
1✔
4145

4146
BObjectImp* UOExecutorModule::mf_ReserveItem()
3✔
4147
{
4148
  Item* item;
4149
  if ( getItemParam( 0, item ) )
3✔
4150
  {
4151
    if ( item->inuse() )
3✔
4152
    {
4153
      if ( is_reserved_to_me( item ) )
2✔
4154
        return new BLong( 2 );
2✔
NEW
4155
      return new BError( "That item is already being used." );
×
4156
    }
4157
    item->inuse( true );
1✔
4158
    reserved_items_.emplace_back( item );
1✔
4159
    return new BLong( 1 );
1✔
4160
  }
4161

NEW
4162
  return new BError( "Invalid parameter" );
×
4163
}
4164

4165
BObjectImp* UOExecutorModule::mf_ReleaseItem()
3✔
4166
{
4167
  Item* item;
4168
  if ( getItemParam( 0, item ) )
3✔
4169
  {
4170
    if ( item->inuse() )
3✔
4171
    {
4172
      for ( unsigned i = 0; i < reserved_items_.size(); ++i )
3✔
4173
      {
4174
        if ( reserved_items_[i].get() == item )
3✔
4175
        {
4176
          item->inuse( false );
3✔
4177
          reserved_items_[i] = reserved_items_.back();
3✔
4178
          reserved_items_.pop_back();
3✔
4179
          return new BLong( 1 );
3✔
4180
        }
4181
      }
4182
      return new BError( "That item is not reserved by this script." );
×
4183
    }
4184

NEW
4185
    return new BError( "That item is not reserved." );
×
4186
  }
4187

NEW
4188
  return new BError( "Invalid parameter" );
×
4189
}
4190

4191

4192
BObjectImp* UOExecutorModule::mf_SendSkillWindow()
×
4193
{
4194
  Character *towhom, *forwhom;
4195
  if ( getCharacterParam( 0, towhom ) && getCharacterParam( 1, forwhom ) )
×
4196
  {
4197
    if ( towhom->has_active_client() )
×
4198
    {
4199
      send_skillmsg( towhom->client, forwhom );
×
4200
      return new BLong( 1 );
×
4201
    }
4202

NEW
4203
    return new BError( "No client attached" );
×
4204
  }
4205

NEW
4206
  return new BError( "Invalid parameter type" );
×
4207
}
4208

4209

4210
BObjectImp* UOExecutorModule::mf_OpenPaperdoll()
×
4211
{
4212
  Character *towhom, *forwhom;
4213
  if ( getCharacterParam( 0, towhom ) && getCharacterParam( 1, forwhom ) )
×
4214
  {
4215
    if ( towhom->has_active_client() )
×
4216
    {
4217
      send_paperdoll( towhom->client, forwhom );
×
4218
      return new BLong( 1 );
×
4219
    }
4220

NEW
4221
    return new BError( "No client attached" );
×
4222
  }
4223

NEW
4224
  return new BError( "Invalid parameter type" );
×
4225
}
4226

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

4231
BObjectImp* UOExecutorModule::mf_ConsumeSubstance()
×
4232
{
4233
  Item* cont_item;
4234
  unsigned int objtype;
4235
  int amount;
4236
  if ( getItemParam( 0, cont_item ) && getObjtypeParam( 1, objtype ) && getParam( 2, amount ) )
×
4237
  {
4238
    if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
4239
      return new BError( "That is not a container" );
×
4240
    if ( amount < 0 )
×
4241
      return new BError( "Amount cannot be negative" );
×
4242

4243
    UContainer* cont = static_cast<UContainer*>( cont_item );
×
4244
    int amthave = cont->find_sumof_objtype_noninuse( objtype );
×
4245
    if ( amthave < amount )
×
4246
      return new BError( "Not enough of that substance in container" );
×
4247

4248
    cont->consume_sumof_objtype_noninuse( objtype, amount );
×
4249

4250
    return new BLong( 1 );
×
4251
  }
4252

NEW
4253
  return new BError( "Invalid parameter type" );
×
4254
}
4255

4256
bool UOExecutorModule::is_reserved_to_me( Item* item )
2✔
4257
{
4258
  for ( unsigned i = 0; i < reserved_items_.size(); ++i )
2✔
4259
  {
4260
    if ( reserved_items_[i].get() == item )
2✔
4261
      return true;
2✔
4262
  }
4263
  return false;
×
4264
}
4265

4266
BObjectImp* UOExecutorModule::mf_Shutdown()
2✔
4267
{
4268
  int exit_code = 0;
2✔
4269

4270
  if ( exec.hasParams( 1 ) )
2✔
4271
  {
4272
    getParam( 0, exit_code );
2✔
4273
  }
4274

4275
  Clib::signal_exit( exit_code );
2✔
4276
#ifndef _WIN32
4277
  // the catch_signals_thread (actually main) sits with sigwait(),
4278
  // so it won't wake up except by being signalled.
4279
  signal_catch_thread();
2✔
4280
#endif
4281
  return new BLong( 1 );
2✔
4282
}
4283

4284

4285
BObjectImp* UOExecutorModule::mf_GetCommandHelp()
×
4286
{
4287
  Character* chr;
4288
  const String* cmd;
4289
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, cmd ) )
×
4290
  {
4291
    std::string help = get_textcmd_help( chr, cmd->value() );
×
4292
    if ( !help.empty() )
×
4293
    {
4294
      return new String( help );
×
4295
    }
4296

NEW
4297
    return new BError( "No help for that command found" );
×
UNCOV
4298
  }
×
4299

NEW
4300
  return new BError( "Invalid parameter type" );
×
4301
}
4302

4303

4304
BObjectImp* UOExecutorModule::mf_SendStringAsTipWindow()
×
4305
{
4306
  Character* chr;
4307
  const String* str;
4308
  if ( getCharacterParam( 0, chr ) && getStringParam( 1, str ) )
×
4309
  {
4310
    if ( chr->has_active_client() )
×
4311
    {
4312
      send_tip( chr->client, str->value() );
×
4313
      return new BLong( 1 );
×
4314
    }
4315

NEW
4316
    return new BError( "No client attached" );
×
4317
  }
4318

NEW
4319
  return new BError( "Invalid parameter type" );
×
4320
}
4321

4322
BObjectImp* UOExecutorModule::mf_ListItemsNearLocationWithFlag(
×
4323
    /* x, y, z, range, flags, realm */ )  // DAVE
4324
{
4325
  Pos2d pos;
×
4326
  u16 range;
4327
  int z, flags;
4328
  Realms::Realm* realm;
4329

4330
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4331
       getParam( 3, range ) && getParam( 4, flags ) )
×
4332
  {
4333
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4334
    WorldIterator<ItemFilter>::InRange(
×
4335
        pos, realm, range,
4336
        [&]( Item* item )
×
4337
        {
4338
          if ( ( Plib::tile_uoflags( item->graphic ) & flags ) )
×
4339
          {
4340
            if ( item->in_range( pos, range ) )
×
4341
            {
4342
              if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z() - z ) < CONST_DEFAULT_ZRANGE ) )
×
4343
                newarr->addElement( new EItemRefObjImp( item ) );
×
4344
            }
4345
          }
4346
        } );
×
4347

4348
    return newarr.release();
×
4349
  }
×
4350

4351
  return new BError( "Invalid parameter" );
×
4352
}
4353

4354
BObjectImp* UOExecutorModule::mf_ListStaticsAtLocation( /* x, y, z, flags, realm */ )
×
4355
{
4356
  Core::Pos2d pos;
×
4357
  int z, flags;
4358
  Realms::Realm* realm;
4359

4360
  if ( getRealmParam( 4, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4361
       getParam( 3, flags ) )
×
4362
  {
4363
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4364

4365
    if ( !( flags & ITEMS_IGNORE_STATICS ) )
×
4366
    {
4367
      Plib::StaticEntryList slist;
×
4368
      realm->getstatics( slist, pos );
×
4369

4370
      for ( unsigned i = 0; i < slist.size(); ++i )
×
4371
      {
4372
        if ( ( z == LIST_IGNORE_Z ) || ( slist[i].z == z ) )
×
4373
        {
4374
          std::unique_ptr<BStruct> arr( new BStruct );
×
4375
          arr->addMember( "x", new BLong( pos.x() ) );
×
4376
          arr->addMember( "y", new BLong( pos.y() ) );
×
4377
          arr->addMember( "z", new BLong( slist[i].z ) );
×
4378
          arr->addMember( "objtype", new BLong( slist[i].objtype ) );
×
4379
          arr->addMember( "hue", new BLong( slist[i].hue ) );
×
4380
          newarr->addElement( arr.release() );
×
4381
        }
×
4382
      }
4383
    }
×
4384

4385
    if ( !( flags & ITEMS_IGNORE_MULTIS ) )
×
4386
    {
4387
      Plib::StaticList mlist;
×
4388
      realm->readmultis( mlist, pos );
×
4389

4390
      for ( unsigned i = 0; i < mlist.size(); ++i )
×
4391
      {
4392
        if ( ( z == LIST_IGNORE_Z ) || ( mlist[i].z == z ) )
×
4393
        {
4394
          std::unique_ptr<BStruct> arr( new BStruct );
×
4395
          arr->addMember( "x", new BLong( pos.x() ) );
×
4396
          arr->addMember( "y", new BLong( pos.y() ) );
×
4397
          arr->addMember( "z", new BLong( mlist[i].z ) );
×
4398
          arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
×
4399
          newarr->addElement( arr.release() );
×
4400
        }
×
4401
      }
4402
    }
×
4403

4404
    return newarr.release();
×
4405
  }
×
NEW
4406
  return new BError( "Invalid parameter" );
×
4407
}
4408

4409
BObjectImp* UOExecutorModule::mf_ListStaticsNearLocation( /* x, y, z, range, flags, realm */ )
×
4410
{
4411
  Core::Pos2d pos;
×
4412
  int z, flags;
4413
  short range;
4414
  Realms::Realm* realm;
4415

4416
  if ( getRealmParam( 5, &realm ) && getPos2dParam( 0, 1, &pos, realm ) && getParam( 2, z ) &&
×
4417
       getParam( 3, range ) && getParam( 4, flags ) )
×
4418
  {
4419
    std::unique_ptr<ObjArray> newarr( new ObjArray );
×
4420
    Core::Vec2d radius( range, range );
×
4421
    Core::Range2d area( pos - radius, pos + radius, realm );
×
4422
    for ( const auto& tile : area )
×
4423
    {
4424
      if ( !( flags & ITEMS_IGNORE_STATICS ) )
×
4425
      {
4426
        Plib::StaticEntryList slist;
×
4427
        realm->getstatics( slist, tile );
×
4428

4429
        for ( unsigned i = 0; i < slist.size(); ++i )
×
4430
        {
4431
          if ( ( z == LIST_IGNORE_Z ) || ( abs( slist[i].z - z ) < CONST_DEFAULT_ZRANGE ) )
×
4432
          {
4433
            std::unique_ptr<BStruct> arr( new BStruct );
×
4434
            arr->addMember( "x", new BLong( tile.x() ) );
×
4435
            arr->addMember( "y", new BLong( tile.y() ) );
×
4436
            arr->addMember( "z", new BLong( slist[i].z ) );
×
4437
            arr->addMember( "objtype", new BLong( slist[i].objtype ) );
×
4438
            arr->addMember( "hue", new BLong( slist[i].hue ) );
×
4439
            newarr->addElement( arr.release() );
×
4440
          }
×
4441
        }
4442
      }
×
4443

4444
      if ( !( flags & ITEMS_IGNORE_MULTIS ) )
×
4445
      {
4446
        Plib::StaticList mlist;
×
4447
        realm->readmultis( mlist, tile );
×
4448

4449
        for ( unsigned i = 0; i < mlist.size(); ++i )
×
4450
        {
4451
          if ( ( z == LIST_IGNORE_Z ) || ( abs( mlist[i].z - z ) < CONST_DEFAULT_ZRANGE ) )
×
4452
          {
4453
            std::unique_ptr<BStruct> arr( new BStruct );
×
4454
            arr->addMember( "x", new BLong( tile.x() ) );
×
4455
            arr->addMember( "y", new BLong( tile.y() ) );
×
4456
            arr->addMember( "z", new BLong( mlist[i].z ) );
×
4457
            arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
×
4458
            newarr->addElement( arr.release() );
×
4459
          }
×
4460
        }
4461
      }
×
4462
    }
4463

4464
    return newarr.release();
×
4465
  }
×
NEW
4466
  return new BError( "Invalid parameter" );
×
4467
}
4468

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

4559
using UOSearch = Plib::AStarSearch<UOPathState>;
4560

4561
BObjectImp* UOExecutorModule::mf_FindPath()
7✔
4562
{
4563
  Pos3d pos1, pos2;
7✔
4564
  Realms::Realm* realm;
4565
  const String* movemode_name;
4566

4567
  if ( !getPos3dParam( 0, 1, 2, &pos1 ) || !getPos3dParam( 3, 4, 5, &pos2 ) ||
14✔
4568
       !getRealmParam( 6, &realm ) || !getStringParam( 9, movemode_name ) )
14✔
4569
    return new BError( "Invalid parameter" );
×
4570
  if ( !pos1.in_range( pos2, settingsManager.ssopt.max_pathfind_range ) )
7✔
4571
    return new BError( "Beyond Max Range." );
×
4572

4573
  short theSkirt;
4574
  int flags;
4575

4576
  if ( !getParam( 7, flags ) )
7✔
4577
    flags = FP_IGNORE_MOBILES;
×
4578

4579
  if ( !getParam( 8, theSkirt ) )
7✔
4580
    theSkirt = 5;
×
4581

4582
  Plib::MOVEMODE movemode = Character::decode_movemode( movemode_name->value() );
7✔
4583
  if ( movemode == Plib::MOVEMODE_NONE )
7✔
4584
    return new BError( "Wrong movemode parameter" );
1✔
4585

4586
  if ( theSkirt < 0 )
6✔
4587
    theSkirt = 0;
×
4588

4589
  if ( !realm->valid( pos1.xy() ) )
6✔
4590
    return new BError( "Start Coordinates Invalid for Realm" );
×
4591
  if ( !realm->valid( pos2.xy() ) )
6✔
4592
    return new BError( "End Coordinates Invalid for Realm" );
×
4593

4594
  auto astarsearch = std::make_unique<UOSearch>();
6✔
4595

4596
  Range2d range( pos1.xy().min( pos2.xy() ) - Vec2d( theSkirt, theSkirt ),
6✔
4597
                 pos1.xy().max( pos2.xy() ) + Vec2d( theSkirt, theSkirt ), realm );
12✔
4598

4599
  if ( Plib::systemstate.config.loglevel >= 12 )
6✔
4600
  {
4601
    POLLOGLN( "[FindPath] Calling FindPath({}, {}, {}, {:#x}, {})", pos1, pos2, realm->name(),
×
4602
              flags, theSkirt );
4603
    POLLOGLN( "[FindPath]   search for Blockers inside {}", range );
×
4604
  }
4605

4606
  bool doors_block = ( flags & FP_IGNORE_DOORS ) ? false : true;
6✔
4607
  AStarParams params( range, doors_block, movemode, realm );
6✔
4608

4609
  if ( !( flags & FP_IGNORE_MOBILES ) )
6✔
4610
  {
4611
    WorldIterator<MobileFilter>::InBox( range, realm,
×
4612
                                        [&]( Mobile::Character* chr )
×
4613
                                        {
4614
                                          params.AddBlocker( chr->pos3d() );
×
4615

4616
                                          if ( Plib::systemstate.config.loglevel >= 12 )
×
4617
                                            POLLOGLN( "[FindPath]   add Blocker {} at {}",
×
4618
                                                      chr->name(), chr->pos() );
×
4619
                                        } );
×
4620
  }
4621

4622
  if ( Plib::systemstate.config.loglevel >= 12 )
6✔
4623
  {
4624
    POLLOGLN( "[FindPath]   use StartNode {}", pos1 );
×
4625
    POLLOGLN( "[FindPath]   use EndNode {}", pos2 );
×
4626
  }
4627

4628
  // Create a start state
4629
  UOPathState nodeStart( pos1, &params );
6✔
4630
  // Define the goal state
4631
  UOPathState nodeEnd( pos2, &params );
6✔
4632
  // Set Start and goal states
4633
  astarsearch->SetStartAndGoalStates( nodeStart, nodeEnd );
6✔
4634
  unsigned int SearchState;
4635
  do
4636
  {
4637
    SearchState = astarsearch->SearchStep();
28✔
4638
  } while ( SearchState == UOSearch::SEARCH_STATE_SEARCHING );
28✔
4639
  if ( SearchState == UOSearch::SEARCH_STATE_SUCCEEDED )
6✔
4640
  {
4641
    UOPathState* node = astarsearch->GetSolutionStart();
3✔
4642

4643
    auto nodeArray = std::make_unique<ObjArray>();
3✔
4644
    while ( ( node = astarsearch->GetSolutionNext() ) != nullptr )
12✔
4645
    {
4646
      auto nextStep = std::make_unique<BStruct>();
9✔
4647
      const auto& pos = node->position();
9✔
4648
      nextStep->addMember( "x", new BLong( pos.x() ) );
9✔
4649
      nextStep->addMember( "y", new BLong( pos.y() ) );
9✔
4650
      nextStep->addMember( "z", new BLong( pos.z() ) );
9✔
4651
      nodeArray->addElement( nextStep.release() );
9✔
4652
    }
9✔
4653
    astarsearch->FreeSolutionNodes();
3✔
4654
    return nodeArray.release();
3✔
4655
  }
3✔
4656
  if ( SearchState == UOSearch::SEARCH_STATE_FAILED )
3✔
4657
  {
4658
    return new BError( "Failed to find a path." );
3✔
4659
  }
NEW
4660
  if ( SearchState == UOSearch::SEARCH_STATE_OUT_OF_MEMORY )
×
4661
  {
4662
    return new BError( "Out of memory." );
×
4663
  }
NEW
4664
  if ( SearchState == UOSearch::SEARCH_STATE_SOLUTION_CORRUPTED )
×
4665
  {
4666
    return new BError( "Solution Corrupted!" );
×
4667
  }
4668

4669
  return new BError( "Pathfind Error." );
×
4670
}
6✔
4671

4672

4673
BObjectImp* UOExecutorModule::mf_UseItem()
×
4674
{
4675
  Item* item;
4676
  Character* chr;
4677

4678
  if ( getItemParam( 0, item ) && getCharacterParam( 1, chr ) )
×
4679
  {
4680
    const ItemDesc& itemdesc = find_itemdesc( item->objtype_ );
×
4681

4682
    if ( itemdesc.requires_attention && ( chr->skill_ex_active() || chr->casting_spell() ) )
×
4683
    {
4684
      if ( chr->client != nullptr )
×
4685
      {
4686
        send_sysmessage( chr->client, "I am already doing something else." );
×
4687
        return new BError( "Character busy." );
×
4688
        ;
4689
      }
4690
    }
4691

4692
    if ( itemdesc.requires_attention && chr->hidden() )
×
4693
      chr->unhide();
×
4694

4695
    ref_ptr<EScriptProgram> prog;
×
4696

4697
    std::string on_use_script = item->get_use_script_name();
×
4698

4699
    if ( !on_use_script.empty() )
×
4700
    {
4701
      ScriptDef sd( on_use_script, nullptr, "" );
×
4702
      prog = find_script2( sd,
×
4703
                           true,  // complain if not found
4704
                           Plib::systemstate.config.cache_interactive_scripts );
×
4705
    }
×
4706
    else if ( !itemdesc.on_use_script.empty() )
×
4707
    {
4708
      prog = find_script2( itemdesc.on_use_script, true,
×
4709
                           Plib::systemstate.config.cache_interactive_scripts );
×
4710
    }
4711

4712
    if ( prog.get() != nullptr )
×
4713
    {
4714
      if ( chr->start_itemuse_script( prog.get(), item, itemdesc.requires_attention ) )
×
4715
        return new BLong( 1 );
×
NEW
4716
      return new BError( "Failed to start script!" );
×
4717
      // else log the fact?
4718
    }
4719

NEW
4720
    if ( chr->client != nullptr )
×
NEW
4721
      item->builtin_on_use( chr->client );
×
NEW
4722
    return new BLong( 0 );
×
UNCOV
4723
  }
×
4724

NEW
4725
  return new BError( "Invalid parameter" );
×
4726
}
4727

4728
BObjectImp* UOExecutorModule::mf_FindSubstance()
3✔
4729
{
4730
  UContainer::Contents substanceVector;
3✔
4731

4732
  Item* cont_item;
4733
  unsigned int objtype;
4734
  int amount;
4735

4736
  if ( getItemParam( 0, cont_item ) && getObjtypeParam( 1, objtype ) && getParam( 2, amount ) )
3✔
4737
  {
4738
    bool makeInUse;
4739
    int flags;
4740
    if ( !getParam( 3, makeInUse ) )
3✔
4741
      makeInUse = false;
×
4742

4743
    if ( !getParam( 4, flags ) )
3✔
4744
      flags = 0;
×
4745

4746
    if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
3✔
4747
      return new BError( "That is not a container" );
×
4748
    if ( amount < 0 )
3✔
4749
      return new BError( "Amount cannot be negative" );
×
4750

4751
    UContainer* cont = static_cast<UContainer*>( cont_item );
3✔
4752
    int amthave = cont->find_sumof_objtype_noninuse( objtype, amount, substanceVector, flags );
3✔
4753
    if ( ( amthave < amount ) && ( !( flags & FINDSUBSTANCE_FIND_ALL ) ) )
3✔
4754
      return new BError( "Not enough of that substance in container" );
1✔
4755

4756
    std::unique_ptr<ObjArray> theArray( new ObjArray() );
2✔
4757
    Item* item;
4758

4759
    for ( UContainer::Contents::const_iterator itr = substanceVector.begin();
2✔
4760
          itr != substanceVector.end(); ++itr )
4✔
4761
    {
4762
      item = *itr;
2✔
4763
      if ( item != nullptr )
2✔
4764
      {
4765
        if ( ( makeInUse ) && ( !item->inuse() ) )
2✔
4766
        {
4767
          item->inuse( true );
2✔
4768
          reserved_items_.emplace_back( item );
2✔
4769
        }
4770
        theArray->addElement( new EItemRefObjImp( item ) );
2✔
4771
      }
4772
    }
4773
    return theArray.release();
2✔
4774
  }
2✔
4775

NEW
4776
  return new BError( "Invalid parameter type" );
×
4777
}
3✔
4778

4779
BObjectImp* UOExecutorModule::mf_IsStackable()
×
4780
{
4781
  Item* item1;
4782
  Item* item2;
4783

4784
  if ( !( getItemParam( 0, item1 ) && getItemParam( 1, item2 ) ) )
×
4785
  {
4786
    return new BError( "Invalid parameter type" );
×
4787
  }
4788

4789
  if ( item1->objtype_ != item2->objtype_ )
×
4790
    return new BError( "Objtypes differs" );
×
4791
  if ( !item1->stackable() )
×
4792
    return new BError( "That item type is not stackable." );
×
4793

4794
  if ( item1->can_add_to_self( *item2, false ) )
×
4795
    return new BLong( 1 );
×
NEW
4796
  return new BError( "Failed to stack" );
×
4797
}
4798

4799
BObjectImp* UOExecutorModule::mf_UpdateMobile()
×
4800
{
4801
  Character* chr;
4802
  int flags;
4803

4804
  if ( getCharacterParam( 0, chr ) && getParam( 1, flags ) )
×
4805
  {
4806
    if ( flags == 1 )
×
4807
    {
4808
      if ( ( !chr->isa( UOBJ_CLASS::CLASS_NPC ) ) && ( chr->client ) )  // no npc and active client
×
4809
        send_owncreate( chr->client, chr );                             // inform self
×
4810
      if ( ( chr->isa( UOBJ_CLASS::CLASS_NPC ) ) || ( chr->client ) )   // npc or active client
×
4811
        send_create_mobile_to_nearby_cansee( chr );                     // inform other
×
4812
      else
4813
        return new BError( "Mobile is offline" );
×
4814
    }
4815
    else
4816
    {
4817
      if ( ( !chr->isa( UOBJ_CLASS::CLASS_NPC ) ) && ( chr->client ) )  // no npc and active client
×
4818
        send_move( chr->client, chr );                                  // inform self
×
4819
      if ( ( chr->isa( UOBJ_CLASS::CLASS_NPC ) ) || ( chr->client ) )   // npc or active client
×
4820
        send_move_mobile_to_nearby_cansee( chr );                       // inform other
×
4821
      else
4822
        return new BError( "Mobile is offline" );
×
4823
    }
4824
    return new BLong( 1 );
×
4825
  }
4826
  return new BError( "Invalid parameter type" );
×
4827
}
4828

4829
BObjectImp* UOExecutorModule::mf_UpdateItem()
×
4830
{
4831
  Item* item;
4832

4833
  if ( getItemParam( 0, item ) )
×
4834
  {
4835
    send_item_to_inrange( item );
×
4836
    return new BLong( 1 );
×
4837
  }
4838

NEW
4839
  return new BError( "Invalid parameter type" );
×
4840
}
4841

4842

4843
BObjectImp* UOExecutorModule::mf_CanWalk(
×
4844
    /*movemode, x1, y1, z1, x2_or_dir, y2 := -1, realm := DEF*/ )
4845
{
4846
  Pos4d p;
×
4847
  int x2_or_dir, y2_;
4848
  const String* movemode_name;
4849

4850
  if ( ( getStringParam( 0, movemode_name ) ) && ( getPos4dParam( 1, 2, 3, 6, &p ) ) &&
×
4851
       ( getParam( 4, x2_or_dir ) ) && ( getParam( 5, y2_ ) ) )
×
4852
  {
4853
    Plib::MOVEMODE movemode = Character::decode_movemode( movemode_name->value() );
×
4854

4855
    Core::UFACING dir;
4856
    if ( y2_ == -1 )
×
4857
      dir = static_cast<Core::UFACING>( x2_or_dir & 0x7 );
×
4858
    else
4859
    {
4860
      auto p1 = Pos2d( static_cast<u16>( x2_or_dir ), static_cast<u16>( y2_ ) );
×
4861
      if ( !p.realm()->valid( p1 ) )
×
4862
        return new BError( "Invalid coordinates for realm." );
×
4863

4864
      dir = p.xy().direction_toward( p1 );
×
4865
    }
4866

4867
    if ( dir & 1 )  // check if diagonal movement is allowed
×
4868
    {
4869
      short new_z;
4870
      u8 tmp_facing = ( dir + 1 ) & 0x7;
×
4871
      auto tmp_pos = p.move( static_cast<Core::UFACING>( tmp_facing ) );
×
4872

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

4877
      tmp_facing = ( dir - 1 ) & 0x7;
×
4878
      tmp_pos = p.move( static_cast<Core::UFACING>( tmp_facing ) );
×
4879

4880
      if ( !walk1 && !p.realm()->walkheight( tmp_pos.xy(), tmp_pos.z(), &new_z, nullptr, nullptr,
×
4881
                                             true, movemode, nullptr ) )
4882
        return new BError( "Cannot walk there" );
×
4883
    }
4884

4885
    p.move_to( dir );
×
4886
    short newz;
4887

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

4891
    return new BLong( newz );
×
4892
  }
NEW
4893
  return new BError( "Invalid parameter" );
×
4894
}
4895

4896

4897
BObjectImp* UOExecutorModule::mf_SendCharProfile(
×
4898
    /*chr, of_who, title, uneditable_text := array, editable_text := array*/ )
4899
{
4900
  Character *chr, *of_who;
4901
  const String* title;
4902
  const String* uText;
4903
  const String* eText;
4904

4905
  if ( getCharacterParam( 0, chr ) && getCharacterParam( 1, of_who ) &&
×
4906
       getStringParam( 2, title ) && getUnicodeStringParam( 3, uText ) &&
×
4907
       getUnicodeStringParam( 4, eText ) )
×
4908
  {
4909
    if ( chr->logged_in() && of_who->logged_in() )
×
4910
    {
4911
      if ( uText->length() > SPEECH_MAX_LEN || eText->length() > SPEECH_MAX_LEN )
×
4912
        return new BError( "Text exceeds maximum size." );
×
4913

4914
      sendCharProfile( chr, of_who, title->value(), uText->value(), eText->value() );
×
4915
      return new BLong( 1 );
×
4916
    }
NEW
4917
    return new BError( "Mobile must be online." );
×
4918
  }
NEW
4919
  return new BError( "Invalid parameter type" );
×
4920
}
4921

4922
BObjectImp* UOExecutorModule::mf_SendOverallSeason( /*season_id, playsound := 1*/ )
×
4923
{
4924
  int season_id, playsound;
4925

4926
  if ( getParam( 0, season_id ) && getParam( 1, playsound ) )
×
4927
  {
4928
    if ( season_id < 0 || season_id > 4 )
×
4929
      return new BError( "Invalid season id" );
×
4930

4931
    Network::PktHelper::PacketOut<Network::PktOut_BC> msg;
×
4932
    msg->Write<u8>( static_cast<u16>( season_id ) );
×
4933
    msg->Write<u8>( static_cast<u16>( playsound ) );
×
4934

4935
    for ( Clients::iterator itr = networkManager.clients.begin(),
×
4936
                            end = networkManager.clients.end();
×
4937
          itr != end; ++itr )
×
4938
    {
4939
      Network::Client* client = *itr;
×
4940
      if ( !client->chr || !client->chr->logged_in() || client->getversiondetail().major < 1 )
×
4941
        continue;
×
4942
      msg.Send( client );
×
4943
    }
4944
    return new BLong( 1 );
×
4945
  }
×
NEW
4946
  return new BError( "Invalid parameter" );
×
4947
}
4948

4949
// bresenham circle calculates the coords based on center coords and radius
4950
BObjectImp* UOExecutorModule::mf_GetMidpointCircleCoords( /* xcenter, ycenter, radius */ )
×
4951
{
4952
  int xcenter, ycenter, radius;
4953
  if ( !( getParam( 0, xcenter ) && getParam( 1, ycenter ) && getParam( 2, radius ) ) )
×
4954
  {
4955
    return new BError( "Invalid parameter type" );
×
4956
  }
4957
  std::unique_ptr<ObjArray> coords( new ObjArray );
×
4958

4959
  std::vector<std::tuple<int, int>> points;
×
4960
  auto add_point = [&coords]( int x, int y )
×
4961
  {
4962
    std::unique_ptr<BStruct> point( new BStruct );
×
4963
    point->addMember( "x", new BLong( x ) );
×
4964
    point->addMember( "y", new BLong( y ) );
×
4965
    coords->addElement( point.release() );
×
4966
  };
×
4967

4968
  if ( radius == 0 )
×
4969
  {
4970
    add_point( xcenter, ycenter );
×
4971
    return coords.release();
×
4972
  }
4973

4974
  // inside of each quadrant the points are sorted,
4975
  // store the quadrands in seperated vectors and merge them later
4976
  // -> automatically sorted
4977
  std::vector<std::tuple<int, int>> q1, q2, q3, q4;
×
4978
  int x = -radius, y = 0, err = 2 - 2 * radius; /* II. Quadrant */
×
4979
  do
4980
  {
4981
    q1.emplace_back( xcenter - x, ycenter + y ); /*   I. Quadrant */
×
4982
    q2.emplace_back( xcenter - y, ycenter - x ); /*  II. Quadrant */
×
4983
    q3.emplace_back( xcenter + x, ycenter - y ); /* III. Quadrant */
×
4984
    q4.emplace_back( xcenter + y, ycenter + x ); /*  IV. Quadrant */
×
4985
    radius = err;
×
4986
    if ( radius <= y )
×
4987
      err += ++y * 2 + 1;
×
4988
    if ( radius > x || err > y )
×
4989
      err += ++x * 2 + 1;
×
4990
  } while ( x < 0 );
×
4991

4992
  for ( const auto& p : q1 )
×
4993
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
4994
  for ( const auto& p : q2 )
×
4995
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
4996
  for ( const auto& p : q3 )
×
4997
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
4998
  for ( const auto& p : q4 )
×
4999
    add_point( std::get<0>( p ), std::get<1>( p ) );
×
5000

5001
  return coords.release();
×
5002
}
×
5003

5004
size_t UOExecutorModule::sizeEstimate() const
14✔
5005
{
5006
  size_t size = sizeof( *this ) + Clib::memsize( reserved_items_ );
14✔
5007
  return size;
14✔
5008
}
5009
}  // namespace Module
5010
}  // 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