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