Pol  Revision:cb584c9
uomod.cpp
Go to the documentation of this file.
1 
68 #include "pol_global_config.h"
69 
70 #include "uomod.h"
71 
72 #include <cmath>
73 #include <cstddef>
74 #include <exception>
75 #include <stdlib.h>
76 #include <string>
77 
78 #include "../../bscript/berror.h"
79 #include "../../bscript/bobject.h"
80 #include "../../bscript/bstruct.h"
81 #include "../../bscript/dict.h"
82 #include "../../bscript/impstr.h"
83 #include "../../clib/cfgelem.h"
84 #include "../../clib/clib.h"
85 #include "../../clib/clib_endian.h"
86 #include "../../clib/compilerspecifics.h"
87 #include "../../clib/esignal.h"
88 #include "../../clib/logfacility.h"
89 #include "../../clib/passert.h"
90 #include "../../clib/refptr.h"
91 #include "../../plib/mapcell.h"
92 #include "../../plib/mapshape.h"
93 #include "../../plib/maptile.h"
94 #include "../../plib/staticblock.h"
95 #include "../../plib/stlastar.h"
96 #include "../../plib/systemstate.h"
97 #include "../action.h"
98 #include "../cfgrepos.h"
99 #include "../clidata.h"
100 #include "../containr.h"
101 #include "../core.h"
102 #include "../eventid.h"
103 #include "../fnsearch.h"
104 #include "../gameclck.h"
105 #include "../globals/object_storage.h"
106 #include "../globals/uvars.h"
107 #include "../guardrgn.h"
108 #include "../item/item.h"
109 #include "../item/itemdesc.h"
110 #include "../layers.h"
111 #include "../lightlvl.h"
112 #include "../listenpt.h"
113 #include "../los.h"
114 #include "../mdelta.h"
115 #include "../menu.h"
116 #include "../miscrgn.h"
117 #include "../mobile/charactr.h"
118 #include "../mobile/npc.h"
119 #include "../mobile/ufacing.h"
120 #include "../multi/boat.h"
121 #include "../multi/house.h"
122 #include "../multi/multi.h"
123 #include "../multi/multidef.h"
124 #include "../network/cgdata.h"
125 #include "../network/client.h"
126 #include "../network/packethelper.h"
127 #include "../network/packets.h"
128 #include "../npctmpl.h"
129 #include "../objtype.h"
130 #include "../pktboth.h"
131 #include "../pktdef.h"
132 #include "../polcfg.h"
133 #include "../polclass.h"
134 #include "../polclock.h"
135 #include "../polsig.h"
136 #include "../profile.h"
137 #include "../realms.h"
138 #include "../realms/realm.h"
139 #include "../resource.h"
140 #include "../savedata.h"
141 #include "../scrdef.h"
142 #include "../scrsched.h"
143 #include "../scrstore.h"
144 #include "../spells.h"
145 #include "../target.h"
146 #include "../uconst.h"
147 #include "../udatfile.h"
148 #include "../ufunc.h"
149 #include "../uimport.h"
150 #include "../umanip.h"
151 #include "../unicode.h"
152 #include "../uobject.h"
153 #include "../uoexec.h"
154 #include "../uopathnode.h"
155 #include "../uoscrobj.h"
156 #include "../uworld.h"
157 #include "../wthrtype.h"
158 #include "cfgmod.h"
159 #include "osmod.h"
160 
161 namespace Pol
162 {
163 namespace Core
164 {
165 void cancel_trade( Mobile::Character* chr1 );
166 void cancel_all_trades();
167 Bscript::BObjectImp* place_item_in_secure_trade_container( Network::Client* client,
168  Items::Item* item );
169 Bscript::BObjectImp* open_trade_window( Network::Client* client, Mobile::Character* dropon );
170 void send_tip( Network::Client* client, const std::string& tiptext );
171 std::string get_textcmd_help( Mobile::Character* chr, const char* cmd );
172 void send_paperdoll( Network::Client* client, Mobile::Character* chr );
173 void send_skillmsg( Network::Client* client, const Mobile::Character* chr );
174 Bscript::BObjectImp* equip_from_template( Mobile::Character* chr, const char* template_name );
175 } // namespace Core
176 namespace Module
177 {
178 using namespace Bscript;
179 using namespace Items;
180 using namespace Mobile;
181 using namespace Core;
182 
183 #define CONST_DEFAULT_ZRANGE 19
184 
185 class EMenuObjImp : public BApplicObj<Menu>
186 {
187 public:
188  EMenuObjImp( const Menu& m ) : BApplicObj<Menu>( &menu_type, m ) {}
189  virtual const char* typeOf() const POL_OVERRIDE { return "MenuRef"; }
190  virtual u8 typeOfInt() const POL_OVERRIDE { return OTMenuRef; }
191  virtual BObjectImp* copy() const POL_OVERRIDE { return new EMenuObjImp( value() ); }
192 };
193 
194 
196  : TmplExecutorModule<UOExecutorModule>( "UO", exec ),
197  uoexec( exec ),
198  target_cursor_chr( nullptr ),
199  menu_selection_chr( nullptr ),
200  popup_menu_selection_chr( nullptr ),
201  popup_menu_selection_above( nullptr ),
202  prompt_chr( nullptr ),
203  gump_chr( nullptr ),
204  textentry_chr( nullptr ),
205  resurrect_chr( nullptr ),
206  selcolor_chr( nullptr ),
207  target_options( 0 ),
208  attached_chr_( nullptr ),
209  attached_npc_( nullptr ),
210  attached_item_( nullptr ),
211  controller_( nullptr ),
212  reserved_items_(),
213  registered_for_speech_events( false )
214 {
215 }
216 
218 {
219  while ( !reserved_items_.empty() )
220  {
221  Item* item = reserved_items_.back().get();
222  item->inuse( false );
223  reserved_items_.pop_back();
224  }
225 
226  if ( target_cursor_chr != nullptr )
227  {
228  // CHECKME can we cancel the cursor request?
229  if ( target_cursor_chr->client != nullptr && target_cursor_chr->client->gd != nullptr )
231  target_cursor_chr = nullptr;
232  }
233  if ( menu_selection_chr != nullptr )
234  {
235  if ( menu_selection_chr->client != nullptr && menu_selection_chr->client->gd != nullptr )
237  menu_selection_chr = nullptr;
238  }
239  if ( popup_menu_selection_chr != nullptr )
240  {
241  if ( popup_menu_selection_chr->client != nullptr &&
242  popup_menu_selection_chr->client->gd != nullptr )
244  popup_menu_selection_chr = nullptr;
245  popup_menu_selection_above = nullptr;
246  }
247  if ( prompt_chr != nullptr )
248  {
249  if ( prompt_chr->client != nullptr && prompt_chr->client->gd != nullptr )
250  prompt_chr->client->gd->prompt_uoemod = nullptr;
251  prompt_chr = nullptr;
252  }
253  if ( gump_chr != nullptr )
254  {
255  if ( gump_chr->client != nullptr && gump_chr->client->gd != nullptr )
256  gump_chr->client->gd->remove_gumpmods( this );
257  gump_chr = nullptr;
258  }
259  if ( textentry_chr != nullptr )
260  {
261  if ( textentry_chr->client != nullptr && textentry_chr->client->gd != nullptr )
263  textentry_chr = nullptr;
264  }
265  if ( resurrect_chr != nullptr )
266  {
267  if ( resurrect_chr->client != nullptr && resurrect_chr->client->gd != nullptr )
269  resurrect_chr = nullptr;
270  }
271  if ( selcolor_chr != nullptr )
272  {
273  if ( selcolor_chr->client != nullptr && selcolor_chr->client->gd != nullptr )
274  selcolor_chr->client->gd->selcolor_uoemod = nullptr;
275  selcolor_chr = nullptr;
276  }
277  if ( attached_chr_ != nullptr )
278  {
280  attached_chr_->script_ex = nullptr;
281  attached_chr_ = nullptr;
282  }
283  if ( attached_item_ )
284  {
285  attached_item_->process( nullptr );
286  attached_item_ = nullptr;
287  }
289  {
291  }
292 }
293 
295 {
296  Character* chr;
297  if ( getCharacterParam( exec, 0, chr ) )
298  {
299  if ( attached_chr_ == nullptr )
300  {
301  if ( chr->script_ex == nullptr )
302  {
303  attached_chr_ = chr;
305 
306  return new BLong( 1 );
307  }
308  else
309  return new BError( "Another script still attached." );
310  }
311  else
312  return new BError( "Another character still attached." );
313  }
314  else
315  return new BError( "Invalid parameter" );
316 }
317 
319 {
320  if ( attached_chr_ != nullptr )
321  {
323  attached_chr_->script_ex = nullptr;
324  attached_chr_ = nullptr;
325  return new BLong( 1 );
326  }
327  else
328  {
329  return new BLong( 0 );
330  }
331 }
332 
333 static bool item_create_params_ok( u32 objtype, int amount )
334 {
335  return ( objtype >= UOBJ_ITEM__LOWEST && objtype <= Plib::systemstate.config.max_objtype ) &&
336  amount > 0 && amount <= 60000L;
337 }
338 
340  unsigned short amount, bool force_stacking,
341  UOExecutorModule* uoemod )
342 {
343  if ( ( tile_flags( descriptor->graphic ) & Plib::FLAG::STACKABLE ) || force_stacking )
344  {
345  for ( UContainer::const_iterator itr = cont->begin(); itr != cont->end(); ++itr )
346  {
347  Item* item = GET_ITEM_PTR( itr );
348  ItemRef itemref( item ); // dave 1/28/3 prevent item from being destroyed before function
349  // ends
350 
351  if ( item->objtype_ == descriptor->objtype && !item->newbie() &&
352  item->insured() == descriptor->insured &&
353  item->color ==
354  descriptor
355  ->color && // dave added 5/11/3, only add to existing stack if is default color
357  descriptor ) && // dave added 5/11/3, only add to existing stack if default cprops
358  ( !item->inuse() || ( uoemod && uoemod->is_reserved_to_me( item ) ) ) &&
359  item->can_add_to_self( amount, force_stacking ) )
360  {
361  // DAVE added this 11/17, call can/onInsert scripts for this container
362  Character* chr_owner = cont->GetCharacterOwner();
363  if ( chr_owner == nullptr )
364  if ( uoemod != nullptr )
365  chr_owner = uoemod->controller_.get();
366 
367  // If the can insert script fails for combining a stack, we'll let the create new item code
368  // below handle it
369  // what if a cannInsert script modifies (adds to) the container we're iterating over? (they
370  // shouldn't do that)
371  // FIXME oh my, this makes no sense. 'item' in this case is already in the container.
372  if ( !cont->can_insert_increase_stack( chr_owner, UContainer::MT_CORE_CREATED, item, amount,
373  nullptr ) )
374  continue;
375  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
376  {
377  return new BError( "Item was destroyed in CanInsert Script" );
378  }
379 #ifdef PERGON
380  item->ct_merge_stacks_pergon(
381  amount ); // Pergon: Re-Calculate Property CreateTime after Adding Items to a Stack
382 #endif
383 
384  int newamount = item->getamount();
385  newamount += amount;
386  item->setamount( static_cast<unsigned short>( newamount ) );
387 
388  update_item_to_inrange( item );
389  UpdateCharacterWeight( item );
390 
391  // FIXME again, this makes no sense, item is already in the container.
392  cont->on_insert_increase_stack( chr_owner, UContainer::MT_CORE_CREATED, item, amount );
393  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
394  {
395  return new BError( "Item was destroyed in OnInsert Script" );
396  }
397 
398 
399  return new EItemRefObjImp( item );
400  }
401  }
402  }
403  else if ( amount != 1 && !force_stacking )
404  {
405  return new BError( "That item is not stackable. Create one at a time." );
406  }
407 
408  Item* item = Item::create( *descriptor );
409  if ( item != nullptr )
410  {
411  ItemRef itemref( item ); // dave 1/28/3 prevent item from being destroyed before function ends
412  item->realm = cont->realm;
413  item->setamount( amount );
414 
415  if ( cont->can_add( *item ) )
416  {
417  if ( !descriptor->create_script.empty() )
418  {
419  BObjectImp* res =
420  run_script_to_completion( descriptor->create_script, new EItemRefObjImp( item ) );
421  if ( !res->isTrue() )
422  {
423  item->destroy();
424  return res;
425  }
426  else
427  {
428  BObject ob( res );
429  }
430  if ( !cont->can_add( *item ) )
431  { // UNTESTED
432  item->destroy();
433  return new BError( "Couldn't add item to container after create script ran" );
434  }
435  }
436 
437  // run before owner is found. No need to find owner if not even able to use slots.
438  u8 slotIndex = item->slot_index();
439  if ( !cont->can_add_to_slot( slotIndex ) )
440  {
441  item->destroy();
442  return new BError( "No slots available in this container" );
443  }
444  if ( !item->slot_index( slotIndex ) )
445  {
446  item->destroy();
447  return new BError( "Couldn't set slot index on item" );
448  }
449 
450  // DAVE added this 11/17, call can/onInsert scripts for this container
451  Character* chr_owner = cont->GetCharacterOwner();
452  if ( chr_owner == nullptr )
453  if ( uoemod != nullptr )
454  chr_owner = uoemod->controller_.get();
455 
456  if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_CREATED, item ) )
457  {
458  item->destroy();
459  // FIXME: try to propogate error from returned result of the canInsert script
460  return new BError( "Could not insert item into container." );
461  }
462  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
463  {
464  return new BError( "Item was destroyed in CanInsert Script" );
465  }
466 
467  cont->add_at_random_location( item );
468  update_item_to_inrange( item );
469  // DAVE added this 11/17, refresh owner's weight on item insert
470  UpdateCharacterWeight( item );
471 
472  cont->on_insert_add_item( chr_owner, UContainer::MT_CORE_CREATED, item );
473  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
474  {
475  return new BError( "Item was destroyed in OnInsert Script" );
476  }
477 
478  return new EItemRefObjImp( item );
479  }
480  else
481  {
482  item->destroy();
483  return new BError( "That container is full" );
484  }
485  }
486  else
487  {
488  return new BError( "Failed to create that item type" );
489  }
490 }
491 
493 {
494  Item* item;
495  const ItemDesc* descriptor;
496  int amount;
497 
498  if ( getItemParam( exec, 0, item ) && getObjtypeParam( exec, 1, descriptor ) &&
499  getParam( 2, amount ) && item_create_params_ok( descriptor->objtype, amount ) )
500  {
501  if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
502  {
503  return _create_item_in_container( static_cast<UContainer*>( item ), descriptor,
504  static_cast<unsigned short>( amount ), false, this );
505  }
506  else
507  {
508  return new BError( "That is not a container" );
509  }
510  }
511  else
512  {
513  return new BError( "A parameter was invalid" );
514  }
515 }
516 
518 {
519  Item* item;
520  const ItemDesc* descriptor;
521  int amount;
522 
523  if ( getItemParam( exec, 0, item ) && getObjtypeParam( exec, 1, descriptor ) &&
524  getParam( 2, amount ) && item_create_params_ok( descriptor->objtype, amount ) )
525  {
526  if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
527  {
528  return _create_item_in_container( static_cast<UContainer*>( item ), descriptor,
529  static_cast<unsigned short>( amount ), true, this );
530  }
531  else
532  {
533  return new BError( "That is not a container" );
534  }
535  }
536  else
537  {
538  return new BError( "A parameter was invalid" );
539  }
540 }
541 
542 
544 {
545  const char* text;
546  unsigned short font;
547  unsigned short color;
548  unsigned short requiredCmdLevel;
549  text = exec.paramAsString( 0 );
550  if ( text && getParam( 1, font ) && // todo: getFontParam
551  getParam( 2, color ) && // todo: getColorParam
552  getParam( 3, requiredCmdLevel ) ) // todo: getRequiredCmdLevelParam
553  {
554  Core::broadcast( text, font, color, requiredCmdLevel );
555  return new BLong( 1 );
556  }
557  else
558  {
559  return nullptr;
560  }
561 }
562 
563 /* Special containers (specifically, bankboxes, but probably any other
564  "invisible" accessible container) seem to work thus:
565  They sit in layer 0x1D. The player is told to "wear_item" the item,
566  then the gump and container contents are sent.
567  We'll put a reference to this item in the character's additional_legal_items
568  container, which is flushed whenever he moves.
569  It might be better to actually put it in that layer, because that way
570  it implicitly is at the same location (location of the wornitems container)
571  */
573 {
574  Character* chr;
575  Item* item;
576  if ( !getCharacterParam( exec, 0, chr ) || !getItemParam( exec, 1, item ) )
577  {
578  return new BError( "Invalid parameter type" );
579  }
580 
581  if ( !chr->has_active_client() )
582  {
583  return new BError( "No client attached" );
584  }
585  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
586  {
587  return new BError( "That isn't a container" );
588  }
589 
590  u8 save_layer = item->layer;
591  item->layer = LAYER_BANKBOX;
592  send_wornitem( chr->client, chr, item );
593  item->layer = save_layer;
594  item->x = chr->x;
595  item->y = chr->y;
596  item->z = chr->z;
597  item->double_click( chr->client ); // open the container on the client's screen
598  chr->add_remote_container( item );
599 
600  return new BLong( 1 );
601 }
602 
603 
605 {
606  Character* chr;
607  Character* chr2;
608  if ( !getCharacterParam( exec, 0, chr ) || !getCharacterParam( exec, 1, chr2 ) )
609  {
610  return new BError( "Invalid parameter type." );
611  }
612 
613  if ( chr == chr2 )
614  {
615  return new BError( "You can't trade with yourself." );
616  }
617 
618  if ( !chr->has_active_client() )
619  {
620  return new BError( "No client attached." );
621  }
622  if ( !chr2->has_active_client() )
623  {
624  return new BError( "No client attached." );
625  }
626 
627  return Core::open_trade_window( chr->client, chr2 );
628 }
629 
631 {
632  Character* chr;
633  if ( !getCharacterParam( exec, 0, chr ) )
634  {
635  return new BError( "Invalid parameter type." );
636  }
637  if ( !chr->trading_with )
638  return new BError( "Mobile is not currently trading with anyone." );
639 
640  Core::cancel_trade( chr );
641  return new BLong( 1 );
642 }
643 
645 {
646  Character* chr;
647  Item* item;
648  if ( !getCharacterParam( exec, 0, chr ) || !getItemParam( exec, 1, item ) )
649  {
650  return new BError( "Invalid parameter type" );
651  }
652 
653  if ( !chr->has_active_client() )
654  {
655  return new BError( "No client attached" );
656  }
657  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
658  {
659  return new BError( "That isn't a container" );
660  }
661  UContainer* cont = static_cast<UContainer*>( item );
662  chr->client->pause();
663  send_open_gump( chr->client, *cont );
664  send_container_contents( chr->client, *cont );
665  chr->client->restart();
666  return new BLong( 1 );
667 }
668 
670 {
671  Item* item;
672  unsigned int objtype;
673  int flags;
674  if ( !getItemParam( exec, 0, item ) || !getObjtypeParam( exec, 1, objtype ) )
675  {
676  return new BError( "Invalid parameter type" );
677  }
678  if ( !item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
679  {
680  return new BError( "That is not a container" );
681  }
682  if ( !getParam( 4, flags ) )
683  flags = 0;
684 
685  UContainer* cont = static_cast<UContainer*>( item );
686  Item* found = cont->find_objtype( objtype, flags );
687  if ( found == nullptr )
688  return new BError( "No items were found" );
689  else
690  return new EItemRefObjImp( found );
691 }
692 
693 
695 {
696  Character* chr;
697  const String* ptext;
698  unsigned short font;
699  unsigned short color;
700 
701  if ( getCharacterParam( exec, 0, chr ) && ( ( ptext = getStringParam( 1 ) ) != nullptr ) &&
702  getParam( 2, font ) && getParam( 3, color ) )
703  {
704  if ( chr->has_active_client() )
705  {
706  send_sysmessage( chr->client, ptext->data(), font, color );
707  return new BLong( 1 );
708  }
709  else
710  {
711  return new BError( "Mobile has no active client" );
712  }
713  }
714  else
715  {
716  return new BError( "Invalid parameter type" );
717  }
718 }
719 
721 {
722  UObject* obj;
723  const String* ptext;
724  unsigned short font;
725  unsigned short color;
726  int journal_print;
727 
728  if ( getUObjectParam( exec, 0, obj ) && getStringParam( 1, ptext ) && getParam( 2, font ) &&
729  getParam( 3, color ) && getParam( 4, journal_print ) )
730  {
731  return new BLong( say_above( obj, ptext->data(), font, color, journal_print ) );
732  }
733  else
734  {
735  return new BError( "A parameter was invalid" );
736  }
737 }
739 {
740  Character* chr;
741  UObject* obj;
742  const String* ptext;
743  unsigned short font;
744  unsigned short color;
745  int journal_print;
746 
747  if ( getUObjectParam( exec, 0, obj ) && getStringParam( 1, ptext ) &&
748  getCharacterParam( exec, 2, chr ) && getParam( 3, font ) && getParam( 4, color ) &&
749  getParam( 5, journal_print ) )
750  {
751  return new BLong( private_say_above( chr, obj, ptext->data(), font, color, journal_print ) );
752  }
753  else
754  {
755  return new BError( "A parameter was invalid" );
756  }
757 }
758 
759 // const int TGTOPT_NOCHECK_LOS = 0x0000; // currently unused
760 const int TGTOPT_CHECK_LOS = 0x0001;
761 const int TGTOPT_HARMFUL = 0x0002;
762 const int TGTOPT_HELPFUL = 0x0004;
763 
764 // FIXME susceptible to out-of-sequence target cursors
766 {
767  if ( chr != nullptr && chr->client->gd->target_cursor_uoemod != nullptr )
768  {
769  if ( obj != nullptr )
770  {
771  if ( obj->ismobile() )
772  {
773  Character* targetted_chr = static_cast<Character*>( obj );
774  if ( chr->client->gd->target_cursor_uoemod->target_options & TGTOPT_HARMFUL )
775  {
776  targetted_chr->inform_engaged( chr );
777  chr->repsys_on_attack( targetted_chr );
778  }
779  else if ( chr->client->gd->target_cursor_uoemod->target_options & TGTOPT_HELPFUL )
780  {
781  chr->repsys_on_help( targetted_chr );
782  }
783  }
784  chr->client->gd->target_cursor_uoemod->uoexec.ValueStack.back().set(
785  new BObject( obj->make_ref() ) );
786  }
787  // even on cancel, we wake the script up.
790  chr->client->gd->target_cursor_uoemod = nullptr;
791  }
792 }
793 
794 
796 {
797  Character* chr;
798  if ( !getCharacterParam( exec, 0, chr ) )
799  {
800  return new BError( "Invalid parameter type" );
801  }
802  if ( !chr->has_active_client() )
803  {
804  return new BError( "No client connected" );
805  }
806  if ( chr->target_cursor_busy() )
807  {
808  return new BError( "Client busy with another target cursor" );
809  }
810 
811  if ( !getParam( 1, target_options ) )
813 
814  PKTBI_6C::CURSOR_TYPE crstype;
815 
816  if ( target_options & TGTOPT_HARMFUL )
818  else if ( target_options & TGTOPT_HELPFUL )
820  else
822 
823  if ( !uoexec.suspend() )
824  {
825  DEBUGLOG << "Script Error in '" << scriptname() << "' PC=" << exec.PC << ": \n"
826  << "\tCall to function UO::Target():\n"
827  << "\tThe execution of this script can't be blocked!\n";
828  return new Bscript::BError( "Script can't be blocked" );
829  }
830 
831  TargetCursor* tgt_cursor = nullptr;
832 
833  bool is_los_checked = ( target_options & TGTOPT_CHECK_LOS ) && !chr->ignores_line_of_sight();
834  if ( is_los_checked )
835  {
837  }
838  else
839  {
841  }
842 
843  tgt_cursor->send_object_cursor( chr->client, crstype );
844 
845  chr->client->gd->target_cursor_uoemod = this;
846  target_cursor_chr = chr;
847 
848  return new BLong( 0 );
849 }
850 
852 {
853  Character* chr;
854  if ( getCharacterParam( exec, 0, chr ) )
855  {
856  if ( chr->has_active_client() )
857  {
858  if ( chr->target_cursor_busy() )
859  {
861  msg->Write<u8>( PKTBI_6C::UNK1_00 );
862  msg->offset += 4; // u32 target_cursor_serial
863  msg->Write<u8>( 0x3u );
864  // rest 0
865  msg.Send( chr->client, sizeof msg->buffer );
866  return new BLong( 0 );
867  }
868  else
869  {
870  return new BError( "Client does not have an active target cursor" );
871  }
872  }
873  else
874  {
875  return new BError( "No client connected" );
876  }
877  }
878  else
879  {
880  return new BError( "Invalid parameter type" );
881  }
882 }
883 
885 {
886  if ( chr != nullptr && chr->client->gd->target_cursor_uoemod != nullptr )
887  {
888  if ( msg != nullptr )
889  {
890  BStruct* arr = new BStruct;
891  arr->addMember( "x", new BLong( cfBEu16( msg->x ) ) );
892  arr->addMember( "y", new BLong( cfBEu16( msg->y ) ) );
893  arr->addMember( "z", new BLong( msg->z ) );
894  // FIXME: Objtype CANNOT be trusted! Scripts must validate this, or, we must
895  // validate right here. Should we check map/static, or let that reside
896  // for scripts to run ListStatics? In theory, using Injection or similar,
897  // you could mine where no mineable tiles are by faking objtype in packet
898  // and still get the resources?
899  arr->addMember( "objtype", new BLong( cfBEu16( msg->graphic ) ) );
900 
901  u32 selected_serial = cfBEu32( msg->selected_serial );
902  if ( selected_serial )
903  {
904  UObject* obj = system_find_object( selected_serial );
905  if ( obj )
906  {
907  arr->addMember( obj->target_tag(), obj->make_ref() );
908  }
909  }
910 
911  // Never trust packet's objtype is the reason here. They should never
912  // target on other realms. DUH!
913  arr->addMember( "realm", new String( chr->realm->name() ) );
914 
915  Multi::UMulti* multi =
916  chr->realm->find_supporting_multi( cfBEu16( msg->x ), cfBEu16( msg->y ), msg->z );
917  if ( multi != nullptr )
918  arr->addMember( "multi", multi->make_ref() );
919 
920  chr->client->gd->target_cursor_uoemod->uoexec.ValueStack.back().set( new BObject( arr ) );
921  }
922 
925  chr->client->gd->target_cursor_uoemod = nullptr;
926  }
927 }
928 
930 {
931  Character* chr;
932  if ( !getCharacterParam( exec, 0, chr ) )
933  {
934  return new BError( "Invalid parameter type" );
935  }
936  if ( !chr->has_active_client() )
937  {
938  return new BError( "Mobile has no active client" );
939  }
940  if ( chr->target_cursor_busy() )
941  {
942  return new BError( "Client has an active target cursor" );
943  }
944 
945  if ( !uoexec.suspend() )
946  {
947  DEBUGLOG << "Script Error in '" << scriptname() << "' PC=" << exec.PC << ": \n"
948  << "\tCall to function UO::TargetCoordinates():\n"
949  << "\tThe execution of this script can't be blocked!\n";
950  return new Bscript::BError( "Script can't be blocked" );
951  }
952 
954  chr->client->gd->target_cursor_uoemod = this;
955  target_cursor_chr = chr;
956  return new BLong( 0 );
957 }
958 
960 {
961  Character* chr;
962  unsigned int objtype, hue;
963  int flags;
964  int xoffset, yoffset;
965  if ( !( getCharacterParam( exec, 0, chr ) && getObjtypeParam( exec, 1, objtype ) &&
966  getParam( 2, flags ) && getParam( 3, xoffset ) && getParam( 4, yoffset ) &&
967  getParam( 5, hue ) ) )
968  {
969  return new BError( "Invalid parameter type" );
970  }
971 
972 
973  if ( !chr->has_active_client() )
974  {
975  return new BError( "No client attached" );
976  }
977 
978  if ( chr->target_cursor_busy() )
979  {
980  return new BError( "Client busy with another target cursor" );
981  }
982 
983  if ( find_itemdesc( objtype ).type != ItemDesc::BOATDESC &&
984  find_itemdesc( objtype ).type != ItemDesc::HOUSEDESC )
985  {
986  return new BError( "Object Type is out of range for Multis" );
987  }
988 
989  if ( !uoexec.suspend() )
990  {
991  DEBUGLOG << "Script Error in '" << scriptname() << "' PC=" << exec.PC << ": \n"
992  << "\tCall to function UO::TargetMultiPlacement():\n"
993  << "\tThe execution of this script can't be blocked!\n";
994  return new Bscript::BError( "Script can't be blocked" );
995  }
996 
997  chr->client->gd->target_cursor_uoemod = this;
998  target_cursor_chr = chr;
999 
1001  chr->client, objtype, flags, (s16)xoffset, (s16)yoffset, hue );
1002 
1003  return new BLong( 0 );
1004 }
1005 
1007 {
1008  Item* item;
1009  Character* chr;
1010 
1011  if ( getItemParam( exec, 0, item ) )
1012  {
1013  return new BLong( item->objtype_ );
1014  }
1015  else if ( getCharacterParam( exec, 0, chr ) )
1016  {
1017  return new BLong( chr->objtype_ );
1018  }
1019  else
1020  {
1021  return new BLong( 0 );
1022  }
1023 }
1024 
1025 // FIXME character needs an Accessible that takes an Item*
1027 {
1028  Character* chr;
1029  Item* item;
1030  int range;
1031 
1032  if ( getCharacterParam( exec, 0, chr ) && getItemParam( exec, 1, item ) )
1033  {
1034  // Range defaults to -1 if it's not defined in the .em file (old scripts)
1035  // or the user provides a weird object.
1036  if ( exec.numParams() < 3 || !getParam( 2, range ) )
1037  range = -1;
1038 
1039  if ( chr->can_access( item, range ) )
1040  return new BLong( 1 );
1041  else
1042  return new BLong( 0 );
1043  }
1044  return new BLong( 0 );
1045 }
1046 
1048 {
1049  Item* item;
1050 
1051  if ( getItemParam( exec, 0, item ) )
1052  {
1053  return new BLong( item->getamount() );
1054  }
1055  else
1056  {
1057  return new BLong( 0 );
1058  }
1059 }
1060 
1061 
1062 // FIXME, copy-pasted from NPC, copy-pasted to CreateItemInContainer
1064 {
1065  Character* chr;
1066  const ItemDesc* descriptor;
1067  unsigned short amount;
1068 
1069  if ( getCharacterParam( exec, 0, chr ) && getObjtypeParam( exec, 1, descriptor ) &&
1070  getParam( 2, amount ) && item_create_params_ok( descriptor->objtype, amount ) )
1071  {
1072  UContainer* backpack = chr->backpack();
1073  if ( backpack != nullptr )
1074  {
1075  return _create_item_in_container( backpack, descriptor, amount, false, this );
1076  }
1077  else
1078  {
1079  return new BError( "Character has no backpack." );
1080  }
1081  }
1082  else
1083  {
1084  return new BError( "A parameter was invalid." );
1085  }
1086 }
1087 
1088 BObjectImp* _complete_create_item_at_location( Item* item, unsigned short x, unsigned short y,
1089  short z, Realms::Realm* realm )
1090 {
1091  // Dave moved these 3 lines up here 12/20 cuz x,y,z was uninit in the createscript.
1092  item->x = x;
1093  item->y = y;
1094  item->z = static_cast<signed char>( z );
1095  item->realm = realm;
1096 
1097  // ITEMDESCTODO: use the original descriptor
1098  const ItemDesc& id = find_itemdesc( item->objtype_ );
1099  if ( !id.create_script.empty() )
1100  {
1101  BObjectImp* res = run_script_to_completion( id.create_script, new EItemRefObjImp( item ) );
1102  if ( !res->isTrue() )
1103  {
1104  item->destroy();
1105  return res;
1106  }
1107  else
1108  {
1109  BObject ob( res );
1110  }
1111  }
1112 
1113  update_item_to_inrange( item );
1114  add_item_to_world( item );
1116  return new EItemRefObjImp( item );
1117 }
1118 
1119 BObjectImp* UOExecutorModule::mf_CreateItemAtLocation( /* x,y,z,objtype,amount,realm */ )
1120 {
1121  unsigned short x, y;
1122  short z;
1123  const ItemDesc* itemdesc;
1124  unsigned short amount;
1125  const String* strrealm;
1126  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z, ZCOORD_MIN, ZCOORD_MAX ) &&
1127  getObjtypeParam( exec, 3, itemdesc ) && getParam( 4, amount, 1, 60000 ) &&
1128  getStringParam( 5, strrealm ) && item_create_params_ok( itemdesc->objtype, amount ) )
1129  {
1130  if ( !( tile_flags( itemdesc->graphic ) & Plib::FLAG::STACKABLE ) && ( amount != 1 ) )
1131  {
1132  return new BError( "That item is not stackable. Create one at a time." );
1133  }
1134 
1135  Realms::Realm* realm = find_realm( strrealm->value() );
1136  if ( !realm )
1137  return new BError( "Realm not found" );
1138 
1139  if ( !realm->valid( x, y, z ) )
1140  return new BError( "Invalid Coordinates for Realm" );
1141  Item* item = Item::create( *itemdesc );
1142  if ( item != nullptr )
1143  {
1144  item->setamount( amount );
1145  return _complete_create_item_at_location( item, x, y, z, realm );
1146  }
1147  else
1148  {
1149  return new BError( "Unable to create item of objtype " + Clib::hexint( itemdesc->objtype ) );
1150  }
1151  }
1152  return new BError( "Invalid parameter type" );
1153 }
1154 
1156 {
1157  unsigned short x, y;
1158  short z;
1159  Item* origitem;
1160  const String* strrealm;
1161  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z, ZCOORD_MIN, ZCOORD_MAX ) &&
1162  getItemParam( exec, 3, origitem ) && getStringParam( 4, strrealm ) )
1163  {
1164  if ( origitem->script_isa( POLCLASS_MULTI ) )
1165  return new BError( "This function does not work with Multi objects." );
1166 
1167  Realms::Realm* realm = find_realm( strrealm->value() );
1168  if ( !realm )
1169  return new BError( "Realm not found" );
1170 
1171  if ( !realm->valid( x, y, z ) )
1172  return new BError( "Invalid Coordinates for Realm" );
1173  Item* item = origitem->clone();
1174  if ( item != nullptr )
1175  {
1176  return _complete_create_item_at_location( item, x, y, z, realm );
1177  }
1178  else
1179  {
1180  return new BError( "Unable to clone item" );
1181  }
1182  }
1183  else
1184  {
1185  return new BError( "Invalid parameter type" );
1186  }
1187 }
1188 
1189 BObjectImp* UOExecutorModule::mf_CreateMultiAtLocation( /* x,y,z,objtype,flags,realm */ )
1190 {
1191  unsigned short x, y;
1192  short z;
1193  const ItemDesc* descriptor;
1194  int flags = 0;
1195  Realms::Realm* realm = find_realm( "britannia" );
1196  if ( !( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z, ZCOORD_MIN, ZCOORD_MAX ) &&
1197  getObjtypeParam( exec, 3, descriptor ) ) )
1198  {
1199  return new BError( "Invalid parameter type" );
1200  }
1201  if ( exec.hasParams( 5 ) )
1202  {
1203  if ( !getParam( 4, flags ) )
1204  return new BError( "Invalid parameter type" );
1205  }
1206  if ( exec.hasParams( 6 ) )
1207  {
1208  const String* strrealm;
1209  if ( !getStringParam( 5, strrealm ) )
1210  return new BError( "Invalid parameter type" );
1211  realm = find_realm( strrealm->value() );
1212  }
1213 
1214  if ( !realm )
1215  return new BError( "Realm not found" );
1216  if ( !realm->valid( x, y, z ) )
1217  return new BError( "Invalid Coordinates for Realm" );
1218  if ( descriptor->type != ItemDesc::BOATDESC && descriptor->type != ItemDesc::HOUSEDESC )
1219  {
1220  return new BError( "That objtype is not a Multi" );
1221  }
1222 
1223  return Multi::UMulti::scripted_create( *descriptor, x, y, static_cast<signed char>( z ), realm,
1224  flags );
1225 }
1226 
1228 {
1229  for ( BStruct::Contents::const_iterator citr = custom->contents().begin(),
1230  end = custom->contents().end();
1231  citr != end; ++citr )
1232  {
1233  const std::string& name = ( *citr ).first;
1234  BObjectImp* ref = ( *citr ).second->impptr();
1235 
1236  if ( name == "CProps" )
1237  {
1238  if ( ref->isa( BObjectImp::OTDictionary ) )
1239  {
1240  BDictionary* cpropdict = static_cast<BDictionary*>( ref );
1241  const BDictionary::Contents& cprop_cont = cpropdict->contents();
1242  BDictionary::Contents::const_iterator itr;
1243  for ( itr = cprop_cont.begin(); itr != cprop_cont.end(); ++itr )
1244  {
1245  elem.add_prop( "cprop", ( ( *itr ).first->getStringRep() + "\t" +
1246  ( *itr ).second->impptr()->pack() ) );
1247  }
1248  }
1249  else
1250  {
1251  throw std::runtime_error( "NPC override_properties: CProps must be a dictionary, but is: " +
1252  std::string( ref->typeOf() ) );
1253  }
1254  }
1255  else
1256  {
1257  elem.clear_prop( name.c_str() );
1258  elem.add_prop( name, ref->getStringRep() );
1259  }
1260  }
1261 }
1262 
1264 {
1265  const String* tmplname;
1266  unsigned short x, y;
1267  short z;
1268  const String* strrealm;
1269  Realms::Realm* realm = find_realm( "britannia" );
1270 
1271  if ( !( getStringParam( 0, tmplname ) && getParam( 1, x ) && getParam( 2, y ) &&
1272  getParam( 3, z, ZCOORD_MIN, ZCOORD_MAX ) ) )
1273  {
1274  return new BError( "Invalid parameter type" );
1275  }
1276  BObjectImp* imp = getParamImp( 4 );
1277  BStruct* custom_struct = nullptr;
1278  if ( imp->isa( BObjectImp::OTLong ) )
1279  {
1280  custom_struct = nullptr;
1281  }
1282  else if ( imp->isa( BObjectImp::OTStruct ) )
1283  {
1284  custom_struct = static_cast<BStruct*>( imp );
1285  }
1286  else
1287  {
1288  return new BError( std::string( "Parameter 4 must be a Struct or Integer(0), got " ) +
1289  BObjectImp::typestr( imp->type() ) );
1290  }
1291  if ( exec.hasParams( 6 ) )
1292  {
1293  if ( !getStringParam( 5, strrealm ) )
1294  return new BError( "Realm not found" );
1295  realm = find_realm( strrealm->value() );
1296  }
1297 
1298  if ( !realm )
1299  return new BError( "Realm not found" );
1300  if ( !realm->valid( x, y, z ) )
1301  return new BError( "Invalid Coordinates for Realm" );
1302 
1303  Clib::ConfigElem elem;
1304  START_PROFILECLOCK( npc_search );
1305  bool found = FindNpcTemplate( tmplname->data(), elem );
1306  STOP_PROFILECLOCK( npc_search );
1307  INC_PROFILEVAR( npc_searches );
1308 
1309  if ( !found )
1310  {
1311  return new BError( "NPC template '" + tmplname->value() + "' not found" );
1312  }
1313  MOVEMODE movemode = Character::decode_movemode( elem.read_string( "MoveMode", "L" ) );
1314 
1315  short newz;
1316  Multi::UMulti* dummy_multi;
1317  Item* dummy_walkon;
1318  if ( !realm->walkheight( x, y, z, &newz, &dummy_multi, &dummy_walkon, true, movemode ) )
1319  {
1320  return new BError( "Not a valid location for an NPC!" );
1321  }
1322  z = newz;
1323 
1324 
1325  NpcRef npc;
1326  // readProperties can throw, if stuff is missing.
1327  try
1328  {
1329  npc.set( new NPC( elem.remove_ushort( "OBJTYPE" ), elem ) );
1330  npc->set_dirty();
1331  elem.clear_prop( "Serial" );
1332  elem.clear_prop( "X" );
1333  elem.clear_prop( "Y" );
1334  elem.clear_prop( "Z" );
1335 
1336  elem.add_prop( "Serial", GetNextSerialNumber() );
1337  // FIXME sanity check
1338  elem.add_prop( "X", x );
1339  elem.add_prop( "Y", y );
1340  elem.add_prop( "Z", z );
1341  elem.add_prop( "Realm", realm->name() );
1342  if ( custom_struct != nullptr )
1343  replace_properties( elem, custom_struct );
1344  npc->readPropertiesForNewNPC( elem );
1345 
1349 
1350 
1351  // characters.push_back( npc.get() );
1354  npc.get(), [&]( Character* zonechr ) { send_char_data( zonechr->client, npc.get() ); } );
1355  realm->notify_entered( *npc );
1356 
1357  // FIXME: Need to add Walkon checks for multi right here if type is house.
1358  if ( dummy_multi )
1359  {
1360  dummy_multi->register_object( npc.get() );
1361  Multi::UHouse* this_house = dummy_multi->as_house();
1362  if ( npc->registered_house == 0 )
1363  {
1364  npc->registered_house = dummy_multi->serial;
1365 
1366  if ( this_house != nullptr )
1367  this_house->walk_on( npc.get() );
1368  }
1369  }
1370  else
1371  {
1372  if ( npc->registered_house > 0 )
1373  {
1374  Multi::UMulti* multi = system_find_multi( npc->registered_house );
1375  if ( multi != nullptr )
1376  {
1377  multi->unregister_object( npc.get() );
1378  }
1379  npc->registered_house = 0;
1380  }
1381  }
1382 
1383  return new ECharacterRefObjImp( npc.get() );
1384  }
1385  catch ( std::exception& ex )
1386  {
1387  if ( npc.get() != nullptr )
1388  npc->destroy();
1389  return new BError( "Exception detected trying to create npc from template '" +
1390  tmplname->value() + "': " + ex.what() );
1391  }
1392 }
1393 
1394 
1396 {
1397  Item* item;
1398  unsigned short amount;
1399 
1400  if ( getItemParam( exec, 0, item ) && getParam( 1, amount, 1, item->itemdesc().stack_limit ) )
1401  {
1402  if ( item->has_gotten_by() )
1403  item->gotten_by()->clear_gotten_item();
1404  else if ( item->inuse() && !is_reserved_to_me( item ) )
1405  return new BError( "That item is being used." );
1406  subtract_amount_from_item( item, amount );
1407  return new BLong( 1 );
1408  }
1409  else
1410  {
1411  return new BError( "Invalid parameter type" );
1412  }
1413 }
1414 
1416 {
1417  Item* item;
1418  unsigned short amount;
1419 
1420  if ( getItemParam( exec, 0, item ) && getParam( 1, amount, 1, MAX_STACK_ITEMS ) )
1421  {
1422  if ( item->inuse() && !is_reserved_to_me( item ) )
1423  {
1424  return new BError( "That item is being used." );
1425  }
1426  if ( ~tile_flags( item->graphic ) & Plib::FLAG::STACKABLE )
1427  {
1428  return new BError( "That item type is not stackable." );
1429  }
1430  if ( !item->can_add_to_self( amount, false ) )
1431  {
1432  return new BError( "Can't add that much to that stack" );
1433  }
1434 
1435  unsigned short newamount = item->getamount();
1436  newamount += amount;
1437  item->setamount( newamount );
1438  update_item_to_inrange( item );
1439 
1440  // DAVE added this 12/05: if in a Character's pack, update weight.
1441  UpdateCharacterWeight( item );
1442 
1443  return new BLong( 1 );
1444  }
1445  else
1446  {
1447  return new BError( "Invalid parameter type" );
1448  }
1449 }
1450 
1452 {
1453  Character* chr;
1454  unsigned short actionval;
1455  unsigned short framecount, repeatcount;
1456  unsigned short backward, repeatflag, delay;
1457  if ( getCharacterParam( exec, 0, chr ) && getParam( 1, actionval ) && getParam( 2, framecount ) &&
1458  getParam( 3, repeatcount ) && getParam( 4, backward ) && getParam( 5, repeatflag ) &&
1459  getParam( 6, delay ) )
1460  {
1461  UACTION action = static_cast<UACTION>( actionval );
1463  chr, action, framecount, repeatcount, static_cast<DIRECTION_FLAG_OLD>( backward ),
1464  static_cast<REPEAT_FLAG_OLD>( repeatflag ), static_cast<unsigned char>( delay ) );
1465  return new BLong( 1 );
1466  }
1467  else
1468  {
1469  return new BError( "Invalid parameter" );
1470  }
1471 }
1472 
1474 {
1475  UObject* chr;
1476  int effect;
1477  if ( getUObjectParam( exec, 0, chr ) && getParam( 1, effect ) )
1478  {
1479  play_sound_effect( chr, static_cast<u16>( effect ) );
1480  return new BLong( 1 );
1481  }
1482  else
1483  {
1484  return new BError( "Invalid parameter" );
1485  }
1486 }
1487 
1489 {
1490  UObject* center;
1491  int effect;
1492  Character* forchr;
1493  if ( getUObjectParam( exec, 0, center ) && getParam( 1, effect ) &&
1494  getCharacterParam( exec, 2, forchr ) )
1495  {
1496  play_sound_effect_private( center, static_cast<u16>( effect ), forchr );
1497  return new BLong( 1 );
1498  }
1499  else
1500  {
1501  return new BError( "Invalid parameter" );
1502  }
1503 }
1504 
1506 {
1507  unsigned short cx, cy;
1508  short cz;
1509  int effect;
1510  const String* strrealm;
1511  if ( getParam( 0, cx ) && getParam( 1, cy ) && getParam( 2, cz ) && getParam( 3, effect ) &&
1512  getStringParam( 4, strrealm ) )
1513  {
1514  Realms::Realm* realm = find_realm( strrealm->value() );
1515  if ( !realm )
1516  return new BError( "Realm not found" );
1517  if ( !realm->valid( cx, cy, cz ) )
1518  return new BError( "Invalid Coordinates for realm" );
1519  play_sound_effect_xyz( cx, cy, static_cast<signed char>( cz ), static_cast<u16>( effect ),
1520  realm );
1521  return new BLong( 1 );
1522  }
1523  else
1524  {
1525  return new BError( "Invalid parameter" );
1526  }
1527 }
1528 
1530 {
1531  Character* chr;
1532  int musicid;
1533  if ( getCharacterParam( exec, 0, chr ) && getParam( 1, musicid ) )
1534  {
1535  send_midi( chr->client, static_cast<u16>( musicid ) );
1536  return new BLong( 1 );
1537  }
1538  else
1539  {
1540  return new BError( "Invalid parameter" );
1541  }
1542 }
1543 
1545 {
1546  if ( client != nullptr )
1547  {
1548  Character* chr = client->chr;
1549  if ( chr != nullptr && chr->client->gd->menu_selection_uoemod != nullptr )
1550  {
1551  if ( mi != nullptr && msg != nullptr )
1552  {
1553  BStruct* selection = new BStruct;
1554  // FIXME should make sure objtype and choice are within valid range.
1555  selection->addMember( "objtype", new BLong( mi->objtype_ ) );
1556  selection->addMember( "graphic", new BLong( mi->graphic_ ) );
1557  selection->addMember( "index",
1558  new BLong( cfBEu16( msg->choice ) ) ); // this has been validated
1559  selection->addMember( "color", new BLong( mi->color_ ) );
1560  chr->client->gd->menu_selection_uoemod->uoexec.ValueStack.back().set(
1561  new BObject( selection ) );
1562  }
1563  // 0 is already on the value stack, for the case of cancellation.
1566  chr->client->gd->menu_selection_uoemod = nullptr;
1567  }
1568  }
1569 }
1570 
1571 bool UOExecutorModule::getDynamicMenuParam( unsigned param, Menu*& menu )
1572 {
1573  BApplicObjBase* aob = getApplicObjParam( param, &menu_type );
1574  if ( aob != nullptr )
1575  {
1576  EMenuObjImp* menu_imp = static_cast<EMenuObjImp*>( aob );
1577  menu = &menu_imp->value();
1578  return true;
1579  }
1580  else
1581  {
1582  return false;
1583  }
1584 }
1585 
1587 {
1588  BObjectImp* imp = getParamImp( param );
1589  if ( imp->isa( BObjectImp::OTString ) )
1590  {
1591  String* pmenuname = static_cast<String*>( imp );
1592  menu = Menu::find_menu( pmenuname->data() );
1593  return ( menu != nullptr );
1594  }
1595  else if ( imp->isa( BObjectImp::OTApplicObj ) )
1596  {
1597  BApplicObjBase* aob = static_cast<BApplicObjBase*>( imp );
1598  if ( aob->object_type() == &menu_type )
1599  {
1600  EMenuObjImp* menu_imp = static_cast<EMenuObjImp*>( aob );
1601  menu = &menu_imp->value();
1602  return true;
1603  }
1604  }
1605  DEBUGLOG << "SelectMenuItem: expected a menu name (static menu) or a CreateMenu() dynamic menu\n";
1606  return false;
1607 }
1608 
1610 {
1611  Character* chr;
1612  Menu* menu;
1613 
1614  if ( !getCharacterParam( exec, 0, chr ) || !getStaticOrDynamicMenuParam( 1, menu ) ||
1615  ( chr->client->gd->menu_selection_uoemod != nullptr ) )
1616  {
1617  return new BError( "Invalid parameter" );
1618  }
1619 
1620  if ( menu == nullptr || !chr->has_active_client() || menu->menuitems_.empty() )
1621  {
1622  return new BError( "Client is busy, or menu is empty" );
1623  }
1624 
1625  if ( !send_menu( chr->client, menu ) )
1626  {
1627  return new BError( "Menu too large" );
1628  }
1629 
1630  if ( !uoexec.suspend() )
1631  {
1632  DEBUGLOG << "Script Error in '" << scriptname() << "' PC=" << exec.PC << ": \n"
1633  << "\tCall to function UO::SelectMenuItem():\n"
1634  << "\tThe execution of this script can't be blocked!\n";
1635  return new Bscript::BError( "Script can't be blocked" );
1636  }
1637 
1638  chr->menu = menu->getWeakPtr();
1640  chr->client->gd->menu_selection_uoemod = this;
1641  menu_selection_chr = chr;
1642 
1643  return new BLong( 0 );
1644 }
1645 
1646 void append_objtypes( ObjArray* objarr, Menu* menu )
1647 {
1648  for ( unsigned i = 0; i < menu->menuitems_.size(); ++i )
1649  {
1650  MenuItem* mi = &menu->menuitems_[i];
1651 
1652  if ( mi->submenu_id )
1653  {
1654  // Code Analyze: Commented out and replaced with tmp_menu due to hiding
1655  // menu passed to function.
1656  // Menu* menu = find_menu( mi->submenu_id );
1657  Menu* tmp_menu = Menu::find_menu( mi->submenu_id );
1658  if ( tmp_menu != nullptr )
1659  append_objtypes( objarr, tmp_menu );
1660  }
1661  else
1662  {
1663  objarr->addElement( new BLong( mi->objtype_ ) );
1664  }
1665  }
1666 }
1667 
1669 {
1670  Menu* menu;
1671  if ( getStaticOrDynamicMenuParam( 0, menu ) )
1672  {
1673  std::unique_ptr<ObjArray> objarr( new ObjArray );
1674 
1675  append_objtypes( objarr.get(), menu );
1676 
1677  return objarr.release();
1678  }
1679  return new BLong( 0 );
1680 }
1681 
1683 {
1684  ObjArray* arr;
1685  StoredConfigFile* cfile;
1686  const String* propname_str;
1687  int amthave;
1688 
1689  if ( getObjArrayParam( 0, arr ) && getStoredConfigFileParam( *this, 1, cfile ) &&
1690  getStringParam( 2, propname_str ) && getParam( 3, amthave ) )
1691  {
1692  std::unique_ptr<ObjArray> newarr( new ObjArray );
1693 
1694  for ( unsigned i = 0; i < arr->ref_arr.size(); i++ )
1695  {
1696  BObjectRef& ref = arr->ref_arr[i];
1697 
1698  BObject* bo = ref.get();
1699  if ( bo == nullptr )
1700  continue;
1701  if ( !bo->isa( BObjectImp::OTLong ) )
1702  continue;
1703  BLong* blong = static_cast<BLong*>( bo->impptr() );
1704  unsigned int objtype = static_cast<u32>( blong->value() );
1705 
1706  ref_ptr<StoredConfigElem> celem = cfile->findelem( objtype );
1707  if ( celem.get() == nullptr )
1708  continue;
1709 
1710  BObjectImp* propval = celem->getimp( propname_str->value() );
1711  if ( propval == nullptr )
1712  continue;
1713  if ( !propval->isa( BObjectImp::OTLong ) )
1714  continue;
1715  BLong* amtreq = static_cast<BLong*>( propval );
1716  if ( amtreq->value() > amthave )
1717  continue;
1718  newarr->addElement( new BLong( objtype ) );
1719  }
1720 
1721  return newarr.release();
1722  }
1723 
1724  return new UninitObject;
1725 }
1726 
1728 {
1729  const String* title;
1730  if ( getStringParam( 0, title ) )
1731  {
1732  Menu temp;
1733  temp.menu_id = 0;
1734  strzcpy( temp.title, title->data(), sizeof temp.title );
1735  return new EMenuObjImp( temp );
1736  }
1737  return new BLong( 0 );
1738 }
1739 
1741 {
1742  Menu* menu;
1743  unsigned int objtype;
1744  const String* text;
1745  unsigned short color;
1746 
1747  if ( getDynamicMenuParam( 0, menu ) && getObjtypeParam( exec, 1, objtype ) &&
1748  getStringParam( 2, text ) && getParam( 3, color ) )
1749  {
1750  menu->menuitems_.push_back( MenuItem() );
1751  MenuItem* mi = &menu->menuitems_.back();
1752  mi->objtype_ = objtype;
1753  mi->graphic_ = getgraphic( objtype );
1754  strzcpy( mi->title, text->data(), sizeof mi->title );
1756  return new BLong( 1 );
1757  }
1758  else
1759  {
1760  return new BLong( 0 );
1761  }
1762 }
1763 
1765 {
1766  UObject* uobj;
1767  const String* propname_str;
1768  if ( getUObjectParam( exec, 0, uobj ) && getStringParam( 1, propname_str ) )
1769  {
1770  std::string val;
1771  if ( uobj->getprop( propname_str->value(), val ) )
1772  {
1773  return BObjectImp::unpack( val.c_str() );
1774  }
1775  else
1776  {
1777  return new BError( "Property not found" );
1778  }
1779  }
1780  else
1781  {
1782  return new BError( "Invalid parameter type" );
1783  }
1784 }
1785 
1787 {
1788  UObject* uobj;
1789  const String* propname_str;
1790  if ( getUObjectParam( exec, 0, uobj ) && getStringParam( 1, propname_str ) )
1791  {
1792  BObjectImp* propval = getParamImp( 2 );
1793  uobj->setprop( propname_str->value(), propval->pack() );
1794  return new BLong( 1 );
1795  }
1796  else
1797  {
1798  return new BError( "Invalid parameter type" );
1799  }
1800 }
1801 
1803 {
1804  UObject* uobj;
1805  const String* propname_str;
1806  if ( getUObjectParam( exec, 0, uobj ) && getStringParam( 1, propname_str ) )
1807  {
1808  uobj->eraseprop( propname_str->value() );
1809  return new BLong( 1 );
1810  }
1811  else
1812  {
1813  return new BError( "Invalid parameter type" );
1814  }
1815 }
1817 {
1818  UObject* uobj;
1819  if ( getUObjectParam( exec, 0, uobj ) )
1820  {
1821  std::vector<std::string> propnames;
1822  uobj->getpropnames( propnames );
1823  std::unique_ptr<ObjArray> arr( new ObjArray );
1824  for ( unsigned i = 0; i < propnames.size(); ++i )
1825  {
1826  arr->addElement( new String( propnames[i] ) );
1827  }
1828  return arr.release();
1829  }
1830  else
1831  {
1832  return new BError( "Invalid parameter type" );
1833  }
1834 }
1835 
1836 
1838 {
1839  const String* propname_str;
1840  if ( getStringParam( 0, propname_str ) )
1841  {
1842  std::string val;
1843  if ( gamestate.global_properties->getprop( propname_str->value(), val ) )
1844  {
1845  return BObjectImp::unpack( val.c_str() );
1846  }
1847  else
1848  {
1849  return new BError( "Property not found" );
1850  }
1851  }
1852  else
1853  {
1854  return new BError( "Invalid parameter type" );
1855  }
1856 }
1857 
1859 {
1860  const String* propname_str;
1861  if ( exec.getStringParam( 0, propname_str ) )
1862  {
1863  BObjectImp* propval = exec.getParamImp( 1 );
1864  gamestate.global_properties->setprop( propname_str->value(), propval->pack() );
1865  return new BLong( 1 );
1866  }
1867  else
1868  {
1869  return new BError( "Invalid parameter type" );
1870  }
1871 }
1872 
1874 {
1875  const String* propname_str;
1876  if ( getStringParam( 0, propname_str ) )
1877  {
1878  gamestate.global_properties->eraseprop( propname_str->value() );
1879  return new BLong( 1 );
1880  }
1881  else
1882  {
1883  return new BError( "Invalid parameter type" );
1884  }
1885 }
1886 
1888 {
1889  std::vector<std::string> propnames;
1890  gamestate.global_properties->getpropnames( propnames );
1891  std::unique_ptr<ObjArray> arr( new ObjArray );
1892  for ( unsigned i = 0; i < propnames.size(); ++i )
1893  {
1894  arr->addElement( new String( propnames[i] ) );
1895  }
1896  return arr.release();
1897 }
1898 
1900 {
1901  UObject* src;
1902  UObject* dst;
1903  unsigned short effect;
1904  int speed;
1905  int loop;
1906  int explode;
1907  if ( getUObjectParam( exec, 0, src ) && getUObjectParam( exec, 1, dst ) &&
1908  getParam( 2, effect ) && getParam( 3, speed, UCHAR_MAX ) && getParam( 4, loop, UCHAR_MAX ) &&
1909  getParam( 5, explode, UCHAR_MAX ) )
1910  {
1911  if ( src->realm != dst->realm )
1912  return new BError( "Realms must match" );
1913  play_moving_effect( src, dst, effect, static_cast<unsigned char>( speed ),
1914  static_cast<unsigned char>( loop ), static_cast<unsigned char>( explode ) );
1915  return new BLong( 1 );
1916  }
1917  else
1918  {
1919  return new BLong( 0 );
1920  }
1921 }
1922 
1924 {
1925  unsigned short sx, sy;
1926  unsigned short dx, dy;
1927  short sz, dz;
1928 
1929  unsigned short effect;
1930  int speed;
1931  int loop;
1932  int explode;
1933  const String* strrealm;
1934 
1935  if ( getParam( 0, sx ) && getParam( 1, sy ) && getParam( 2, sz ) && getParam( 3, dx ) &&
1936  getParam( 4, dy ) && getParam( 5, dz ) && getParam( 6, effect ) &&
1937  getParam( 7, speed, UCHAR_MAX ) && getParam( 8, loop, UCHAR_MAX ) &&
1938  getParam( 9, explode, UCHAR_MAX ) && getStringParam( 10, strrealm ) )
1939  {
1940  Realms::Realm* realm = find_realm( strrealm->value() );
1941  if ( !realm )
1942  return new BError( "Realm not found" );
1943  if ( !realm->valid( sx, sy, sz ) || !realm->valid( dx, dy, dz ) )
1944  return new BError( "Invalid Coordinates for realm" );
1945  play_moving_effect2( sx, sy, static_cast<signed char>( sz ), dx, dy,
1946  static_cast<signed char>( dz ), effect, static_cast<signed char>( speed ),
1947  static_cast<signed char>( loop ), static_cast<signed char>( explode ),
1948  realm );
1949  return new BLong( 1 );
1950  }
1951  else
1952  {
1953  return nullptr;
1954  }
1955 }
1956 
1957 // FIXME add/test:stationary
1958 
1960 {
1961  UObject* src;
1962  unsigned short effect;
1963  int speed;
1964  int loop;
1965  if ( getUObjectParam( exec, 0, src ) && getParam( 1, effect ) &&
1966  getParam( 2, speed, UCHAR_MAX ) && getParam( 3, loop, UCHAR_MAX ) )
1967  {
1968  play_object_centered_effect( src, effect, static_cast<unsigned char>( speed ),
1969  static_cast<unsigned char>( loop ) );
1970  return new BLong( 1 );
1971  }
1972  else
1973  {
1974  return nullptr;
1975  }
1976 }
1977 
1979 {
1980  unsigned short x, y;
1981  short z;
1982  unsigned short effect;
1983  int speed, loop, explode;
1984  const String* strrealm;
1985  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, effect ) &&
1986  getParam( 4, speed, UCHAR_MAX ) && getParam( 5, loop, UCHAR_MAX ) &&
1987  getParam( 6, explode, UCHAR_MAX ) && getStringParam( 7, strrealm ) )
1988  {
1989  Realms::Realm* realm = find_realm( strrealm->value() );
1990  if ( !realm )
1991  return new BError( "Realm not found" );
1992  if ( !realm->valid( x, y, z ) )
1993  return new BError( "Invalid Coordinates for realm" );
1994 
1995  play_stationary_effect( x, y, static_cast<signed char>( z ), effect,
1996  static_cast<unsigned char>( speed ), static_cast<unsigned char>( loop ),
1997  static_cast<unsigned char>( explode ), realm );
1998  return new BLong( 1 );
1999  }
2000  else
2001  {
2002  return new BError( "Invalid parameter" );
2003  }
2004 }
2005 
2006 
2008 {
2009  UObject* src;
2010  UObject* dst;
2011  unsigned short effect;
2012  int speed;
2013  int duration;
2014  int hue;
2015  int render;
2016  int direction;
2017  int explode;
2018  unsigned short effect3d;
2019  unsigned short effect3dexplode;
2020  unsigned short effect3dsound;
2021 
2022  if ( getUObjectParam( exec, 0, src ) && getUObjectParam( exec, 1, dst ) &&
2023  getParam( 2, effect ) && getParam( 3, speed, UCHAR_MAX ) &&
2024  getParam( 4, duration, UCHAR_MAX ) && getParam( 5, hue, INT_MAX ) &&
2025  getParam( 6, render, INT_MAX ) && getParam( 7, direction, UCHAR_MAX ) &&
2026  getParam( 8, explode, UCHAR_MAX ) && getParam( 9, effect3d ) &&
2027  getParam( 10, effect3dexplode ) && getParam( 11, effect3dsound ) )
2028  {
2029  if ( src->realm != dst->realm )
2030  return new BError( "Realms must match" );
2032  src, dst, effect, static_cast<unsigned char>( speed ),
2033  static_cast<unsigned char>( duration ), static_cast<unsigned int>( hue ),
2034  static_cast<unsigned int>( render ), static_cast<unsigned char>( direction ),
2035  static_cast<unsigned char>( explode ), effect3d, effect3dexplode, effect3dsound );
2036  return new BLong( 1 );
2037  }
2038  else
2039  {
2040  return new BLong( 0 );
2041  }
2042 }
2043 
2045 {
2046  unsigned short sx, sy;
2047  unsigned short dx, dy;
2048  short sz, dz;
2049  const String* strrealm;
2050 
2051  unsigned short effect;
2052  int speed;
2053  int duration;
2054  int hue;
2055  int render;
2056  int direction;
2057  int explode;
2058  unsigned short effect3d;
2059  unsigned short effect3dexplode;
2060  unsigned short effect3dsound;
2061 
2062  if ( getParam( 0, sx ) && getParam( 1, sy ) && getParam( 2, sz ) && getParam( 3, dx ) &&
2063  getParam( 4, dy ) && getParam( 5, dz ) && getStringParam( 6, strrealm ) &&
2064  getParam( 7, effect ) && getParam( 8, speed, UCHAR_MAX ) &&
2065  getParam( 9, duration, UCHAR_MAX ) && getParam( 10, hue, INT_MAX ) &&
2066  getParam( 11, render, INT_MAX ) && getParam( 12, direction, UCHAR_MAX ) &&
2067  getParam( 13, explode, UCHAR_MAX ) && getParam( 14, effect3d ) &&
2068  getParam( 15, effect3dexplode ) && getParam( 16, effect3dsound ) )
2069  {
2070  Realms::Realm* realm = find_realm( strrealm->value() );
2071  if ( !realm )
2072  return new BError( "Realm not found" );
2073  if ( !realm->valid( sx, sy, sz ) || !realm->valid( dx, dy, dz ) )
2074  return new BError( "Invalid Coordinates for realm" );
2076  sx, sy, static_cast<signed char>( sz ), dx, dy, static_cast<signed char>( dz ), realm,
2077  effect, static_cast<unsigned char>( speed ), static_cast<unsigned char>( duration ),
2078  static_cast<unsigned int>( hue ), static_cast<unsigned int>( render ),
2079  static_cast<unsigned char>( direction ), static_cast<unsigned char>( explode ), effect3d,
2080  effect3dexplode, effect3dsound );
2081  return new BLong( 1 );
2082  }
2083  else
2084  {
2085  return nullptr;
2086  }
2087 }
2088 
2090 {
2091  UObject* src;
2092  unsigned short effect;
2093  int speed;
2094  int duration;
2095  int hue;
2096  int render;
2097  int layer;
2098  unsigned short effect3d;
2099 
2100  if ( getUObjectParam( exec, 0, src ) && getParam( 1, effect ) &&
2101  getParam( 2, speed, UCHAR_MAX ) && getParam( 3, duration, UCHAR_MAX ) &&
2102  getParam( 4, hue, INT_MAX ) && getParam( 5, render, INT_MAX ) &&
2103  getParam( 6, layer, UCHAR_MAX ) && getParam( 7, effect3d ) )
2104  {
2106  src, effect, static_cast<unsigned char>( speed ), static_cast<unsigned char>( duration ),
2107  static_cast<unsigned int>( hue ), static_cast<unsigned int>( render ),
2108  static_cast<unsigned char>( layer ), effect3d );
2109  return new BLong( 1 );
2110  }
2111  else
2112  {
2113  return nullptr;
2114  }
2115 }
2116 
2118 {
2119  unsigned short x, y;
2120  short z;
2121  const String* strrealm;
2122  unsigned short effect;
2123  int speed;
2124  int duration;
2125  int hue;
2126  int render;
2127  unsigned short effect3d;
2128 
2129  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getStringParam( 3, strrealm ) &&
2130  getParam( 4, effect ) && getParam( 5, speed, UCHAR_MAX ) &&
2131  getParam( 6, duration, UCHAR_MAX ) && getParam( 7, hue, INT_MAX ) &&
2132  getParam( 8, render, INT_MAX ) && getParam( 9, effect3d ) )
2133  {
2134  Realms::Realm* realm = find_realm( strrealm->value() );
2135  if ( !realm )
2136  return new BError( "Realm not found" );
2137  if ( !realm->valid( x, y, z ) )
2138  return new BError( "Invalid Coordinates for realm" );
2139 
2141  x, y, static_cast<signed char>( z ), realm, effect, static_cast<unsigned char>( speed ),
2142  static_cast<unsigned char>( duration ), static_cast<unsigned int>( hue ),
2143  static_cast<unsigned int>( render ), effect3d );
2144  return new BLong( 1 );
2145  }
2146  else
2147  {
2148  return new BError( "Invalid parameter" );
2149  }
2150 }
2151 
2153 {
2154  UObject* center;
2155  if ( getUObjectParam( exec, 0, center ) )
2156  {
2157  play_lightning_bolt_effect( center );
2158  return new BLong( 1 );
2159  }
2160  else
2161  {
2162  return new BLong( 0 );
2163  }
2164 }
2165 
2167 {
2168  unsigned short x, y;
2169  int z;
2170  short range;
2171  const String* strrealm;
2172  Realms::Realm* realm;
2173 
2174  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, range ) &&
2175  getStringParam( 4, strrealm ) )
2176  {
2177  realm = find_realm( strrealm->value() );
2178  if ( !realm )
2179  return new BError( "Realm not found" );
2180 
2181  if ( z == LIST_IGNORE_Z )
2182  {
2183  if ( !realm->valid( x, y, 0 ) )
2184  return new BError( "Invalid Coordinates for realm" );
2185  }
2186  else
2187  {
2188  if ( !realm->valid( x, y, static_cast<short>( z ) ) )
2189  return new BError( "Invalid Coordinates for realm" );
2190  }
2191 
2192  std::unique_ptr<ObjArray> newarr( new ObjArray );
2193  WorldIterator<ItemFilter>::InRange( x, y, realm, range, [&]( Item* item ) {
2194  if ( ( abs( item->x - x ) <= range ) && ( abs( item->y - y ) <= range ) )
2195  {
2196  if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z - z ) < CONST_DEFAULT_ZRANGE ) )
2197  newarr->addElement( item->make_ref() );
2198  }
2199  } );
2200 
2201  return newarr.release();
2202  }
2203 
2204  return nullptr;
2205 }
2206 
2207 void UOExecutorModule::internal_InBoxAreaChecks( unsigned short& /*x1*/, unsigned short& /*y1*/,
2208  int& z1, unsigned short& x2, unsigned short& y2,
2209  int& z2, Realms::Realm* realm )
2210 {
2211  if ( z1 < ZCOORD_MIN || z1 == LIST_IGNORE_Z )
2212  z1 = ZCOORD_MIN;
2213 
2214  if ( x2 >= realm->width() )
2215  x2 = ( realm->width() - 1 );
2216  if ( y2 >= realm->height() )
2217  y2 = ( realm->height() - 1 );
2218  if ( z2 > ZCOORD_MAX || z2 == LIST_IGNORE_Z )
2219  z2 = ZCOORD_MAX;
2220 }
2221 
2222 BObjectImp* UOExecutorModule::mf_ListObjectsInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
2223 {
2224  unsigned short x1, y1;
2225  int z1;
2226  unsigned short x2, y2;
2227  int z2;
2228  const String* strrealm;
2229  Realms::Realm* realm;
2230 
2231  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, z1 ) && getParam( 3, x2 ) &&
2232  getParam( 4, y2 ) && getParam( 5, z2 ) && getStringParam( 6, strrealm ) ) )
2233  {
2234  return new BError( "Invalid parameter" );
2235  }
2236 
2237  realm = find_realm( strrealm->value() );
2238  if ( !realm )
2239  return new BError( "Realm not found" );
2240 
2241  if ( x1 > x2 )
2242  std::swap( x1, x2 );
2243  if ( y1 > y2 )
2244  std::swap( y1, y2 );
2245  if ( ( z1 > z2 ) && z1 != LIST_IGNORE_Z && z2 != LIST_IGNORE_Z )
2246  std::swap( z1, z2 );
2247  // Disabled again: ShardAdmins "loves" this "bug" :o/
2248  // if ((!realm->valid(x1, y1, z1)) || (!realm->valid(x2, y2, z2)))
2249  // return new BError("Invalid Coordinates for realm");
2250  internal_InBoxAreaChecks( x1, y1, z1, x2, y2, z2, realm );
2251 
2252  std::unique_ptr<ObjArray> newarr( new ObjArray );
2253  WorldIterator<MobileFilter>::InBox( x1, y1, x2, y2, realm, [&]( Mobile::Character* chr ) {
2254  if ( chr->z >= z1 && chr->z <= z2 )
2255  {
2256  newarr->addElement( chr->make_ref() );
2257  }
2258  } );
2259  WorldIterator<ItemFilter>::InBox( x1, y1, x2, y2, realm, [&]( Items::Item* item ) {
2260  if ( item->z >= z1 && item->z <= z2 )
2261  {
2262  newarr->addElement( item->make_ref() );
2263  }
2264  } );
2265 
2266  return newarr.release();
2267 }
2268 
2269 BObjectImp* UOExecutorModule::mf_ListMobilesInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
2270 {
2271  unsigned short x1, y1;
2272  int z1;
2273  unsigned short x2, y2;
2274  int z2;
2275  const String* strrealm;
2276  Realms::Realm* realm;
2277 
2278  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, z1 ) && getParam( 3, x2 ) &&
2279  getParam( 4, y2 ) && getParam( 5, z2 ) && getStringParam( 6, strrealm ) ) )
2280  {
2281  return new BError( "Invalid parameter" );
2282  }
2283 
2284  realm = find_realm( strrealm->value() );
2285  if ( !realm )
2286  return new BError( "Realm not found" );
2287 
2288  if ( x1 > x2 )
2289  std::swap( x1, x2 );
2290  if ( y1 > y2 )
2291  std::swap( y1, y2 );
2292  if ( ( z1 > z2 ) && z1 != LIST_IGNORE_Z && z2 != LIST_IGNORE_Z )
2293  std::swap( z1, z2 );
2294  // Disabled again: ShardAdmins "loves" this "bug" :o/
2295  // if ((!realm->valid(x1, y1, z1)) || (!realm->valid(x2, y2, z2)))
2296  // return new BError("Invalid Coordinates for realm");
2297  internal_InBoxAreaChecks( x1, y1, z1, x2, y2, z2, realm );
2298 
2299  std::unique_ptr<ObjArray> newarr( new ObjArray );
2300  WorldIterator<MobileFilter>::InBox( x1, y1, x2, y2, realm, [&]( Mobile::Character* chr ) {
2301  if ( chr->z >= z1 && chr->z <= z2 )
2302  {
2303  newarr->addElement( chr->make_ref() );
2304  }
2305  } );
2306 
2307  return newarr.release();
2308 }
2309 
2310 BObjectImp* UOExecutorModule::mf_ListMultisInBox( /* x1, y1, z1, x2, y2, z2, realm */ )
2311 {
2312  unsigned short x1, y1;
2313  int z1;
2314  unsigned short x2, y2;
2315  int z2;
2316  const String* strrealm;
2317  Realms::Realm* realm;
2318 
2319  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, z1 ) && getParam( 3, x2 ) &&
2320  getParam( 4, y2 ) && getParam( 5, z2 ) && getStringParam( 6, strrealm ) ) )
2321  {
2322  return new BError( "Invalid parameter" );
2323  }
2324 
2325  realm = find_realm( strrealm->value() );
2326  if ( !realm )
2327  return new BError( "Realm not found" );
2328 
2329  if ( x1 > x2 )
2330  std::swap( x1, x2 );
2331  if ( y1 > y2 )
2332  std::swap( y1, y2 );
2333  if ( ( z1 > z2 ) && z1 != LIST_IGNORE_Z && z2 != LIST_IGNORE_Z )
2334  std::swap( z1, z2 );
2335  // Disabled again: ShardAdmins "loves" this "bug" :o/
2336  // if ((!realm->valid(x1, y1, z1)) || (!realm->valid(x2, y2, z2)))
2337  // return new BError("Invalid Coordinates for realm");
2338  internal_InBoxAreaChecks( x1, y1, z1, x2, y2, z2, realm );
2339 
2340  std::unique_ptr<ObjArray> newarr( new ObjArray );
2341 
2342  // extend the coords to find the center item
2343  // but only as parameter for the filter function
2344  unsigned short x1range = x1;
2345  unsigned short x2range = x2 + RANGE_VISUAL_LARGE_BUILDINGS;
2346  unsigned short y1range = y1;
2347  unsigned short y2range = y2 + RANGE_VISUAL_LARGE_BUILDINGS;
2348 
2349  if ( x1range >= RANGE_VISUAL_LARGE_BUILDINGS )
2350  x1range -= RANGE_VISUAL_LARGE_BUILDINGS;
2351  else
2352  x1range = 0;
2353  if ( y1range >= RANGE_VISUAL_LARGE_BUILDINGS )
2354  y1range -= RANGE_VISUAL_LARGE_BUILDINGS;
2355  else
2356  y1range = 0;
2357 
2358  internal_InBoxAreaChecks( x1range, y1range, z1, x2range, y2range, z2, realm );
2359  // search for multis. this is tricky, since the center might lie outside the box
2361  x1range, y1range, x2range, y2range, realm, [&]( Multi::UMulti* multi ) {
2362  const Multi::MultiDef& md = multi->multidef();
2363  if ( multi->x + md.minrx > x2 || // east of the box
2364  multi->x + md.maxrx < x1 || // west of the box
2365  multi->y + md.minry > y2 || // south of the box
2366  multi->y + md.maxry < y1 || // north of the box
2367  multi->z + md.minrz > z2 || // above the box
2368  multi->z + md.maxrz < z1 ) // below the box
2369  {
2370  return;
2371  }
2372  // some part of it is contained in the box. Look at the individual statics, to see
2373  // if any of them lie within.
2374  for ( const auto& citr : md.components )
2375  {
2376  const Multi::MULTI_ELEM* elem = citr.second;
2377  int absx = multi->x + elem->x;
2378  int absy = multi->y + elem->y;
2379  int absz = multi->z + elem->z;
2380  if ( x1 <= absx && absx <= x2 && y1 <= absy && absy <= y2 )
2381  {
2382  // do Z checking
2383  int height = tileheight( getgraphic( elem->objtype ) );
2384  int top = absz + height;
2385 
2386  if ( ( z1 <= absz && absz <= z2 ) || // bottom point lies between
2387  ( z1 <= top && top <= z2 ) || // top lies between
2388  ( top >= z2 && absz <= z1 ) ) // spans
2389  {
2390  newarr->addElement( multi->make_ref() );
2391  break; // out of for
2392  }
2393  }
2394  }
2395  } );
2396 
2397  return newarr.release();
2398 }
2399 
2400 BObjectImp* UOExecutorModule::mf_ListStaticsInBox( /* x1, y1, z1, x2, y2, z2, flags, realm */ )
2401 {
2402  unsigned short x1, y1;
2403  unsigned short x2, y2;
2404  int z1, z2;
2405  int flags;
2406  const String* strrealm;
2407 
2408  if ( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, z1 ) && getParam( 3, x2 ) &&
2409  getParam( 4, y2 ) && getParam( 5, z2 ) && getParam( 6, flags ) &&
2410  getStringParam( 7, strrealm ) )
2411  {
2412  Realms::Realm* realm = find_realm( strrealm->value() );
2413  if ( !realm )
2414  return new BError( "Realm not found" );
2415 
2416  if ( x1 > x2 )
2417  std::swap( x1, x2 );
2418  if ( y1 > y2 )
2419  std::swap( y1, y2 );
2420  if ( ( z1 > z2 ) && z1 != LIST_IGNORE_Z && z2 != LIST_IGNORE_Z )
2421  std::swap( z1, z2 );
2422  // Disabled again: ShardAdmins "loves" this "bug" :o/
2423  // if ((!realm->valid(x1, y1, z1)) || (!realm->valid(x2, y2, z2)))
2424  // return new BError("Invalid Coordinates for realm");
2425  internal_InBoxAreaChecks( x1, y1, z1, x2, y2, z2, realm );
2426 
2427  std::unique_ptr<ObjArray> newarr( new ObjArray );
2428 
2429  for ( unsigned short wx = x1; wx <= x2; ++wx )
2430  {
2431  for ( unsigned short wy = y1; wy <= y2; ++wy )
2432  {
2433  if ( !( flags & ITEMS_IGNORE_STATICS ) )
2434  {
2435  Plib::StaticEntryList slist;
2436  realm->getstatics( slist, wx, wy );
2437 
2438  for ( unsigned i = 0; i < slist.size(); ++i )
2439  {
2440  if ( ( z1 <= slist[i].z ) && ( slist[i].z <= z2 ) )
2441  {
2442  std::unique_ptr<BStruct> arr( new BStruct );
2443  arr->addMember( "x", new BLong( wx ) );
2444  arr->addMember( "y", new BLong( wy ) );
2445  arr->addMember( "z", new BLong( slist[i].z ) );
2446  arr->addMember( "objtype", new BLong( slist[i].objtype ) );
2447  arr->addMember( "hue", new BLong( slist[i].hue ) );
2448  newarr->addElement( arr.release() );
2449  }
2450  }
2451  }
2452 
2453  if ( !( flags & ITEMS_IGNORE_MULTIS ) )
2454  {
2455  StaticList mlist;
2456  realm->readmultis( mlist, wx, wy );
2457 
2458  for ( unsigned i = 0; i < mlist.size(); ++i )
2459  {
2460  if ( ( z1 <= mlist[i].z ) && ( mlist[i].z <= z2 ) )
2461  {
2462  std::unique_ptr<BStruct> arr( new BStruct );
2463  arr->addMember( "x", new BLong( wx ) );
2464  arr->addMember( "y", new BLong( wy ) );
2465  arr->addMember( "z", new BLong( mlist[i].z ) );
2466  arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
2467  newarr->addElement( arr.release() );
2468  }
2469  }
2470  }
2471  }
2472  }
2473 
2474  return newarr.release();
2475  }
2476  else
2477  return new BError( "Invalid parameter" );
2478 }
2479 
2480 BObjectImp* UOExecutorModule::mf_ListItemsNearLocationOfType( /* x, y, z, range, objtype, realm */ )
2481 {
2482  unsigned short x, y;
2483  int z, range;
2484  unsigned int objtype;
2485  const String* strrealm;
2486 
2487  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, range ) &&
2488  getObjtypeParam( exec, 4, objtype ) && getStringParam( 5, strrealm ) )
2489  {
2490  Realms::Realm* realm = find_realm( strrealm->value() );
2491  if ( !realm )
2492  return new BError( "Realm not found" );
2493 
2494  std::unique_ptr<ObjArray> newarr( new ObjArray );
2495 
2496  if ( z == LIST_IGNORE_Z )
2497  {
2498  if ( !realm->valid( x, y, 0 ) )
2499  return new BError( "Invalid Coordinates for realm" );
2500  }
2501  else
2502  {
2503  if ( !realm->valid( x, y, static_cast<short>( z ) ) )
2504  return new BError( "Invalid Coordinates for realm" );
2505  }
2506 
2507  WorldIterator<ItemFilter>::InRange( x, y, realm, range, [&]( Items::Item* item ) {
2508  if ( ( item->objtype_ == objtype ) && ( abs( item->x - x ) <= range ) &&
2509  ( abs( item->y - y ) <= range ) )
2510  {
2511  if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z - z ) < CONST_DEFAULT_ZRANGE ) )
2512  newarr->addElement( item->make_ref() );
2513  }
2514  } );
2515 
2516  return newarr.release();
2517  }
2518 
2519  return nullptr;
2520 }
2521 
2522 
2524 {
2525  unsigned short x, y;
2526  int z;
2527  const String* strrealm;
2528  Realms::Realm* realm;
2529 
2530  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getStringParam( 3, strrealm ) )
2531  {
2532  realm = find_realm( strrealm->value() );
2533  if ( !realm )
2534  return new BError( "Realm not found" );
2535 
2536  if ( z == LIST_IGNORE_Z )
2537  {
2538  if ( !realm->valid( x, y, 0 ) )
2539  return new BError( "Invalid Coordinates for realm" );
2540  }
2541  else
2542  {
2543  if ( !realm->valid( x, y, static_cast<short>( z ) ) )
2544  return new BError( "Invalid Coordinates for realm" );
2545  }
2546 
2547  std::unique_ptr<ObjArray> newarr( new ObjArray );
2548  WorldIterator<ItemFilter>::InRange( x, y, realm, 0, [&]( Items::Item* item ) {
2549  if ( ( item->x == x ) && ( item->y == y ) )
2550  {
2551  if ( ( z == LIST_IGNORE_Z ) || ( item->z == z ) )
2552  newarr->addElement( item->make_ref() );
2553  }
2554  } );
2555 
2556  return newarr.release();
2557  }
2558 
2559  return nullptr;
2560 }
2561 
2563 {
2564  u16 x;
2565  u16 y;
2566  int z;
2567  int range;
2568  const String* strrealm;
2569 
2570  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, range ) &&
2571  getStringParam( 4, strrealm ) )
2572  {
2573  Realms::Realm* realm = find_realm( strrealm->value() );
2574  if ( !realm )
2575  return new BError( "Realm not found" );
2576 
2577  std::unique_ptr<ObjArray> newarr( new ObjArray );
2578  WorldIterator<PlayerFilter>::InRange( x, y, realm, range, [&]( Mobile::Character* chr ) {
2579  if ( chr->dead() && ( abs( chr->z - z ) < CONST_DEFAULT_ZRANGE ) )
2580  {
2581  newarr->addElement( chr->make_ref() );
2582  }
2583  } );
2584 
2585  return newarr.release();
2586  }
2587  else
2588  {
2589  return new BError( "Invalid parameter" );
2590  }
2591 }
2592 
2593 const unsigned LMBLEX_FLAG_NORMAL = 0x01;
2594 const unsigned LMBLEX_FLAG_HIDDEN = 0x02;
2595 const unsigned LMBLEX_FLAG_DEAD = 0x04;
2596 const unsigned LMBLEX_FLAG_CONCEALED = 0x8;
2597 const unsigned LMBLEX_FLAG_PLAYERS_ONLY = 0x10;
2598 const unsigned LMBLEX_FLAG_NPC_ONLY = 0x20;
2599 
2600 BObjectImp* UOExecutorModule::mf_ListMobilesNearLocationEx( /* x, y, z, range, flags, realm */ )
2601 {
2602  unsigned short x, y;
2603  int z, flags;
2604  short range;
2605  const String* strrealm;
2606 
2607  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, range ) &&
2608  getParam( 4, flags ) && getStringParam( 5, strrealm ) )
2609  {
2610  Realms::Realm* realm = find_realm( strrealm->value() );
2611  if ( !realm )
2612  return new BError( "Realm not found" );
2613 
2614  if ( z == LIST_IGNORE_Z )
2615  {
2616  if ( !realm->valid( x, y, 0 ) )
2617  return new BError( "Invalid Coordinates for realm" );
2618  }
2619  else
2620  {
2621  if ( !realm->valid( x, y, static_cast<short>( z ) ) )
2622  return new BError( "Invalid Coordinates for realm" );
2623  }
2624 
2625  bool inc_normal = ( flags & LMBLEX_FLAG_NORMAL ) ? true : false;
2626  bool inc_hidden = ( flags & LMBLEX_FLAG_HIDDEN ) ? true : false;
2627  bool inc_dead = ( flags & LMBLEX_FLAG_DEAD ) ? true : false;
2628  bool inc_concealed = ( flags & LMBLEX_FLAG_CONCEALED ) ? true : false;
2629  bool inc_players_only = ( flags & LMBLEX_FLAG_PLAYERS_ONLY ) ? true : false;
2630  bool inc_npc_only = ( flags & LMBLEX_FLAG_NPC_ONLY ) ? true : false;
2631 
2632  std::unique_ptr<ObjArray> newarr( new ObjArray );
2633 
2634  auto fill_mobs = [&]( Mobile::Character* _chr ) {
2635  if ( ( inc_hidden && _chr->hidden() ) || ( inc_dead && _chr->dead() ) ||
2636  ( inc_concealed && _chr->concealed() ) ||
2637  ( inc_normal && !( _chr->hidden() || _chr->dead() || _chr->concealed() ) ) )
2638  {
2639  if ( ( z == LIST_IGNORE_Z ) || ( abs( _chr->z - z ) < CONST_DEFAULT_ZRANGE ) )
2640  {
2641  if ( !inc_players_only && !inc_npc_only )
2642  newarr->addElement( _chr->make_ref() );
2643  else if ( inc_players_only && _chr->client )
2644  newarr->addElement( _chr->make_ref() );
2645  else if ( inc_npc_only && _chr->isa( UOBJ_CLASS::CLASS_NPC ) )
2646  newarr->addElement( _chr->make_ref() );
2647  }
2648  }
2649  };
2650  if ( inc_players_only )
2651  WorldIterator<PlayerFilter>::InRange( x, y, realm, range, fill_mobs );
2652  else if ( inc_npc_only )
2653  WorldIterator<NPCFilter>::InRange( x, y, realm, range, fill_mobs );
2654  else
2655  WorldIterator<MobileFilter>::InRange( x, y, realm, range, fill_mobs );
2656 
2657  return newarr.release();
2658  }
2659  else
2660  {
2661  return new BError( "Invalid parameter" );
2662  }
2663 }
2664 
2666 {
2667  unsigned short x, y;
2668  int z;
2669  short range;
2670  const String* strrealm;
2671  Realms::Realm* realm;
2672 
2673  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, range ) &&
2674  getStringParam( 4, strrealm ) )
2675  {
2676  realm = find_realm( strrealm->value() );
2677  if ( !realm )
2678  return new BError( "Realm not found" );
2679 
2680  if ( z == LIST_IGNORE_Z )
2681  {
2682  if ( !realm->valid( x, y, 0 ) )
2683  return new BError( "Invalid Coordinates for realm" );
2684  }
2685  else
2686  {
2687  if ( !realm->valid( x, y, static_cast<short>( z ) ) )
2688  return new BError( "Invalid Coordinates for realm" );
2689  }
2690 
2691  std::unique_ptr<ObjArray> newarr( new ObjArray );
2692  WorldIterator<MobileFilter>::InRange( x, y, realm, range, [&]( Mobile::Character* chr ) {
2693  if ( ( !chr->concealed() ) && ( !chr->hidden() ) && ( !chr->dead() ) )
2694  if ( ( z == LIST_IGNORE_Z ) || ( abs( chr->z - z ) < CONST_DEFAULT_ZRANGE ) )
2695  newarr->addElement( chr->make_ref() );
2696  } );
2697  return newarr.release();
2698  }
2699  else
2700  {
2701  return new BError( "Invalid parameter" );
2702  }
2703 }
2704 
2706 {
2707  UObject* obj;
2708  int range;
2709  if ( getUObjectParam( exec, 0, obj ) && getParam( 1, range ) )
2710  {
2711  obj = obj->toplevel_owner();
2712  std::unique_ptr<ObjArray> newarr( new ObjArray );
2713  WorldIterator<MobileFilter>::InRange( obj->x, obj->y, obj->realm, range,
2714  [&]( Mobile::Character* chr ) {
2715  if ( chr->dead() || chr->hidden() || chr->concealed() )
2716  return;
2717  if ( chr == obj )
2718  return;
2719  if ( ( abs( chr->z - obj->z ) < CONST_DEFAULT_ZRANGE ) )
2720  {
2721  if ( obj->realm->has_los( *obj, *chr ) )
2722  {
2723  newarr->addElement( chr->make_ref() );
2724  }
2725  }
2726  } );
2727 
2728  return newarr.release();
2729  }
2730  else
2731  {
2732  return new BError( "Invalid parameter" );
2733  }
2734 }
2735 
2737 {
2738  const String* strrealm = nullptr;
2739  Realms::Realm* realm = nullptr;
2740 
2741  if ( getStringParam( 0, strrealm ) )
2742  {
2743  realm = find_realm( strrealm->value() );
2744  if ( !realm )
2745  return new BError( "Realm not found" );
2746 
2747  std::unique_ptr<ObjArray> newarr( new ObjArray() );
2748 
2749  for ( const auto& objitr : Pol::Core::objStorageManager.objecthash )
2750  {
2751  UObject* obj = objitr.second.get();
2752  if ( !obj->ismobile() || obj->isa( UOBJ_CLASS::CLASS_NPC ) )
2753  continue;
2754 
2755  Character* chr = static_cast<Character*>( obj );
2756  if ( chr->logged_in() || chr->realm != realm || chr->orphan() )
2757  continue;
2758 
2759  newarr->addElement( new EOfflineCharacterRefObjImp( chr ) );
2760  }
2761  return newarr.release();
2762  }
2763  else
2764  {
2765  return new BError( "Invalid parameter" );
2766  }
2767 }
2768 
2769 // keep this in sync with UO.EM
2770 const int LH_FLAG_LOS = 1; // only include those in LOS
2771 const int LH_FLAG_INCLUDE_HIDDEN = 2; // include hidden characters
2773 {
2774  Character* chr;
2775  int range;
2776  int flags;
2777  if ( getCharacterParam( exec, 0, chr ) && getParam( 1, range ) && getParam( 2, flags ) )
2778  {
2779  std::unique_ptr<ObjArray> arr( new ObjArray );
2780 
2781  const Character::CharacterSet& cont = chr->hostiles();
2782  Character::CharacterSet::const_iterator itr = cont.begin(), end = cont.end();
2783  for ( ; itr != end; ++itr )
2784  {
2785  Character* hostile = *itr;
2786  if ( hostile->concealed() )
2787  continue;
2788  if ( ( flags & LH_FLAG_LOS ) && !chr->realm->has_los( *chr, *hostile ) )
2789  continue;
2790  if ( ( ~flags & LH_FLAG_INCLUDE_HIDDEN ) && hostile->hidden() )
2791  continue;
2792  if ( !inrangex( chr, hostile, range ) )
2793  continue;
2794  arr->addElement( hostile->make_ref() );
2795  }
2796 
2797  return arr.release();
2798  }
2799  else
2800  {
2801  return new BError( "Invalid parameter" );
2802  }
2803 }
2804 
2806 {
2807  UObject* src;
2808  UObject* dst;
2809  if ( getUObjectParam( exec, 0, src ) && getUObjectParam( exec, 1, dst ) )
2810  {
2811  return new BLong( src->realm->has_los( *src, *dst->toplevel_owner() ) );
2812  }
2813  else
2814  {
2815  return new BLong( 0 );
2816  }
2817 }
2818 
2820 {
2821  UObject* src;
2822  unsigned short x, y;
2823  short z;
2824  if ( getUObjectParam( exec, 0, src ) && getParam( 1, x ) && getParam( 2, y ) && getParam( 3, z ) )
2825  {
2826  if ( !src->realm->valid( x, y, z ) )
2827  return new BError( "Invalid Coordinates for realm" );
2828  LosObj tgt( x, y, static_cast<s8>( z ), src->realm );
2829  return new BLong( src->realm->has_los( *src, tgt ) );
2830  }
2831  else
2832  {
2833  return nullptr;
2834  }
2835 }
2836 
2838 {
2839  unsigned short x1, x2;
2840  unsigned short y1, y2;
2841  short z1, z2;
2842  const String* strrealm;
2843  if ( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, z1 ) && getParam( 3, x2 ) &&
2844  getParam( 4, y2 ) && getParam( 5, z2 ) && getStringParam( 6, strrealm ) )
2845  {
2846  Realms::Realm* realm = find_realm( strrealm->value() );
2847  if ( !realm )
2848  return new BError( "Realm not found" );
2849 
2850  if ( ( !realm->valid( x1, y1, z1 ) ) || ( !realm->valid( x2, y2, z2 ) ) )
2851  return new BError( "Invalid Coordinates for Realm" );
2852 
2853  LosObj att( x1, y1, static_cast<s8>( z1 ), realm );
2854  LosObj tgt( x2, y2, static_cast<s8>( z2 ), realm );
2855  return new BLong( realm->has_los( att, tgt ) );
2856  }
2857  else
2858  {
2859  return nullptr;
2860  }
2861 }
2862 
2864 {
2865  Item* item;
2866  if ( getItemParam( exec, 0, item ) )
2867  {
2868  if ( item->has_gotten_by() )
2869  item->gotten_by()->clear_gotten_item();
2870  else if ( item->inuse() && !is_reserved_to_me( item ) )
2871  return new BError( "That item is being used." );
2872 
2873  const ItemDesc& id = find_itemdesc( item->objtype_ );
2874  if ( !id.destroy_script.empty() )
2875  {
2876  BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( item ) );
2877  if ( res->isTrue() )
2878  { // destruction is okay
2879  BObject ob( res );
2880  }
2881  else
2882  {
2883  // destruction isn't okay!
2884  return res;
2885  }
2886  }
2888  UpdateCharacterWeight( item );
2889  destroy_item( item );
2890  return new BLong( 1 );
2891  }
2892  else
2893  {
2894  return new BError( "Invalid parameter type" );
2895  }
2896 }
2897 
2899 {
2900  UObject* obj;
2901  const String* name_str;
2902 
2903  if ( getUObjectParam( exec, 0, obj ) && getStringParam( 1, name_str ) )
2904  {
2905  obj->setname( name_str->value() );
2906  return new BLong( 1 );
2907  }
2908  else
2909  {
2910  return new BLong( 0 );
2911  }
2912 }
2913 
2915 {
2916  UObject* obj;
2917  if ( getUObjectParam( exec, 0, obj ) )
2918  {
2919  std::unique_ptr<BStruct> arr( new BStruct );
2920  arr->addMember( "x", new BLong( obj->x ) );
2921  arr->addMember( "y", new BLong( obj->y ) );
2922  arr->addMember( "z", new BLong( obj->z ) );
2923  return arr.release();
2924  }
2925  else
2926  {
2927  return new BLong( 0 );
2928  }
2929 }
2930 
2932 {
2933  Item* item;
2934  if ( getItemParam( exec, 0, item ) )
2935  {
2936  int flags = 0;
2937  if ( exec.fparams.size() >= 2 )
2938  {
2939  if ( !getParam( 1, flags ) )
2940  return new BError( "Invalid parameter type" );
2941  }
2942 
2943  if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
2944  {
2945  std::unique_ptr<ObjArray> newarr( new ObjArray );
2946 
2947  static_cast<UContainer*>( item )->enumerate_contents( newarr.get(), flags );
2948 
2949  return newarr.release();
2950  }
2951 
2952  return nullptr;
2953  }
2954  else
2955  return new BError( "Invalid parameter type" );
2956 }
2957 
2959 {
2960  std::unique_ptr<ObjArray> newarr( new ObjArray );
2961 
2962  for ( Clients::const_iterator itr = networkManager.clients.begin(),
2963  end = networkManager.clients.end();
2964  itr != end; ++itr )
2965  {
2966  if ( ( *itr )->chr != nullptr )
2967  {
2968  newarr->addElement( new ECharacterRefObjImp( ( *itr )->chr ) );
2969  }
2970  }
2971 
2972  return newarr.release();
2973 }
2974 
2976 {
2977  UObject* center;
2978  int range;
2979  if ( getUObjectParam( exec, 0, center ) && getParam( 1, range ) )
2980  {
2981  int flags = 0;
2982  if ( exec.hasParams( 3 ) )
2983  {
2984  if ( !getParam( 2, flags ) )
2985  return new BError( "Invalid parameter type" );
2986  }
2988  {
2989  register_for_speech_events( center, &uoexec, range, flags );
2991  return new BLong( 1 );
2992  }
2993  else
2994  {
2995  return new BError( "Already registered for speech events" );
2996  }
2997  }
2998  else
2999  {
3000  return new BError( "Invalid parameter type" );
3001  }
3002 }
3003 
3005 {
3006  int eventmask;
3007  if ( getParam( 0, eventmask ) )
3008  {
3009  if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA | EVID_SPOKE ) )
3010  {
3011  unsigned short range;
3012  if ( getParam( 1, range, 0, 32 ) )
3013  {
3014  if ( eventmask & ( EVID_SPOKE ) )
3015  uoexec.speech_size = range;
3016  if ( eventmask & ( EVID_ENTEREDAREA | EVID_LEFTAREA ) )
3017  uoexec.area_size = range;
3018  }
3019  else
3020  {
3021  return nullptr;
3022  }
3023  }
3024  uoexec.eventmask |= eventmask;
3025  return new BLong( uoexec.eventmask );
3026  }
3027  else
3028  {
3029  return new BError( "Invalid parameter" );
3030  }
3031 }
3032 
3034 {
3035  int eventmask;
3036  if ( getParam( 0, eventmask ) )
3037  {
3038  uoexec.eventmask &= ~eventmask;
3039 
3040  return new BLong( uoexec.eventmask );
3041  }
3042  else
3043  {
3044  return new BError( "Invalid parameter" );
3045  }
3046 }
3047 
3049 {
3050  Character* chr;
3051  if ( getCharacterParam( exec, 0, chr ) )
3052  {
3053  if ( !chr->dead() )
3054  return new BError( "That is not dead" );
3055  int flags = 0;
3056  if ( exec.hasParams( 2 ) )
3057  {
3058  if ( !getParam( 1, flags ) )
3059  return new BError( "Invalid parameter type" );
3060  }
3061 
3062  if ( ~flags & RESURRECT_FORCELOCATION )
3063  {
3064  // we want doors to block ghosts in this case.
3065  bool doors_block = !( chr->graphic == UOBJ_GAMEMASTER ||
3066  chr->cached_settings.get( PRIV_FLAGS::IGNORE_DOORS ) );
3067  short newz;
3068  Multi::UMulti* supporting_multi;
3069  Item* walkon_item;
3070  if ( !chr->realm->walkheight( chr->x, chr->y, chr->z, &newz, &supporting_multi, &walkon_item,
3071  doors_block, chr->movemode ) )
3072  {
3073  return new BError( "That location is blocked" );
3074  }
3075  }
3076 
3077  chr->resurrect();
3078  return new BLong( 1 );
3079  }
3080  else
3081  {
3082  return new BError( "Invalid parameter type" );
3083  }
3084 }
3085 
3087 {
3088  int serial;
3089  if ( getParam( 0, serial ) )
3090  {
3091  int sysfind_flags = 0;
3092  if ( exec.hasParams( 2 ) )
3093  {
3094  if ( !getParam( 1, sysfind_flags ) )
3095  return new BError( "Invalid parameter type" );
3096  }
3097  if ( IsCharacter( serial ) )
3098  {
3099  Character* chr = system_find_mobile( serial );
3100  if ( chr != nullptr )
3101  {
3102  if ( sysfind_flags & SYSFIND_SEARCH_OFFLINE_MOBILES )
3103  return new EOfflineCharacterRefObjImp( chr );
3104  else
3105  return new ECharacterRefObjImp( chr );
3106  }
3107  else
3108  {
3109  return new BError( "Character not found" );
3110  }
3111  }
3112  else
3113  {
3114  // dave changed this 3/8/3: objecthash (via system_find_item) will find any kind of item, so
3115  // don't need system_find_multi here.
3116  Item* item = system_find_item( serial );
3117 
3118  if ( item != nullptr )
3119  {
3120  return item->make_ref();
3121  }
3122 
3123  return new BError( "Item not found." );
3124  }
3125  }
3126  else
3127  {
3128  return new BError( "Invalid parameter type" );
3129  }
3130 }
3131 
3133 {
3134  update_gameclock();
3135  int flags = 0;
3136  if ( exec.hasParams( 1 ) )
3137  {
3138  if ( !getParam( 0, flags ) )
3139  return new BError( "Invalid parameter type" );
3140  }
3141  try
3142  {
3144 
3145  PolClockPauser pauser;
3146 
3147  unsigned int dirty, clean;
3148  long long elapsed_ms;
3149  int res;
3150  if ( flags & SAVE_INCREMENTAL )
3151  {
3152  res = save_incremental( dirty, clean, elapsed_ms );
3153  }
3154  else
3155  {
3156  res = write_data( dirty, clean, elapsed_ms );
3157  }
3158  if ( res == 0 )
3159  {
3160  // Code Analyze: C6246
3161  // BStruct* res = new BStruct();
3162  BStruct* ret = new BStruct();
3163  ret->addMember( "DirtyObjects", new BLong( dirty ) );
3164  ret->addMember( "CleanObjects", new BLong( clean ) );
3165  ret->addMember( "ElapsedMilliseconds", new BLong( static_cast<int>( elapsed_ms ) ) );
3166  return ret;
3167  }
3168  else
3169  {
3170  return new BError( "pol.cfg has InhibitSaves=1" );
3171  }
3172  }
3173  catch ( std::exception& ex )
3174  {
3175  POLLOG << "Exception during world save! (" << ex.what() << ")\n";
3176  return new BError( "Exception during world save" );
3177  }
3178 }
3179 
3180 
3182 {
3183  const String* region_name_str;
3184  int lightlevel;
3185  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, lightlevel ) ) )
3186  {
3187  return new BError( "Invalid Parameter type" );
3188  }
3189 
3190  if ( !VALID_LIGHTLEVEL( lightlevel ) )
3191  {
3192  return new BError( "Light Level is out of range" );
3193  }
3194 
3195  LightRegion* lightregion = gamestate.lightdef->getregion( region_name_str->value() );
3196  if ( lightregion == nullptr )
3197  {
3198  return new BError( "Light region not found" );
3199  }
3200 
3201  SetRegionLightLevel( lightregion, lightlevel );
3202  return new BLong( 1 );
3203 }
3204 
3206 {
3207  const String* region_name_str;
3208  int type;
3209  int severity;
3210  int aux;
3211  int lightoverride;
3212  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, type ) && getParam( 2, severity ) &&
3213  getParam( 3, aux ) && getParam( 4, lightoverride ) ) )
3214  {
3215  return new BError( "Invalid Parameter type" );
3216  }
3217 
3218  WeatherRegion* weatherregion = gamestate.weatherdef->getregion( region_name_str->value() );
3219  if ( weatherregion == nullptr )
3220  {
3221  return new BError( "Weather region not found" );
3222  }
3223 
3224  SetRegionWeatherLevel( weatherregion, type, severity, aux, lightoverride );
3225 
3226  return new BLong( 1 );
3227 }
3229 {
3230  const String* region_name_str;
3231  unsigned short xwest, ynorth, xeast, ysouth;
3232  const String* strrealm;
3233 
3234  if ( !( getStringParam( 0, region_name_str ) && getParam( 1, xwest ) && getParam( 2, ynorth ) &&
3235  getParam( 3, xeast ) && getParam( 4, ysouth ) && getStringParam( 5, strrealm ) ) )
3236  {
3237  return new BError( "Invalid Parameter type" );
3238  }
3239  Realms::Realm* realm = find_realm( strrealm->value() );
3240  if ( !realm )
3241  return new BError( "Realm not found" );
3242  if ( !realm->valid( xwest, ynorth, 0 ) )
3243  return new BError( "Invalid Coordinates for realm" );
3244  if ( !realm->valid( xeast, ysouth, 0 ) )
3245  return new BError( "Invalid Coordinates for realm" );
3246 
3247  bool res = gamestate.weatherdef->assign_zones_to_region( region_name_str->data(), xwest, ynorth,
3248  xeast, ysouth, realm );
3249  if ( res )
3250  return new BLong( 1 );
3251  else
3252  return new BError( "Weather region not found" );
3253 }
3254 
3256 {
3257  UObject* obj1;
3258  UObject* obj2;
3259  if ( getUObjectParam( exec, 0, obj1 ) && getUObjectParam( exec, 1, obj2 ) )
3260  {
3261  const UObject* tobj1 = obj1->toplevel_owner();
3262  const UObject* tobj2 = obj2->toplevel_owner();
3263  int xd = tobj1->x - tobj2->x;
3264  int yd = tobj1->y - tobj2->y;
3265  if ( xd < 0 )
3266  xd = -xd;
3267  if ( yd < 0 )
3268  yd = -yd;
3269  if ( xd > yd )
3270  return new BLong( xd );
3271  else
3272  return new BLong( yd );
3273  }
3274  else
3275  {
3276  return new BError( "Invalid parameter type" );
3277  }
3278 }
3279 
3281 {
3282  UObject* obj1;
3283  UObject* obj2;
3284  if ( getUObjectParam( exec, 0, obj1 ) && getUObjectParam( exec, 1, obj2 ) )
3285  {
3286  const UObject* tobj1 = obj1->toplevel_owner();
3287  const UObject* tobj2 = obj2->toplevel_owner();
3288  return new Double( sqrt( pow( (double)( tobj1->x - tobj2->x ), 2 ) +
3289  pow( (double)( tobj1->y - tobj2->y ), 2 ) ) );
3290  }
3291  else
3292  {
3293  return new BError( "Invalid parameter type" );
3294  }
3295 }
3296 
3298 {
3299  unsigned short x1, y1, x2, y2;
3300  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
3301  {
3302  return new BError( "Invalid parameter type" );
3303  }
3304  return new BLong( pol_distance( x1, y1, x2, y2 ) );
3305 }
3306 
3308 {
3309  unsigned short x1, y1, x2, y2;
3310  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
3311  {
3312  return new BError( "Invalid parameter type" );
3313  }
3314  return new Double( sqrt( pow( (double)( x1 - x2 ), 2 ) + pow( (double)( y1 - y2 ), 2 ) ) );
3315 }
3316 
3318 {
3319  int x1, y1, x2, y2;
3320  if ( !( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, x2 ) && getParam( 3, y2 ) ) )
3321  {
3322  return new BError( "Invalid parameter type" );
3323  }
3324  else if ( x1 == x2 && y1 == y2 )
3325  { // Same exact coordinates ... just give them the coordinate back!
3326  ObjArray* coords = new ObjArray;
3327  BStruct* point = new BStruct;
3328  point->addMember( "x", new BLong( x1 ) );
3329  point->addMember( "y", new BLong( y1 ) );
3330  coords->addElement( point );
3331  return coords;
3332  }
3333 
3334  double dx = abs( x2 - x1 ) + 0.5;
3335  double dy = abs( y2 - y1 ) + 0.5;
3336  int vx = 0, vy = 0;
3337 
3338  if ( x2 > x1 )
3339  vx = 1;
3340  else if ( x2 < x1 )
3341  vx = -1;
3342  if ( y2 > y1 )
3343  vy = 1;
3344  else if ( y2 < y1 )
3345  vy = -1;
3346 
3347  std::unique_ptr<ObjArray> coords( new ObjArray );
3348  if ( dx >= dy )
3349  {
3350  dy = dy / dx;
3351 
3352  for ( int c = 0; c <= dx; c++ )
3353  {
3354  int point_x = x1 + ( c * vx );
3355 
3356  double float_y = double( c ) * double( vy ) * dy;
3357  if ( float_y - floor( float_y ) >= 0.5 )
3358  float_y = ceil( float_y );
3359  int point_y = int( float_y ) + y1;
3360 
3361  std::unique_ptr<BStruct> point( new BStruct );
3362  point->addMember( "x", new BLong( point_x ) );
3363  point->addMember( "y", new BLong( point_y ) );
3364  coords->addElement( point.release() );
3365  }
3366  }
3367  else
3368  {
3369  dx = dx / dy;
3370  for ( int c = 0; c <= dy; c++ )
3371  {
3372  int point_y = y1 + ( c * vy );
3373 
3374  double float_x = double( c ) * double( vx ) * dx;
3375  if ( float_x - floor( float_x ) >= 0.5 )
3376  float_x = ceil( float_x );
3377  int point_x = int( float_x ) + x1;
3378 
3379  std::unique_ptr<BStruct> point( new BStruct );
3380  point->addMember( "x", new BLong( point_x ) );
3381  point->addMember( "y", new BLong( point_y ) );
3382  coords->addElement( point.release() );
3383  }
3384  }
3385  return coords.release();
3386 }
3387 
3389 {
3390  unsigned short from_x, from_y, to_x, to_y;
3391  if ( !( getParam( 0, from_x ) && getParam( 1, from_y ) && getParam( 2, to_x ) &&
3392  getParam( 3, to_y ) ) )
3393  {
3394  return new BError( "Invalid parameter type" );
3395  }
3396 
3397  double x = to_x - from_x;
3398  double y = to_y - from_y;
3399  double pi = acos( double( -1 ) );
3400  double r = sqrt( x * x + y * y );
3401 
3402  double angle = ( ( acos( x / r ) * 180.0 ) / pi );
3403 
3404  double y_check = ( ( asin( y / r ) * 180.0 ) / pi );
3405  if ( y_check < 0 )
3406  {
3407  angle = 360 - angle;
3408  }
3409 
3410  unsigned short facing = ( ( short( angle / 40 ) + 10 ) % 8 );
3411 
3412  return new BLong( facing );
3413 }
3414 
3415 // FIXME : Should we do an Orphan check here as well? Ugh.
3416 void true_extricate( Item* item )
3417 {
3419  if ( item->container != nullptr )
3420  {
3421  item->extricate();
3422  }
3423  else
3424  {
3425  remove_item_from_world( item );
3426  }
3427 }
3428 
3430 {
3431  Item* item;
3432  Item* cont_item;
3433  int px;
3434  int py;
3435  int add_to_existing_stack;
3436  if ( !( getItemParam( exec, 0, item ) && getItemParam( exec, 1, cont_item ) &&
3437  getParam( 2, px, -1, 65535 ) && getParam( 3, py, -1, 65535 ) &&
3438  getParam( 4, add_to_existing_stack, 0, 2 ) ) )
3439  {
3440  return new BError( "Invalid parameter type" );
3441  }
3442 
3443  ItemRef itemref( item ); // dave 1/28/3 prevent item from being destroyed before function ends
3444  if ( !item->movable() )
3445  {
3446  Character* chr = controller_.get();
3447  if ( chr == nullptr || !chr->can_move( item ) )
3448  return new BError( "That is immobile" );
3449  }
3450  if ( item->inuse() && !is_reserved_to_me( item ) )
3451  {
3452  return new BError( "That item is being used." );
3453  }
3454 
3455 
3456  if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
3457  {
3458  return new BError( "Non-container selected as target" );
3459  }
3460  UContainer* cont = static_cast<UContainer*>( cont_item );
3461 
3462  if ( cont->serial == item->serial )
3463  {
3464  return new BError( "Can't put a container into itself" );
3465  }
3466  if ( is_a_parent( cont, item->serial ) )
3467  {
3468  return new BError( "Can't put a container into an item in itself" );
3469  }
3470  if ( !cont->can_add( *item ) )
3471  {
3472  return new BError( "Container is too full to add that" );
3473  }
3474  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3475  Character* chr_owner = cont->GetCharacterOwner();
3476  if ( chr_owner == nullptr )
3477  if ( controller_.get() != nullptr )
3478  chr_owner = controller_.get();
3479 
3480  // daved changed order 1/26/3 check canX scripts before onX scripts.
3481  UContainer* oldcont = item->container;
3482  Item* existing_stack = nullptr;
3483 
3484  if ( ( oldcont != nullptr ) &&
3485  ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
3486  return new BError( "Could not remove item from its container." );
3487  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3488  {
3489  return new BError( "Item was destroyed in CanRemove script" );
3490  }
3491 
3492  if ( add_to_existing_stack )
3493  {
3494  existing_stack = cont->find_addable_stack( item );
3495  if ( existing_stack != nullptr )
3496  {
3497  if ( !cont->can_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack,
3498  item->getamount(), item ) )
3499  return new BError( "Could not add to existing stack" );
3500  }
3501  else if ( add_to_existing_stack == 2 )
3502  add_to_existing_stack = 0;
3503  else
3504  return new BError( "There is no existing stack" );
3505  }
3506 
3507  if ( !add_to_existing_stack )
3508  if ( !cont->can_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item ) )
3509  return new BError( "Could not insert item into container." );
3510 
3511  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3512  {
3513  return new BError( "Item was destroyed in CanInsert Script" );
3514  }
3515 
3516  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
3517  return new BError( "Item cannot be unequipped" );
3518  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3519  {
3520  return new BError( "Item was destroyed in Equip Script" );
3521  }
3522 
3523  if ( oldcont != nullptr )
3524  {
3525  oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
3526  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3527  {
3528  return new BError( "Item was destroyed in OnRemove script" );
3529  }
3530  }
3531 
3532  if ( !add_to_existing_stack )
3533  {
3534  u8 slotIndex = item->slot_index();
3535  if ( !cont->can_add_to_slot( slotIndex ) )
3536  {
3537  item->destroy();
3538  return new BError( "No slots available in new container" );
3539  }
3540  if ( !item->slot_index( slotIndex ) )
3541  {
3542  item->destroy();
3543  return new BError( "Couldn't set slot index on item" );
3544  }
3545 
3546  short x = static_cast<short>( px );
3547  short y = static_cast<short>( py );
3548  if ( /*x < 0 || y < 0 ||*/ !cont->is_legal_posn( item, x, y ) )
3549  {
3550  u16 tx, ty;
3551  cont->get_random_location( &tx, &ty );
3552  x = tx;
3553  y = ty;
3554  }
3555 
3556  // item->set_dirty();
3557 
3558  true_extricate( item );
3559 
3560  item->x = x;
3561  item->y = y;
3562  item->z = 0;
3563 
3564  cont->add( item );
3565  update_item_to_inrange( item );
3566  // DAVE added this 11/17: if in a Character's pack, update weight.
3567  UpdateCharacterWeight( item );
3568 
3569  cont->on_insert_add_item( chr_owner, UContainer::MT_CORE_MOVED, item );
3570  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3571  {
3572  return new BError( "Item was destroyed in OnInsert script" );
3573  }
3574  }
3575  else
3576  {
3577  u16 amount = item->getamount();
3578  true_extricate( item );
3579  existing_stack->add_to_self( item );
3580  update_item_to_inrange( existing_stack );
3581  UpdateCharacterWeight( existing_stack );
3582 
3583  cont->on_insert_increase_stack( chr_owner, UContainer::MT_CORE_MOVED, existing_stack, amount );
3584  }
3585 
3586  return new BLong( 1 );
3587 }
3588 
3589 
3591 {
3592  Item* item;
3593  Character* chr;
3594  if ( !( getItemParam( exec, 0, item ) && getCharacterParam( exec, 1, chr ) ) )
3595  {
3596  return new BError( "Invalid parameter type" );
3597  }
3598 
3599  ItemRef itemref( item ); // dave 1/28/3 prevent item from being destroyed before function ends
3600  if ( !item->movable() )
3601  {
3602  Character* _chr = controller_.get();
3603  if ( _chr == nullptr || !_chr->can_move( item ) )
3604  return new BError( "That is immobile" );
3605  }
3606  if ( item->inuse() && !is_reserved_to_me( item ) )
3607  {
3608  return new BError( "That item is being used." );
3609  }
3610 
3611  // daved changed order 1/26/3 check canX scripts before onX scripts.
3612  UContainer* oldcont = item->container;
3613 
3614  // DAVE added this 12/04, call can/onInsert & can/onRemove scripts for this container
3615  Character* chr_owner = nullptr;
3616  if ( oldcont != nullptr )
3617  chr_owner = oldcont->GetCharacterOwner();
3618  if ( chr_owner == nullptr )
3619  if ( controller_.get() != nullptr )
3620  chr_owner = controller_.get();
3621 
3622  if ( ( oldcont != nullptr ) &&
3623  ( !oldcont->check_can_remove_script( chr_owner, item, UContainer::MT_CORE_MOVED ) ) )
3624  return new BError( "Could not remove item from its container." );
3625  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3626  {
3627  return new BError( "Item was destroyed in CanRemove script" );
3628  }
3629 
3630  if ( !item->check_unequiptest_scripts() || !item->check_unequip_script() )
3631  return new BError( "Item cannot be unequipped" );
3632  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3633  {
3634  return new BError( "Item was destroyed in Equip Script" );
3635  }
3636 
3637  if ( oldcont != nullptr )
3638  {
3639  oldcont->on_remove( chr_owner, item, UContainer::MT_CORE_MOVED );
3640  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3641  {
3642  return new BError( "Item was destroyed in OnRemove script" );
3643  }
3644  }
3645 
3646  true_extricate( item );
3647 
3648  return place_item_in_secure_trade_container( chr->client, item );
3649 }
3650 
3652 {
3653  Character* chr;
3654  Item* item;
3655  if ( getCharacterParam( exec, 0, chr ) && getItemParam( exec, 1, item ) )
3656  {
3657  ItemRef itemref( item ); // dave 1/28/3 prevent item from being destroyed before function ends
3658  if ( !item->movable() )
3659  {
3660  Character* _chr = controller_.get();
3661  if ( _chr == nullptr || !_chr->can_move( item ) )
3662  return new BError( "That is immobile" );
3663  }
3664 
3665  if ( item->inuse() && !is_reserved_to_me( item ) )
3666  {
3667  return new BError( "That item is being used." );
3668  }
3669 
3670  if ( !chr->equippable( item ) || !item->check_equiptest_scripts( chr ) )
3671  {
3672  return new BError( "That item is not equippable by that character" );
3673  }
3674  if ( item->orphan() ) // dave added 1/28/3, item might be destroyed in RTC script
3675  {
3676  return new BError( "Item was destroyed in EquipTest script" );
3677  }
3678 
3679  item->layer = tilelayer( item->graphic );
3680 
3681  if ( item->has_equip_script() )
3682  {
3683  BObjectImp* res = item->run_equip_script( chr, false );
3684  if ( !res->isTrue() )
3685  return res;
3686  else
3687  BObject obj( res );
3688  }
3689 
3690 
3691  true_extricate( item );
3692 
3693  // at this point, 'item' is free - doesn't belong to the world, or a container.
3694  chr->equip( item );
3695  send_wornitem_to_inrange( chr, item );
3696 
3697  return new BLong( 1 );
3698  }
3699  else
3700  {
3701  return new BError( "Invalid parameter type" );
3702  }
3703 }
3704 
3706 {
3707  Character* chr;
3708  if ( getCharacterParam( exec, 0, chr ) )
3709  {
3710  if ( chr->isa( UOBJ_CLASS::CLASS_NPC ) )
3711  {
3712  NPC* npc = static_cast<NPC*>( chr );
3713  npc->restart_script();
3714  return new BLong( 1 );
3715  }
3716  else
3717  {
3718  return new BError( "RestartScript only operates on NPCs" );
3719  }
3720  }
3721  else
3722  {
3723  return new BError( "Invalid parameter" );
3724  }
3725 }
3726 
3727 
3729 {
3730  const String* resource;
3731  xcoord x;
3732  ycoord y;
3733  unsigned short tiletype;
3734  const String* strrealm;
3735 
3736  if ( getStringParam( 0, resource ) && getParam( 1, x ) && getParam( 2, y ) &&
3737  getParam( 3, tiletype ) && getStringParam( 4, strrealm ) )
3738  {
3739  Realms::Realm* realm = find_realm( strrealm->value() );
3740  if ( !realm )
3741  return new BError( "Realm not found" );
3742  if ( !realm->valid( x, y, 0 ) )
3743  return new BError( "Invalid Coordinates for realm" );
3744 
3745  return get_harvest_difficulty( resource->data(), x, y, realm, tiletype );
3746  }
3747  else
3748  {
3749  return new BError( "Invalid parameter" );
3750  }
3751 }
3752 
3754 {
3755  xcoord x;
3756  ycoord y;
3757  const String* resource;
3758  int b;
3759  int n;
3760  const String* strrealm;
3761 
3762  if ( getStringParam( 0, resource ) && getParam( 1, x ) && getParam( 2, y ) && getParam( 3, b ) &&
3763  getParam( 4, n ) && getStringParam( 5, strrealm ) )
3764  {
3765  Realms::Realm* realm = find_realm( strrealm->value() );
3766  if ( !realm )
3767  return new BError( "Realm not found" );
3768  if ( !realm->valid( x, y, 0 ) )
3769  return new BError( "Invalid Coordinates for realm" );
3770 
3771  if ( b <= 0 )
3772  return new BError( "b must be >= 0" );
3773  return harvest_resource( resource->data(), x, y, realm, b, n );
3774  }
3775  else
3776  {
3777  return new BError( "Invalid parameter" );
3778  }
3779 }
3780 
3782 {
3783  UObject* obj;
3784 
3785  if ( getUObjectParam( exec, 0, obj ) )
3786  {
3787  JusticeRegion* justice_region;
3788  if ( obj->isa( UOBJ_CLASS::CLASS_ITEM ) )
3789  obj = obj->toplevel_owner();
3790 
3791  if ( obj->isa( UOBJ_CLASS::CLASS_CHARACTER ) )
3792  {
3793  Character* chr = static_cast<Character*>( obj );
3794 
3795  if ( chr->logged_in() )
3796  justice_region = chr->client->gd->justice_region;
3797  else
3798  justice_region = gamestate.justicedef->getregion( chr->x, chr->y, chr->realm );
3799  }
3800  else
3801  justice_region = gamestate.justicedef->getregion( obj->x, obj->y, obj->realm );
3802 
3803  if ( justice_region == nullptr )
3804  return new BError( "No Region defined at this Location" );
3805  else
3806  return new String( justice_region->region_name() );
3807  }
3808  else
3809  return new BError( "Invalid parameter" );
3810 }
3811 
3813 {
3814  unsigned short x, y;
3815  const String* strrealm;
3816 
3817  if ( getParam( 0, x ) && getParam( 1, y ) && getStringParam( 2, strrealm ) )
3818  {
3819  Realms::Realm* realm = find_realm( strrealm->value() );
3820  if ( !realm )
3821  return new BError( "Realm not found" );
3822  if ( !realm->valid( x, y, 0 ) )
3823  return new BError( "Invalid Coordinates for realm" );
3824 
3825  JusticeRegion* justice_region = gamestate.justicedef->getregion( x, y, realm );
3826  if ( justice_region == nullptr )
3827  return new BError( "No Region defined at this Location" );
3828  else
3829  return new String( justice_region->region_name() );
3830  }
3831  else
3832  return new BError( "Invalid parameter" );
3833 }
3834 
3836 {
3837  const String* resource;
3838  unsigned short x, y;
3839  const String* propname;
3840  const String* strrealm;
3841 
3842  if ( getStringParam( 0, resource ) && getParam( 1, x ) && getParam( 2, y ) &&
3843  getStringParam( 3, propname ) && getStringParam( 4, strrealm ) )
3844  {
3845  Realms::Realm* realm = find_realm( strrealm->value() );
3846  if ( !realm )
3847  return new BError( "Realm not found" );
3848  if ( !realm->valid( x, y, 0 ) )
3849  return new BError( "Invalid Coordinates for realm" );
3850 
3851  return get_region_string( resource->data(), x, y, realm, propname->value() );
3852  }
3853  else
3854  {
3855  return new BError( "Invalid parameter" );
3856  }
3857 }
3858 
3860 {
3861  unsigned short x, y;
3862  const String* strrealm;
3863 
3864  if ( getParam( 0, x ) && getParam( 1, y ) && getStringParam( 2, strrealm ) )
3865  {
3866  Realms::Realm* realm = find_realm( strrealm->value() );
3867  if ( !realm )
3868  return new BError( "Realm not found" );
3869  if ( !realm->valid( x, y, 0 ) )
3870  return new BError( "Invalid Coordinates for realm" );
3871  LightRegion* light_region = gamestate.lightdef->getregion( x, y, realm );
3872  int lightlevel;
3873  if ( light_region != nullptr )
3874  lightlevel = light_region->lightlevel;
3875  else
3877  return new BLong( lightlevel );
3878  }
3879  else
3880  {
3881  return new BError( "Invalid parameter" );
3882  }
3883 }
3884 
3885 
3887 {
3888  Character* chr;
3889  const String* template_name;
3890  if ( getCharacterParam( exec, 0, chr ) && getStringParam( 1, template_name ) )
3891  {
3892  return equip_from_template( chr, template_name->data() );
3893  }
3894  else
3895  {
3896  return new BError( "Invalid parameter" );
3897  }
3898 }
3899 
3900 // FIXME: Use a PrivUpdater here
3902 {
3903  Character* chr;
3904  const String* privstr;
3905  if ( getCharacterParam( exec, 0, chr ) && getStringParam( 1, privstr ) )
3906  {
3907  chr->grant_privilege( privstr->data() );
3908  return new BLong( 1 );
3909  }
3910  else
3911  {
3912  return new BError( "Invalid parameter" );
3913  }
3914 }
3915 
3916 // FIXME: Use a PrivUpdater here
3918 {
3919  Character* chr;
3920  const String* privstr;
3921  if ( getCharacterParam( exec, 0, chr ) && getStringParam( 1, privstr ) )
3922  {
3923  chr->revoke_privilege( privstr->data() );
3924  return new BLong( 1 );
3925  }
3926  else
3927  {
3928  return new BError( "Invalid parameter" );
3929  }
3930 }
3931 
3933 {
3934  return new BLong( read_gameclock() );
3935 }
3936 
3937 unsigned char decode_xdigit( unsigned char ch )
3938 {
3939  if ( ch >= '0' && ch <= '9' )
3940  ch -= '0';
3941  else if ( ch >= 'A' && ch <= 'F' )
3942  ch = ch - 'A' + 0xa;
3943  else if ( ch >= 'a' && ch <= 'f' )
3944  ch = ch - 'a' + 0xa;
3945 
3946  return ch;
3947 }
3948 
3950 {
3951  Character* chr;
3952  Network::Client* client;
3953  const String* str;
3954  if ( getCharacterOrClientParam( exec, 0, chr, client ) && getStringParam( 1, str ) )
3955  {
3956  if ( str->length() % 2 > 0 )
3957  {
3958  return new BError( "Invalid packet string length." );
3959  }
3961  buffer; // encryptedbuffer is the only one without getID buffer[0]
3962  unsigned char* buf = reinterpret_cast<unsigned char*>( buffer->getBuffer() );
3963  const char* s = str->data();
3964  while ( buffer->offset < 2000 && isxdigit( s[0] ) && isxdigit( s[1] ) )
3965  {
3966  unsigned char ch;
3967  ch = ( decode_xdigit( s[0] ) << 4 ) | decode_xdigit( s[1] );
3968  *( buf++ ) = ch;
3969  buffer->offset++;
3970  s += 2;
3971  }
3972  if ( chr != nullptr )
3973  {
3974  if ( chr->has_active_client() )
3975  {
3976  buffer.Send( chr->client );
3977  return new BLong( 1 );
3978  }
3979  else
3980  {
3981  return new BError( "No client attached" );
3982  }
3983  }
3984  else if ( client != nullptr )
3985  {
3986  if ( client->isConnected() )
3987  {
3988  buffer.Send( client );
3989  return new BLong( 1 );
3990  }
3991  else
3992  {
3993  return new BError( "Client is disconnected" );
3994  }
3995  }
3996  else
3997  {
3998  return new BError( "Invalid parameter type" );
3999  }
4000  }
4001  else
4002  {
4003  return new BError( "Invalid parameter type" );
4004  }
4005 }
4006 
4008 {
4009  Character* chr;
4010  int x, y;
4011  UObject* target = nullptr;
4012 
4013  if ( getCharacterParam( exec, 0, chr ) && getParam( 1, x, -1, 1000000 ) &&
4014  getParam( 2, y, -1, 1000000 ) ) // max values checked below
4015  {
4016  if ( !chr->has_active_client() )
4017  return new BError( "No client attached" );
4018 
4019  bool usesNewPktSize = ( chr->client->ClientType & Network::CLIENTTYPE_7090 ) > 0;
4020 
4022  if ( x == -1 && y == -1 )
4023  {
4024  msg->Write<u8>( PKTOUT_BA_ARROW_OFF );
4025  msg->offset += 4; // u16 x_tgt,y_tgt
4026  if ( usesNewPktSize )
4027  msg->offset += 4; // u32 serial
4028  }
4029  else
4030  {
4031  if ( !chr->realm->valid( static_cast<unsigned short>( x ), static_cast<unsigned short>( y ),
4032  0 ) )
4033  return new BError( "Invalid Coordinates for Realm" );
4034  msg->Write<u8>( PKTOUT_BA_ARROW_ON );
4035  msg->WriteFlipped<u16>( static_cast<u16>( x & 0xFFFF ) );
4036  msg->WriteFlipped<u16>( static_cast<u16>( y & 0xFFFF ) );
4037  if ( usesNewPktSize )
4038  {
4039  if ( !getUObjectParam( exec, 3, target ) )
4040  {
4041  exec.setFunctionResult( nullptr );
4042  return new BError( "No valid target for HSA client" );
4043  }
4044  msg->Write<u32>( static_cast<u32>( target->serial_ext & 0xFFFFFFFF ) );
4045  }
4046  }
4047  msg.Send( chr->client );
4048  return new BLong( 1 );
4049  }
4050  else
4051  {
4052  return new BError( "Invalid parameter" );
4053  }
4054 }
4055 
4057 {
4058  Character* chr;
4059  int spellid;
4060  if ( getCharacterParam( exec, 0, chr ) && getParam( 1, spellid ) )
4061  {
4062  if ( !VALID_SPELL_ID( spellid ) )
4063  {
4064  return new BError( "Spell ID out of range" );
4065  }
4066  USpell* spell = gamestate.spells[spellid];
4067  if ( spell == nullptr )
4068  {
4069  return new BError( "No such spell" );
4070  }
4071 
4072  return new BLong( spell->consume_reagents( chr ) ? 1 : 0 );
4073  }
4074  else
4075  {
4076  return new BError( "Invalid parameter" );
4077  }
4078 }
4079 
4081 {
4082  Character* chr;
4083  int spellid;
4084  if ( getCharacterParam( exec, 0, chr ) && getParam( 1, spellid ) )
4085  {
4086  if ( !VALID_SPELL_ID( spellid ) )
4087  {
4088  return new BError( "Spell ID out of range" );
4089  }
4090  USpell* spell = gamestate.spells[spellid];
4091  if ( spell == nullptr )
4092  {
4093  return new BError( "No such spell" );
4094  }
4095 
4096  spell->cast( chr );
4097  return new BLong( 1 );
4098  }
4099  else
4100  {
4101  return new BError( "Invalid parameter" );
4102  }
4103 }
4105 {
4106  int spellid;
4107  if ( getParam( 0, spellid ) )
4108  {
4109  if ( !VALID_SPELL_ID( spellid ) )
4110  {
4111  return new BError( "Spell ID out of range" );
4112  }
4113  USpell* spell = gamestate.spells[spellid];
4114  if ( spell == nullptr )
4115  {
4116  return new BError( "No such spell" );
4117  }
4118 
4119  return new BLong( spell->difficulty() );
4120  }
4121  else
4122  {
4123  return new BError( "Invalid parameter" );
4124  }
4125 }
4127 {
4128  Character* chr;
4129  int spellid;
4130  unsigned short font;
4131  unsigned short color;
4132 
4133  if ( getCharacterParam( exec, 0, chr ) && getParam( 1, spellid ) && getParam( 2, font ) &&
4134  getParam( 3, color ) )
4135  {
4136  if ( !VALID_SPELL_ID( spellid ) )
4137  {
4138  return new BError( "Spell ID out of range" );
4139  }
4140  USpell* spell = gamestate.spells[spellid];
4141  if ( spell == nullptr )
4142  {
4143  return new BError( "No such spell" );
4144  }
4145 
4146  spell->speak_power_words( chr, font, color );
4147 
4148  return new BLong( 1 );
4149  }
4150  else
4151  {
4152  return new BError( "Invalid parameter" );
4153  }
4154 }
4155 
4157 {
4158  Character* chr;
4159  if ( getCharacterParam( exec, 0, chr ) )
4160  {
4161  std::unique_ptr<ObjArray> arr( new ObjArray );
4162  for ( int layer = LAYER_EQUIP__LOWEST; layer <= LAYER_EQUIP__HIGHEST; ++layer )
4163  {
4164  Item* item = chr->wornitem( layer );
4165  if ( item != nullptr )
4166  {
4167  arr->addElement( new EItemRefObjImp( item ) );
4168  }
4169  }
4170  return arr.release();
4171  }
4172  else
4173  {
4174  return new BError( "Invalid parameter" );
4175  }
4176 }
4177 
4179 {
4180  Character* chr;
4181  int layer;
4182  if ( getCharacterParam( exec, 0, chr ) && getParam( 1, layer ) )
4183  {
4184  if ( layer < LOWEST_LAYER || layer > HIGHEST_LAYER )
4185  {
4186  return new BError( "Invalid layer" );
4187  }
4188 
4189  Item* item = chr->wornitem( layer );
4190  if ( item == nullptr )
4191  {
4192  return new BError( "Nothing equipped on that layer." );
4193  }
4194  else
4195  {
4196  return new EItemRefObjImp( item );
4197  }
4198  }
4199  else
4200  {
4201  return new BError( "Invalid parameter" );
4202  }
4203 }
4204 
4206 {
4207  Character* chr;
4208  Network::Client* client;
4209 
4210  if ( getCharacterOrClientParam( exec, 0, chr, client ) )
4211  {
4212  if ( chr != nullptr )
4213  {
4214  if ( !chr->has_active_client() )
4215  return new BError( "No client attached" );
4216 
4217  client = chr->client;
4218  }
4219 
4220  if ( client != nullptr )
4221  {
4222  if ( client->isConnected() )
4223  {
4224  client->Disconnect();
4225  return new BLong( 1 );
4226  }
4227  else
4228  return new BError( "Client is disconnected" );
4229  }
4230  else
4231  return new BError( "Invalid parameter type" );
4232  }
4233  else
4234  {
4235  return new BError( "Invalid parameter type" );
4236  }
4237 }
4238 
4240 {
4241  xcoord x;
4242  ycoord y;
4243  const String* strrealm;
4244 
4245  // note that this uses WORLD_MAX_X, not WORLD_X,
4246  // because we can't read the outermost edge of the map
4247  if ( getParam( 0, x ) && // FIXME realm size
4248  getParam( 1, y ) && getStringParam( 2, strrealm ) ) // FIXME realm size
4249  {
4250  Realms::Realm* realm = find_realm( strrealm->value() );
4251  if ( !realm )
4252  return new BError( "Realm not found" );
4253  if ( !realm->valid( x, y, 0 ) )
4254  return new BError( "Invalid Coordinates for realm" );
4255 
4256  Plib::MAPTILE_CELL cell =
4257  realm->getmaptile( static_cast<unsigned short>( x ), static_cast<unsigned short>( y ) );
4258 
4259  std::unique_ptr<BStruct> result( new BStruct );
4260  result->addMember( "z", new BLong( cell.z ) );
4261  result->addMember( "landtile", new BLong( cell.landtile ) );
4262 
4263  return result.release();
4264  }
4265  else
4266  {
4267  return new BError( "Invalid parameter" );
4268  }
4269 }
4271 {
4272  unsigned short x, y;
4273  const String* strrealm;
4274  if ( getParam( 0, x ) && getParam( 1, y ) && getStringParam( 2, strrealm ) )
4275  {
4276  Realms::Realm* realm = find_realm( strrealm->value() );
4277  if ( !realm )
4278  return new BError( "Realm not found" );
4279  if ( !realm->valid( x, y, 0 ) )
4280  return new BError( "Invalid Coordinates for Realm" );
4281 
4282  short z = -255;
4283  if ( realm->lowest_standheight( x, y, &z ) )
4284  return new BLong( z );
4285  else
4286  return new BError( "Nowhere" );
4287  }
4288  else
4289  {
4290  return new BError( "Invalid parameter" );
4291  }
4292 }
4293 
4295 {
4296  const String* namestr;
4297  if ( getStringParam( 0, namestr ) )
4298  {
4299  unsigned int objtype = get_objtype_byname( namestr->data() );
4300  if ( objtype != 0 )
4301  return new BLong( objtype );
4302  else
4303  return new BError( "No objtype by that name" );
4304  }
4305  else
4306  {
4307  return new BError( "Invalid parameter" );
4308  }
4309 }
4310 
4312 {
4313  Character* chr;
4314  if ( getCharacterParam( exec, 0, chr ) )
4315  {
4316  BObjectImp* event = exec.getParamImp( 1 );
4317  if ( event != nullptr )
4318  {
4319  if ( chr->isa( UOBJ_CLASS::CLASS_NPC ) )
4320  {
4321  NPC* npc = static_cast<NPC*>( chr );
4322  // event->add_ref(); // UNTESTED
4323  return npc->send_event_script( event->copy() );
4324  }
4325  else
4326  {
4327  return new BError( "That mobile is not an NPC" );
4328  }
4329  }
4330  else
4331  {
4332  return new BError( "Huh? Not enough parameters" );
4333  }
4334  }
4335  else
4336  {
4337  return new BError( "Invalid parameter" );
4338  }
4339 }
4340 
4342 {
4343  Multi::UMulti* multi;
4344  if ( getMultiParam( exec, 0, multi ) )
4345  {
4346  const ItemDesc& id = find_itemdesc( multi->objtype_ );
4347  if ( !id.destroy_script.empty() )
4348  {
4349  BObjectImp* res = run_script_to_completion( id.destroy_script, new EItemRefObjImp( multi ) );
4350  if ( !res->isTrue() )
4351  { // destruction is okay
4352  return res;
4353  }
4354  }
4355 
4356  Multi::UBoat* boat = multi->as_boat();
4357  if ( boat != nullptr )
4358  {
4359  return Multi::destroy_boat( boat );
4360  }
4361  Multi::UHouse* house = multi->as_house();
4362  if ( house != nullptr )
4363  {
4364  return Multi::destroy_house( house );
4365  }
4366  return new BError( "WTF!? Don't know what kind of multi that is!" );
4367  }
4368  else
4369  {
4370  return new BError( "Invalid parameter type" );
4371  }
4372 }
4373 
4374 
4376 {
4377  u16 multiid;
4378  if ( getParam( 0, multiid ) )
4379  {
4380  if ( !Multi::MultiDefByMultiIDExists( multiid ) )
4381  return new BError( "MultiID not found" );
4382 
4383  const Multi::MultiDef& md = *Multi::MultiDefByMultiID( multiid );
4384  std::unique_ptr<BStruct> ret( new BStruct );
4385  ret->addMember( "xmin", new BLong( md.minrx ) );
4386  ret->addMember( "xmax", new BLong( md.maxrx ) );
4387  ret->addMember( "ymin", new BLong( md.minry ) );
4388  ret->addMember( "ymax", new BLong( md.maxry ) );
4389  return ret.release();
4390  }
4391  else
4392  return new BError( "Invalid parameter" );
4393 }
4394 
4396 {
4397  Character* old_controller = controller_.get();
4398  BObjectImp* param0 = getParamImp( 0 );
4399  bool handled = false;
4400 
4401  if ( param0->isa( BObjectImp::OTLong ) )
4402  {
4403  BLong* lng = static_cast<BLong*>( param0 );
4404  if ( lng->value() == 0 )
4405  {
4406  controller_.clear();
4407  handled = true;
4408  }
4409  }
4410 
4411  if ( !handled )
4412  {
4413  Character* chr;
4414  if ( getCharacterParam( exec, 0, chr ) )
4415  controller_.set( chr );
4416  else
4417  controller_.clear();
4418  }
4419 
4420  if ( old_controller )
4421  return new ECharacterRefObjImp( old_controller );
4422  else
4423  return new BLong( 0 );
4424 }
4425 
4426 
4427 /*
4428 BObjectImp* UOExecutorModule::mf_AssignMultiComponent()
4429 {
4430 UMulti* multi;
4431 Item* item;
4432 if (getMultiParam( *this, 0, multi ) &&
4433 getItemParan( 1, item ))
4434 {
4435 UHouse* house = multi->as_house();
4436 if (house != nullptr)
4437 {
4438 house->add_component( item );
4439 return new BLong(1);
4440 }
4441 else
4442 {
4443 return new BError( "AssignMultiComponent only functions on houses" );
4444 }
4445 }
4446 else
4447 {
4448 return new BError( "Invalid parameter type" );
4449 }
4450 }
4451 */
4452 
4454 {
4455  unsigned short x, y;
4456  short z;
4457  const String* strrealm;
4458  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getStringParam( 3, strrealm ) )
4459  {
4460  Realms::Realm* realm = find_realm( strrealm->value() );
4461  if ( !realm )
4462  return new BError( "Realm not found" );
4463  if ( !realm->valid( x, y, z ) )
4464  return new BError( "Coordinates Invalid for Realm" );
4465  short newz;
4466  Multi::UMulti* multi;
4467  Item* walkon;
4468  if ( realm->lowest_walkheight( x, y, z, &newz, &multi, &walkon, true, MOVEMODE_LAND ) )
4469  {
4470  std::unique_ptr<BStruct> arr( new BStruct );
4471  arr->addMember( "z", new BLong( newz ) );
4472  if ( multi != nullptr )
4473  arr->addMember( "multi", new EMultiRefObjImp( multi ) );
4474  return arr.release();
4475  }
4476  else
4477  {
4478  return new BError( "Can't stand there" );
4479  }
4480  }
4481  else
4482  {
4483  return new BError( "Invalid parameter type" );
4484  }
4485 }
4486 
4488 {
4489  unsigned short x, y;
4490  int flags;
4491  const String* strrealm;
4492 
4493  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, flags ) &&
4494  getStringParam( 3, strrealm ) )
4495  {
4496  Realms::Realm* realm = find_realm( strrealm->value() );
4497  if ( !realm )
4498  return new BError( "Realm not found" );
4499 
4500  if ( !realm->valid( x, y, 0 ) )
4501  return new BError( "Coordinates Invalid for Realm" );
4502 
4503  std::unique_ptr<ObjArray> newarr( new ObjArray );
4504 
4505  Plib::MapShapeList mlist;
4506  realm->readmultis( mlist, x, y, flags );
4507  realm->getmapshapes( mlist, x, y, flags );
4508 
4509  for ( unsigned i = 0; i < mlist.size(); ++i )
4510  {
4511  std::unique_ptr<BStruct> arr( new BStruct );
4512 
4513  if ( mlist[i].flags & ( Plib::FLAG::MOVELAND | Plib::FLAG::MOVESEA ) )
4514  arr->addMember( "z", new BLong( mlist[i].z + 1 ) );
4515  else
4516  arr->addMember( "z", new BLong( mlist[i].z ) );
4517 
4518  arr->addMember( "height", new BLong( mlist[i].height ) );
4519  arr->addMember( "flags", new BLong( mlist[i].flags ) );
4520  newarr->addElement( arr.release() );
4521  }
4522 
4523  return newarr.release();
4524  }
4525  else
4526  return new BError( "Invalid parameter type" );
4527 }
4528 
4530 {
4531  Item* item;
4532  if ( getItemParam( exec, 0, item ) )
4533  {
4534  if ( item->inuse() )
4535  {
4536  if ( is_reserved_to_me( item ) )
4537  return new BLong( 2 );
4538  else
4539  return new BError( "That item is already being used." );
4540  }
4541  item->inuse( true );
4542  reserved_items_.push_back( ItemRef( item ) );
4543  return new BLong( 1 );
4544  }
4545  else
4546  {
4547  return new BError( "Invalid parameter" );
4548  }
4549 }
4550 
4552 {
4553  Item* item;
4554  if ( getItemParam( exec, 0, item ) )
4555  {
4556  if ( item->inuse() )
4557  {
4558  for ( unsigned i = 0; i < reserved_items_.size(); ++i )
4559  {
4560  if ( reserved_items_[i].get() == item )
4561  {
4562  item->inuse( false );
4563  reserved_items_[i] = reserved_items_.back();
4564  reserved_items_.pop_back();
4565  return new BLong( 1 );
4566  }
4567  }
4568  return new BError( "That item is not reserved by this script." );
4569  }
4570  else
4571  {
4572  return new BError( "That item is not reserved." );
4573  }
4574  }
4575  else
4576  {
4577  return new BError( "Invalid parameter" );
4578  }
4579 }
4580 
4581 
4583 {
4584  Character *towhom, *forwhom;
4585  if ( getCharacterParam( exec, 0, towhom ) && getCharacterParam( exec, 1, forwhom ) )
4586  {
4587  if ( towhom->has_active_client() )
4588  {
4589  send_skillmsg( towhom->client, forwhom );
4590  return new BLong( 1 );
4591  }
4592  else
4593  {
4594  return new BError( "No client attached" );
4595  }
4596  }
4597  else
4598  {
4599  return new BError( "Invalid parameter type" );
4600  }
4601 }
4602 
4603 
4605 {
4606  Character *towhom, *forwhom;
4607  if ( getCharacterParam( exec, 0, towhom ) && getCharacterParam( exec, 1, forwhom ) )
4608  {
4609  if ( towhom->has_active_client() )
4610  {
4611  send_paperdoll( towhom->client, forwhom );
4612  return new BLong( 1 );
4613  }
4614  else
4615  {
4616  return new BError( "No client attached" );
4617  }
4618  }
4619  else
4620  {
4621  return new BError( "Invalid parameter type" );
4622  }
4623 }
4624 
4625 // TODO: a FindSubstance call that does the 'find' portion below, but returns an
4626 // array of the stacks. All returned items would be marked 'inuse'.
4627 // Then, make ConsumeSubstance able to use such an array of owned items.
4628 
4630 {
4631  Item* cont_item;
4632  unsigned int objtype;
4633  int amount;
4634  if ( getItemParam( exec, 0, cont_item ) && getObjtypeParam( exec, 1, objtype ) &&
4635  getParam( 2, amount ) )
4636  {
4637  if ( !cont_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
4638  return new BError( "That is not a container" );
4639  if ( amount < 0 )
4640  return new BError( "Amount cannot be negative" );
4641 
4642  UContainer* cont = static_cast<UContainer*>( cont_item );
4643  int amthave = cont->find_sumof_objtype_noninuse( objtype );
4644  if ( amthave < amount )
4645  return new BError( "Not enough of that substance in container" );
4646 
4647  cont->consume_sumof_objtype_noninuse( objtype, amount );
4648 
4649  return new BLong( 1 );
4650  }
4651  else
4652  {
4653  return new BError( "Invalid parameter type" );
4654  }
4655 }
4656 
4658 {
4659  for ( unsigned i = 0; i < reserved_items_.size(); ++i )
4660  {
4661  if ( reserved_items_[i].get() == item )
4662  return true;
4663  }
4664  return false;
4665 }
4666 
4668 {
4670 #ifndef _WIN32
4671  // the catch_signals_thread (actually main) sits with sigwait(),
4672  // so it won't wake up except by being signalled.
4674 #endif
4675  return new BLong( 1 );
4676 }
4677 
4678 
4680 {
4681  Character* chr;
4682  const String* cmd;
4683  if ( getCharacterParam( exec, 0, chr ) && getStringParam( 1, cmd ) )
4684  {
4685  std::string help = get_textcmd_help( chr, cmd->value().c_str() );
4686  if ( !help.empty() )
4687  {
4688  return new String( help );
4689  }
4690  else
4691  {
4692  return new BError( "No help for that command found" );
4693  }
4694  }
4695  else
4696  {
4697  return new BError( "Invalid parameter type" );
4698  }
4699 }
4700 
4701 
4703 {
4704  Character* chr;
4705  const String* str;
4706  if ( getCharacterParam( exec, 0, chr ) && getStringParam( 1, str ) )
4707  {
4708  if ( chr->has_active_client() )
4709  {
4710  send_tip( chr->client, str->value() );
4711  return new BLong( 1 );
4712  }
4713  else
4714  {
4715  return new BError( "No client attached" );
4716  }
4717  }
4718  else
4719  {
4720  return new BError( "Invalid parameter type" );
4721  }
4722 }
4723 
4725  /* x, y, z, range, flags, realm */ ) // DAVE
4726 {
4727  unsigned short x, y;
4728  short range;
4729  int z, flags;
4730  const String* strrealm;
4731  Realms::Realm* realm;
4732 
4733  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, range ) &&
4734  getParam( 4, flags ) && getStringParam( 5, strrealm ) )
4735  {
4736  realm = find_realm( strrealm->value() );
4737  if ( !realm )
4738  return new BError( "Realm not found" );
4739 
4740  if ( z == LIST_IGNORE_Z )
4741  {
4742  if ( !realm->valid( x, y, 0 ) )
4743  return new BError( "Invalid Coordinates for realm" );
4744  }
4745  else
4746  {
4747  if ( !realm->valid( x, y, static_cast<short>( z ) ) )
4748  return new BError( "Invalid Coordinates for realm" );
4749  }
4750 
4751  std::unique_ptr<ObjArray> newarr( new ObjArray );
4752  WorldIterator<ItemFilter>::InRange( x, y, realm, range, [&]( Item* item ) {
4753  if ( ( tile_uoflags( item->graphic ) & flags ) )
4754  {
4755  if ( ( abs( item->x - x ) <= range ) && ( abs( item->y - y ) <= range ) )
4756  {
4757  if ( ( z == LIST_IGNORE_Z ) || ( abs( item->z - z ) < CONST_DEFAULT_ZRANGE ) )
4758  newarr->addElement( new EItemRefObjImp( item ) );
4759  }
4760  }
4761  } );
4762 
4763  return newarr.release();
4764  }
4765 
4766  return new BError( "Invalid parameter" );
4767 }
4768 
4770 {
4771  unsigned short x, y;
4772  int z, flags;
4773  const String* strrealm;
4774 
4775  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, flags ) &&
4776  getStringParam( 4, strrealm ) )
4777  {
4778  Realms::Realm* realm = find_realm( strrealm->value() );
4779  if ( !realm )
4780  return new BError( "Realm not found" );
4781 
4782  if ( z == LIST_IGNORE_Z )
4783  {
4784  if ( !realm->valid( x, y, 0 ) )
4785  return new BError( "Invalid Coordinates for realm" );
4786  }
4787  else
4788  {
4789  if ( !realm->valid( x, y, static_cast<short>( z ) ) )
4790  return new BError( "Invalid Coordinates for realm" );
4791  }
4792 
4793  std::unique_ptr<ObjArray> newarr( new ObjArray );
4794 
4795  if ( !( flags & ITEMS_IGNORE_STATICS ) )
4796  {
4797  Plib::StaticEntryList slist;
4798  realm->getstatics( slist, x, y );
4799 
4800  for ( unsigned i = 0; i < slist.size(); ++i )
4801  {
4802  if ( ( z == LIST_IGNORE_Z ) || ( slist[i].z == z ) )
4803  {
4804  std::unique_ptr<BStruct> arr( new BStruct );
4805  arr->addMember( "x", new BLong( x ) );
4806  arr->addMember( "y", new BLong( y ) );
4807  arr->addMember( "z", new BLong( slist[i].z ) );
4808  arr->addMember( "objtype", new BLong( slist[i].objtype ) );
4809  arr->addMember( "hue", new BLong( slist[i].hue ) );
4810  newarr->addElement( arr.release() );
4811  }
4812  }
4813  }
4814 
4815  if ( !( flags & ITEMS_IGNORE_MULTIS ) )
4816  {
4817  StaticList mlist;
4818  realm->readmultis( mlist, x, y );
4819 
4820  for ( unsigned i = 0; i < mlist.size(); ++i )
4821  {
4822  if ( ( z == LIST_IGNORE_Z ) || ( mlist[i].z == z ) )
4823  {
4824  std::unique_ptr<BStruct> arr( new BStruct );
4825  arr->addMember( "x", new BLong( x ) );
4826  arr->addMember( "y", new BLong( y ) );
4827  arr->addMember( "z", new BLong( mlist[i].z ) );
4828  arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
4829  newarr->addElement( arr.release() );
4830  }
4831  }
4832  }
4833 
4834  return newarr.release();
4835  }
4836  else
4837  return new BError( "Invalid parameter" );
4838 }
4839 
4840 BObjectImp* UOExecutorModule::mf_ListStaticsNearLocation( /* x, y, z, range, flags, realm */ )
4841 {
4842  unsigned short x, y;
4843  int z, flags;
4844  short range;
4845  const String* strrealm;
4846  Realms::Realm* realm;
4847 
4848  if ( getParam( 0, x ) && getParam( 1, y ) && getParam( 2, z ) && getParam( 3, range ) &&
4849  getParam( 4, flags ) && getStringParam( 5, strrealm ) )
4850  {
4851  realm = find_realm( strrealm->value() );
4852  if ( !realm )
4853  return new BError( "Realm not found" );
4854 
4855  if ( z == LIST_IGNORE_Z )
4856  {
4857  if ( !realm->valid( x, y, 0 ) )
4858  return new BError( "Invalid Coordinates for realm" );
4859  }
4860  else
4861  {
4862  if ( !realm->valid( x, y, static_cast<short>( z ) ) )
4863  return new BError( "Invalid Coordinates for realm" );
4864  }
4865 
4866  std::unique_ptr<ObjArray> newarr( new ObjArray );
4867 
4868  short wxL, wyL, wxH, wyH;
4869  wxL = x - range;
4870  if ( wxL < 0 )
4871  wxL = 0;
4872  wyL = y - range;
4873  if ( wyL < 0 )
4874  wyL = 0;
4875  wxH = x + range;
4876  if ( wxH > realm->width() - 1 )
4877  wxH = realm->width() - 1;
4878  wyH = y + range;
4879  if ( wyH > realm->height() - 1 )
4880  wyH = realm->height() - 1;
4881 
4882  for ( unsigned short wx = wxL; wx <= wxH; ++wx )
4883  {
4884  for ( unsigned short wy = wyL; wy <= wyH; ++wy )
4885  {
4886  if ( !( flags & ITEMS_IGNORE_STATICS ) )
4887  {
4888  Plib::StaticEntryList slist;
4889  realm->getstatics( slist, wx, wy );
4890 
4891  for ( unsigned i = 0; i < slist.size(); ++i )
4892  {
4893  if ( ( z == LIST_IGNORE_Z ) || ( abs( slist[i].z - z ) < CONST_DEFAULT_ZRANGE ) )
4894  {
4895  std::unique_ptr<BStruct> arr( new BStruct );
4896  arr->addMember( "x", new BLong( wx ) );
4897  arr->addMember( "y", new BLong( wy ) );
4898  arr->addMember( "z", new BLong( slist[i].z ) );
4899  arr->addMember( "objtype", new BLong( slist[i].objtype ) );
4900  arr->addMember( "hue", new BLong( slist[i].hue ) );
4901  newarr->addElement( arr.release() );
4902  }
4903  }
4904  }
4905 
4906  if ( !( flags & ITEMS_IGNORE_MULTIS ) )
4907  {
4908  StaticList mlist;
4909  realm->readmultis( mlist, wx, wy );
4910 
4911  for ( unsigned i = 0; i < mlist.size(); ++i )
4912  {
4913  if ( ( z == LIST_IGNORE_Z ) || ( abs( mlist[i].z - z ) < CONST_DEFAULT_ZRANGE ) )
4914  {
4915  std::unique_ptr<BStruct> arr( new BStruct );
4916  arr->addMember( "x", new BLong( wx ) );
4917  arr->addMember( "y", new BLong( wy ) );
4918  arr->addMember( "z", new BLong( mlist[i].z ) );
4919  arr->addMember( "objtype", new BLong( mlist[i].graphic ) );
4920  newarr->addElement( arr.release() );
4921  }
4922  }
4923  }
4924  }
4925  }
4926 
4927  return newarr.release();
4928  }
4929  else
4930  return new BError( "Invalid parameter" );
4931 }
4932 
4933 // Birdy : (Notes on Pathfinding)
4934 //
4935 // This implimentation of pathfinding is actually from a very nice stl based A*
4936 // implimentation. It will, within reason, succeed in finding a path in a wide
4937 // variety of circumstance, including traversing stairs and multiple level
4938 // structures.
4939 //
4940 // Unfortunately, it, or my POL support classes implimenting it, seems to at times
4941 // generate corrupted results in finding a path. These corruptions take the form
4942 // of basically looping within the result list, or linking in non-closed list nodes
4943 // which may be on the open list and generate more looping or which may have even
4944 // been deleted by the algorithm, generally causing an illegal reference and crash.
4945 //
4946 // Thus far, the above senerio has only been replicated, though fairly reliably, in
4947 // cases where there are many many(about 100) npc's trying to pathfind at the same time,
4948 // in close proximity to one another.
4949 //
4950 // "Looping" will, of course, cause the shard to hang while the pathfinding routine
4951 // tries to build the child list for the solution set. An illegal reference will
4952 // cause the shard to crash most likely. Neither is tollerable of course, so I have
4953 // put in two safeguards against this.
4954 //
4955 // Safeguard #1) As we go through the nodes in the solution, I make sure each of them
4956 // is on the Closed List. If not, the pathfind errors out with a
4957 // "Solution Corrupt!" error. This is an attempt to short circuit any
4958 // solution containing erroneous nodes in it.
4959 //
4960 // Safeguard #2) I keep a vector of the nodes in the solution as we go through them.
4961 // Before adding each node to the vector, I search that vector for that
4962 // node. If that vector already contains the node, the pathfind errors
4963 // out with a "Solution Corrupt!" error. THis is an attempt to catch
4964 // the cases where Closed List nodes have looped for some reason.
4965 //
4966 // These two safeguards should not truly be necessary for this algorithm, and take up
4967 // space and time to guard against. Thus, finding out why these problems are occurring
4968 // in the first place would be great, as we could fix that instead and remove these
4969 // checks. If necessary to live with long term, then the solution vector should probably
4970 // be made into a hash table for quick lookups.
4971 //
4972 // I have also implimented a sort blocking list that is supposed to represent those
4973 // spots that mobiles occupy and are thus not walkable on. This is only maintained if
4974 // the mobilesBlock parameter passed from escript is true. It would probably be good
4975 // to make this a hash table. It may be said that it is a good idea to pre-process all
4976 // blocking spots on the map, but this would hinder the ability to manage 3d shifts, or
4977 // probably be prohibitive if an attempt were made to check for traversal at all possible
4978 // various z's. So it may be that checking such at the time of the search for the path
4979 // is ultimately the best means of handling this.
4980 //
4981 // Other details that may be nice to have here might be a method for interacting with
4982 // escript, letting escript tell the pathfinder to ignore doors, and then the pathfinder
4983 // putting in the solution list an indication to escript that at the given node a door
4984 // was encountered, so that AI can open the door before attempting to traverse that
4985 // path. Providing the user the ability to define an array of objects which are to be
4986 // considered as blocking, or an array of objects which are to be ignored even if they
4987 // are blocking might be nice as well. The former could be easily implimented by
4988 // adding to the mobile's blocking list. The latter may be more difficult to do
4989 // considering if the item blocks, the walk function will not let you traverse it, and
4990 // it is the walk function that we use to accurately replicate the ability of the
4991 // mobile to traverse from one place to the next. Additional functionality of some
4992 // sort may be necessary to do this, or making a copy of the walk function and putting
4993 // in a special walk function strictly for pathfinding which can take this array into
4994 // account.
4995 //
4996 // For now, however, these concerns are all unaddressed. I wish to see if this
4997 // function works and is useful, and also would prefer to find some way to track down
4998 // the corruption of the solution nodes before adding more complexity to the problem.
4999 //
5000 // Other than the below function, the following files will be added to CVS to support
5001 // it in the src module. They are :
5002 //
5003 // stlastar.h -- virtually untouched by me really, it is the original author's
5004 // implimentation, pretty nicely done and documented if you are
5005 // interested in learning about A*.
5006 //
5007 // fsa.h -- a "fixed size allocation" module which I toy with enabling and
5008 // disabling. Using it is supposed to get you some speed, but it
5009 // limits your maps because it will only allocate a certain # of
5010 // nodes at once, and after that, it will return out of memory.
5011 // Presently, it is disabled, but will be submitted to CVS for
5012 // completion in case we wish to use it later.
5013 //
5014 // uopathnode.h -- this is stricly my work, love it or hate it, it's pretty quick
5015 // and dirty, and can stand to be cleaned up, optimized, and so forth.
5016 // I have the name field and function in there to allow for some
5017 // debugging, though the character array could probably be removed to
5018 // make the nodes smaller. I didn't find it mattered particularly, since
5019 // these puppies are created and destroyed at a pretty good clip.
5020 // It is this class that encapsulates the necessary functionality to
5021 // make the otherwise fairly generic stlastar class work.
5022 
5024 
5026 {
5027  unsigned short x1, x2;
5028  unsigned short y1, y2;
5029  short z1, z2;
5030  const String* strrealm;
5031 
5032  if ( getParam( 0, x1 ) && getParam( 1, y1 ) && getParam( 2, z1, ZCOORD_MIN, ZCOORD_MAX ) &&
5033  getParam( 3, x2 ) && getParam( 4, y2 ) && getParam( 5, z2, ZCOORD_MIN, ZCOORD_MAX ) &&
5034  getStringParam( 6, strrealm ) )
5035  {
5036  if ( pol_distance( x1, y1, x2, y2 ) > settingsManager.ssopt.max_pathfind_range )
5037  return new BError( "Beyond Max Range." );
5038 
5039  short theSkirt;
5040  int flags;
5041 
5042  if ( !getParam( 7, flags ) )
5043  flags = FP_IGNORE_MOBILES;
5044 
5045  if ( !getParam( 8, theSkirt ) )
5046  theSkirt = 5;
5047 
5048  if ( theSkirt < 0 )
5049  theSkirt = 0;
5050 
5051  Realms::Realm* realm = find_realm( strrealm->value() );
5052  if ( !realm )
5053  return new BError( "Realm not found" );
5054  if ( !realm->valid( x1, y1, z1 ) )
5055  return new BError( "Start Coordinates Invalid for Realm" );
5056  if ( !realm->valid( x2, y2, z2 ) )
5057  return new BError( "End Coordinates Invalid for Realm" );
5058  UOSearch* astarsearch;
5059  unsigned int SearchState;
5060  astarsearch = new UOSearch;
5061  short xL, xH, yL, yH;
5062 
5063  if ( x1 < x2 )
5064  {
5065  xL = x1 - theSkirt;
5066  xH = x2 + theSkirt;
5067  }
5068  else
5069  {
5070  xH = x1 + theSkirt;
5071  xL = x2 - theSkirt;
5072  }
5073 
5074  if ( y1 < y2 )
5075  {
5076  yL = y1 - theSkirt;
5077  yH = y2 + theSkirt;
5078  }
5079  else
5080  {
5081  yH = y1 + theSkirt;
5082  yL = y2 - theSkirt;
5083  }
5084 
5085  if ( xL < 0 )
5086  xL = 0;
5087  if ( yL < 0 )
5088  yL = 0;
5089  if ( xH >= realm->width() )
5090  xH = realm->width() - 1;
5091  if ( yH >= realm->height() )
5092  yH = realm->height() - 1;
5093 
5094  if ( Plib::systemstate.config.loglevel >= 12 )
5095  {
5096  POLLOG.Format( "[FindPath] Calling FindPath({}, {}, {}, {}, {}, {}, {}, 0x{:X}, {})\n" )
5097  << x1 << y1 << z1 << x2 << y2 << z2 << strrealm->data() << flags << theSkirt;
5098  POLLOG.Format( "[FindPath] search for Blockers inside {} {} {} {}\n" )
5099  << xL << yL << xH << yH;
5100  }
5101 
5102  AStarBlockers theBlockers( xL, xH, yL, yH );
5103 
5104  if ( !( flags & FP_IGNORE_MOBILES ) )
5105  {
5106  WorldIterator<MobileFilter>::InBox( xL, yL, xH, yH, realm, [&]( Mobile::Character* chr ) {
5107  theBlockers.AddBlocker( chr->x, chr->y, chr->z );
5108 
5109  if ( Plib::systemstate.config.loglevel >= 12 )
5110  POLLOG.Format( "[FindPath] add Blocker {} at {} {} {}\n" )
5111  << chr->name() << chr->x << chr->y << chr->z;
5112  } );
5113  }
5114 
5115  // passed via GetSuccessors to realm->walkheight
5116  bool doors_block = ( flags & FP_IGNORE_DOORS ) ? false : true;
5117 
5118  if ( Plib::systemstate.config.loglevel >= 12 )
5119  {
5120  POLLOG.Format( "[FindPath] use StartNode {} {} {}\n" ) << x1 << y1 << z1;
5121  POLLOG.Format( "[FindPath] use EndNode {} {} {}\n" ) << x2 << y2 << z2;
5122  }
5123 
5124  // Create a start state
5125  UOPathState nodeStart( x1, y1, z1, realm, &theBlockers );
5126  // Define the goal state
5127  UOPathState nodeEnd( x2, y2, z2, realm, &theBlockers );
5128  // Set Start and goal states
5129  astarsearch->SetStartAndGoalStates( nodeStart, nodeEnd );
5130  do
5131  {
5132  SearchState = astarsearch->SearchStep( doors_block );
5133  } while ( SearchState == UOSearch::SEARCH_STATE_SEARCHING );
5134  if ( SearchState == UOSearch::SEARCH_STATE_SUCCEEDED )
5135  {
5136  UOPathState* node = astarsearch->GetSolutionStart();
5137  ObjArray* nodeArray = nullptr;
5138  BStruct* nextStep = nullptr;
5139 
5140  nodeArray = new ObjArray();
5141  while ( ( node = astarsearch->GetSolutionNext() ) != nullptr )
5142  {
5143  nextStep = new BStruct;
5144  nextStep->addMember( "x", new BLong( node->x ) );
5145  nextStep->addMember( "y", new BLong( node->y ) );
5146  nextStep->addMember( "z", new BLong( node->z ) );
5147  nodeArray->addElement( nextStep );
5148  }
5149  astarsearch->FreeSolutionNodes();
5150  delete astarsearch;
5151  return nodeArray;
5152  }
5153  else if ( SearchState == UOSearch::SEARCH_STATE_FAILED )
5154  {
5155  delete astarsearch;
5156  return new BError( "Failed to find a path." );
5157  }
5158  else if ( SearchState == UOSearch::SEARCH_STATE_OUT_OF_MEMORY )
5159  {
5160  delete astarsearch;
5161  return new BError( "Out of memory." );
5162  }
5163  else if ( SearchState == UOSearch::SEARCH_STATE_SOLUTION_CORRUPTED )
5164  {
5165  delete astarsearch;
5166  return new BError( "Solution Corrupted!" );
5167  }
5168 
5169  delete astarsearch;
5170  return new BError( "Pathfind Error." );
5171  }
5172  else
5173  {
5174  return new BError( "Invalid parameter" );
5175  }
5176 }
5177 
5178 
5180 {
5181  Item* item;
5182  Character* chr;
5183 
5184  if ( getItemParam( exec, 0, item ) && getCharacterParam( exec, 1, chr ) )
5185  {
5186  const ItemDesc& itemdesc = find_itemdesc( item->objtype_ );
5187 
5188  if ( itemdesc.requires_attention && ( chr->skill_ex_active() || chr->casting_spell() ) )
5189  {
5190  if ( chr->client != nullptr )
5191  {
5192  send_sysmessage( chr->client, "I am already doing something else." );
5193  return new BError( "Character busy." );
5194  ;