Pol  Revision:cb584c9
npc.cpp
Go to the documentation of this file.
1 
27 #include "npc.h"
28 
29 #include <stdlib.h>
30 
31 #include "../../bscript/berror.h"
32 #include "../../clib/cfgelem.h"
33 #include "../../clib/fileutil.h"
34 #include "../../clib/logfacility.h"
35 #include "../../clib/passert.h"
36 #include "../../clib/random.h"
37 #include "../../clib/refptr.h"
38 #include "../../clib/streamsaver.h"
39 #include "../baseobject.h"
40 #include "../dice.h"
41 #include "../fnsearch.h"
42 #include "../globals/state.h"
43 #include "../globals/uvars.h"
44 #include "../item/armor.h"
45 #include "../item/weapon.h"
46 #include "../listenpt.h"
47 #include "../mdelta.h"
48 #include "../module/npcmod.h"
49 #include "../module/osmod.h"
50 #include "../module/uomod.h"
51 #include "../multi/multi.h"
52 #include "../npctmpl.h"
53 #include "../scrdef.h"
54 #include "../scrsched.h"
55 #include "../scrstore.h"
56 #include "../ufunc.h"
57 #include "../uobjcnt.h"
58 #include "../uobject.h"
59 #include "../uoexec.h"
60 #include "../uoscrobj.h"
61 #include "../uworld.h"
62 #include "attribute.h"
63 #include "charactr.h"
64 #include "wornitems.h"
65 
66 
67 /* An area definition is as follows:
68  pt: (x,y)
69  rect: [pt-pt]
70  area: rect,rect,...
71  So, format is: [(x1,y1)-(x2,y2)],[],[],...
72  Well, right now, the format is x1 y1 x2 y2 ... (ick)
73 */
74 namespace Pol
75 {
76 namespace Mobile
77 {
78 unsigned short calc_thru_damage( double damage, unsigned short ar );
79 
80 NPC::NPC( u32 objtype, const Clib::ConfigElem& elem )
81  : Character( objtype, Core::UOBJ_CLASS::CLASS_NPC ),
82  // UOBJECT INTERFACE
83  // NPC INTERFACE
84  npc_ar_( 0 ),
85  // MOVEMENT
86  run_speed( dexterity() ),
87  anchor(),
88  // EVENTS
89  // SCRIPT
90  script( "" ),
91  ex( nullptr ),
92  // MISC
93  damaged_sound( 0 ),
94  template_name(),
95  master_( nullptr ),
96  template_( Core::find_npc_template( elem ) )
97 {
98  connected( true );
99  logged_in( true );
100  use_adjustments( true );
102 }
103 
105 {
106  stop_scripts();
108 }
109 
111 {
112  if ( ex != nullptr )
113  {
114  // this will force the execution engine to stop running this script immediately
115  // dont delete the executor here, since it could currently run
116  ex->seterror( true );
117  ex->os_module->revive();
120  }
121 }
122 
124 {
125  // stop_scripts();
126  wornitems->destroy_contents();
127  if ( registered_house > 0 )
128  {
130  if ( multi != nullptr )
131  {
132  multi->unregister_object( (UObject*)this );
133  }
134  registered_house = 0;
135  }
136  base::destroy();
137 }
138 
139 const char* NPC::classname() const
140 {
141  return "NPC";
142 }
143 
144 
145 // 8-25-05 Austin
146 // Moved unsigned short pol_distance( unsigned short x1, unsigned short y1,
147 // unsigned short x2, unsigned short y2 )
148 // to ufunc.cpp
149 
151 {
152  unsigned short newx = x + Core::move_delta[fdir].xmove;
153  unsigned short newy = y + Core::move_delta[fdir].ymove;
154 
155  if ( anchor.enabled && !warmode() )
156  {
157  unsigned short curdist = Core::pol_distance( x, y, anchor.x, anchor.y );
158  unsigned short newdist = Core::pol_distance( newx, newy, anchor.x, anchor.y );
159  if ( newdist > curdist ) // if we're moving further away, see if we can
160  {
161  if ( newdist > anchor.dstart )
162  {
163  int perc = 100 - ( newdist - anchor.dstart ) * anchor.psub;
164  if ( perc < 5 )
165  perc = 5;
166  if ( Clib::random_int( 99 ) > perc )
167  return false;
168  }
169  }
170  }
171  return true;
172 }
173 
174 bool NPC::could_move( Core::UFACING fdir ) const
175 {
176  short newz;
177  Multi::UMulti* supporting_multi;
178  Items::Item* walkon_item;
179  // Check for diagonal move - use Nandos change from charactr.cpp -- OWHorus (2011-04-26)
180  if ( fdir & 1 ) // check if diagonal movement is allowed -- Nando (2009-02-26)
181  {
182  u8 tmp_facing = ( fdir + 1 ) & 0x7;
183  unsigned short tmp_newx = x + Core::move_delta[tmp_facing].xmove;
184  unsigned short tmp_newy = y + Core::move_delta[tmp_facing].ymove;
185 
186  // needs to save because if only one direction is blocked, it shouldn't block ;)
187  short current_boost = gradual_boost;
188  bool walk1 = realm->walkheight( this, tmp_newx, tmp_newy, z, &newz, &supporting_multi,
189  &walkon_item, &current_boost );
190 
191  tmp_facing = ( fdir - 1 ) & 0x7;
192  tmp_newx = x + Core::move_delta[tmp_facing].xmove;
193  tmp_newy = y + Core::move_delta[tmp_facing].ymove;
194  current_boost = gradual_boost;
195  if ( !walk1 && !realm->walkheight( this, tmp_newx, tmp_newy, z, &newz, &supporting_multi,
196  &walkon_item, &current_boost ) )
197  return false;
198  }
199  unsigned short newx = x + Core::move_delta[fdir].xmove;
200  unsigned short newy = y + Core::move_delta[fdir].ymove;
201  short current_boost = gradual_boost;
202  return realm->walkheight( this, newx, newy, z, &newz, &supporting_multi, &walkon_item,
203  &current_boost ) &&
204  !npc_path_blocked( fdir ) && anchor_allows_move( fdir );
205 }
206 
208 {
209  if ( can_freemove() ||
210  ( !this->master() && !Core::settingsManager.ssopt.mobiles_block_npc_movement ) )
211  return false;
212 
213  unsigned short newx = x + Core::move_delta[fdir].xmove;
214  unsigned short newy = y + Core::move_delta[fdir].ymove;
215 
216  unsigned short wx, wy;
217  Core::zone_convert_clip( newx, newy, realm, &wx, &wy );
218 
219  if ( Core::settingsManager.ssopt.mobiles_block_npc_movement )
220  {
221  for ( const auto& chr : realm->zone[wx][wy].characters )
222  {
223  // First check if there really is a character blocking
224  if ( chr->x == newx && chr->y == newy && chr->z >= z - 10 && chr->z <= z + 10 )
225  {
226  if ( !chr->dead() && is_visible_to_me( chr ) )
227  return true;
228  }
229  }
230  }
231  for ( const auto& chr : realm->zone[wx][wy].npcs )
232  {
233  // First check if there really is a character blocking
234  if ( chr->x == newx && chr->y == newy && chr->z >= z - 10 && chr->z <= z + 10 )
235  {
236  // Check first with the ssopt false to now allow npcs of same master running on top of
237  // each other
238  if ( !Core::settingsManager.ssopt.mobiles_block_npc_movement )
239  {
240  NPC* npc = static_cast<NPC*>( chr );
241  if ( npc->master() && this->master() == npc->master() && !npc->dead() &&
242  is_visible_to_me( npc ) )
243  return true;
244  }
245  else
246  {
247  if ( !chr->dead() && is_visible_to_me( chr ) )
248  return true;
249  }
250  }
251  }
252  return false;
253 }
254 
256 {
257  sw() << classname() << " " << template_name.get() << pf_endl;
258  sw() << "{" << pf_endl;
259  printProperties( sw );
260  sw() << "}" << pf_endl;
261  sw() << pf_endl;
262  // sw.flush();
263 }
264 
266 {
267  printOn( sw );
268 }
269 
271 {
272  using namespace fmt;
273 
274  base::printProperties( sw );
275 
276  if ( registered_house )
277  sw() << "\tRegisteredHouse\t0x" << hex( registered_house ) << pf_endl;
278 
279  if ( npc_ar_ )
280  sw() << "\tAR\t" << npc_ar_ << pf_endl;
281 
282  if ( !script.get().empty() )
283  sw() << "\tscript\t" << script.get() << pf_endl;
284 
285  if ( master_.get() != nullptr )
286  sw() << "\tmaster\t" << master_->serial << pf_endl;
287 
288  if ( has_speech_color() )
289  sw() << "\tSpeechColor\t" << speech_color() << pf_endl;
290 
291  if ( has_speech_font() )
292  sw() << "\tSpeechFont\t" << speech_font() << pf_endl;
293 
294  if ( run_speed != dexterity() )
295  sw() << "\tRunSpeed\t" << run_speed << pf_endl;
296 
297  if ( use_adjustments() != true )
298  sw() << "\tUseAdjustments\t" << use_adjustments() << pf_endl;
299 
300  if ( has_orig_fire_resist() )
301  sw() << "\tFireResist\t" << orig_fire_resist() << pf_endl;
302  if ( has_orig_cold_resist() )
303  sw() << "\tColdResist\t" << orig_cold_resist() << pf_endl;
304  if ( has_orig_energy_resist() )
305  sw() << "\tEnergyResist\t" << orig_energy_resist() << pf_endl;
306  if ( has_orig_poison_resist() )
307  sw() << "\tPoisonResist\t" << orig_poison_resist() << pf_endl;
308  if ( has_orig_physical_resist() )
309  sw() << "\tPhysicalResist\t" << orig_physical_resist() << pf_endl;
310 
311  if ( has_orig_fire_damage() )
312  sw() << "\tFireDamage\t" << orig_fire_damage() << pf_endl;
313  if ( has_orig_cold_damage() )
314  sw() << "\tColdDamage\t" << orig_cold_damage() << pf_endl;
315  if ( has_orig_energy_damage() )
316  sw() << "\tEnergyDamage\t" << orig_energy_damage() << pf_endl;
317  if ( has_orig_poison_damage() )
318  sw() << "\tPoisonDamage\t" << orig_poison_damage() << pf_endl;
319  if ( has_orig_physical_damage() )
320  sw() << "\tPhysicalDamage\t" << orig_physical_damage() << pf_endl;
321  if ( no_drop_exception() )
322  sw() << "\tNoDropException\t" << no_drop_exception() << pf_endl;
323 }
324 
326 {
328  sw() << "# template: " << template_.name << pf_endl;
329  if ( anchor.enabled )
330  {
331  sw() << "# anchor: x=" << anchor.x << " y=" << anchor.y << " dstart=" << anchor.dstart
332  << " psub=" << anchor.psub << pf_endl;
333  }
334 }
335 
337 {
338  registered_house = elem.remove_ulong( "REGISTEREDHOUSE", 0 );
339 
340  Items::UWeapon* wpn = static_cast<Items::UWeapon*>(
342  if ( wpn == nullptr )
344  if ( wpn != nullptr )
345  weapon = wpn;
346 
347  Items::UArmor* sld = static_cast<Items::UArmor*>(
349  if ( sld == nullptr )
351  if ( sld != nullptr )
352  shield = sld;
353 
354  // Load the base, equiping items etc will refresh_ar() to update for reals.
356 
357  // dave 3/19/3, read templatename only if empty
358  if ( template_name.get().empty() )
359  {
360  template_name = elem.rest();
361 
362  if ( template_name.get().empty() )
363  {
364  std::string tmp;
365  if ( getprop( "template", tmp ) )
366  {
367  template_name = tmp.c_str() + 1;
368  }
369  }
370  }
371 
372  unsigned int master_serial;
373  if ( elem.remove_prop( "MASTER", &master_serial ) )
374  {
375  Character* chr = Core::system_find_mobile( master_serial );
376  if ( chr != nullptr )
377  master_.set( chr );
378  }
379 
380  script = elem.remove_string( "script", "" );
381  if ( !script.get().empty() )
382  start_script();
383 
384  speech_color( elem.remove_ushort( "SpeechColor", Core::DEFAULT_TEXT_COLOR ) );
385  speech_font( elem.remove_ushort( "SpeechFont", Core::DEFAULT_TEXT_FONT ) );
386  saveonexit( elem.remove_bool( "SaveOnExit", true ) );
387 
388  mob_flags_.change( MOB_FLAGS::USE_ADJUSTMENTS, elem.remove_bool( "UseAdjustments", true ) );
389  run_speed = elem.remove_ushort( "RunSpeed", dexterity() );
390 
391  damaged_sound = elem.remove_ushort( "DamagedSound", 0 );
392  no_drop_exception( elem.remove_bool( "NoDropException", false ) );
393 }
394 
396 {
397  // for ar and elemental damage/resist the mod values are loaded before in character code!
398  auto diceValue = []( const std::string& dicestr, int* value ) -> bool {
399  Core::Dice dice;
400  std::string errmsg;
401  if ( !dice.load( dicestr.c_str(), &errmsg ) )
402  *value = atoi( dicestr.c_str() );
403  else
404  *value = dice.roll();
405  return *value != 0;
406  };
407  auto apply = []( Core::ValueModPack v, int value ) -> Core::ValueModPack {
408  return v.addToValue( static_cast<s16>( value ) );
409  };
410  auto refresh = []( Core::ValueModPack v ) -> Core::ValueModPack { return v.addToValue( v.mod ); };
411 
412  std::string tmp;
413  int value;
414  if ( elem.remove_prop( "AR", &tmp ) && diceValue( tmp, &value ) )
415  npc_ar_ = static_cast<u16>( value );
416 
417  // elemental start
418  // first apply template value as value and if mod or value exist sum them
419  if ( elem.remove_prop( "FIRERESIST", &tmp ) && diceValue( tmp, &value ) )
420  {
421  fire_resist( apply( fire_resist(), value ) );
422  orig_fire_resist( static_cast<s16>( value ) );
423  }
424  if ( has_fire_resist() )
425  fire_resist( refresh( fire_resist() ) );
426  if ( elem.remove_prop( "COLDRESIST", &tmp ) && diceValue( tmp, &value ) )
427  {
428  cold_resist( apply( cold_resist(), value ) );
429  orig_cold_resist( static_cast<s16>( value ) );
430  }
431  if ( has_cold_resist() )
432  cold_resist( refresh( cold_resist() ) );
433  if ( elem.remove_prop( "ENERGYRESIST", &tmp ) && diceValue( tmp, &value ) )
434  {
435  energy_resist( apply( energy_resist(), value ) );
436  orig_energy_resist( static_cast<s16>( value ) );
437  }
438  if ( has_energy_resist() )
439  energy_resist( refresh( energy_resist() ) );
440  if ( elem.remove_prop( "POISONRESIST", &tmp ) && diceValue( tmp, &value ) )
441  {
442  poison_resist( apply( poison_resist(), value ) );
443  orig_poison_resist( static_cast<s16>( value ) );
444  }
445  if ( has_poison_resist() )
446  poison_resist( refresh( poison_resist() ) );
447  if ( elem.remove_prop( "PHYSICALRESIST", &tmp ) && diceValue( tmp, &value ) )
448  {
449  physical_resist( apply( physical_resist(), value ) );
450  orig_physical_resist( static_cast<s16>( value ) );
451  }
452  if ( has_physical_resist() )
453  physical_resist( refresh( physical_resist() ) );
454 
455  if ( elem.remove_prop( "FIREDAMAGE", &tmp ) && diceValue( tmp, &value ) )
456  {
457  fire_damage( apply( fire_damage(), value ) );
458  orig_fire_damage( static_cast<s16>( value ) );
459  }
460  if ( has_fire_damage() )
461  fire_damage( refresh( fire_damage() ) );
462  if ( elem.remove_prop( "COLDDAMAGE", &tmp ) && diceValue( tmp, &value ) )
463  {
464  cold_damage( apply( cold_damage(), value ) );
465  orig_cold_damage( static_cast<s16>( value ) );
466  }
467  if ( has_cold_damage() )
468  cold_damage( refresh( cold_damage() ) );
469  if ( elem.remove_prop( "ENERGYDAMAGE", &tmp ) && diceValue( tmp, &value ) )
470  {
471  energy_damage( apply( energy_damage(), value ) );
472  orig_energy_damage( static_cast<s16>( value ) );
473  }
474  if ( has_energy_damage() )
475  energy_damage( refresh( energy_damage() ) );
476  if ( elem.remove_prop( "POISONDAMAGE", &tmp ) && diceValue( tmp, &value ) )
477  {
478  poison_damage( apply( poison_damage(), value ) );
479  orig_poison_damage( static_cast<s16>( value ) );
480  }
481  if ( has_poison_damage() )
482  poison_damage( refresh( poison_damage() ) );
483  if ( elem.remove_prop( "PHYSICALDAMAGE", &tmp ) && diceValue( tmp, &value ) )
484  {
485  physical_damage( apply( physical_damage(), value ) );
486  orig_physical_damage( static_cast<s16>( value ) );
487  }
488  if ( has_physical_damage() )
489  physical_damage( refresh( physical_damage() ) );
490 }
491 
493 {
494  // 3/18/3 dave copied this npctemplate code from readNpcProperties, because base::readProperties
495  // will call the exported vital functions before npctemplate is set (distro uses npctemplate in
496  // the exported funcs).
497  template_name = elem.rest();
498 
499  if ( template_name.get().empty() )
500  {
501  std::string tmp;
502  if ( getprop( "template", tmp ) )
503  {
504  template_name = tmp.c_str() + 1;
505  }
506  }
507  base::readProperties( elem );
508  readNpcProperties( elem );
509 }
510 
512 {
513  std::string diestring;
514  Core::Dice dice;
515  std::string errmsg;
516 
517  for ( Attribute* pAttr = Attribute::FindAttribute( 0 ); pAttr; pAttr = pAttr->next )
518  {
519  AttributeValue& av = attribute( pAttr->attrid );
520  for ( unsigned i = 0; i < pAttr->aliases.size(); ++i )
521  {
522  if ( elem.remove_prop( pAttr->aliases[i].c_str(), &diestring ) )
523  {
524  if ( !dice.load( diestring.c_str(), &errmsg ) )
525  {
526  elem.throw_error( "Error reading Attribute " + pAttr->name + ": " + errmsg );
527  }
528  int base = dice.roll() * 10;
529  if ( base > static_cast<int>( ATTRIBUTE_MAX_BASE ) )
530  base = ATTRIBUTE_MAX_BASE;
531 
532  av.base( static_cast<unsigned short>( base ) );
533 
534  break;
535  }
536  }
537  }
538 }
539 
541 {
542  readCommonProperties( elem );
543  readNewNpcAttributes( elem );
544  readNpcProperties( elem );
547 
548  // readNpcProperties( elem );
549 }
550 
552 {
553  if ( ex != nullptr )
554  {
555  ex->seterror( true );
556  // A Sleeping script would otherwise sit and wait until it wakes up to be killed.
557  ex->os_module->revive();
560  ex = nullptr;
561  // when the NPC executor module destructs, it checks this NPC to see if it points
562  // back at it. If not, it leaves us alone.
563  }
564  if ( !script.get().empty() )
565  start_script();
566 }
567 
568 void NPC::on_death( Items::Item* corpse )
569 {
570  // base::on_death intentionally not called
572 
573 
574  corpse->setprop( "npctemplate", "s" + template_name.get() );
575  if ( Clib::FileExists( "scripts/misc/death.ecl" ) )
576  Core::start_script( "misc/death", new Module::EItemRefObjImp( corpse ) );
577 
579  if ( ex != nullptr )
580  {
581  // this will force the execution engine to stop running this script immediately
582  ex->seterror( true );
583  ex->os_module->revive();
586  }
587 
588  destroy();
589 }
590 
591 
593 {
594  passert( ex == nullptr );
595  passert( !script.get().empty() );
596  Core::ScriptDef sd( script, template_.pkg, "scripts/ai/" );
597  // Log( "NPC script starting: %s\n", sd.name().c_str() );
598 
600  // find_script( "ai/" + script );
601 
602  if ( prog.get() == nullptr )
603  {
604  ERROR_PRINT << "Unable to read script " << sd.name() << " for NPC " << name() << "(0x"
605  << fmt::hexu( serial ) << ")\n";
606  throw std::runtime_error( "Error loading NPCs" );
607  }
608 
610  ex->addModule( new Module::NPCExecutorModule( *ex, *this ) );
612  ex->addModule( uoemod );
613  if ( ex->setProgram( prog.get() ) == false )
614  {
615  ERROR_PRINT << "There was an error running script " << script.get() << " for NPC " << name()
616  << "(0x" << fmt::hexu( serial ) << ")\n";
617  throw std::runtime_error( "Error loading NPCs" );
618  }
619 
620  uoemod->attached_npc_ = this;
621 
623 }
624 
625 
626 bool NPC::can_be_renamed_by( const Character* chr ) const
627 {
628  return ( master_.get() == chr );
629 }
630 
631 
632 void NPC::on_pc_spoke( Character* src_chr, const char* speech, u8 texttype )
633 {
634  /*
635  cerr << "PC " << src_chr->name()
636  << " spoke in range of NPC " << name()
637  << ": '" << speech << "'" << endl;
638  */
639 
640  if ( ex != nullptr )
641  {
642  if ( ( ex->eventmask & Core::EVID_SPOKE ) && inrangex( this, src_chr, ex->speech_size ) &&
643  !deafened() )
644  {
645  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
646  is_visible_to_me( src_chr ) )
648  new Module::SpeechEvent( src_chr, speech,
649  Core::TextTypeToString( texttype ) ) ); // DAVE added texttype
650  }
651  }
652 }
653 
654 void NPC::on_ghost_pc_spoke( Character* src_chr, const char* speech, u8 texttype )
655 {
656  if ( ex != nullptr )
657  {
658  if ( ( ex->eventmask & Core::EVID_GHOST_SPEECH ) &&
659  inrangex( this, src_chr, ex->speech_size ) && !deafened() )
660  {
661  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
662  is_visible_to_me( src_chr ) )
664  new Module::SpeechEvent( src_chr, speech,
665  Core::TextTypeToString( texttype ) ) ); // DAVE added texttype
666  }
667  }
668 }
669 
670 void NPC::on_pc_spoke( Character* src_chr, const char* speech, u8 texttype, const u16* wspeech,
671  const char lang[4], Bscript::ObjArray* speechtokens )
672 {
673  if ( ex != nullptr )
674  {
675  if ( Core::settingsManager.ssopt.seperate_speechtoken )
676  {
677  if ( speechtokens != nullptr && ( ( ex->eventmask & Core::EVID_TOKEN_SPOKE ) == 0 ) )
678  return;
679  else if ( speechtokens == nullptr && ( ( ex->eventmask & Core::EVID_SPOKE ) == 0 ) )
680  return;
681  }
682  if ( ( ( ex->eventmask & Core::EVID_SPOKE ) || ( ex->eventmask & Core::EVID_TOKEN_SPOKE ) ) &&
683  inrangex( this, src_chr, ex->speech_size ) && !deafened() )
684  {
685  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
686  is_visible_to_me( src_chr ) )
687  {
689  src_chr, speech, Core::TextTypeToString( texttype ), wspeech, lang, speechtokens ) );
690  }
691  }
692  }
693 }
694 
695 void NPC::on_ghost_pc_spoke( Character* src_chr, const char* speech, u8 texttype,
696  const u16* wspeech, const char lang[4],
697  Bscript::ObjArray* speechtokens )
698 {
699  if ( ex != nullptr )
700  {
701  if ( Core::settingsManager.ssopt.seperate_speechtoken )
702  {
703  if ( speechtokens != nullptr && ( ( ex->eventmask & Core::EVID_TOKEN_GHOST_SPOKE ) == 0 ) )
704  return;
705  else if ( speechtokens == nullptr && ( ( ex->eventmask & Core::EVID_GHOST_SPEECH ) == 0 ) )
706  return;
707  }
708  if ( ( ( ex->eventmask & Core::EVID_GHOST_SPEECH ) ||
710  inrangex( this, src_chr, ex->speech_size ) && !deafened() )
711  {
712  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
713  is_visible_to_me( src_chr ) )
714  {
716  src_chr, speech, Core::TextTypeToString( texttype ), wspeech, lang, speechtokens ) );
717  }
718  }
719  }
720 }
721 
723 {
724  // someone has targetted us. Create an event if appropriate.
725  if ( ex != nullptr )
726  {
728  {
729  ex->os_module->signal_event( new Module::EngageEvent( engaged ) );
730  }
731  }
732  // Note, we don't do the base class thing, 'cause we have no client.
733 }
734 
736 {
737  // someone has targetted us. Create an event if appropriate.
738  if ( ex != nullptr )
739  {
741  {
742  ex->os_module->signal_event( new Module::DisengageEvent( disengaged ) );
743  }
744  }
745  // Note, we don't do the base class thing, 'cause we have no client.
746 }
747 
748 void NPC::inform_criminal( Character* thecriminal )
749 {
750  if ( ex != nullptr )
751  {
752  if ( ( ex->eventmask & ( Core::EVID_GONE_CRIMINAL ) ) &&
753  inrangex( this, thecriminal, ex->area_size ) )
754  {
755  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
756  is_visible_to_me( thecriminal ) )
758  new Module::SourcedEvent( Core::EVID_GONE_CRIMINAL, thecriminal ) );
759  }
760  }
761 }
762 
764 {
765  if ( ex != nullptr )
766  {
767  if ( ex->eventmask & ( Core::EVID_LEFTAREA ) )
768  {
769  if ( pol_distance( this, wholeft ) <= ex->area_size )
770  {
771  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
772  is_visible_to_me( wholeft ) )
774  }
775  }
776  }
777 }
778 
780 {
781  if ( ex != nullptr )
782  {
783  if ( ex->eventmask & ( Core::EVID_ENTEREDAREA ) )
784  {
785  if ( pol_distance( this, whoentered ) <= ex->area_size )
786  {
787  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
788  is_visible_to_me( whoentered ) )
790  new Module::SourcedEvent( Core::EVID_ENTEREDAREA, whoentered ) );
791  }
792  }
793  }
794 }
795 
797 {
798  // 8-26-05 Austin
799  // Note: This does not look at realms at all, just X Y coords.
800  // ^is_visible_to_me checks realm - Turley
801 
802  if ( ex != nullptr )
803  {
804  bool signaled = false;
805  passert( moved != nullptr );
807  {
808  // egcs may have a compiler bug when calling these as inlines
809  bool are_inrange =
810  ( abs( x - moved->x ) <= ex->area_size ) && ( abs( y - moved->y ) <= ex->area_size );
811 
812  // inrangex_inline( this, moved, ex->area_size );
813  bool were_inrange = ( abs( x - moved->lastx ) <= ex->area_size ) &&
814  ( abs( y - moved->lasty ) <= ex->area_size );
815 
816  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
817  is_visible_to_me( moved ) )
818  {
819  if ( are_inrange && !were_inrange && ( ex->eventmask & ( Core::EVID_ENTEREDAREA ) ) )
820  {
822  signaled = true;
823  }
824  else if ( !are_inrange && were_inrange && ( ex->eventmask & ( Core::EVID_LEFTAREA ) ) )
825  {
827  signaled = true;
828  }
829  }
830  }
831 
832  if ( !signaled ) // only send moved event if left/enteredarea wasnt send
833  {
834  if ( ( moved == opponent_ ) && ( ex->eventmask & ( Core::EVID_OPPONENT_MOVED ) ) )
835  {
836  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
837  is_visible_to_me( moved ) )
840  }
841  }
842  }
843 }
844 
845 //
846 // This NPC moved. Tell him about other mobiles that have "entered" his area
847 // (through his own movement)
848 //
849 
851 {
852  if ( ex != nullptr )
853  {
854  passert( chr != nullptr );
856  {
857  // egcs may have a compiler bug when calling these as inlines
858  bool are_inrange =
859  ( abs( x - chr->x ) <= ex->area_size ) && ( abs( y - chr->y ) <= ex->area_size );
860 
861  // inrangex_inline( this, moved, ex->area_size );
862  bool were_inrange =
863  ( abs( lastx - chr->x ) <= ex->area_size ) && ( abs( lasty - chr->y ) <= ex->area_size );
864 
865  if ( ( !Core::settingsManager.ssopt.event_visibility_core_checks ) ||
866  is_visible_to_me( chr ) )
867  {
868  if ( are_inrange && !were_inrange && ( ex->eventmask & ( Core::EVID_ENTEREDAREA ) ) )
870  else if ( !are_inrange && were_inrange && ( ex->eventmask & ( Core::EVID_LEFTAREA ) ) )
872  }
873  }
874  }
875 }
876 
878 {
879  if ( ex == nullptr )
880  return false;
881  if ( ex->eventmask & eventid )
882  return true;
883  else
884  return false;
885 }
886 
888 {
889  if ( ex != nullptr )
890  {
891  if ( ex->os_module->signal_event( event ) )
892  return true;
893  }
894  else
895  {
896  // There's no executor, so we must delete it ourselves.
897  Bscript::BObject bo( event );
898  }
899  return false;
900 }
901 
903 {
904  if ( ex != nullptr )
905  {
906  if ( ex->os_module->signal_event( event ) )
907  return new Bscript::BLong( 1 );
908  else
909  {
910  return new Bscript::BError( "Event queue is full, discarding event" );
911  }
912  }
913  else
914  {
915  // Because there is no control script, we must delete it ourselves.
916  Bscript::BObject bo( event );
917  return new Bscript::BError( "That NPC doesn't have a control script" );
918  }
919 }
920 
921 void NPC::apply_raw_damage_hundredths( unsigned int damage, Character* source, bool userepsys,
922  bool send_damage_packet )
923 {
924  if ( ex != nullptr )
925  {
927  {
929  new Module::DamageEvent( source, static_cast<unsigned short>( damage / 100 ) ) );
930  }
931  }
932 
933  base::apply_raw_damage_hundredths( damage, source, userepsys, send_damage_packet );
934 }
935 
936 // keep this in sync with Character::armor_absorb_damage
937 double NPC::armor_absorb_damage( double damage )
938 {
939  if ( !npc_ar_ )
940  {
941  return base::armor_absorb_damage( damage );
942  }
943  else
944  {
945  int blocked = npc_ar_ + ar_mod();
946  if ( blocked < 0 )
947  blocked = 0;
948  int absorbed = blocked / 2;
949 
950  blocked -= absorbed;
951  absorbed += Clib::random_int( blocked );
952  if ( Core::settingsManager.watch.combat )
953  INFO_PRINT << absorbed << " hits absorbed by NPC armor.\n";
954  damage -= absorbed;
955  if ( damage < 0 )
956  damage = 0;
957  }
958  return damage;
959 }
960 
961 void NPC::get_hitscript_params( double damage, Items::UArmor** parmor, unsigned short* rawdamage )
962 {
963  if ( !npc_ar_ )
964  {
965  base::get_hitscript_params( damage, parmor, rawdamage );
966  }
967  else
968  {
969  *rawdamage = static_cast<unsigned short>( calc_thru_damage( damage, npc_ar_ + ar_mod() ) );
970  }
971 }
972 
974 {
976  return template_.intrinsic_weapon;
977  else
979 }
980 
982 {
983  // Check if the NPC is using intrinsic armor. If yes, use it and set armor to 0
984  // regardless of what is equipped. If not, just calculate like a PC.
985  // TODO: intrinsic and equipped armor should sum, but many code will need to be
986  // modified for this to work.
987  // NOTE: Keep in mind an armor could have just been destroyed when calling this
988  // and it could've been the last piece of armor equipped, so checking for
989  // an equipped armor will not be wise. Intrinsic armor is assumed to never
990  // change instead.
991  if ( npc_ar_ )
992  {
993  for ( unsigned zone = 0; zone < Core::gamestate.armorzones.size(); ++zone )
994  armor_[zone] = nullptr;
995  ar_ = 0;
997  }
998  else
999  {
1000  base::refresh_ar();
1001  }
1002 }
1003 
1005 {
1006  if ( has_fire_resist() || has_orig_fire_resist() )
1007  fire_resist( fire_resist().resetModAsValue().addToValue( orig_fire_resist() ) );
1008  if ( has_cold_resist() || has_orig_cold_resist() )
1009  cold_resist( cold_resist().resetModAsValue().addToValue( orig_cold_resist() ) );
1010  if ( has_energy_resist() || has_orig_energy_resist() )
1011  energy_resist( energy_resist().resetModAsValue().addToValue( orig_energy_resist() ) );
1012  if ( has_poison_resist() || has_orig_poison_resist() )
1013  poison_resist( poison_resist().resetModAsValue().addToValue( orig_poison_resist() ) );
1014  if ( has_physical_resist() || has_orig_physical_resist() )
1015  physical_resist( physical_resist().resetModAsValue().addToValue( orig_physical_resist() ) );
1016 
1017  if ( has_fire_damage() || has_orig_fire_damage() )
1018  fire_damage( fire_damage().resetModAsValue().addToValue( orig_fire_damage() ) );
1019  if ( has_cold_damage() || has_orig_cold_damage() )
1020  cold_damage( cold_damage().resetModAsValue().addToValue( orig_cold_damage() ) );
1021  if ( has_energy_damage() || has_orig_energy_damage() )
1022  energy_damage( energy_damage().resetModAsValue().addToValue( orig_energy_damage() ) );
1023  if ( has_poison_damage() || has_orig_poison_damage() )
1024  poison_damage( poison_damage().resetModAsValue().addToValue( orig_poison_damage() ) );
1025  if ( has_physical_damage() || has_orig_physical_damage() )
1026  physical_damage( physical_damage().resetModAsValue().addToValue( orig_physical_damage() ) );
1027 }
1028 
1029 size_t NPC::estimatedSize() const
1030 {
1031  return base::estimatedSize() + sizeof( unsigned short ) /*damaged_sound*/
1032  + sizeof( unsigned short ) /*run_speed*/
1033  + sizeof( Core::UOExecutor* ) /*ex*/
1034  + sizeof( unsigned short ) /*npc_ar_*/
1035  + sizeof( Core::CharacterRef ) /*master_*/
1036  + sizeof( anchor ) /*anchor*/
1037  + sizeof( boost_utils::script_name_flystring ) /*script*/
1038  + sizeof( boost_utils::npctemplate_name_flystring ); /*template_name*/
1039 }
1040 
1042 {
1043  if ( damaged_sound != 0 )
1044  return damaged_sound;
1045  return base::get_damaged_sound();
1046 }
1047 
1049 {
1051 }
1052 
1053 void NPC::use_adjustments( bool newvalue )
1054 {
1056 }
1057 
1059 {
1061 }
1062 
1063 void NPC::no_drop_exception( bool newvalue )
1064 {
1066 }
1067 
1068 std::string NPC::templatename() const
1069 {
1070  return template_name;
1071 }
1072 }
1073 }
boost_utils::script_name_flystring script
Definition: npc.h:247
unsigned char u8
Definition: rawtypes.h:25
bool can_accept_event(Core::EVENTID eventid)
Definition: npc.cpp:877
Character * opponent_
Definition: charactr.h:828
void restart_script()
Definition: npc.cpp:551
ref_ptr< Bscript::EScriptProgram > find_script2(const ScriptDef &script, bool complain_if_not_found, bool cache_script)
Definition: scrstore.cpp:83
const Plib::Package * pkg
Definition: npctmpl.h:41
virtual size_t estimatedSize() const POL_OVERRIDE
Definition: charactr.cpp:4252
bool in_debugger_holdlist() const
Definition: osmod.cpp:869
std::string remove_string(const char *propname)
Definition: cfgfile.cpp:381
void send_remove_character_to_nearby(const Character *chr)
Definition: ufunc.cpp:379
u16 dexterity() const
Definition: charactr.cpp:4089
bool send_event(Bscript::BObjectImp *event)
Definition: npc.cpp:887
unsigned short roll(void) const
Definition: dice.cpp:26
bool deafened() const
Definition: charactr.cpp:4061
void resetEquipablePropertiesNPC()
Definition: npc.cpp:1004
void setprop(const std::string &propname, const std::string &propvalue)
Definition: uobject.cpp:160
Character * master() const
Definition: npc.h:265
virtual u16 get_damaged_sound() const POL_OVERRIDE
Definition: npc.cpp:1041
std::string templatename() const
Definition: npc.cpp:1068
void seterror(bool err)
Definition: executor.h:437
bool walkheight(unsigned short x, unsigned short y, short oldz, short *newz, Multi::UMulti **pmulti, Items::Item **pwalkon, bool doors_block, Core::MOVEMODE movemode, short *gradual_boost=nullptr)
Definition: realmfunc.cpp:340
virtual void inform_enteredarea(Character *whoentered) POL_OVERRIDE
Definition: npc.cpp:779
void addModule(ExecutorModule *module)
Definition: executor.cpp:3032
const Core::NpcTemplate & template_
Definition: npc.h:262
ValueModPack & addToValue(const ValueModPack &other)
T * get() const
Definition: refptr.h:176
virtual void printDebugProperties(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: npc.cpp:325
bool signal_event(Bscript::BObjectImp *eventimp)
Definition: osmod.cpp:769
unsigned int remove_ulong(const char *propname)
Definition: cfgfile.cpp:461
bool is_visible_to_me(const Character *chr) const
Definition: charactr.cpp:2658
ref_ptr< Core::WornItemsContainer > wornitems
Definition: charactr.h:787
unsigned short speech_size
Definition: uoexec.h:58
void calc_vital_stuff(bool i_mod=true, bool v_mod=true)
Definition: charactr.cpp:1581
bool get(ENUM flag) const
Definition: uobject.h:108
virtual void get_hitscript_params(double damage, Items::UArmor **parmor, unsigned short *rawdamage)
Definition: charactr.cpp:1899
unsigned short calc_thru_damage(double damage, unsigned short ar)
Definition: charactr.cpp:1847
virtual void apply_raw_damage_hundredths(unsigned int damage, Character *source, bool userepsys=true, bool send_damage_packet=false) POL_OVERRIDE
Definition: npc.cpp:921
Items::UWeapon * weapon
Definition: charactr.h:783
const unsigned short DEFAULT_TEXT_COLOR
Definition: uconst.h:97
virtual void printOn(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: npc.cpp:255
bool connected() const
Definition: charactr.cpp:438
bool logged_in() const
Definition: charactr.cpp:428
virtual double armor_absorb_damage(double damage)
Definition: charactr.cpp:1874
virtual Items::UWeapon * intrinsic_weapon() POL_OVERRIDE
Definition: npc.cpp:973
virtual void inform_engaged(Character *engaged) POL_OVERRIDE
Definition: npc.cpp:722
std::vector< Items::UArmor * > armor_
Definition: charactr.h:785
virtual void readProperties(Clib::ConfigElem &elem) POL_OVERRIDE
Definition: npc.cpp:492
MoveDelta move_delta[8]
Definition: ufacing.cpp:16
const unsigned short DEFAULT_TEXT_FONT
Definition: uconst.h:96
void loadEquipablePropertiesNPC(Clib::ConfigElem &elem)
Definition: npc.cpp:395
const NpcTemplate & find_npc_template(const Clib::ConfigElem &elem)
Definition: npctmpl.cpp:154
bool can_freemove() const
Definition: charactr.cpp:1271
virtual const char * classname() const POL_OVERRIDE
Definition: npc.cpp:139
virtual void printSelfOn(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: npc.cpp:265
unsigned short area_size
Definition: uoexec.h:57
ZoneCharacters characters
Definition: uworld.h:70
unsigned short u16
Definition: rawtypes.h:26
const char * rest() const
Definition: cfgfile.cpp:71
virtual void destroy() POL_OVERRIDE
Definition: npc.cpp:123
unsigned int u32
Definition: rawtypes.h:27
void zone_convert_clip(int x, int y, const Realms::Realm *realm, unsigned short *wx, unsigned short *wy)
Definition: uworld.h:86
void readNpcProperties(Clib::ConfigElem &elem)
Definition: npc.cpp:336
bool getprop(const std::string &propname, std::string &propvalue) const
Definition: uobject.cpp:155
POL_NORETURN void throw_error(const std::string &errmsg) const
Definition: cfgfile.cpp:285
Multi::UMulti * system_find_multi(u32 serial)
Definition: fnsearch.cpp:50
Core::UOExecutor * ex
Definition: npc.h:250
virtual ~NPC()
Definition: npc.cpp:104
void readCommonProperties(Clib::ConfigElem &elem)
Definition: charactr.cpp:802
bool warmode() const
Definition: charactr.cpp:3067
virtual void inform_imoved(Character *chr) POL_OVERRIDE
Definition: npc.cpp:850
AttributeFlags< OBJ_FLAGS > flags_
Definition: uobject.h:274
UOExecutor * create_script_executor()
Definition: scrsched.cpp:644
virtual void inform_criminal(Character *thecriminal) POL_OVERRIDE
Definition: npc.cpp:748
bool npc_path_blocked(Core::UFACING dir) const
Definition: npc.cpp:207
virtual double armor_absorb_damage(double damage) POL_OVERRIDE
Definition: npc.cpp:937
boost_utils::npctemplate_name_flystring template_name
Definition: npc.h:258
void start_script()
Definition: npc.cpp:592
int random_int(int i)
Definition: random.cpp:34
virtual void get_hitscript_params(double damage, Items::UArmor **parmor, unsigned short *rawdamage) POL_OVERRIDE
Definition: npc.cpp:961
const AttributeValue & attribute(unsigned attrid) const
Definition: charactr.h:1051
virtual bool can_be_renamed_by(const Character *chr) const POL_OVERRIDE
Definition: npc.cpp:626
Core::CharacterRef master_
Definition: npc.h:261
UArmor * create_intrinsic_shield_from_npctemplate(Clib::ConfigElem &elem, const Plib::Package *pkg)
Creates a new intrinic shield for an NPC template and returns it.
Definition: armor.cpp:237
bool enabled
Definition: npc.h:77
virtual void readProperties(Clib::ConfigElem &elem) POL_OVERRIDE
Definition: charactr.cpp:1082
bool setProgram(EScriptProgram *prog)
Definition: executor.cpp:731
bool could_move(Core::UFACING dir) const
Definition: npc.cpp:174
ref_ptr< Mobile::Character > CharacterRef
Definition: reftypes.h:42
#define passert(exp)
Definition: passert.h:62
void readNewNpcAttributes(Clib::ConfigElem &elem)
Definition: npc.cpp:511
unsigned int eventmask
Definition: uoexec.h:56
bool use_adjustments() const
Definition: npc.cpp:1048
unsigned short y
Definition: npc.h:80
void ClrCharacterWorldPosition(Mobile::Character *chr, Realms::WorldChangeReason reason)
Definition: uworld.cpp:149
GameState gamestate
Definition: uvars.cpp:74
Attribute * next
Definition: attribute.h:45
Bscript::BObjectImp * send_event_script(Bscript::BObjectImp *event)
Definition: npc.cpp:902
const char * TextTypeToString(u8 texttype)
Definition: listenpt.cpp:33
SettingsManager settingsManager
Definition: settings.cpp:14
Core::AttributeFlags< MOB_FLAGS > mob_flags_
Definition: charactr.h:896
virtual void destroy() POL_OVERRIDE
Definition: charactr.cpp:482
bool saveonexit() const
Definition: uobject.cpp:385
StateManager stateManager
Definition: state.cpp:8
Mobile::Character * system_find_mobile(u32 serial)
Definition: fnsearch.cpp:32
virtual void on_death(Items::Item *corpse) POL_OVERRIDE
Definition: npc.cpp:568
void on_pc_spoke(Character *src_chr, const char *speech, u8 texttype)
Definition: npc.cpp:632
boost::flyweight< std::string, boost::flyweights::tag< script_name_tag >, FLYWEIGHT_HASH_FACTORY > script_name_flystring
Definition: boostutils.h:148
bool remove_prop(const char *propname, std::string *value)
Definition: cfgfile.cpp:128
const unsigned ATTRIBUTE_MAX_BASE
Definition: attribute.h:70
NPC(u32 objtype, const Clib::ConfigElem &elem)
Definition: npc.cpp:80
ZoneCharacters npcs
Definition: uworld.h:71
virtual void printProperties(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: charactr.cpp:549
ArmorZones armorzones
Definition: uvars.h:194
virtual void inform_leftarea(Character *wholeft) POL_OVERRIDE
Definition: npc.cpp:763
unsigned short dstart
Definition: npc.h:81
void on_ghost_pc_spoke(Character *src_chr, const char *speech, u8 texttype)
Definition: npc.cpp:654
unsigned short pol_distance(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2)
Definition: ufunc.cpp:481
Realms::Realm * realm
Definition: baseobject.h:56
Items::UArmor * shield
Definition: charactr.h:784
Items::UWeapon * intrinsic_weapon
Definition: npctmpl.h:39
Anchor anchor
Definition: npc.h:240
bool anchor_allows_move(Core::UFACING dir) const
Definition: npc.cpp:150
unsigned short remove_ushort(const char *propname)
Definition: cfgfile.cpp:318
virtual void printDebugProperties(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: charactr.cpp:748
virtual void inform_moved(Character *moved) POL_OVERRIDE
Definition: npc.cpp:796
Core::Zone ** zone
Definition: realm.h:133
virtual void apply_raw_damage_hundredths(unsigned int damage, Character *source, bool userepsys=true, bool send_damage_packet=false)
Definition: charactr.cpp:1794
unsigned short npc_ar_
Definition: npc.h:234
void change(ENUM flag, bool value)
Definition: uobject.h:115
UWeapon * create_intrinsic_weapon_from_npctemplate(Clib::ConfigElem &elem, const Plib::Package *pkg)
Creates a new intrinic weapon for an NPC template and returns it.
Definition: weapon.cpp:278
virtual void refresh_ar()
Definition: charactr.cpp:2484
unsigned short psub
Definition: npc.h:82
void stop_scripts()
Definition: npc.cpp:110
#define ERROR_PRINT
Definition: logfacility.h:230
bool no_drop_exception() const
Definition: npc.cpp:1058
void schedule_executor(UOExecutor *ex)
Definition: scrsched.cpp:662
virtual void refresh_ar() POL_OVERRIDE
Definition: npc.cpp:981
bool FileExists(const char *filename)
Definition: fileutil.cpp:118
unsigned short run_speed
Definition: npc.h:237
virtual void inform_disengaged(Character *disengaged) POL_OVERRIDE
Definition: npc.cpp:735
Items::UWeapon * wrestling_weapon
Definition: uvars.h:146
bool load(const char *dice, std::string *errormsg)
Definition: dice.cpp:42
bool inrangex(const Character *c1, const Character *c2, int maxdist)
Definition: ufunc.cpp:443
#define INFO_PRINT
Definition: logfacility.h:223
void start_script(const char *filename, Bscript::BObjectImp *param0, Bscript::BObjectImp *param1)
Definition: scrsched.cpp:150
virtual std::string name() const
Definition: uobject.cpp:196
virtual u16 get_damaged_sound() const
Definition: charactr.cpp:3205
#define pf_endl
Definition: proplist.cpp:25
virtual void unregister_object(UObject *obj)
Definition: multis.cpp:62
Definition: berror.cpp:12
bool remove_bool(const char *propname)
Definition: cfgfile.cpp:426
virtual size_t estimatedSize() const POL_OVERRIDE
Definition: npc.cpp:1029
void readPropertiesForNewNPC(Clib::ConfigElem &elem)
Definition: npc.cpp:540
unsigned short damaged_sound
Definition: npc.h:255
virtual void printProperties(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: npc.cpp:270
bool dead() const
Definition: charactr.h:931
boost::flyweight< std::string, boost::flyweights::tag< npctemplate_name_tag >, FLYWEIGHT_HASH_FACTORY > npctemplate_name_flystring
Definition: boostutils.h:154
Mobile::Character * attached_npc_
Definition: uomod.h:312
Equipment * find_intrinsic_equipment(const std::string &name, u8 layer)
Looks up for an existing intrinsic equipment and return it or nullptr if not found.
Definition: equipmnt.cpp:128
unsigned short x
Definition: npc.h:79
Module::OSExecutorModule * os_module
Definition: uoexec.h:37
static Attribute * FindAttribute(const std::string &str)
Definition: attribute.cpp:22
UObjCount uobjcount
Definition: state.h:49