Pol  Revision:4b29d2b
charactr.cpp
Go to the documentation of this file.
1 
82 #include "pol_global_config.h"
83 
84 #include "charactr.h"
85 
86 #include <stdlib.h>
87 #include <string>
88 
89 #include "../../clib/cfgelem.h"
90 #include "../../clib/cfgfile.h"
91 #include "../../clib/clib.h"
92 #include "../../clib/clib_endian.h"
93 #include "../../clib/esignal.h"
94 #include "../../clib/fileutil.h"
95 #include "../../clib/logfacility.h"
96 #include "../../clib/passert.h"
97 #include "../../clib/random.h"
98 #include "../../clib/stlutil.h"
99 #include "../../clib/streamsaver.h"
100 #include "../../plib/mapcell.h"
101 #include "../../plib/systemstate.h"
102 #include "../accounts/account.h"
103 #include "../accounts/accounts.h"
104 #include "../checkpnt.h"
105 #include "../clidata.h"
106 #include "../cmbtcfg.h"
107 #include "../cmdlevel.h"
108 #include "../containr.h"
109 #include "../dice.h"
110 #include "../extobj.h"
111 #include "../fnsearch.h"
112 #include "../globals/settings.h"
113 #include "../globals/state.h"
114 #include "../globals/uvars.h"
115 #include "../guardrgn.h"
116 #include "../guilds.h"
117 #include "../item/armor.h"
118 #include "../item/item.h"
119 #include "../item/itemdesc.h"
120 #include "../item/weapon.h"
121 #include "../item/wepntmpl.h"
122 #include "../layers.h"
123 #include "../mdelta.h"
124 #include "../miscrgn.h"
125 #include "../mkscrobj.h"
126 #include "../module/osmod.h"
127 #include "../module/uomod.h"
128 #include "../movecost.h"
129 #include "../multi/customhouses.h"
130 #include "../multi/house.h"
131 #include "../multi/multi.h"
132 #include "../multi/multidef.h"
133 #include "../musicrgn.h"
134 #include "../network/cgdata.h"
135 #include "../network/client.h"
136 #include "../network/cliface.h"
137 #include "../network/packetdefs.h"
138 #include "../network/packethelper.h"
139 #include "../network/packets.h"
140 #include "../objtype.h"
141 #include "../party.h"
142 #include "../pktdef.h"
143 #include "../polclass.h"
144 #include "../polsig.h"
145 #include "../polvar.h"
146 #include "../profile.h"
147 #include "../realms/WorldChangeReasons.h"
148 #include "../realms/realm.h"
149 #include "../schedule.h"
150 #include "../scrdef.h"
151 #include "../scrsched.h"
152 #include "../scrstore.h"
153 #include "../sfx.h"
154 #include "../skilladv.h"
155 #include "../spelbook.h"
156 #include "../statmsg.h"
157 #include "../syshook.h"
158 #include "../ufunc.h"
159 #include "../ufuncstd.h"
160 #include "../uobjcnt.h"
161 #include "../uoexec.h"
162 #include "../uoscrobj.h"
163 #include "../uworld.h"
164 #include "../vital.h"
165 #include "attribute.h"
166 #include "corpse.h"
167 #include "privupdater.h"
168 #include "wornitems.h"
169 
170 #ifdef _MSC_VER
171 #pragma warning( disable : 4505 ) // unreferenced local function has been removed
172 #endif
173 
174 namespace Pol
175 {
176 namespace Core
177 {
178 void cancel_trade( Mobile::Character* chr1 );
179 }
180 namespace Mobile
181 {
182 unsigned short layer_to_zone( unsigned short layer )
183 {
184  for ( unsigned short zone = 0; zone < Core::gamestate.armorzones.size(); ++zone )
185  {
186  for ( unsigned i = 0; i < Core::gamestate.armorzones[zone].layers.size(); ++i )
187  {
188  if ( Core::gamestate.armorzones[zone].layers[i] == layer )
189  return zone;
190  }
191  }
192  ERROR_PRINT << "Couldn't find an Armor Zone in armrzone.cfg for layer " << layer << "\n";
193  throw std::runtime_error( "Configuration file error" );
194 }
195 
196 const char* zone_to_zone_name( unsigned short zone )
197 {
198  return Core::gamestate.armorzones[zone].name.c_str();
199 }
200 
201 unsigned short zone_name_to_zone( const char* zname )
202 {
203  for ( unsigned short zone = 0; zone < Core::gamestate.armorzones.size(); ++zone )
204  {
205  if ( stricmp( Core::gamestate.armorzones[zone].name.c_str(), zname ) == 0 )
206  {
207  return zone;
208  }
209  }
210  ERROR_PRINT << "Couldn't find an armrzone.cfg config elem named '" << zname << "'\n";
211 
212  throw std::runtime_error( "Configuration file error" );
213 }
215 {
216  if ( !Clib::FileExists( "config/armrzone.cfg" ) )
217  {
218  if ( Plib::systemstate.config.loglevel > 1 )
219  INFO_PRINT << "File config/armrzone.cfg not found, skipping.\n";
220  return;
221  }
222  Clib::ConfigFile cf( "config/armrzone.cfg" );
223  Clib::ConfigElem elem;
224 
226  Core::gamestate.armorzones.clear();
227 
228  while ( cf.read( elem ) )
229  {
230  // armorzones.push_back( ArmorZone( elem ) );
231  Core::ArmorZone az;
232  az.name = elem.remove_string( "NAME" );
233  az.chance = static_cast<double>( elem.remove_ushort( "CHANCE" ) ) / 100.0;
234  unsigned short in_layer;
235  while ( elem.remove_prop( "LAYER", &in_layer ) )
236  {
237  if ( in_layer < Core::LOWEST_LAYER || in_layer > Core::HIGHEST_LAYER )
238  {
239  ERROR_PRINT << "ArmorZone " << az.name << ": Layer " << in_layer << " is out of range.\n"
240  << "Valid range is " << Core::LOWEST_LAYER << " to " << Core::HIGHEST_LAYER
241  << "\n";
242  throw std::runtime_error( "Configuration file error" );
243  }
244  az.layers.push_back( in_layer );
245  }
246 
247  Core::gamestate.armorzones.push_back( az );
249  }
250 }
251 
253 {
254  Core::gamestate.armorzones.clear();
256 }
257 
258 
260  : UObject( objtype, uobj_class ),
261  // NPC
262  // EQUIPMENT / ITEMS
263  weapon( Core::gamestate.wrestling_weapon ),
264  shield( nullptr ),
265  armor_( Core::gamestate.armorzones.size() ),
266  wornitems( new Core::WornItemsContainer ), // default objtype is in containr.cpp,
267  // WornItemsContainer class
268  gotten_item_source( GOTTEN_ITEM_ON_GROUND ),
269  remote_containers_(),
270  // MOVEMENT
271  dir( 0 ),
272  gradual_boost( 0 ),
273  lastx( 0 ),
274  lasty( 0 ),
275  lastz( 0 ),
276  move_reason( OTHER ),
277  movemode( Core::MOVEMODE_LAND ),
278  // COMBAT
279  warmode_wait( 0 ),
280  ar_( 0 ),
281  opponent_( nullptr ),
282  opponent_of(),
283  swing_timer_start_clock_( 0 ),
284  swing_task( nullptr ),
285  // ATTRIBUTES / VITALS
286  disable_regeneration_until( 0 ),
287  attributes( Core::gamestate.numAttributes ),
288  vitals( Core::gamestate.numVitals ),
289  // REPUTATION
290  aggressor_to_(),
291  lawfully_damaged_(),
292  criminal_until_( 0 ),
293  repsys_task_( nullptr ),
294  to_be_reportable_(),
295  reportable_(),
296  // GUILD
297  // PARTY
298  party_decline_timeout_( nullptr ),
299  // SECURE TRADING
300  trading_cont(),
301  trading_with( nullptr ),
302  // SCRIPT
303  tcursor2( nullptr ),
304  menu( nullptr ),
305  on_menu_selection( nullptr ),
306  on_popup_menu_selection( nullptr ),
307  script_ex( nullptr ),
308  spell_task( nullptr ),
309  // CLIENT
310  client( nullptr ),
311  uclang( "enu" ),
312  _last_textcolor( 0 ),
313  // PRIVS SETTINGS STATUS
314  cmdlevel_( 0 ),
315  concealed_( 0 ),
316  stealthsteps_( 0 ),
317  mountedsteps_( 0 ),
318  privs(),
319  settings(),
320  cached_settings(),
321  mob_flags_(),
322  // SERIALIZATION
323  // CREATION
324  created_at( 0 ),
325  // BUFFS
326  buffs_(),
327  // MISC
328  acct( nullptr ),
329  registered_house( 0 ),
330  truecolor( 0 ),
331  trueobjtype( 0 ),
332  // Note, Item uses the named constructor idiom, but here, it is not used.
333  // this is probably okay, but something to keep in mind.
334  gender( Core::GENDER_MALE ),
335  race( Core::RACE_HUMAN ),
336  last_corpse( 0 )
337 {
338  logged_in( true ); // so initialization scripts etc can see
339 
341  .default_character_height; // this gets overwritten in UObject::readProperties!
342  wornitems->chr_owner = this; // FIXME, dangerous.
343 
345 
346  // vector
347  refresh_cached_settings( false );
349 }
350 
352 {
353  if ( acct.get() )
354  {
355  for ( int i = 0; i < Plib::systemstate.config.character_slots; i++ )
356  {
357  if ( acct->get_character( i ) == this )
358  {
359  acct->clear_character( i );
360  }
361  }
362  }
363  acct.clear();
364 
365  if ( client && ( client->chr == this ) )
366  client->chr = nullptr;
367  client = nullptr;
368 
369  // It might be nice to do this only when the system isn't shutting down...
370  // if (!opponent_of.empty())
371  //{
372  // Clib::Log( "Destroying character with nonempty opponent_of! (But cleaning up..)\n" );
373  //}
374 
375  removal_cleanup();
376 
377  // clean up wornitems, so it can be reaped by the objecthash later
378  wornitems->destroy();
379 
380  // clean up trade container if it exists
381  if ( trading_cont != nullptr )
382  trading_cont->destroy();
383 
384  if ( repsys_task_ != nullptr )
385  repsys_task_->cancel();
386 
387  if ( party_decline_timeout_ != nullptr )
389 
391 }
392 
394 {
396 
397  /* This used to be a call to set_opponent(nullptr),
398  which was slick,
399  but that was sending disengage events, which were
400  trying to resurrect this object. (C++)
401  */
402  if ( opponent_ != nullptr )
403  {
404  opponent_->opponent_of.erase( this );
405  // This is cleanup, wtf we doing trying to send highlights?!
406  // opponent_->send_highlight();
407  // opponent_->schedule_attack();
408  opponent_ = nullptr;
409  }
410 
411  if ( swing_task != nullptr )
412  swing_task->cancel();
413 
415 }
416 
418 {
419  if ( is_trading() )
420  Core::cancel_trade( this );
421 
422  tcursor2 = nullptr;
423 
425  on_loggoff_party( this );
426 }
427 
429 {
431 }
432 
433 void Character::logged_in( bool newvalue )
434 {
436 }
437 
439 {
441 }
442 
443 void Character::connected( bool newvalue )
444 {
446 }
447 
449 {
450  return ( client != nullptr && client->isActive() );
451 }
452 
454 {
455  return ( client != nullptr && client->gd != nullptr &&
457 }
458 
460 {
461  return ( client != nullptr && client->gd != nullptr && !client->gd->gumpmods.empty() );
462 }
463 
465 {
466  return ( client != nullptr && client->gd != nullptr && client->gd->custom_house_serial != 0 );
467 }
468 
470 {
471  auto item = gotten_item();
472  if ( item != nullptr )
473  {
474  gotten_item( nullptr );
475  item->inuse( false );
476  if ( connected() )
478  undo_get_item( this, item );
479  }
480 }
481 
483 {
485  if ( registered_house > 0 )
486  {
488  if ( multi != nullptr )
489  {
490  multi->unregister_object( (UObject*)this );
491  }
492  registered_house = 0;
493  }
494  base::destroy();
495 }
496 
498 {
499  if ( script_ex != nullptr )
500  {
501  // this will force the execution engine to stop running this script immediately
502  // dont delete the executor here, since it could currently run
503  script_ex->seterror( true );
507  }
508 }
509 
513 unsigned int Character::weight() const
514 {
515  unsigned int wt = 10 + wornitems->weight();
516  if ( has_gotten_item() )
517  wt += gotten_item()->weight();
518  if ( trading_cont.get() )
519  wt += trading_cont->weight();
520  return wt;
521 }
522 
527 unsigned short Character::carrying_capacity() const
528 {
529  return static_cast<u16>( floor( ( 40 + strength() * 7 / 2 + carrying_capacity_mod() ) *
530  Core::settingsManager.ssopt.carrying_capacity_mod ) );
531 }
532 
534 {
535  if ( acct == nullptr )
536  return -1;
537 
538  for ( int i = 0; i < Plib::systemstate.config.character_slots; i++ )
539  {
540  if ( acct->get_character( i ) == this )
541  return i;
542  }
543 
544  POLLOG_INFO << "Warning: Can't find charidx for Character 0x" << fmt::hexu( serial ) << "\n";
545  return -1;
546 }
547 
548 
550 {
551  using namespace fmt;
552 
553  if ( acct != nullptr )
554  {
555  sw() << "\tAccount\t" << acct->name() << pf_endl;
556  sw() << "\tCharIdx\t" << charindex() << pf_endl;
557  }
558 
559  base::printProperties( sw );
560 
561  if ( cmdlevel_ )
562  {
563  sw() << "\tCmdLevel\t" << Core::gamestate.cmdlevels[cmdlevel_].name << pf_endl;
564  }
565  if ( concealed_ )
566  {
567  sw() << "\tConcealed\t" << int( concealed_ ) << pf_endl;
568  }
569  sw() << "\tTrueColor\t0x" << hex( truecolor ) << pf_endl;
570  sw() << "\tTrueObjtype\t0x" << hex( trueobjtype ) << pf_endl;
571 
572  if ( registered_house )
573  sw() << "\tRegisteredHouse\t0x" << hex( registered_house ) << pf_endl;
574 
575  sw() << "\tGender\t" << static_cast<int>( gender ) << pf_endl;
576  sw() << "\tRace\t" << static_cast<int>( race ) << pf_endl;
577 
578  if ( dead() )
579  sw() << "\tDead\t" << static_cast<int>( dead() ) << pf_endl;
580 
581  if ( mountedsteps_ )
582  sw() << "\tMountedSteps\t" << static_cast<unsigned int>( mountedsteps_ ) << pf_endl;
583 
584  if ( hidden() )
585  sw() << "\tHidden\t" << static_cast<int>( hidden() ) << pf_endl;
586 
587  if ( frozen() )
588  sw() << "\tFrozen\t" << static_cast<int>( frozen() ) << pf_endl;
589 
590  s16 value = fire_resist().mod;
591  if ( value != 0 )
592  sw() << "\tFireResistMod\t" << static_cast<int>( value ) << pf_endl;
593  value = cold_resist().mod;
594  if ( value != 0 )
595  sw() << "\tColdResistMod\t" << static_cast<int>( value ) << pf_endl;
596  value = energy_resist().mod;
597  if ( value != 0 )
598  sw() << "\tEnergyResistMod\t" << static_cast<int>( value ) << pf_endl;
599  value = poison_resist().mod;
600  if ( value != 0 )
601  sw() << "\tPoisonResistMod\t" << static_cast<int>( value ) << pf_endl;
602  value = physical_resist().mod;
603  if ( value != 0 )
604  sw() << "\tPhysicalResistMod\t" << static_cast<int>( value ) << pf_endl;
605 
606  value = fire_damage().mod;
607  if ( value != 0 )
608  sw() << "\tFireDamageMod\t" << static_cast<int>( value ) << pf_endl;
609  value = cold_damage().mod;
610  if ( value != 0 )
611  sw() << "\tColdDamageMod\t" << static_cast<int>( value ) << pf_endl;
612  value = energy_damage().mod;
613  if ( value != 0 )
614  sw() << "\tEnergyDamageMod\t" << static_cast<int>( value ) << pf_endl;
615  value = poison_damage().mod;
616  if ( value != 0 )
617  sw() << "\tPoisonDamageMod\t" << static_cast<int>( value ) << pf_endl;
618  value = physical_damage().mod;
619  if ( value != 0 )
620  sw() << "\tPhysicalDamageMod\t" << static_cast<int>( value ) << pf_endl;
621 
622  if ( has_movement_cost() )
623  {
624  auto movecost_value = movement_cost();
625  if ( movecost_value.walk != Core::MovementCostMod::DEFAULT.walk )
626  sw() << "\tMovementWalkMod\t" << static_cast<double>( movecost_value.walk ) << pf_endl;
627  if ( movecost_value.run != Core::MovementCostMod::DEFAULT.run )
628  sw() << "\tMovementRunMod\t" << static_cast<double>( movecost_value.run ) << pf_endl;
629  if ( movecost_value.walk_mounted != Core::MovementCostMod::DEFAULT.walk_mounted )
630  sw() << "\tMovementWalkMountedMod\t" << static_cast<double>( movecost_value.walk_mounted )
631  << pf_endl;
632  if ( movecost_value.run_mounted != Core::MovementCostMod::DEFAULT.run_mounted )
633  sw() << "\tMovementRunMountedMod\t" << static_cast<double>( movecost_value.run_mounted )
634  << pf_endl;
635  }
636  if ( has_carrying_capacity_mod() )
637  sw() << "\tCarryingCapacityMod\t" << static_cast<int>( carrying_capacity_mod() ) << pf_endl;
638 
639 
640  // output Attributes
641  for ( Attribute* pAttr = Attribute::FindAttribute( 0 ); pAttr != nullptr; pAttr = pAttr->next )
642  {
643  const AttributeValue& av = attribute( pAttr->attrid );
644  short lock = av.lock();
645  unsigned cap = av.cap();
646 
647  if ( av.base() || lock ||
648  cap != pAttr->default_cap ) // it kind of floods the file... but... :/ (Nando)
649  {
650  unsigned ones = av.base() / 10;
651  unsigned tenths = av.base() % 10;
652  sw() << "\t" << pAttr->name << "\t" << ones;
653  if ( tenths )
654  sw() << "." << tenths;
655 
656  if ( cap != pAttr->default_cap )
657  {
658  unsigned cap_ones = cap / 10;
659  unsigned cap_tenths = cap % 10;
660 
661  sw() << ":" << cap_ones;
662  if ( tenths )
663  sw() << "." << cap_tenths;
664  }
665 
666  if ( lock )
667  sw() << ";" << lock;
668 
669  sw() << pf_endl;
670  }
671  }
672 
673  // output Vitals
674  for ( Core::Vital* pVital = Core::FindVital( 0 ); pVital != nullptr; pVital = pVital->next )
675  {
676  const VitalValue& vv = vital( pVital->vitalid );
677  if ( vv.current_ones() )
678  {
679  sw() << "\t" << pVital->name << "\t" << vv.current_ones() << pf_endl;
680  }
681  }
682 
683  if ( has_skillstatcap() )
684  {
685  auto cap_value = skillstatcap();
686  if ( cap_value.statcap != Core::SkillStatCap::DEFAULT.statcap )
687  sw() << "\tStatcap\t" << static_cast<int>( cap_value.statcap ) << pf_endl;
688  if ( cap_value.skillcap != Core::SkillStatCap::DEFAULT.skillcap )
689  sw() << "\tSkillcap\t" << static_cast<int>( cap_value.skillcap ) << pf_endl;
690  }
691 
692  if ( has_luck() )
693  sw() << "\tLuck\t" << static_cast<int>( luck() ) << pf_endl;
694  if ( has_followers() )
695  {
696  auto followers_value = followers();
697  if ( followers_value.followers_max != Core::ExtStatBarFollowers::DEFAULT.followers_max )
698  sw() << "\tFollowersMax\t" << static_cast<int>( followers_value.followers_max ) << pf_endl;
699  if ( followers_value.followers != Core::ExtStatBarFollowers::DEFAULT.followers )
700  sw() << "\tFollowers\t" << static_cast<int>( followers_value.followers ) << pf_endl;
701  }
702  if ( has_tithing() )
703  sw() << "\tTithing\t" << static_cast<int>( tithing() ) << pf_endl;
704 
705 
706  if ( movemode != Core::MOVEMODE_LAND )
707  sw() << "\tMoveMode\t" << encode_movemode( movemode ) << pf_endl;
708 
709  if ( !privs.empty() )
710  {
711  sw() << "\tPrivs\t" << privs.extract() << pf_endl;
712  }
713  if ( !settings.empty() )
714  {
715  sw() << "\tSettings\t" << settings.extract() << pf_endl;
716  }
717 
718  sw() << "\tCreatedAt\t" << created_at << pf_endl;
719 
720  if ( has_squelched_until() )
721  sw() << "\tSquelchedUntil\t" << squelched_until() << pf_endl;
722  if ( has_deafened_until() )
723  sw() << "\tDeafenedUntil\t" << deafened_until() << pf_endl;
724 
725  if ( has_title_prefix() )
726  sw() << "\tTitlePrefix\t" << Clib::getencodedquotedstring( title_prefix() ) << pf_endl;
727  if ( has_title_suffix() )
728  sw() << "\tTitleSuffix\t" << Clib::getencodedquotedstring( title_suffix() ) << pf_endl;
729  if ( has_title_guild() )
730  sw() << "\tTitleGuild\t" << Clib::getencodedquotedstring( title_guild() ) << pf_endl;
731  if ( has_title_race() )
732  sw() << "\tTitleRace\t" << Clib::getencodedquotedstring( title_race() ) << pf_endl;
733 
734  if ( is_murderer() )
735  sw() << "\tMurderer\t" << is_murderer() << pf_endl;
736  if ( party_can_loot() )
737  sw() << "\tPartyCanLoot\t" << party_can_loot() << pf_endl;
738  for ( const auto& rt : reportable_ )
739  {
740  sw() << "\tReportable\t" << Clib::hexint( rt.serial ) << " " << rt.polclock << pf_endl;
741  }
742 
743  Core::UCorpse* corpse_obj = static_cast<Core::UCorpse*>( Core::system_find_item( last_corpse ) );
744  if ( corpse_obj != nullptr && !corpse_obj->orphan() )
745  sw() << "\tLastCorpse\t" << last_corpse << pf_endl;
746 }
747 
749 {
751 }
752 
753 const char* Character::classname() const
754 {
755  return "Character";
756 }
757 
759 {
760  base::printOn( sw );
761 }
762 
764 {
765  base::printOn( sw );
766 }
768 {
769  wornitems->print( sw_pc, sw_equip );
770 }
771 
773 {
775 
776  const auto not_found = std::string::npos;
777  if ( str.find( 'L' ) != not_found )
778  mm = static_cast<Core::MOVEMODE>( mm + Core::MOVEMODE_LAND );
779  if ( str.find( 'S' ) != not_found )
780  mm = static_cast<Core::MOVEMODE>( mm + Core::MOVEMODE_SEA );
781  if ( str.find( 'A' ) != not_found )
782  mm = static_cast<Core::MOVEMODE>( mm + Core::MOVEMODE_AIR );
783  if ( str.find( 'F' ) != not_found )
784  mm = static_cast<Core::MOVEMODE>( mm + Core::MOVEMODE_FLY );
785  return mm;
786 }
787 
789 {
790  std::string res;
791  if ( mm & Core::MOVEMODE_LAND )
792  res += "L";
793  if ( mm & Core::MOVEMODE_SEA )
794  res += "S";
795  if ( mm & Core::MOVEMODE_AIR )
796  res += "A";
797  if ( mm & Core::MOVEMODE_FLY )
798  res += "F";
799  return res;
800 }
801 
803 {
804  serial = elem.remove_ulong( "SERIAL" );
805 
806  if ( Plib::systemstate.config.check_integrity )
807  {
809  {
810  ERROR_PRINT.Format( "Character 0x{:X} defined more than once.\n" ) << serial;
811  throw std::runtime_error( "Data integrity error" );
812  }
813  }
816 
817  std::string acctname;
818  if ( elem.remove_prop( "ACCOUNT", &acctname ) )
819  {
820  unsigned short charindex;
821  charindex = elem.remove_ushort( "CHARIDX" );
822 
823  if ( charindex >= Plib::systemstate.config.character_slots )
824  {
825  ERROR_PRINT << "Account " << acctname << ": "
826  << "CHARIDX of " << charindex << " is too high for character serial (0x"
827  << fmt::hexu( serial ) << ")\n";
828 
829  throw std::runtime_error( "Data integrity error" );
830  }
831  Accounts::Account* search_acct = Accounts::find_account( acctname.c_str() );
832  if ( search_acct == nullptr )
833  {
834  ERROR_PRINT << "Character '" << name() << "': "
835  << "Account '" << acctname << "' doesn't exist.\n";
836  throw std::runtime_error( "Data integrity error" );
837  }
838  if ( search_acct->get_character( charindex ) != nullptr )
839  {
840  ERROR_PRINT << "Account " << acctname << " has two characters with CHARIDX of " << charindex
841  << "\n";
842  throw std::runtime_error( "Data integrity error" );
843  }
844 
845  acct.set( search_acct );
846  acct->set_character( charindex, this );
847  }
848 
849  trueobjtype = elem.remove_unsigned( "TRUEOBJTYPE", objtype_ ); // dave 1/30/3
850  graphic = static_cast<u16>( objtype_ );
851 
852  registered_house = elem.remove_ulong( "REGISTEREDHOUSE", 0 );
853 
854  base::readProperties( elem );
855 
856  if ( name_ == "" )
857  {
858  ERROR_PRINT << "Character '0x" << fmt::hexu( serial ) << "' has no name!\n";
859  throw std::runtime_error( "Data integrity error" );
860  }
861  wornitems->serial = serial;
862  wornitems->serial_ext = serial_ext;
864 
865  std::string cmdaccstr = elem.remove_string( "CMDLEVEL", "player" );
866  Core::CmdLevel* cmdlevel_search = Core::find_cmdlevel( cmdaccstr.c_str() );
867  if ( cmdlevel_search == nullptr )
868  elem.throw_error( "Didn't understand cmdlevel of '" + cmdaccstr + "'" );
869  cmdlevel_ = cmdlevel_search->cmdlevel;
870 
871  movemode = decode_movemode( elem.remove_string( "MOVEMODE", "L" ) );
872  concealed_ = static_cast<unsigned char>( elem.remove_ushort(
873  "CONCEALED", 0 ) ); // DAVE changed from remove_bool 11/25. concealed is a char, not a bool!
874  // if (concealed_ > cmdlevel)
875  // concealed_ = cmdlevel;
876 
877  truecolor = elem.remove_ushort( "TRUECOLOR" );
878 
879  mountedsteps_ = elem.remove_ulong( "MOUNTEDSTEPS", 0 );
880 
881  gender = static_cast<Core::UGENDER>( elem.remove_ushort( "GENDER" ) );
882  race = static_cast<Core::URACE>( elem.remove_ushort( "RACE", Core::RACE_HUMAN ) );
883 
884  if ( elem.remove_bool( "DEAD", false ) )
886  if ( elem.remove_bool( "HIDDEN", false ) )
888  if ( elem.remove_bool( "FROZEN", false ) )
890 
891  s16 mod_value = static_cast<s16>( elem.remove_int( "FIRERESISTMOD", 0 ) );
892  if ( mod_value != 0 )
893  fire_resist( fire_resist().setAsMod( mod_value ) );
894  mod_value = static_cast<s16>( elem.remove_int( "COLDRESISTMOD", 0 ) );
895  if ( mod_value != 0 )
896  cold_resist( cold_resist().setAsMod( mod_value ) );
897  mod_value = static_cast<s16>( elem.remove_int( "ENERGYRESISTMOD", 0 ) );
898  if ( mod_value != 0 )
899  energy_resist( energy_resist().setAsMod( mod_value ) );
900  mod_value = static_cast<s16>( elem.remove_int( "POISONRESISTMOD", 0 ) );
901  if ( mod_value != 0 )
902  poison_resist( poison_resist().setAsMod( mod_value ) );
903  mod_value = static_cast<s16>( elem.remove_int( "PHYSICALRESISTMOD", 0 ) );
904  if ( mod_value != 0 )
905  physical_resist( physical_resist().setAsMod( mod_value ) );
906 
907  mod_value = static_cast<s16>( elem.remove_int( "FIREDAMAGEMOD", 0 ) );
908  if ( mod_value != 0 )
909  fire_damage( fire_damage().setAsMod( mod_value ) );
910  mod_value = static_cast<s16>( elem.remove_int( "COLDDAMAGEMOD", 0 ) );
911  if ( mod_value != 0 )
912  cold_damage( cold_damage().setAsMod( mod_value ) );
913  mod_value = static_cast<s16>( elem.remove_int( "ENERGYDAMAGEMOD", 0 ) );
914  if ( mod_value != 0 )
915  energy_damage( energy_damage().setAsMod( mod_value ) );
916  mod_value = static_cast<s16>( elem.remove_int( "POISONDAMAGEMOD", 0 ) );
917  if ( mod_value != 0 )
918  poison_damage( poison_damage().setAsMod( mod_value ) );
919  mod_value = static_cast<s16>( elem.remove_int( "PHYSICALDAMAGEMOD", 0 ) );
920  if ( mod_value != 0 )
921  physical_damage( physical_damage().setAsMod( mod_value ) );
922 
923  movement_cost( Core::MovementCostMod(
924  elem.remove_double( "MovementWalkMod", Core::MovementCostMod::DEFAULT.walk ),
925  elem.remove_double( "MovementRunMod", Core::MovementCostMod::DEFAULT.run ),
926  elem.remove_double( "MovementWalkMountedMod", Core::MovementCostMod::DEFAULT.walk_mounted ),
927  elem.remove_double( "MovementRunMountedMod", Core::MovementCostMod::DEFAULT.run_mounted ) ) );
928 
929  carrying_capacity_mod( static_cast<s16>( elem.remove_int( "CarryingCapacityMod", 0 ) ) );
930 
931  height = Core::settingsManager.ssopt.default_character_height; // no really, height is 9
932 
933  created_at = elem.remove_ulong( "CreatedAt", 0 );
934  squelched_until( elem.remove_ulong( "SquelchedUntil", 0 ) );
935  deafened_until( elem.remove_ulong( "DeafenedUntil", 0 ) );
936 
937  title_prefix( elem.remove_string( "TITLEPREFIX", "" ) );
938  title_suffix( elem.remove_string( "TITLESUFFIX", "" ) );
939  title_guild( elem.remove_string( "TITLEGUILD", "" ) );
940  title_race( elem.remove_string( "TITLERACE", "" ) );
941 
942  unsigned int tmp_guildid;
943  if ( elem.remove_prop( "GUILDID", &tmp_guildid ) )
944  guild( Core::Guild::FindOrCreateGuild( tmp_guildid, serial ) );
945 
946  if ( elem.remove_bool( "MURDERER", false ) )
948  if ( elem.remove_bool( "PARTYCANLOOT", false ) )
950 
951  std::string rt;
952  while ( elem.remove_prop( "REPORTABLE", &rt ) )
953  {
954  ISTRINGSTREAM is( rt );
955  reportable_t rt_t;
956  if ( is >> rt_t.serial >> rt_t.polclock )
957  {
958  reportable_.insert( rt_t );
959  }
960  }
961 
962  uclang = elem.remove_string( "UCLang", "enu" );
963  skillstatcap( Core::SkillStatCap(
964  static_cast<s16>( elem.remove_int( "STATCAP", Core::SkillStatCap::DEFAULT.statcap ) ),
965  static_cast<u16>( elem.remove_int( "SKILLCAP", Core::SkillStatCap::DEFAULT.skillcap ) ) ) );
966  luck( static_cast<s16>( elem.remove_int( "LUCK", 0 ) ) );
967  followers( Core::ExtStatBarFollowers(
968  static_cast<s8>(
970  static_cast<s8>(
971  elem.remove_int( "FOLLOWERSMAX", Core::ExtStatBarFollowers::DEFAULT.followers_max ) ) ) );
972  tithing( elem.remove_int( "TITHING", 0 ) );
973 
974  privs.readfrom( elem.remove_string( "Privs", "" ) );
975  settings.readfrom( elem.remove_string( "Settings", "" ) );
977 }
978 
979 // these are read here to allow NPCs to have "die-roll" type values
981 {
982  // read Attributes
983  for ( Attribute* pAttr = Attribute::FindAttribute( 0 ); pAttr != nullptr; pAttr = pAttr->next )
984  {
985  AttributeValue& av = attribute( pAttr->attrid );
986 
987  for ( unsigned i = 0; i < pAttr->aliases.size(); ++i )
988  {
989  std::string temp;
990  if ( elem.remove_prop( pAttr->aliases[i].c_str(), &temp ) )
991  {
992  // read
993  unsigned int base;
994  unsigned int cap = pAttr->default_cap;
995  unsigned char lock = 0;
996  if ( Core::settingsManager.polvar.DataWrittenBy == 93 &&
998  {
999  unsigned int raw = strtoul( temp.c_str(), nullptr, 10 );
1000  base = Core::raw_to_base( raw );
1001  }
1002  else
1003  {
1004  if ( !Core::settingsManager.polvar.DataWrittenBy &&
1006  {
1007  elem.throw_error( "Pol.txt 'System' element needs to specify CoreVersion" );
1008  }
1009 
1010  const char* pval = temp.c_str();
1011  char* pdot = nullptr;
1012  unsigned int ones = strtoul( pval, &pdot, 10 );
1013  unsigned int tenths = 0;
1014  if ( pdot && *pdot == '.' )
1015  tenths = strtoul( pdot + 1, &pdot, 10 );
1016  base = ones * 10 + tenths;
1017 
1018  // Do we have caps?
1019  if ( pdot && *pdot == ':' )
1020  {
1021  unsigned int cap_ones = strtoul( pdot + 1, &pdot, 10 );
1022  unsigned int cap_tenths = 0;
1023 
1024  // Tenths in cap?
1025  if ( pdot && *pdot == '.' )
1026  cap_tenths = strtoul( pdot + 1, &pdot, 10 );
1027 
1028  cap = cap_ones * 10 + cap_tenths;
1029  }
1030 
1031  if ( pdot && *pdot == ';' )
1032  lock = (unsigned char)strtoul( pdot + 1, nullptr, 10 );
1033  }
1034  if ( /*base < ATTRIBUTE_MIN_BASE ||*/ // ATTRIBUTE_MIN_BASE is currently 0
1035  base > ATTRIBUTE_MAX_BASE )
1036  elem.throw_error( "Character " + Clib::hexint( serial ) + " attribute " +
1037  pAttr->aliases[i] + " is out of range" );
1038  av.base( static_cast<unsigned short>( base ) );
1039 
1040  if ( /*cap < ATTRIBUTE_MIN_BASE ||*/ // ATTRIBUTE_MIN_BASE is currently 0
1041  cap > ATTRIBUTE_MAX_BASE )
1042  elem.throw_error( "Character " + Clib::hexint( serial ) + " attribute cap from " +
1043  pAttr->aliases[i] + " is out of range" );
1044  av.cap( static_cast<unsigned short>( cap ) );
1045 
1046 
1047  if ( lock > 2 ) // FIXME: HardCoded Value
1048  elem.throw_error( "Character " + Clib::hexint( serial ) + " attribute " +
1049  pAttr->aliases[i] + " has illegal lock state!" );
1050 
1051  av.lock( lock );
1052 
1053  break;
1054  }
1055  }
1056  }
1057 
1058  calc_vital_stuff();
1059 
1060  // read Vitals
1061  for ( Core::Vital* pVital = Core::FindVital( 0 ); pVital != nullptr; pVital = pVital->next )
1062  {
1063  VitalValue& vv = vital( pVital->vitalid );
1064  for ( const auto& _i : pVital->aliases )
1065  {
1066  unsigned int temp;
1067  if ( elem.remove_prop( _i.c_str(), &temp ) )
1068  {
1069  // read
1070  // these are always just stored as points
1071  if ( /*temp < Core::VITAL_MIN_VALUE ||*/ // VITAL_MIN_VALUE is currently 0
1072  temp > Core::VITAL_MAX_VALUE )
1073  elem.throw_error( "Character " + Clib::hexint( serial ) + " vital " + _i +
1074  " is out of range" );
1075  vv.current_ones( temp );
1076  break;
1077  }
1078  }
1079  }
1080 }
1081 
1083 {
1084  readCommonProperties( elem );
1085  readAttributesAndVitals( elem );
1086 
1087  last_corpse = elem.remove_ulong( "LastCorpse", 0 );
1088 }
1089 
1090 
1091 bool Character::has_privilege( const char* priv ) const
1092 {
1093  return privs.contains( priv ) || privs.contains( "all" );
1094 }
1095 
1096 bool Character::setting_enabled( const char* setting ) const
1097 {
1098  return cached_settings.get( PRIV_FLAGS::ALL ) || settings.contains( setting );
1099 }
1100 
1102 {
1103  PrivUpdater invulwatch( update ? this : nullptr, PRIV_FLAGS::INVUL );
1104  PrivUpdater seeghostswatch( update ? this : nullptr, PRIV_FLAGS::SEE_GHOSTS );
1105  PrivUpdater seehiddenwatch( update ? this : nullptr, PRIV_FLAGS::SEE_HIDDEN );
1106  PrivUpdater seeinvisitemswatch( update ? this : nullptr, PRIV_FLAGS::SEE_INVIS_ITEMS );
1107 
1109 
1110  if ( setting_enabled( "all" ) )
1111  {
1113  return;
1114  }
1115  if ( setting_enabled( "clotheany" ) )
1117  if ( setting_enabled( "dblclickany" ) )
1119  if ( setting_enabled( "hearghosts" ) )
1121  if ( setting_enabled( "invul" ) )
1123  if ( setting_enabled( "losany" ) )
1125  if ( setting_enabled( "moveany" ) )
1127  if ( setting_enabled( "renameany" ) )
1129  if ( setting_enabled( "seeghosts" ) )
1131  if ( setting_enabled( "seehidden" ) )
1133  if ( setting_enabled( "seeinvisitems" ) )
1135  if ( setting_enabled( "ignoredoors" ) )
1137  if ( setting_enabled( "freemove" ) )
1139  if ( setting_enabled( "firewhilemoving" ) )
1141  if ( setting_enabled( "attackhidden" ) )
1143  if ( setting_enabled( "hiddenattack" ) )
1145  if ( setting_enabled( "plogany" ) )
1147  if ( setting_enabled( "moveanydist" ) )
1149  if ( setting_enabled( "canbeheardasghost" ) )
1151  if ( setting_enabled( "runwhilestealth" ) )
1153  if ( setting_enabled( "speedhack" ) )
1155 }
1156 
1157 void Character::set_setting( const char* setting, bool value )
1158 {
1159  set_dirty();
1160  if ( value == false )
1161  settings.remove( setting );
1162  else if ( has_privilege( setting ) )
1163  settings.add( setting );
1164 
1166 }
1167 
1168 std::string Character::all_settings() const
1169 {
1170  return settings.extract();
1171 }
1172 
1173 std::string Character::all_privs() const
1174 {
1175  return privs.extract();
1176 }
1177 
1178 void Character::set_privs( const std::string& privlist )
1179 {
1180  set_dirty();
1181  privs.readfrom( privlist );
1182 }
1183 
1184 void Character::grant_privilege( const char* priv )
1185 {
1186  if ( !privs.contains( priv ) )
1187  {
1188  set_dirty();
1189  privs.add( priv );
1190  }
1191 }
1192 
1193 void Character::revoke_privilege( const char* priv )
1194 {
1195  set_dirty();
1196  privs.remove( priv );
1197  settings.remove( priv );
1199 }
1200 
1201 bool Character::can_access( const Items::Item* item, int range ) const
1202 {
1203  // TODO: find_legal_item() is awful, we should just check the item
1204  // properties directly instead of going around searching for a given serial in the world
1205 
1206  // Range < 0 has special meaning. -1 is the default accessible range,
1207  // anything smaller ignores the range check.
1208  if ( range == -1 )
1210 
1211  const bool within_range = ( range < -1 ) || pol_distance( this, item ) <= range;
1212  if ( within_range && ( find_legal_item( this, item->serial ) != nullptr ) )
1213  return true;
1214 
1215  return false;
1216 }
1217 
1218 bool Character::can_move( const Items::Item* item ) const
1219 {
1220  if ( item->objtype_ != UOBJ_CORPSE )
1221  {
1222  return cached_settings.get( PRIV_FLAGS::MOVE_ANY ) || item->movable();
1223  }
1224  else
1225  {
1226  return false;
1227  }
1228 }
1229 
1230 bool Character::can_be_renamed_by( const Character* /*chr*/ ) const
1231 {
1232  // consider command levels?
1233  return false;
1234 }
1235 
1236 bool Character::can_rename( const Character* chr ) const
1237 {
1238  return cached_settings.get( PRIV_FLAGS::RENAME_ANY ) || chr->can_be_renamed_by( this );
1239 }
1240 
1241 bool Character::can_clothe( const Character* chr ) const
1242 {
1243  return ( ( chr == this ) || cached_settings.get( PRIV_FLAGS::CLOTHE_ANY ) );
1244 }
1245 
1247 {
1249 }
1250 
1252 {
1254 }
1255 
1257 {
1259 }
1260 
1262 {
1264 }
1265 
1267 {
1269 }
1270 
1272 {
1274 }
1275 
1277 {
1278  return static_cast<Core::UContainer*>( wornitems->GetItemOnLayer( Core::LAYER_BACKPACK ) );
1279 }
1280 
1282 {
1284  if ( _item != nullptr && _item->script_isa( Core::POLCLASS_SPELLBOOK ) )
1285  {
1286  Core::Spellbook* book = static_cast<Core::Spellbook*>( _item );
1287  if ( book->spell_school == school )
1288  return book;
1289  }
1290 
1291  Core::UContainer* cont = backpack();
1292  if ( cont != nullptr )
1293  {
1294  for ( Core::UContainer::const_iterator itr = cont->begin(); itr != cont->end(); ++itr )
1295  {
1296  const Items::Item* item = GET_ITEM_PTR( itr );
1297 
1298  if ( item != nullptr && item->script_isa( Core::POLCLASS_SPELLBOOK ) )
1299  {
1300  const Core::Spellbook* book = static_cast<const Core::Spellbook*>( item );
1301  if ( book->spell_school == school )
1302  return const_cast<Core::Spellbook*>( book );
1303  }
1304  }
1305  }
1306  return nullptr;
1307 }
1308 
1309 unsigned int Character::gold_carried() const
1310 {
1311  Core::UContainer* bp = backpack();
1312  if ( bp != nullptr )
1314  else
1315  return 0;
1316 }
1317 
1318 // TODO: This could be more efficient, by inlining 'spend' logic
1319 // in a recursive function
1320 
1321 void Character::spend_gold( unsigned int amount )
1322 {
1323  passert( gold_carried() >= amount );
1324 
1325  Core::UContainer* bp = backpack();
1326  if ( bp != nullptr )
1328  if ( client != nullptr )
1329  send_full_statmsg( client, this );
1330 }
1331 
1333 {
1334  return wornitems->GetItemOnLayer( layer );
1335 }
1336 
1337 bool Character::layer_is_equipped( int layer ) const
1338 {
1339  return ( wornitems->GetItemOnLayer( layer ) != nullptr );
1340 }
1341 
1342 bool Character::is_equipped( const Items::Item* item ) const
1343 {
1344  if ( !Items::valid_equip_layer( item ) )
1345  return false;
1346 
1347  return ( wornitems->GetItemOnLayer( item->tile_layer ) == item );
1348 }
1349 
1351 {
1352  const Items::ItemDesc& desc = item->itemdesc();
1353  return ( attribute( Core::gamestate.pAttrStrength->attrid ).base() >= desc.base_str_req );
1354 }
1355 
1356 bool Character::equippable( const Items::Item* item ) const
1357 {
1358  if ( !Items::valid_equip_layer( item ) )
1359  {
1360  if ( item->objtype_ == Core::settingsManager.extobj.mount )
1361  {
1362  POLLOG_INFO.Format(
1363  "\nWarning: Character 0x{:X} tried to mount Item 0x{:X}, but it doesn't have a mount "
1364  "graphic (current graphic: 0x{:X}). Check that the list of mounts in uoconvert.cfg is "
1365  "correct and re-run uoconvert if necessary.\n" )
1366  << this->serial << item->serial << item->graphic;
1367  }
1368 
1369  return false;
1370  }
1371  if ( layer_is_equipped( item->tile_layer ) )
1372  {
1373  return false;
1374  }
1375  if ( ( item->tile_layer == Core::LAYER_BACKPACK ) &&
1377  {
1378  return false;
1379  }
1380 
1381  // Only allow mounts if they have the mount objtype as defined in extobj.cfg
1384  {
1385  POLLOG_INFO.Format(
1386  "\nWarning: Character 0x{:X} tried to mount Item 0x{:X}, but it doesn't have the mount "
1387  "objtype (as defined in extobj.cfg) and EnforceMountObjtype in pol.cfg is true.\n" )
1388  << this->serial << item->serial;
1389  return false;
1390  }
1391 
1393  {
1394  return false;
1395  }
1396  // redundant sanity check
1397  if ( Core::tilelayer( item->graphic ) != item->tile_layer )
1398  {
1399  return false;
1400  }
1401 
1402  const Items::ItemDesc& desc = item->itemdesc();
1403  if ( attribute( Core::gamestate.pAttrStrength->attrid ).base() < desc.base_str_req )
1404  {
1405  return false;
1406  }
1407 
1408  if ( item->tile_layer == Core::LAYER_HAND1 || item->tile_layer == Core::LAYER_HAND2 )
1409  {
1410  if ( weapon != nullptr )
1411  {
1413  {
1414  return false;
1415  }
1416  }
1417  if ( item->isa( Core::UOBJ_CLASS::CLASS_WEAPON ) )
1418  {
1419  const Items::UWeapon* wpn_item = static_cast<const Items::UWeapon*>( item );
1420  if ( wpn_item->descriptor().two_handed )
1421  {
1423  {
1424  return false;
1425  }
1426  }
1427  }
1428  }
1429 
1430  return true;
1431 }
1432 
1434 {
1435  passert_r( equippable( item ),
1436  "It is impossible to equip Item with ObjType " + Clib::hexint( item->objtype_ ) );
1437 
1438  wornitems->PutItemOnLayer( item );
1439 
1440  // PutItemOnLayer sets the layer, so we can go on now
1441  // checking item->layer instead of item->tile_layer
1442  if ( item->isa( Core::UOBJ_CLASS::CLASS_WEAPON ) &&
1443  ( item->layer == Core::LAYER_HAND1 || item->layer == Core::LAYER_HAND2 ) )
1444  {
1445  weapon = static_cast<Items::UWeapon*>( item );
1447  }
1448  else if ( item->isa( Core::UOBJ_CLASS::CLASS_ARMOR ) )
1449  {
1450  if ( item->layer == Core::LAYER_HAND1 || item->layer == Core::LAYER_HAND2 )
1451  {
1452  shield = static_cast<Items::UArmor*>( item );
1453  }
1455  }
1456  refresh_ar();
1457 }
1458 
1460 {
1462 }
1463 
1465 {
1466  passert( Items::valid_equip_layer( item ) );
1467  // assume any item being de-equipped is in fact being worn.
1468  passert( item->container == wornitems.get() );
1469  passert( is_equipped( item ) );
1470 
1471  wornitems->RemoveItemFromLayer( item );
1472 
1473  if ( item == weapon )
1474  {
1476  /* we don't reset the swing timer here, 'cause
1477  you can switch from weapon to H2H quickly.
1478  Note, this _could_ let you use a faster weapon
1479  to wrestle faster, hmm.
1480  */
1481  }
1482  else if ( item == shield )
1483  {
1484  shield = nullptr;
1486  }
1487  refresh_ar();
1488 }
1489 
1491 {
1492  if ( race == Core::RACE_GARGOYLE )
1493  return ( movemode & Core::MOVEMODE_FLY ) == 0 ? false : true;
1494 
1496 }
1497 
1498 
1500 {
1501  for ( unsigned layer = Core::LAYER_EQUIP__LOWEST; layer <= Core::LAYER_EQUIP__HIGHEST; layer++ )
1502  {
1503  Items::Item* item = wornitems->GetItemOnLayer( layer );
1504  if ( item )
1505  {
1506  if ( item->serial == find_serial )
1507  return item;
1508  // 4/2007 - MuadDib
1509  // Added cont check and using cont->find to check here
1510  // for equipped cont items like the ML added Quivers.
1511  // Using redundant null check.
1512  if ( item != nullptr && item->script_isa( Core::POLCLASS_CONTAINER ) )
1513  {
1514  if ( layer != Core::LAYER_HAIR && layer != Core::LAYER_FACE && layer != Core::LAYER_BEARD &&
1515  layer != Core::LAYER_BACKPACK && layer != Core::LAYER_MOUNT )
1516  {
1517  Core::UContainer* cont = static_cast<Core::UContainer*>( item );
1518  item = nullptr;
1519  item = cont->find( find_serial );
1520  if ( item != nullptr )
1521  return item;
1522  }
1523  }
1524  }
1525  }
1526  return nullptr;
1527 }
1528 
1529 void Character::produce( const Core::Vital* pVital, VitalValue& vv, unsigned int amt )
1530 {
1531  int start_ones = vv.current_ones();
1532  set_dirty();
1533  vv.produce( amt );
1534  if ( start_ones != vv.current_ones() )
1535  {
1537  }
1538 }
1539 
1540 bool Character::consume( const Core::Vital* pVital, VitalValue& vv, unsigned int amt )
1541 {
1542  bool res;
1543  int start_ones = vv.current_ones();
1544  set_dirty();
1545  res = vv.consume( amt );
1546  if ( start_ones != vv.current_ones() )
1547  {
1549  }
1550  return res;
1551 }
1552 
1553 void Character::set_current_ones( const Core::Vital* pVital, VitalValue& vv, unsigned int ones )
1554 {
1555  set_dirty();
1556  vv.current_ones( ones );
1558 }
1559 
1560 void Character::set_current( const Core::Vital* pVital, VitalValue& vv, unsigned int ones )
1561 {
1562  set_dirty();
1563  vv.current( ones );
1565 }
1566 
1568 {
1569  VitalValue& vv = vital( pVital->vitalid );
1570  int rr = vv.regenrate();
1571  if ( rr > 0 )
1572  {
1573  produce( pVital, vv, rr / 12 );
1574  }
1575  else if ( rr < 0 )
1576  {
1577  consume( pVital, vv, -rr / 12 );
1578  }
1579 }
1580 
1581 void Character::calc_vital_stuff( bool i_mod, bool v_mod )
1582 {
1583  if ( i_mod )
1584  {
1585  for ( unsigned ai = 0; ai < Core::gamestate.numAttributes; ++ai )
1586  {
1588  }
1589  }
1590 
1591  if ( v_mod )
1592  {
1593  for ( unsigned vi = 0; vi < Core::gamestate.numVitals; ++vi )
1594  {
1596  }
1597  }
1598 }
1599 
1601 {
1602  VitalValue& vv = vital( pVital->vitalid );
1603 
1604  int start_ones = vv.current_ones();
1605  int start_max = vv.maximum_ones();
1606 
1607  // dave change the order of maximum and regen function 3/19/3
1608  int mv = pVital->get_maximum_func->call_long( new Module::ECharacterRefObjImp( this ) );
1609 
1610  if ( mv < static_cast<int>( Core::VITAL_LOWEST_MAX_HUNDREDTHS ) )
1612  if ( mv > static_cast<int>( Core::VITAL_HIGHEST_MAX_HUNDREDTHS ) )
1614 
1615  vv.maximum( mv );
1616 
1617  int rr = pVital->get_regenrate_func->call_long( new Module::ECharacterRefObjImp( this ) );
1618 
1619  if ( rr < Core::VITAL_LOWEST_REGENRATE )
1621  if ( rr > Core::VITAL_HIGHEST_REGENRATE )
1623 
1624  vv.regenrate( rr );
1625 
1626  if ( ( start_ones != vv.current_ones() ) || ( start_max != vv.maximum_ones() ) )
1628 }
1629 
1631 {
1632  AttributeValue& av = attribute( pAttr->attrid );
1633 
1634  if ( pAttr->getintrinsicmod_func )
1635  {
1636  int im = pAttr->getintrinsicmod_func->call_long( new Module::ECharacterRefObjImp( this ) );
1637 
1638  if ( im < ATTRIBUTE_MIN_INTRINSIC_MOD )
1640  if ( im > ATTRIBUTE_MAX_INTRINSIC_MOD )
1642 
1643  av.intrinsic_mod( static_cast<short>( im ) );
1644  }
1645 }
1646 
1648 {
1649  set_dirty();
1650  for ( unsigned vi = 0; vi < Core::gamestate.numVitals; ++vi )
1651  {
1652  VitalValue& vv = vital( vi );
1653  vv.current( vv.maximum() );
1654 
1656  }
1657 }
1658 
1659 bool Character::setgraphic( u16 newgraphic )
1660 {
1661  if ( newgraphic < 1 ||
1662  newgraphic >
1663  0x800 ) // Maximum graphic: 2048, changed to allow new graphics -- Nando - 2009-01-14
1664  return false;
1665 
1666  set_dirty();
1667  graphic = newgraphic;
1669  if ( client )
1670  send_goxyz( client, client->chr );
1672 
1673  return true;
1674 }
1675 
1677 {
1679  if ( client )
1680  send_goxyz( client, client->chr );
1682 }
1683 
1685 {
1687 
1688  // only if client is active or for npcs
1689  if ( ( client ) || ( this->isa( Core::UOBJ_CLASS::CLASS_NPC ) ) )
1690  {
1691  if ( client )
1692  {
1693  send_goxyz( client, client->chr );
1694  // if poisoned send_goxyz handles 0x17 packet
1695  if ( !poisoned() )
1697  }
1698  // This is a KR only packet, so transmit it only to KR clients
1699  // who are in range.
1700  // if poisoned send_move_mobile_to_nearby_cansee handles 0x17 packet
1701  if ( !poisoned() )
1702  {
1703  Network::HealthBarStatusUpdate msg( serial_ext, Network::HealthBarStatusUpdate::Color::GREEN,
1704  poisoned() );
1706  this, [&]( Character* zonechr ) { msg.Send( zonechr->client ); } );
1707  }
1708  }
1709 }
1710 
1712 {
1713  if ( hidden() )
1714  {
1715  set_stealthsteps( 0 );
1716  if ( client )
1717  send_move( client, this );
1720  }
1721  else
1722  {
1723  unhide();
1724  set_stealthsteps( 0 );
1725  }
1726 }
1727 
1729 {
1730  if ( concealed() )
1731  {
1732  if ( client )
1733  send_move( client, this );
1736  }
1737  else if ( is_visible() )
1738  unhide();
1739  set_stealthsteps( 0 );
1740 }
1741 
1743 {
1746 }
1747 
1749 {
1750  if ( client )
1751  send_goxyz( client, client->chr );
1753 }
1754 
1756 {
1757  if ( client )
1758  {
1760  {
1762  }
1763  }
1764 }
1765 
1766 void Character::setfacing( u8 newfacing )
1767 {
1768  facing = newfacing & 7;
1769 }
1770 
1772 {
1773  // Breaks paperdoll
1774  u8 flag1 = 0;
1775  if ( gender )
1776  flag1 |= Core::CHAR_FLAG1_GENDER;
1777  if ( ( poisoned() ) &&
1778  ( ~other_client->ClientType &
1779  Network::CLIENTTYPE_7000 ) ) // client >=7 receive the poisonflag with 0x17
1780  flag1 |= Core::CHAR_FLAG1_POISONED;
1781  if ( ( movemode & Core::MOVEMODE_FLY ) &&
1782  ( other_client->ClientType & Network::CLIENTTYPE_7000 ) )
1783  flag1 |= Core::CHAR_FLAG1_FLYING;
1784  if ( ( Core::settingsManager.ssopt.invul_tag == 2 ) && ( invul() ) )
1786  if ( warmode() )
1787  flag1 |= Core::CHAR_FLAG1_WARMODE;
1788  if ( !is_visible() )
1789  flag1 |= Core::CHAR_FLAG1_INVISIBLE;
1790 
1791  return flag1;
1792 }
1793 
1794 void Character::apply_raw_damage_hundredths( unsigned int amount, Character* source, bool userepsys,
1795  bool send_damage_packet )
1796 {
1797  if ( dead() )
1798  {
1799  // cerr << "Waah! " << name() << " " << hexint(serial) << " is dead, but taking damage?" <<
1800  // endl;
1801  return;
1802  }
1803 
1804  if ( ( source ) && ( userepsys ) )
1805  source->repsys_on_attack( this );
1806 
1807  if ( ( amount == 0 ) || cached_settings.get( PRIV_FLAGS::INVUL ) )
1808  return;
1809 
1810  set_dirty();
1811  if ( hidden() )
1812  unhide();
1813 
1814  if ( send_damage_packet && source )
1815  {
1816  u16 showdmg;
1817  if ( amount > 6553500 ) // 0xFFFF*100
1818  showdmg = 0xFFFF;
1819  else
1820  showdmg = static_cast<u16>( amount / 100 );
1821  send_damage( source, this, showdmg );
1822  }
1823 
1824  if ( paralyzed() )
1826 
1827  disable_regeneration_for( 2 ); // FIXME depend on amount?
1828 
1829  // 0.xx is still 0
1830  VitalValue& vv = vital( Core::gamestate.pVitalLife->vitalid );
1831  if ( vv.current() - amount <= 99 )
1832  amount = vv.current(); // be greedy with that last point
1833  consume( Core::gamestate.pVitalLife, vv, amount );
1834 
1835  if ( ( source ) && ( userepsys ) )
1836  source->repsys_on_damage( this );
1837 
1839 
1840  if ( vv.current() == 0 )
1841  die();
1842 }
1843 
1844 // keep this in sync with NPC::armor_absorb_damage
1845 
1846 
1847 unsigned short calc_thru_damage( double damage, unsigned short ar )
1848 {
1849  int blocked = ar;
1850  if ( blocked < 0 )
1851  blocked = 0;
1852 
1853  int absorbed = blocked / 2;
1854 
1855  blocked -= absorbed;
1856  absorbed += Clib::random_int( blocked );
1857  damage -= absorbed;
1858 
1859  if ( damage >= 2.0 )
1860  {
1861  return static_cast<unsigned short>( damage * 0.5 );
1862  }
1863  else
1864  {
1865  int dmg = static_cast<int>( damage );
1866  if ( dmg >= 0 )
1867  return static_cast<unsigned short>( dmg );
1868  else
1869  return 0;
1870  }
1871 }
1872 
1873 
1874 double Character::armor_absorb_damage( double damage )
1875 {
1876  Items::UArmor* armor = choose_armor();
1877  if ( armor != nullptr )
1878  {
1879  damage = calc_thru_damage( damage, armor->ar() + ar_mod() );
1880 
1881  armor->reduce_hp_from_hit();
1882  }
1883  return damage;
1884 }
1885 
1886 double Character::apply_damage( double damage, Character* source, bool userepsys,
1887  bool send_damage_packet )
1888 {
1889  damage = armor_absorb_damage( damage );
1890  if ( Core::settingsManager.watch.combat )
1891  INFO_PRINT << "Final damage: " << damage << "\n";
1892  do_imhit_effects();
1893  apply_raw_damage_hundredths( static_cast<unsigned int>( damage * 100 ), source, userepsys,
1894  send_damage_packet );
1895 
1896  return damage;
1897 }
1898 
1899 void Character::get_hitscript_params( double damage, Items::UArmor** parmor,
1900  unsigned short* rawdamage )
1901 {
1902  Items::UArmor* armor = choose_armor();
1903  if ( armor )
1904  {
1905  *rawdamage = calc_thru_damage( damage, armor->ar() + ar_mod() );
1906  }
1907  else
1908  {
1909  *rawdamage = static_cast<unsigned short>( damage );
1910  }
1911  *parmor = armor;
1912 }
1913 
1914 void Character::run_hit_script( Character* defender, double damage )
1915 {
1918  if ( prog.get() == nullptr )
1919  return;
1920 
1921  std::unique_ptr<Core::UOExecutor> ex( Core::create_script_executor() );
1922  auto uoemod = new Module::UOExecutorModule( *ex );
1923  ex->addModule( uoemod );
1924 
1925  unsigned short rawdamage = 0;
1926  unsigned short basedamage = static_cast<unsigned short>( damage );
1927 
1928  Items::UArmor* armor = nullptr;
1929 
1930  defender->get_hitscript_params( damage, &armor, &rawdamage );
1931 
1932 
1933  ex->pushArg( new Bscript::BLong( rawdamage ) );
1934  ex->pushArg( new Bscript::BLong( basedamage ) );
1935  if ( armor )
1936  ex->pushArg( new Module::EItemRefObjImp( armor ) );
1937  else
1938  ex->pushArg( new Bscript::BLong( 0 ) );
1939  ex->pushArg( new Module::EItemRefObjImp( weapon ) );
1940  ex->pushArg( new Module::ECharacterRefObjImp( defender ) );
1941  ex->pushArg( new Module::ECharacterRefObjImp( this ) );
1942 
1943  ex->os_module->priority = 100;
1944 
1945  if ( ex->setProgram( prog.get() ) )
1946  {
1947  uoemod->controller_.set( this );
1948  schedule_executor( ex.release() );
1949  }
1950  else
1951  {
1952  POLLOG << "Blech, couldn't start hitscript " << weapon->hit_script().name() << "\n";
1953  }
1954 }
1955 
1963 {
1964  if ( vital( Core::gamestate.pVitalLife->vitalid ).is_at_maximum() && !poisoned() && !paralyzed() )
1965  {
1967  }
1968 }
1969 
1970 
1979 void Character::heal_damage_hundredths( unsigned int amount )
1980 {
1981  if ( dead() )
1982  {
1983  // cerr << "Waah! " << name() << " is dead, but healing damage?" << endl;
1984  return;
1985  }
1986 
1987  if ( amount == 0 )
1988  return;
1989 
1990  produce( Core::gamestate.pVitalLife, vital( Core::gamestate.pVitalLife->vitalid ), amount );
1991 
1992  check_undamaged();
1993 
1995 }
1996 
1998 {
2000  item->layer = Core::LAYER_ROBE_DRESS;
2001  return item;
2002 }
2004 {
2006  item->layer = Core::LAYER_ROBE_DRESS;
2007  return item;
2008 }
2010 {
2012  item->layer = Core::LAYER_BACKPACK;
2013  return item;
2014 }
2015 
2016 
2018 {
2020  msg->Write<u8>( warmode() ? 1u : 0u );
2021  msg->offset++; // u8 unk2
2022  msg->Write<u8>( 0x32u );
2023  msg->offset++; // u8 unk4
2024  msg.Send( client );
2025 }
2026 
2028 {
2029  if ( !inrange( chr, client->chr ) )
2030  return;
2031 
2032  if ( !client->chr->is_visible_to_me( chr ) )
2033  {
2034  send_remove_character( client, chr );
2035  }
2036 }
2038 {
2039  if ( !inrange( chr, client->chr ) )
2040  return;
2041 
2042  if ( chr->dead() && client->chr->is_visible_to_me( chr ) )
2043  {
2044  send_owncreate( client, chr );
2045  }
2046 }
2047 
2049 {
2050  if ( !dead() )
2051  {
2052  ERROR_PRINT << "uh, trying to resurrect " << name() << ", who isn't dead.\n";
2053  return;
2054  }
2055  set_dirty();
2056  if ( graphic == UOBJ_HUMAN_MALE_GHOST )
2058  else if ( graphic == UOBJ_HUMAN_FEMALE_GHOST )
2060  else if ( graphic == UOBJ_ELF_MALE_GHOST )
2062  else if ( graphic == UOBJ_ELF_FEMALE_GHOST )
2064  else if ( graphic == UOBJ_GARGOYLE_MALE_GHOST )
2066  else if ( graphic == UOBJ_GARGOYLE_FEMALE_GHOST )
2068 
2073 
2074  color = truecolor;
2075 
2076  if ( Core::gamestate.pVitalLife->regen_while_dead )
2077  {
2078  VitalValue& vv = vital( Core::gamestate.pVitalLife->vitalid );
2079  if ( vv._current == 0 ) // set in die()
2080  set_current_ones( Core::gamestate.pVitalLife, vv, 1 );
2081  }
2082  else
2083  set_current_ones( Core::gamestate.pVitalLife, vital( Core::gamestate.pVitalLife->vitalid ), 1 );
2084 
2085  if ( !Core::gamestate.pVitalMana->regen_while_dead )
2086  set_current_ones( Core::gamestate.pVitalMana, vital( Core::gamestate.pVitalMana->vitalid ), 0 );
2087 
2088  if ( !Core::gamestate.pVitalStamina->regen_while_dead )
2089  set_current_ones( Core::gamestate.pVitalStamina,
2090  vital( Core::gamestate.pVitalStamina->vitalid ), 1 );
2091 
2092  // Replace the death shroud with a death robe
2093  bool equip_death_robe = true;
2095  {
2096  Items::Item* death_shroud = wornitems->GetItemOnLayer( Core::LAYER_ROBE_DRESS );
2097  if ( death_shroud->objtype_ == UOBJ_DEATH_SHROUD )
2098  {
2099  unequip( death_shroud );
2100  death_shroud->destroy();
2101  death_shroud = nullptr;
2102  }
2103  else
2104  {
2105  // Do not destroy and replace the already equipped robe
2106  equip_death_robe = false;
2107  }
2108  }
2109  if ( equip_death_robe )
2110  {
2111  Items::Item* death_robe = create_death_robe();
2112  death_robe->realm = realm;
2113  equip( death_robe );
2114  }
2115 
2116  // equip( create_backpack() );
2117 
2118  // if this has a connected client, tell him his new position.
2119  if ( client )
2120  {
2121  client->pause();
2122  send_warmode();
2123  send_goxyz( client, this );
2124  send_owncreate( client, this );
2126  send_remove_if_hidden_ghost( zonechr, client );
2127  } );
2128  client->restart();
2129  }
2130 
2131  // Tell other connected players, if in range, about this character.
2134  realm->notify_resurrected( *this );
2135 }
2136 
2138 {
2139  Items::Item* death_shroud = create_death_shroud();
2140  death_shroud->realm = realm;
2141  if ( equippable( death_shroud ) ) // check it or passert will trigger
2142  {
2143  equip( death_shroud );
2144  send_wornitem_to_inrange( this, death_shroud );
2145  }
2146  else
2147  {
2148  ERROR_PRINT.Format( "Create Character: Failed to equip death shroud 0x{:X}\n" )
2149  << death_shroud->graphic;
2150  death_shroud->destroy();
2151  }
2152 
2153  if ( client != nullptr )
2154  {
2155  if ( opponent_ )
2156  opponent_->inform_disengaged( this );
2157 
2158  client->pause();
2159  send_warmode();
2160 
2161  // Sends the complete corpse to the client itself, so he knows where his
2162  // items went.
2163  send_full_corpse( client, corpse );
2164 
2165  send_goxyz( client, this );
2167  client->chr, [&]( Character* zonechr ) { send_create_ghost( zonechr, client ); } );
2168 
2169  client->restart();
2170  }
2171 
2172  // change self to ghost for ghosts, remove self for living
2174 
2176 
2177  if ( Clib::FileExists( "scripts/misc/chrdeath.ecl" ) )
2178  Core::start_script( "misc/chrdeath", new Module::EItemRefObjImp( corpse ),
2179  new Module::ECharacterRefObjImp( this ) );
2180 }
2181 
2183 {
2184  while ( !opponent_of.empty() )
2185  {
2186  Character* chr = *opponent_of.begin();
2187  // note that chr->set_opponent is going to remove
2188  // its entry from our opponent_of collection,
2189  // so eventually this loop will exit.
2190  chr->set_opponent( nullptr, false );
2191  }
2192 }
2193 
2195 {
2196  if ( Core::gamestate.system_hooks.can_die != nullptr )
2197  {
2198  if ( !Core::gamestate.system_hooks.can_die->call( make_mobileref( this ) ) )
2199  return;
2200  }
2201 
2202  set_current_ones( Core::gamestate.pVitalLife, vital( Core::gamestate.pVitalLife->vitalid ), 0 );
2206 
2207 
2208  u16 save_graphic = graphic;
2209 
2213  {
2214  switch ( race )
2215  {
2216  case Core::RACE_HUMAN:
2218  break;
2219  case Core::RACE_ELF:
2221  break;
2222  case Core::RACE_GARGOYLE:
2223  graphic =
2225  break;
2226  }
2227  }
2229 
2234 
2236  /* FIXME: corpse container difficulties.
2237 
2238  What am I gonna do about the corpse. I've said that
2239  MAX_CONTAINER_ITEMS can be in a container, but if I put
2240  worn items, plus what's in the backpack, in the
2241  corpse 'container' I'm going to violate this. If
2242  I put everything in a backpack in the corpse,
2243  it's too easy to grab everything.
2244  Maybe I have to limit the backpack to MAX_CONTAINER_ITEM
2245  - NUM_LAYERS or something, or grow the "send container"
2246  message to be able to hold a full backpack, plus
2247  all worn items. That doesn't work, tho, 'cause it
2248  overfills the corpse container. ick.
2249 
2250  The current solution is to put the backpack
2251  on the corpse.
2252  */
2253 
2254  Core::UCorpse* corpse = static_cast<Core::UCorpse*>( Items::Item::create( UOBJ_CORPSE ) );
2255  this->last_corpse = corpse->serial;
2256 
2257  corpse->ownerserial = this->serial;
2258  corpse->setname( "A corpse of " + name_.get() );
2259  corpse->take_contents_to_grave( acct == nullptr );
2260 
2262 
2263  corpse->color = truecolor;
2264  corpse->x = x;
2265  corpse->y = y;
2266  corpse->z = z;
2267  corpse->realm = realm;
2268  corpse->facing = facing;
2269  corpse->corpsetype = save_graphic;
2270  // corpse->dir = dir;
2272 
2274  if ( is_trading() )
2275  Core::cancel_trade( this );
2277  corpse->copyprops( *this );
2279 
2280  // Change the character's color to grey
2281  color = 0;
2283 
2284  // small lambdas to reduce the mess inside the loops
2285  auto _copy_item = [&]( Items::Item* _item ) { // copy a item into the corpse
2286  Items::Item* copy = _item->clone();
2287  copy->invisible( true );
2288  copy->movable( false );
2289  corpse->add( copy );
2290  };
2291  auto _drop_item_to_world = [&]( Items::Item* _item ) { // places the item onto the corpse coords
2292  _item->x = corpse->x;
2293  _item->y = corpse->y;
2294  _item->z = corpse->z;
2295  add_item_to_world( _item );
2297  move_item( _item, corpse->x, corpse->y, corpse->z, nullptr );
2298  };
2299 
2300  // WARNING: never ever touch or be 10000% sure what you are doing!!!!
2301  for ( unsigned layer = Core::LAYER_EQUIP__LOWEST; layer <= Core::LAYER_EQUIP__HIGHEST; ++layer )
2302  {
2303  Items::Item* item = wornitems->GetItemOnLayer( layer );
2304  if ( item == nullptr )
2305  continue;
2306  if ( item->layer == Core::LAYER_BACKPACK ) // These needs to be the first!!!!
2307  continue;
2308  // never ever touch this order
2309  // first only copy the hair layers and only these!
2310  // then check for newbie and then I dont care
2311  if ( item->layer == Core::LAYER_BEARD || item->layer == Core::LAYER_HAIR ||
2312  item->layer == Core::LAYER_FACE )
2313  {
2314  // Copies hair items onto the corpse
2315  _copy_item( item );
2316  continue;
2317  }
2318  if ( item->newbie() || item->insured() || item->no_drop() )
2319  continue;
2320  else if ( item->layer != Core::LAYER_MOUNT && item->layer != Core::LAYER_ROBE_DRESS &&
2321  !item->movable() ) // dress layer needs to be unequipped for deathrobe
2322  {
2323  _copy_item( item );
2324  continue;
2325  }
2331  if ( Core::settingsManager.ssopt.honor_unequip_script_on_death )
2332  {
2333  if ( !item->check_unequiptest_scripts() )
2334  continue;
2335  if ( !item->check_unequip_script() )
2336  continue;
2337  }
2338  else
2339  {
2340  item->check_unequip_script();
2341  }
2343  unequip( item );
2345 
2346  u8 newSlot = 1;
2347  if ( !corpse->can_add_to_slot( newSlot ) || !item->slot_index( newSlot ) )
2348  {
2349  _drop_item_to_world( item );
2350  }
2351  else
2352  {
2353  corpse->add_at_random_location( item );
2354  }
2356  }
2358 
2359  // For some reason, the backpack shows up as a small child.. So change its layer.
2360  Core::UContainer* bp = backpack();
2361  if ( bp )
2362  {
2365  bp->extract( tmp );
2367  // We set slot to 1 outside the loop. As it cycles through, this will continue
2368  // to increase. This will reduce the amount of checks to find next available
2369  // slots
2370  u8 packSlot = 1;
2371  // u8 corpseSlot = 1;
2372  while ( !tmp.empty() )
2373  {
2374  Items::Item* bp_item = ITEM_ELEM_PTR( tmp.back() );
2375  tmp.pop_back();
2376  bp_item->container = nullptr;
2377  bp_item->layer = 0;
2379  if ( ( bp_item->newbie() || bp_item->no_drop() || bp_item->use_insurance() ) &&
2380  bp->can_add( *bp_item ) )
2381  {
2382  if ( !bp->can_add_to_slot( packSlot ) || !bp_item->slot_index( packSlot ) )
2383  {
2384  _drop_item_to_world( bp_item );
2385  }
2386  else
2387  {
2388  bp->add( bp_item );
2389  }
2391  }
2392  else if ( corpse->can_add( *bp_item ) )
2393  {
2394  if ( !corpse->can_add_to_slot( packSlot ) || !bp_item->slot_index( packSlot ) )
2395  {
2396  _drop_item_to_world( bp_item );
2397  }
2398  else
2399  {
2400  corpse->add_at_random_location( bp_item );
2401  }
2403  }
2404  else
2405  {
2407  _drop_item_to_world( bp_item );
2408  }
2410  }
2411 
2413 
2414  for ( unsigned layer = Core::LAYER_EQUIP__LOWEST; layer <= Core::LAYER_EQUIP__HIGHEST; ++layer )
2415  {
2416  Items::Item* item = wornitems->GetItemOnLayer( layer );
2417  if ( item == nullptr )
2418  continue;
2419  if ( item->layer == Core::LAYER_BACKPACK ) // These needs to be the first!!!!
2420  continue;
2421  if ( item->layer == Core::LAYER_BEARD || item->layer == Core::LAYER_HAIR ||
2422  item->layer == Core::LAYER_FACE )
2423  continue;
2424  if ( item->layer != Core::LAYER_MOUNT && item->layer != Core::LAYER_ROBE_DRESS &&
2425  !item->movable() )
2426  continue;
2427  if ( ( item->newbie() || item->no_drop() || item->use_insurance() ) && bp->can_add( *item ) )
2428  {
2430  if ( Core::settingsManager.ssopt.honor_unequip_script_on_death )
2431  {
2432  if ( !item->check_unequiptest_scripts() )
2433  continue;
2434  if ( !item->check_unequip_script() )
2435  continue;
2436  }
2437  else
2438  {
2439  item->check_unequip_script();
2440  }
2442  unequip( item );
2443  item->layer = 0;
2445  if ( !bp->can_add_to_slot( packSlot ) || !item->slot_index( packSlot ) )
2446  {
2447  _drop_item_to_world( item );
2448  }
2449  else
2450  {
2451  bp->add_at_random_location( item );
2452  update_item_to_inrange( item );
2453  }
2455  }
2456  }
2458  }
2459 
2460 
2462  send_death_message( this, corpse );
2463 
2465  corpse->restart_decay_timer();
2467  add_item_to_world( corpse );
2469  send_item_to_inrange( corpse );
2470 
2472 
2474 
2475  set_opponent( nullptr );
2476 
2478 
2479  on_death( corpse );
2480 
2481  CLEAR_CHECKPOINT();
2482 }
2483 
2485 {
2486  // find_armor(); <-- MuadDib commented out, put code inside here to cut down on iter.
2487  // Figure out what's in each zone
2488  // FIXME? NZONES * NLAYERS (5 * 24 = 124) iterations.
2489  // okay, reverse, for each wornitem, for each coverage area, upgrade.
2490  // Turley: should be fixed now only iterators over armor's coverage zones instead of all zones
2491  for ( unsigned zone = 0; zone < Core::gamestate.armorzones.size(); ++zone )
2492  armor_[zone] = nullptr;
2493  // we need to reset each resist to 0, then add the base back using calc.
2495 
2496  for ( unsigned layer = Core::LAYER_EQUIP__LOWEST; layer <= Core::LAYER_EQUIP__HIGHEST; ++layer )
2497  {
2498  Items::Item* item = wornitems->GetItemOnLayer( layer );
2499  if ( item == nullptr )
2500  continue;
2501  // Let's check all items as base, and handle their element_resists.
2502  updateEquipableProperties( item );
2503 
2504  if ( item->isa( Core::UOBJ_CLASS::CLASS_ARMOR ) )
2505  {
2506  Items::UArmor* armor = static_cast<Items::UArmor*>( item );
2507  std::set<unsigned short> tmplzones = armor->tmplzones();
2508  std::set<unsigned short>::iterator itr;
2509  for ( itr = tmplzones.begin(); itr != tmplzones.end(); ++itr )
2510  {
2511  if ( ( armor_[*itr] == nullptr ) || ( armor->ar() > armor_[*itr]->ar() ) )
2512  armor_[*itr] = armor;
2513  }
2514  }
2515  }
2516 
2517  // calculate_ar(); <-- MuadDib Commented out, mixed code within ported find_armor to reduce
2518  // iter.
2519  double new_ar = 0.0;
2520  for ( unsigned zone = 0; zone < Core::gamestate.armorzones.size(); ++zone )
2521  {
2522  Items::UArmor* armor = armor_[zone];
2523  if ( armor != nullptr )
2524  {
2525  new_ar += armor->ar() * Core::gamestate.armorzones[zone].chance;
2526  }
2527  }
2528 
2529  /* add AR due to shield : parry skill / 2 is percent of AR */
2530  // FIXME: Should we allow this to be adjustable via a prop? Hrmmmmm
2531  if ( shield != nullptr )
2532  {
2533  double add =
2534  0.5 * 0.01 * shield->ar() * attribute( Core::gamestate.pAttrParry->attrid ).effective();
2535  if ( add > 1.0 )
2536  new_ar += add;
2537  else
2538  new_ar += 1.0;
2539  }
2540 
2541  new_ar += ar_mod();
2542 
2543  short s_new_ar = static_cast<short>( new_ar );
2544  if ( s_new_ar >= 0 )
2545  ar_ = s_new_ar;
2546  else
2547  ar_ = 0;
2548 
2549  if ( client != nullptr )
2550  { // CHECKME consider sending less frequently
2551  send_full_statmsg( client, this );
2552  }
2553 }
2554 
2556 {
2557  if ( item->has_fire_resist() )
2558  fire_resist( fire_resist().addToValue( item->fire_resist() ) );
2559  if ( item->has_cold_resist() )
2560  cold_resist( cold_resist().addToValue( item->cold_resist() ) );
2561  if ( item->has_energy_resist() )
2562  energy_resist( energy_resist().addToValue( item->energy_resist() ) );
2563  if ( item->has_poison_resist() )
2564  poison_resist( poison_resist().addToValue( item->poison_resist() ) );
2565  if ( item->has_physical_resist() )
2566  physical_resist( physical_resist().addToValue( item->physical_resist() ) );
2567 
2568  if ( item->has_fire_damage() )
2569  fire_damage( fire_damage().addToValue( item->fire_damage() ) );
2570  if ( item->has_cold_damage() )
2571  cold_damage( cold_damage().addToValue( item->cold_damage() ) );
2572  if ( item->has_energy_damage() )
2573  energy_damage( energy_damage().addToValue( item->energy_damage() ) );
2574  if ( item->has_poison_damage() )
2575  poison_damage( poison_damage().addToValue( item->poison_damage() ) );
2576  if ( item->has_physical_damage() )
2577  physical_damage( physical_damage().addToValue( item->physical_damage() ) );
2578 }
2579 
2581 {
2582  if ( has_fire_resist() )
2583  fire_resist( fire_resist().resetModAsValue() );
2584  if ( has_cold_resist() )
2585  cold_resist( cold_resist().resetModAsValue() );
2586  if ( has_energy_resist() )
2587  energy_resist( energy_resist().resetModAsValue() );
2588  if ( has_poison_resist() )
2589  poison_resist( poison_resist().resetModAsValue() );
2590  if ( has_physical_resist() )
2591  physical_resist( physical_resist().resetModAsValue() );
2592 
2593  if ( has_fire_damage() )
2594  fire_damage( fire_damage().resetModAsValue() );
2595  if ( has_cold_damage() )
2596  cold_damage( cold_damage().resetModAsValue() );
2597  if ( has_energy_damage() )
2598  energy_damage( energy_damage().resetModAsValue() );
2599  if ( has_poison_damage() )
2600  poison_damage( poison_damage().resetModAsValue() );
2601  if ( has_physical_damage() )
2602  physical_damage( physical_damage().resetModAsValue() );
2603 }
2604 
2606 {
2607  if ( client != nullptr )
2608  {
2609  Core::send_sysmessage( client, "Your armor coverage:" );
2610  for ( unsigned i = 0; i < armor_.size(); ++i )
2611  {
2612  std::string text = Core::gamestate.armorzones[i].name + ": ";
2613  if ( armor_[i] == nullptr )
2614  text += "Nothing";
2615  else
2616  text += armor_[i]->name();
2617  Core::send_sysmessage( client, text );
2618  }
2619  }
2620 }
2621 
2622 /* check skill: test skill, advance, reset atrophy timers, blah blah..
2623  obviously, needs work, and more parameters.
2624  */
2625 
2626 bool Character::check_skill( Core::USKILLID skillid, int difficulty, unsigned short pointvalue )
2627 {
2628  INC_PROFILEVAR( skill_checks );
2629  static bool in_here = false;
2630  if ( !in_here && Core::gamestate.system_hooks.check_skill_hook != nullptr )
2631  {
2632  in_here = true;
2634  new Module::ECharacterRefObjImp( this ), new Bscript::BLong( skillid ),
2635  new Bscript::BLong( difficulty ), new Bscript::BLong( pointvalue ) );
2636  in_here = false;
2637  return res;
2638  }
2639  else
2640  {
2641  return false;
2642  }
2643 }
2644 
2646 {
2647  if ( concealed() > cmdlevel() )
2648  concealed( cmdlevel() );
2649 }
2650 
2651 // you can only be concealed from
2652 // those of lower stature
2654 {
2655  return ( chr->concealed() > cmdlevel() );
2656 }
2657 
2658 bool Character::is_visible_to_me( const Character* chr ) const
2659 {
2660  if ( chr == nullptr )
2661  return false;
2662  if ( chr == this )
2663  return true; // I can always see myself (?)
2664  if ( is_concealed_from_me( chr ) )
2665  return false;
2666 
2667  if ( chr->realm != this->realm )
2668  return false; // noone can see across different realms.
2669  if ( !chr->logged_in() )
2670  return false;
2671  if ( chr->hidden() && !cached_settings.get( PRIV_FLAGS::SEE_HIDDEN ) )
2672  return false; // noone can see anyone hidden.
2673  if ( dead() )
2674  return true; // If I'm dead, I can see anything
2675  if ( !chr->dead() || cached_settings.get( PRIV_FLAGS::SEE_GHOSTS ) )
2676  return true; // Anyone can see the living
2677  if ( chr->warmode() )
2678  return true; // Anyone can see someone in warmode
2679  return false;
2680 };
2681 
2682 // NOTE: chr is at new position, lastx/lasty have old position.
2683 void PropagateMove( /*Client *client,*/ Character* chr )
2684 {
2685  using namespace Network;
2686  if ( chr == nullptr )
2687  return;
2688  RemoveObjectPkt msgremove( chr->serial_ext );
2689  HealthBarStatusUpdate msgpoison( chr->serial_ext, HealthBarStatusUpdate::Color::GREEN,
2690  chr->poisoned() );
2691  HealthBarStatusUpdate msginvul( chr->serial_ext, HealthBarStatusUpdate::Color::YELLOW,
2692  chr->invul() );
2694  MoveChrPkt msgmove( chr );
2695  build_owncreate( chr, msgcreate.Get() );
2696 
2698  Client* client = zonechr->client;
2699  if ( zonechr == chr )
2700  return;
2701  if ( !zonechr->is_visible_to_me( chr ) )
2702  return;
2703  /* The two characters exist, and are in range of each other.
2704  Character 'chr''s lastx and lasty coordinates are valid.
2705  SO, if lastx/lasty are out of range of client->chr, we
2706  should send a 'create' type message. If they are in range,
2707  we should just send a move.
2708  */
2709  if ( chr->move_reason == Character::MULTIMOVE )
2710  {
2711  if ( client->ClientType & Network::CLIENTTYPE_7090 )
2712  {
2713  if ( chr->poisoned() ) // if poisoned send 0x17 for newer clients
2714  msgpoison.Send( client );
2715 
2716  if ( chr->invul() ) // if invul send 0x17 for newer clients
2717  msginvul.Send( client );
2718  return;
2719  }
2720  else
2721  {
2722 // NOTE: uncomment this line to make movement smoother (no stepping anims)
2723 // but basically makes it very difficult to talk while the ship
2724 // is moving.
2725 #ifdef PERGON
2726  send_remove_character( client, chr, msgremove );
2727 #else
2728 // send_remove_character( client, chr );
2729 #endif
2730  send_owncreate( client, chr, msgcreate.Get() );
2731  if ( chr->poisoned() )
2732  msgpoison.Send( client );
2733  if ( chr->invul() )
2734  msginvul.Send( client );
2735  }
2736  }
2737  else if ( Core::inrange( zonechr->x, zonechr->y, chr->lastx, chr->lasty ) )
2738  {
2739  msgmove.Send( client );
2740  if ( chr->poisoned() )
2741  msgpoison.Send( client );
2742  if ( chr->invul() )
2743  msginvul.Send( client );
2744  }
2745  else
2746  {
2747  send_owncreate( client, chr, msgcreate.Get() );
2748  if ( chr->poisoned() )
2749  msgpoison.Send( client );
2750  if ( chr->invul() )
2751  msginvul.Send( client );
2752  }
2753  } );
2754 
2755  // iter over all old in range players and send remove
2757  chr->lastx, chr->lasty, chr->realm, RANGE_VISUAL, [&]( Character* zonechr ) {
2758  Client* client = zonechr->client;
2759  if ( !zonechr->is_visible_to_me( chr ) )
2760  return;
2761 
2762  if ( Core::inrange( zonechr, chr ) ) // already handled
2763  return;
2764  // if we just walked out of range of this character, send its
2765  // client a remove object, or else a ghost character will remain.
2766  send_remove_character( client, chr, msgremove );
2767  } );
2768 }
2769 
2770 void Character::getpos_ifmove( Core::UFACING i_facing, unsigned short* px, unsigned short* py )
2771 {
2772  *px = x + Core::move_delta[i_facing].xmove;
2773  *py = y + Core::move_delta[i_facing].ymove;
2774 }
2775 
2777 {
2778  THREAD_CHECKPOINT( tasks, 800 );
2779  INFO_PRINT_TRACE( 20 ) << "swing_task_func(0x" << fmt::hexu( chr->serial ) << ")\n";
2781  chr->check_attack_after_move();
2782  THREAD_CHECKPOINT( tasks, 899 );
2783 }
2784 
2786 {
2787  INFO_PRINT_TRACE( 18 ) << "schedule_attack(0x" << fmt::hexu( this->serial ) << ")\n";
2788  // we'll get here with a swing_task already set, if
2789  // while in an adjacent cell to your opponent, you turn/move
2790  // while waiting for your timeout.
2791  if ( swing_task == nullptr )
2792  {
2793  unsigned int weapon_speed = weapon->speed();
2794  unsigned int weapon_delay = weapon->delay();
2795  Core::polclock_t clocks;
2796 
2797  if ( !weapon_delay )
2798  {
2799  INFO_PRINT_TRACE( 19 ) << "clocks[speed] = (" << Core::POLCLOCKS_PER_SEC << "*15000)/(("
2800  << dexterity() << "+100)*" << weapon_speed << ")\n";
2801 
2802  clocks = Core::POLCLOCKS_PER_SEC * static_cast<Core::polclock_t>( 15000L );
2803  clocks /= ( dexterity() + 100 ) * weapon_speed;
2804  }
2805  else
2806  {
2807  int delay_sum = weapon_delay + delay_mod();
2808  if ( delay_sum < 0 )
2809  delay_sum = 0;
2810 
2811  INFO_PRINT_TRACE( 19 ) << "clocks[delay] = ((" << weapon_delay << "+" << delay_mod() << "="
2812  << delay_sum << ")*" << Core::POLCLOCKS_PER_SEC << ")/1000\n";
2813 
2814  clocks = ( delay_sum * Core::POLCLOCKS_PER_SEC ) / 1000;
2815  }
2816 
2817  if ( clocks < ( Core::POLCLOCKS_PER_SEC / 5 ) )
2818  {
2819  INFO_PRINT_TRACE( 20 ) << name() << " attack timer: " << clocks << "\n";
2820  }
2821  INFO_PRINT_TRACE( 19 ) << "clocks=" << clocks << "\n";
2822 
2824  swing_task_func, this );
2825  }
2826 }
2827 
2829 {
2830  INFO_PRINT_TRACE( 15 ) << "reset_swing_timer(0x" << fmt::hexu( this->serial ) << ")\n";
2832 
2834  if ( swing_task )
2835  swing_task->cancel();
2836 
2837  if ( opponent_ || !opponent_of.empty() )
2838  {
2839  schedule_attack();
2840  }
2841 }
2842 
2844 {
2845  INFO_PRINT_TRACE( 15 ) << "manual_set_swing_timer(0x" << fmt::hexu( this->serial )
2846  << ") delay: " << clocks << "\n";
2848 
2850  if ( swing_task )
2851  swing_task->cancel();
2852 
2853  if ( opponent_ || !opponent_of.empty() )
2854  {
2856  swing_task_func, this );
2857  return true;
2858  }
2859  else
2860  return false;
2861 }
2862 
2863 /* The highlighted character is:
2864  Your selected_opponent, if you have one.
2865  If not, then the first char that has you as their opponent.
2866  Or, noone.
2867  */
2869 {
2870  if ( opponent_ != nullptr )
2871  return opponent_;
2872  else if ( !opponent_of.empty() )
2873  return *opponent_of.begin();
2874  else
2875  return nullptr;
2876 }
2877 
2879 {
2880  passert( who != nullptr );
2881  if ( Core::settingsManager.combat_config.scripted_attack_checks )
2882  {
2883  INFO_PRINT_TRACE( 21 ) << "is_attackable(0x" << fmt::hexu( this->serial ) << ",0x"
2884  << fmt::hexu( who->serial ) << "): will be handled by combat hook.\n";
2885  return true;
2886  }
2887  else
2888  {
2889  INFO_PRINT_TRACE( 21 ) << "is_attackable(0x" << fmt::hexu( this->serial ) << ",0x"
2890  << fmt::hexu( who->serial ) << "):\n"
2891  << " who->dead: " << who->dead() << "\n"
2892  << " wpn->inrange: " << weapon->in_range( this, who ) << "\n"
2893  << " hidden: " << hidden() << "\n"
2894  << " who->hidden: " << who->hidden() << "\n"
2895  << " concealed: " << is_concealed_from_me( who ) << "\n";
2896  if ( who->dead() )
2897  return false;
2898  else if ( !weapon->in_range( this, who ) )
2899  return false;
2901  return false;
2902  else if ( who->hidden() && !cached_settings.get( PRIV_FLAGS::ATTACK_HIDDEN ) )
2903  return false;
2904  else if ( is_concealed_from_me( who ) )
2905  return false;
2906  else if ( !realm->has_los( *this, *who ) )
2907  return false;
2908  else
2909  return true;
2910  }
2911 }
2912 
2914 {
2915  if ( opponent_ != nullptr )
2916  {
2917  INFO_PRINT_TRACE( 20 ) << "get_attackable_opponent(0x" << fmt::hexu( this->serial )
2918  << "): checking opponent, 0x" << fmt::hexu( opponent_->serial ) << "\n";
2919  if ( is_attackable( opponent_ ) )
2920  return opponent_;
2921  }
2922 
2923  if ( !opponent_of.empty() )
2924  {
2925  for ( auto& who : opponent_of )
2926  {
2927  INFO_PRINT_TRACE( 20 ) << "get_attackable_opponent(0x" << fmt::hexu( this->serial )
2928  << "): checking opponent_of, 0x" << fmt::hexu( who->serial ) << "\n";
2929  if ( is_attackable( who ) )
2930  return who;
2931  }
2932  }
2933 
2934  return nullptr;
2935 }
2936 
2938 {
2939  if ( client != nullptr && has_active_client() )
2940  {
2941  Character* opponent = get_opponent();
2942 
2944  if ( opponent != nullptr )
2945  msg->Write<u32>( opponent->serial_ext );
2946  else
2947  msg->offset += 4;
2948  msg.Send( client );
2949  }
2950 }
2951 
2953 {
2954  // do nothing
2955 }
2956 
2958 {
2959  // someone has just disengaged. If we don't have an explicit opponent,
2960  // pick one of those that has us targetted as the highlight character.
2961  if ( opponent_ == nullptr )
2962  send_highlight();
2963 }
2964 
2966 {
2967  // someone has targetted us. If we don't have an explicit opponent,
2968  // pick one of those that has us targetted as the highlight character.
2969  if ( opponent_ == nullptr )
2970  send_highlight();
2971 }
2972 
2974 {
2975  // virtual that does nothing at character level, but fires event for NPCs
2976 }
2977 
2979 {
2980  // virtual that does nothing at character level, but fires event for NPCs
2981 }
2982 
2984 {
2985  // virtual that does nothing at character level, but fires event for NPCs
2986 }
2987 
2989 {
2990  // consider moving PropagateMove here!
2991 }
2993 
2994 void Character::set_opponent( Character* new_opponent, bool inform_old_opponent )
2995 {
2996  INFO_PRINT_TRACE( 12 ) << "set_opponent(0x" << fmt::hexu( this->serial ) << ",0x"
2997  << fmt::hex( new_opponent->serial ) << ")\n";
2998  if ( new_opponent != nullptr )
2999  {
3000  if ( new_opponent->dead() )
3001  return;
3002 
3003  if ( !warmode() && ( script_isa( Core::POLCLASS_NPC ) || has_active_client() ) )
3004  set_warmode( true );
3005  }
3006 
3007  if ( opponent_ != nullptr )
3008  {
3009  opponent_->opponent_of.erase( this );
3010  // Turley 05/26/09 no need to send disengaged event on shutdown
3011  if ( !Clib::exit_signalled )
3012  {
3013  if ( inform_old_opponent && opponent_ != nullptr )
3014  opponent_->inform_disengaged( this );
3015  }
3016  }
3017 
3018  opponent_ = new_opponent;
3019 
3020 
3021  // Turley 05/26/09 possible shutdown crashfix during cleanup
3022  // (inside schedule_attack() the rest is also senseless on shutdowncleanup)
3023  if ( !Clib::exit_signalled )
3024  {
3026 
3027  if ( opponent_ != nullptr )
3028  {
3030  if ( opponent_->get_opponent() == nullptr )
3032 
3033  opponent_->opponent_of.insert( this );
3034 
3035  opponent_->inform_engaged( this );
3036 
3038  }
3039 
3040  send_highlight();
3041  }
3042 }
3043 
3045 {
3046  // test for setting to same so swing timer doesn't reset
3047  // if you double-click the same guy over and over
3048  if ( opponent_ == nullptr || opponent_->serial != opp_serial )
3049  {
3050  Character* new_opponent = Core::find_character( opp_serial );
3051  if ( new_opponent != nullptr )
3052  {
3053  if ( realm != new_opponent->realm )
3054  return;
3055  set_opponent( new_opponent );
3056  }
3057  }
3058 }
3059 
3061 {
3062  time_t new_disable_time = Core::poltime() + seconds;
3063  if ( new_disable_time > disable_regeneration_until )
3064  disable_regeneration_until = new_disable_time;
3065 }
3066 
3068 {
3069  return mob_flags_.get( MOB_FLAGS::WARMODE );
3070 }
3071 
3072 void Character::set_warmode( bool i_warmode )
3073 {
3074  if ( Core::gamestate.system_hooks.warmode_change )
3076  new Bscript::BLong( i_warmode ) );
3077 
3078  if ( warmode() != i_warmode )
3079  {
3081  }
3082 
3083  mob_flags_.change( MOB_FLAGS::WARMODE, i_warmode );
3084  if ( i_warmode == false )
3085  {
3086  set_opponent( nullptr );
3087  }
3089 
3090  if ( dead() )
3091  {
3092  if ( warmode() ) // if entered warmode, display me now
3093  {
3095  }
3096  else // if leaving warmode, I go invisible.
3097  {
3099  }
3100  }
3101  else
3102  {
3103  Network::MoveChrPkt msgmove( this );
3105  if ( chr == this )
3106  return;
3107  msgmove.Send( chr->client );
3108  } );
3109  }
3110 }
3111 
3113 {
3114  return attribute( weapon->attribute().attrid );
3115 }
3116 
3117 unsigned short Character::random_weapon_damage() const
3118 {
3119  return weapon->get_random_damage();
3120 }
3121 
3122 unsigned short Character::min_weapon_damage() const
3123 {
3124  return weapon->descriptor().damage_dice.min_value() + weapon->damage_mod();
3125 }
3126 
3127 unsigned short Character::max_weapon_damage() const
3128 {
3129  return weapon->descriptor().damage_dice.max_value() + weapon->damage_mod();
3130 }
3131 
3133 {
3134  if ( !weapon->is_intrinsic() && !weapon->is_projectile() )
3135  {
3137  }
3138 }
3139 
3141 {
3142  double f = Clib::random_double( Core::gamestate.armor_zone_chance_sum );
3143  for ( unsigned zone = 0; zone < Core::gamestate.armorzones.size(); ++zone )
3144  {
3145  f -= Core::gamestate.armorzones[zone].chance;
3146  if ( f <= 0.0 )
3147  {
3148  return armor_[zone];
3149  }
3150  }
3151  return nullptr;
3152 }
3153 
3155 {
3156  if ( on_mount() )
3157  return weapon->mounted_anim();
3158  else
3159  return weapon->anim();
3160 }
3161 
3163 {
3164  if ( weapon->is_projectile() )
3165  {
3166  // 234 is hit, 238 is miss??
3168  play_moving_effect( this, target, weapon->projectile_anim(),
3169  9, // Speed (??)
3170  0, // Loop
3171  0 ); // Explode
3172  if ( graphic >= UOBJ_HUMAN_MALE )
3173  {
3175  }
3176  else
3177  {
3179  }
3180  }
3181  else if ( graphic >= UOBJ_HUMAN_MALE )
3182  {
3184  }
3185  else
3186  {
3188  }
3189 }
3190 
3192 {
3193  unsigned short sound = weapon->hit_sound();
3194  if ( sound )
3195  play_sound_effect( this, sound );
3196 }
3197 
3199 {
3200  unsigned short sound = weapon->miss_sound();
3201  if ( sound )
3202  play_sound_effect( this, sound );
3203 }
3204 
3206 {
3207  if ( gender == Core::GENDER_MALE )
3210 }
3211 
3213 {
3214  if ( Core::settingsManager.combat_config.core_hit_sounds )
3215  {
3217  }
3218  if ( objtype_ >= UOBJ_HUMAN_MALE )
3220 }
3221 
3222 
3223 void Character::attack( Character* opponent )
3224 {
3225  INC_PROFILEVAR( combat_operations );
3226 
3227  if ( Core::gamestate.system_hooks.attack_hook )
3228  {
3229  if ( Core::gamestate.system_hooks.attack_hook->call(
3230  new Module::ECharacterRefObjImp( this ),
3231  new Module::ECharacterRefObjImp( opponent ) ) )
3232  return;
3233  }
3234 
3235  if ( Core::settingsManager.watch.combat )
3236  INFO_PRINT << name() << " attacks " << opponent->name() << "\n";
3237 
3238  if ( weapon->is_projectile() )
3239  {
3240  Core::UContainer* bp = backpack();
3241  if ( ( bp == nullptr ) || ( weapon->consume_projectile( bp ) == false ) )
3242  {
3243  // 04/2007 - MuadDib
3244  // Range through wornitems to find containers and check
3245  // here also if backpack fails. Use the mainpack first this way.
3246  bool projectile_check = false;
3247  for ( unsigned layer = Core::LAYER_EQUIP__LOWEST; layer <= Core::LAYER_EQUIP__HIGHEST;
3248  layer++ )
3249  {
3250  Items::Item* item = wornitems->GetItemOnLayer( layer );
3251  if ( item )
3252  {
3253  if ( item != nullptr && item->script_isa( Core::POLCLASS_CONTAINER ) )
3254  {
3255  if ( layer != Core::LAYER_HAIR && layer != Core::LAYER_FACE &&
3256  layer != Core::LAYER_BEARD && layer != Core::LAYER_BACKPACK &&
3257  layer != Core::LAYER_MOUNT )
3258  {
3259  Core::UContainer* cont = static_cast<Core::UContainer*>( item );
3260  if ( weapon->consume_projectile( cont ) == true )
3261  {
3262  projectile_check = true;
3263  break;
3264  }
3265  }
3266  }
3267  }
3268  }
3269  // I'm out of projectiles.
3270  if ( projectile_check == false )
3271  return;
3272  }
3273  }
3274 
3275  repsys_on_attack( opponent );
3276  repsys_on_damage( opponent );
3277 
3278  do_attack_effects( opponent );
3279 
3280  if ( Core::gamestate.system_hooks.combat_advancement_hook )
3281  {
3284  new Module::ECharacterRefObjImp( opponent ) );
3285  }
3286 
3287  double hit_chance = ( weapon_attribute().effective() + 50.0 ) /
3288  ( 2.0 * ( opponent->weapon_attribute().effective() + 50.0 ) );
3289  hit_chance += hitchance_mod() * 0.001f;
3290  hit_chance -= opponent->evasionchance_mod() * 0.001f;
3291  if ( Core::settingsManager.watch.combat )
3292  INFO_PRINT << "Chance to hit: " << hit_chance << ": ";
3293  if ( Clib::random_double( 1.0 ) < hit_chance )
3294  {
3295  if ( Core::settingsManager.watch.combat )
3296  INFO_PRINT << "Hit!\n";
3298 
3299  double damage = random_weapon_damage();
3300  damage_weapon();
3301 
3302  if ( Core::settingsManager.watch.combat )
3303  INFO_PRINT << "Base damage: " << damage << "\n";
3304 
3305  double damage_multiplier = attribute( Core::gamestate.pAttrTactics->attrid ).effective() + 50;
3306  damage_multiplier += strength() * 0.20f;
3307  damage_multiplier *= 0.01f;
3308 
3309  damage *= damage_multiplier;
3310 
3311  if ( Core::settingsManager.watch.combat )
3312  INFO_PRINT << "Damage multiplier due to tactics/STR: " << damage_multiplier
3313  << " Result: " << damage << "\n";
3314 
3315  if ( opponent->shield != nullptr )
3316  {
3317  if ( Core::gamestate.system_hooks.parry_advancement_hook )
3318  {
3321  new Module::ECharacterRefObjImp( opponent ),
3322  new Module::EItemRefObjImp( opponent->shield ) );
3323  }
3324 
3325  double parry_chance =
3326  opponent->attribute( Core::gamestate.pAttrParry->attrid ).effective() / 200.0;
3327  if ( Core::settingsManager.watch.combat )
3328  INFO_PRINT << "Parry Chance: " << parry_chance << ": ";
3329  if ( Clib::random_double( 1.0 ) < parry_chance )
3330  {
3331  if ( Core::settingsManager.watch.combat )
3332  INFO_PRINT << opponent->shield->ar() << " hits deflected\n";
3333  if ( Core::settingsManager.combat_config.display_parry_success_messages &&
3334  opponent->client )
3335  Core::send_sysmessage( opponent->client, "You successfully parried the attack!" );
3336 
3337  damage -= opponent->shield->ar();
3338  if ( damage < 0 )
3339  damage = 0;
3340  }
3341  else
3342  {
3343  if ( Core::settingsManager.watch.combat )
3344  INFO_PRINT << "failed.\n";
3345  }
3346  }
3347  if ( weapon->hit_script().empty() )
3348  {
3349  opponent->apply_damage( damage, this, true,
3350  Core::settingsManager.combat_config.send_damage_packet );
3351  }
3352  else
3353  {
3354  run_hit_script( opponent, damage );
3355  }
3356  }
3357  else
3358  {
3359  if ( Core::settingsManager.watch.combat )
3360  INFO_PRINT << "Miss!\n";
3361  opponent->on_swing_failure( this );
3363  if ( Core::gamestate.system_hooks.hitmiss_hook )
3364  {
3366  new Module::ECharacterRefObjImp( this ), new Module::ECharacterRefObjImp( opponent ) );
3367  }
3368  }
3369 }
3370 
3372 {
3374  Character* opponent = get_attackable_opponent();
3376  INFO_PRINT_TRACE( 20 ) << "check_attack_after_move(0x" << fmt::hexu( this->serial )
3377  << "): opponent is 0x" << fmt::hexu( opponent->serial ) << "\n";
3378  if ( opponent != nullptr && // and I have an opponent
3379  !dead() && // If I'm not dead
3380  ( Core::settingsManager.combat_config.attack_while_frozen ||
3381  ( !paralyzed() && !frozen() ) ) )
3382  {
3384  if ( mob_flags_.get( MOB_FLAGS::READY_TO_SWING ) ) // and I can swing now,
3385  { // do so.
3387  if ( Core::settingsManager.combat_config.send_swing_packet && client != nullptr )
3388  send_fight_occuring( client, opponent );
3389  attack( opponent );
3393  }
3394  else
3395  {
3397  INFO_PRINT_TRACE( 20 ) << "not ready to swing\n";
3398  schedule_attack();
3400  }
3401  }
3403 }
3404 
3405 
3407 {
3408  auto light_unil = lightoverride_until();
3409  if ( light_unil < Core::read_gameclock() && light_unil != ~0u )
3410  {
3411  lightoverride_until( 0 );
3412  lightoverride( -1 );
3413  }
3415  !has_lightoverride() )
3416  return;
3417 
3418  int newlightlevel;
3419  if ( has_lightoverride() )
3420  newlightlevel = lightoverride();
3421  else
3422  {
3423  // dave 12-22 check for no regions
3425  if ( light_region != nullptr )
3426  newlightlevel = light_region->lightlevel;
3427  else
3429  }
3430 
3431  if ( newlightlevel != client->gd->lightlevel )
3432  {
3433  Core::send_light( client, newlightlevel );
3434  client->gd->lightlevel = newlightlevel;
3435  }
3436 }
3437 
3439 {
3440  Core::JusticeRegion* cur_justice_region = client->gd->justice_region;
3441  Core::JusticeRegion* new_justice_region =
3443 
3444  if ( cur_justice_region != new_justice_region )
3445  {
3446  if ( cur_justice_region != nullptr )
3447  cur_justice_region->RunLeaveScript( client->chr );
3448  if ( new_justice_region != nullptr )
3449  new_justice_region->RunEnterScript( client->chr );
3450 
3451  // print 'leaving' message
3452  bool printmsgs;
3453  if ( cur_justice_region != nullptr && new_justice_region != nullptr &&
3454  cur_justice_region->entertext() == new_justice_region->entertext() &&
3455  cur_justice_region->leavetext() == new_justice_region->leavetext() )
3456  {
3457  printmsgs = false;
3458  }
3459  else
3460  {
3461  printmsgs = true;
3462  }
3463 
3464  if ( printmsgs && cur_justice_region )
3465  {
3466  const std::string& leavetext = cur_justice_region->leavetext();
3467  if ( !leavetext.empty() )
3468  {
3469  Core::send_sysmessage( client, leavetext );
3470  }
3471  }
3472 
3473  client->gd->justice_region = new_justice_region;
3474 
3475  if ( new_justice_region && new_justice_region->RunNoCombatCheck( client ) == true )
3476  {
3477  Character* opp2 = get_opponent();
3478  if ( ( opp2 != nullptr && opp2->client ) )
3479  {
3480  opp2->opponent_of.erase( client->chr );
3481  opp2->set_opponent( nullptr, true );
3482  opp2->schedule_attack();
3483  opp2->opponent_ = nullptr;
3484  opp2->clear_opponent_of();
3485  set_opponent( nullptr, true );
3486  if ( swing_task != nullptr )
3487  swing_task->cancel();
3488  }
3489  }
3490 
3491 
3492  // print 'entering' message
3493  // handle nocombat while we have entered.
3494  if ( printmsgs && new_justice_region )
3495  {
3496  const std::string& entertext = new_justice_region->entertext();
3497  if ( !entertext.empty() )
3498  {
3499  Core::send_sysmessage( client, entertext );
3500  }
3501  }
3502  }
3503 }
3504 
3506 {
3507  Core::MusicRegion* cur_music_region = client->gd->music_region;
3508  Core::MusicRegion* new_music_region = Core::gamestate.musicdef->getregion( x, y, realm );
3509 
3510  // may want to consider changing every n minutes, too, even if region didn't change
3511  if ( cur_music_region != new_music_region )
3512  {
3513  client->gd->music_region = new_music_region;
3514  if ( new_music_region != nullptr )
3515  {
3516  Core::send_midi( client, new_music_region->getmidi() );
3517  }
3518  else
3519  {
3520  Core::send_midi( client, 0 );
3521  }
3522  }
3523 }
3524 
3525 void Character::check_weather_region_change( bool force ) // dave changed 5/26/03 - use force
3526  // boolean if current weather region
3527  // changed type/intensity
3528 {
3529  Core::WeatherRegion* cur_weather_region = client->gd->weather_region;
3530  Core::WeatherRegion* new_weather_region = Core::gamestate.weatherdef->getregion( x, y, realm );
3531 
3532  // eric 5/31/03: I don't think this is right. it's possible to go from somewhere that has no
3533  // weather region,
3534  // and to walk to somewhere that doesn't have a weather region.
3535  //
3536  if ( force || ( cur_weather_region != new_weather_region ) )
3537  {
3538  if ( new_weather_region != nullptr && new_weather_region->lightoverride != -1 &&
3539  !has_lightoverride() )
3540  {
3541  Core::send_light( client, new_weather_region->lightoverride );
3542  client->gd->lightlevel = new_weather_region->lightoverride;
3543  }
3544 
3545  // eric removed this 5/31/03, it's calling itself recursively:
3546  // move_character_to -> tellmove -> check_region_changes -> check_weather_region_change (here,
3547  // doh)
3548  // if you need to send the client something special, just do it.
3549  // move_character_to(this,x,y,z,0); //dave added 5/26/03: client doesn't refresh properly
3550  // without a teleport :| and send_goxyz causes weather effects to stop if character is
3551  // walking/running too
3552 
3553  if ( new_weather_region )
3554  {
3555  Core::send_weather( client, new_weather_region->weathertype, new_weather_region->severity,
3556  new_weather_region->aux );
3557  }
3558  else
3559  {
3560  Core::send_weather( client, 0xff, 0, 0 ); // turn off
3561  }
3562  client->gd->weather_region = new_weather_region;
3563  }
3564 }
3565 
3567 {
3568  if ( client != nullptr )
3569  {
3571 
3573 
3575 
3577  }
3578 }
3579 
3581 {
3582  wornitems->x = x;
3583  wornitems->y = y;
3584  wornitems->z = z;
3585  wornitems->realm = realm;
3586 }
3587 
3589 {
3590  if ( Core::gamestate.system_hooks.un_hide != nullptr )
3591  {
3592  if ( !Core::gamestate.system_hooks.un_hide->call( make_mobileref( this ) ) )
3593  return;
3594  }
3595 
3596  hidden( false );
3597  if ( is_visible() )
3598  {
3599  if ( client != nullptr )
3600  send_owncreate( client, this );
3601 
3603  if ( chr == this )
3604  return;
3605  if ( !chr->is_visible_to_me( this ) )
3606  return;
3607  send_owncreate( chr->client, this );
3608  } );
3609 
3610  realm->notify_unhid( *this );
3611  }
3612 }
3613 
3614 void Character::set_stealthsteps( unsigned short newval )
3615 {
3616  stealthsteps_ = newval;
3617 }
3618 
3620 {
3626 }
3627 
3628 /*
3629  This isn't quite right, for hidden characters.
3630  What normally happens:
3631  1) Client sends "use skill hiding"
3632  2) Server sends "77 move" with the hidden flag set.
3633  3) client sends move-request
3634  4) server sends move-approve
3635  5) server sense "78 create" message with hidden flag clear, at new posn.
3636 
3637  We're sending the "78 create" _before_ the move-approve.
3638  */
3639 
3641 {
3642  if ( can_freemove() )
3643  return true;
3644 
3645  if ( frozen() || paralyzed() )
3646  {
3647  if ( client != nullptr )
3648  {
3649  if ( frozen() )
3650  private_say_above( this, this, "I am frozen and cannot move." );
3651  else if ( paralyzed() )
3652  private_say_above( this, this, "I am paralyzed and cannot move." );
3653  }
3654  return false;
3655  }
3656 
3657  if ( Core::settingsManager.ssopt.movement_uses_stamina &&
3658  vital( Core::gamestate.pVitalStamina->vitalid ).current_ones() == 0 && !dead() )
3659  {
3660  private_say_above( this, this, "You are too fatigued to move." );
3661  return false;
3662  }
3663 
3664  return true;
3665 }
3666 
3667 
3668 bool Character::face( Core::UFACING i_facing, int flags )
3669 {
3670  if ( ( flags & 1 ) == 0 )
3671  {
3672  if ( !can_face( i_facing ) )
3673  return false;
3674  }
3675 
3676  if ( i_facing != facing )
3677  {
3678  setfacing( static_cast<u8>( i_facing ) );
3679 
3680  if ( Core::settingsManager.combat_config.reset_swing_onturn &&
3684  {
3685  if ( stealthsteps_ == 0 )
3686  unhide();
3687  else
3688  stealthsteps_--;
3689  }
3690  }
3691 
3692  return true;
3693 }
3694 
3695 
3696 bool Character::CustomHousingMove( unsigned char i_dir )
3697 {
3698  passert( facing < 8 );
3699 
3701  if ( multi != nullptr )
3702  {
3703  Multi::UHouse* house = multi->as_house();
3704  if ( house != nullptr )
3705  {
3706  Core::UFACING i_facing = static_cast<Core::UFACING>( i_dir & PKTIN_02_FACING_MASK );
3707  if ( i_facing != facing )
3708  {
3709  setfacing( static_cast<u8>( i_facing ) );
3710  set_dirty();
3711  dir = i_dir;
3712  return true;
3713  }
3714  else
3715  {
3716  s8 newz = house->z +
3718  u16 newx = x + Core::move_delta[facing].xmove;
3719  u16 newy = y + Core::move_delta[facing].ymove;
3720  const Multi::MultiDef& def = house->multidef();
3721  if ( newx > ( house->x + def.minrx ) && newx <= ( house->x + def.maxrx ) &&
3722  newy > ( house->y + def.minry ) && newy <= ( house->y + def.maxry ) )
3723  {
3724  x = static_cast<u16>( newx );
3725  y = static_cast<u16>( newy );
3726  z = static_cast<s8>( newz );
3727  MoveCharacterWorldPosition( lastx, lasty, x, y, this, nullptr );
3728 
3729  position_changed();
3730  set_dirty();
3731  return true;
3732  }
3733  }
3734  }
3735  }
3736  return false;
3737 }
3738 
3739 //************************************
3740 // Method: move
3741 // FullName: Character::move
3742 // Access: public
3743 // Returns: bool
3744 // Qualifier:
3745 // Parameter: unsigned char i_dir
3746 //************************************
3747 bool Character::move( unsigned char i_dir )
3748 {
3749  lastx = x;
3750  lasty = y;
3751  lastz = z;
3752 
3753  // if currently building a house chr can move free inside the multi
3754  if ( is_house_editing() )
3755  return CustomHousingMove( i_dir );
3756 
3757  u8 oldFacing = facing;
3758 
3759  Core::UFACING i_facing = static_cast<Core::UFACING>( i_dir & PKTIN_02_FACING_MASK );
3760  if ( !face( i_facing ) )
3761  return false;
3762 
3763  // If we're a player, and we used our "move" command to turn,
3764  // we want to skip the meat of this function
3765  if ( ( script_isa( Core::POLCLASS_NPC ) ) || ( facing == oldFacing ) )
3766  {
3767  if ( facing & 1 ) // check if diagonal movement is allowed -- Nando (2009-02-26)
3768  {
3769  short new_z;
3770  u8 tmp_facing = ( facing + 1 ) & 0x7;
3771  unsigned short tmp_newx = x + Core::move_delta[tmp_facing].xmove;
3772  unsigned short tmp_newy = y + Core::move_delta[tmp_facing].ymove;
3773 
3774  // needs to save because if only one direction is blocked, it shouldn't block ;)
3775  bool walk1 =
3776  realm->walkheight( this, tmp_newx, tmp_newy, z, &new_z, nullptr, nullptr, nullptr );
3777 
3778  tmp_facing = ( facing - 1 ) & 0x7;
3779  tmp_newx = x + Core::move_delta[tmp_facing].xmove;
3780  tmp_newy = y + Core::move_delta[tmp_facing].ymove;
3781 
3782  if ( !walk1 &&
3783  !realm->walkheight( this, tmp_newx, tmp_newy, z, &new_z, nullptr, nullptr, nullptr ) )
3784  return false;
3785  }
3786 
3787  unsigned short newx = x + Core::move_delta[facing].xmove;
3788  unsigned short newy = y + Core::move_delta[facing].ymove;
3789 
3790  // FIXME consider consolidating with similar code in UOEMOD.CPP
3791  short newz;
3792  Multi::UMulti* supporting_multi;
3793  Items::Item* walkon_item;
3794 
3795  short current_boost = gradual_boost;
3796  if ( !realm->walkheight( this, newx, newy, z, &newz, &supporting_multi, &walkon_item,
3797  &current_boost ) )
3798  return false;
3799 
3800  remote_containers_.clear();
3801 
3802  if ( !CheckPushthrough() )
3803  return false;
3804 
3805  if ( !can_freemove() && Core::settingsManager.ssopt.movement_uses_stamina && !dead() )
3806  {
3807  int carry_perc = weight() * 100 / carrying_capacity();
3808  unsigned short tmv = movecost(
3809  this, carry_perc, ( i_dir & PKTIN_02_DIR_RUNNING_BIT ) ? true : false, on_mount() );
3810  VitalValue& stamina = vital( Core::gamestate.pVitalStamina->vitalid );
3811  // u16 old_stamina = stamina.current_ones();
3812  if ( !consume( Core::gamestate.pVitalStamina, stamina, tmv ) )
3813  {
3814  private_say_above( this, this, "You are too fatigued to move." );
3815  return false;
3816  }
3817  }
3818 
3819  // Maybe have a flag for this in servspecopt?
3822 
3823  x = static_cast<u16>( newx );
3824  y = static_cast<u16>( newy );
3825  z = static_cast<s8>( newz );
3826 
3827  if ( on_mount() && !script_isa( Core::POLCLASS_NPC ) )
3828  {
3829  mountedsteps_++;
3830  }
3831  // FIXME: Need to add Walkon checks for multi right here if type is house.
3832  if ( supporting_multi != nullptr )
3833  {
3834  supporting_multi->register_object( this );
3835  Multi::UHouse* this_house = supporting_multi->as_house();
3836  if ( this->registered_house == 0 )
3837  {
3838  this->registered_house = supporting_multi->serial;
3839 
3840  if ( this_house != nullptr )
3841  this_house->walk_on( this );
3842  }
3843  }
3844  else
3845  {
3846  if ( registered_house > 0 )
3847  {
3849  if ( multi != nullptr )
3850  {
3851  multi->unregister_object( (UObject*)this );
3852  }
3853  registered_house = 0;
3854  }
3855  }
3856 
3857  gradual_boost = current_boost;
3858  MoveCharacterWorldPosition( lastx, lasty, x, y, this, nullptr );
3859 
3860  position_changed();
3861  if ( walkon_item != nullptr )
3862  {
3863  walkon_item->walk_on( this );
3864  }
3865 
3866  if ( hidden() )
3867  {
3868  if ( ( ( i_dir & PKTIN_02_DIR_RUNNING_BIT ) &&
3870  ( stealthsteps_ == 0 ) )
3871  unhide();
3872  else if ( stealthsteps_ )
3873  --stealthsteps_;
3874  }
3875 
3876  if ( Core::gamestate.system_hooks.ouch_hook != nullptr )
3877  {
3878  if ( ( lastz - z ) > 21 )
3879  Core::gamestate.system_hooks.ouch_hook->call(
3880  make_mobileref( this ), new Bscript::BLong( lastx ), new Bscript::BLong( lasty ),
3881  new Bscript::BLong( lastz ) );
3882  }
3883  }
3884 
3885  set_dirty();
3886  dir = i_dir;
3887 
3888  return true;
3889 
3890 
3891  // this would be a great place for tellmove(), except that
3892  // we want to send the move acknowledge to the client before
3893  // sending the move/create messages to the neighboring clients.
3894 
3895  // Why? Maybe to give the best response time to the client.
3896 
3897  // may want to rethink this, since it's starting to complicate
3898  // things.
3899 }
3900 
3902 {
3903  // Commented out the explicit backpack handling, should be handled
3904  // automagically by wormitems realm handling. There is a slim
3905  // possibility that backpacks might be assigned to a character but
3906  // not be a worn item? If this is the case, that will be broken.
3907  // backpack()->realm = realm;
3908  // backpack()->for_each_item(setrealm, (void*)realm);
3909  wornitems->for_each_item( Core::setrealm, (void*)realm );
3910  if ( has_gotten_item() )
3911  gotten_item()->realm = realm;
3912  if ( trading_cont.get() )
3913  trading_cont->realm = realm;
3914 
3915  if ( has_active_client() )
3916  {
3917  // these are important to keep here in this order
3920  if ( Core::settingsManager.ssopt.core_sends_season )
3924  }
3925 }
3926 
3928 {
3929  if ( !can_freemove() && Core::gamestate.system_hooks.pushthrough_hook != nullptr )
3930  {
3931  unsigned short newx = x + Core::move_delta[facing].xmove;
3932  unsigned short newy = y + Core::move_delta[facing].ymove;
3933  auto mobs = std::unique_ptr<Bscript::ObjArray>();
3934 
3936  newx, newy, realm, 0, [&]( Mobile::Character* _chr ) {
3937  if ( _chr->z >= z - 10 && _chr->z <= z + 10 && !_chr->dead() &&
3938  ( is_visible_to_me( _chr ) ||
3939  _chr->hidden() ) ) // add hidden mobs even if they're not visible to me
3940  {
3941  if ( !mobs )
3942  mobs.reset( new Bscript::ObjArray );
3943  mobs->addElement( make_mobileref( _chr ) );
3944  }
3945  } );
3946 
3947  if ( mobs )
3948  {
3950  mobs.release() );
3951  }
3952  return true;
3953  }
3954  return true;
3955 }
3956 
3958 {
3960  PropagateMove( this );
3961 
3962  // notify npcs and items (maybe the PropagateMove should also go there eventually? - Nando
3963  // 2018-06-16)
3964  realm->notify_moved( *this );
3965 
3967 
3968  if ( opponent_ != nullptr )
3970 
3971  // attacking can change the opponent_of array drastically.
3972  std::set<Character*> tmp( opponent_of );
3973  for ( auto& chr : tmp )
3974  {
3975  chr->check_attack_after_move();
3976  }
3977 
3978  move_reason = OTHER;
3979 }
3980 
3982 {
3983  remote_containers_.push_back( Core::ItemRef( item ) );
3984 }
3985 
3986 Items::Item* Character::search_remote_containers( u32 find_serial, bool* isRemoteContainer ) const
3987 {
3988  if ( isRemoteContainer )
3989  *isRemoteContainer = false;
3990  for ( const auto& elem : remote_containers_ )
3991  {
3992  Items::Item* item = elem.get();
3993  if ( item->orphan() )
3994  continue; // it'll be cleaned up when they move
3995  if ( item->serial == find_serial )
3996  {
3997  if ( isRemoteContainer )
3998  *isRemoteContainer = true;
3999  return item;
4000  }
4001  if ( item->isa( Core::UOBJ_CLASS::CLASS_CONTAINER ) )
4002  {
4003  item = ( (Core::UContainer*)item )->find( find_serial );
4004  if ( item )
4005  {
4006  if ( isRemoteContainer )
4007  *isRemoteContainer = false;
4008  return item;
4009  }
4010  }
4011  }
4012  return nullptr;
4013 }
4014 
4015 bool Character::mightsee( const Items::Item* item ) const
4016 {
4017  while ( item->container != nullptr )
4018  item = item->container;
4019 
4020  for ( const auto& elem : remote_containers_ )
4021  {
4022  Items::Item* additional_item = elem.get();
4023  if ( additional_item == item )
4024  return true;
4025  }
4026 
4027 
4028  return ( ( item->realm == realm ) && ( abs( x - item->x ) <= RANGE_VISUAL ) &&
4029  ( abs( y - item->y ) <= RANGE_VISUAL ) );
4030 }
4031 
4033 {
4034  Core::gameclock_t squelched = squelched_until();
4035  if ( squelched == 0 )
4036  return false;
4037  else if ( squelched == ~0u )
4038  return true;
4039 
4040  if ( Core::read_gameclock() < squelched )
4041  {
4042  return true;
4043  }
4044  else
4045  {
4046  const_cast<Character*>( this )->squelched_until( 0 );
4047  return false;
4048  }
4049 }
4050 
4052 {
4053  Core::gameclock_t deafened = deafened_until();
4054  if ( deafened == 0 )
4055  return false;
4056  else if ( deafened == ~0u )
4057  return true;
4058 
4059  if ( Core::read_gameclock() < deafened )
4060  {
4061  return true;
4062  }
4063  else
4064  {
4065  const_cast<Character*>( this )->deafened_until( 0 );
4066  return false;
4067  }
4068 }
4069 
4070 bool Character::invul() const
4071 {
4073 }
4074 
4076 {
4077  return static_cast<u16>( attribute( Core::gamestate.pAttrStrength->attrid ).effective() );
4078 }
4080 {
4081  return static_cast<u16>( attribute( Core::gamestate.pAttrDexterity->attrid ).effective() );
4082 }
4084 {
4085  return static_cast<u16>( attribute( Core::gamestate.pAttrIntelligence->attrid ).effective() );
4086 }
4087 
4089 {
4090  if ( tcursor2 != nullptr )
4091  return true;
4092  if ( client && client->gd && client->gd->target_cursor_uoemod != nullptr )
4093  return true;
4094  return false;
4095 }
4096 
4097 // get_legal_item removed, wasn't being used. - MuadDib
4098 
4100 {
4101  menu.clear();
4102  if ( on_menu_selection != nullptr )
4103  on_menu_selection( client, nullptr, nullptr );
4104  on_menu_selection = nullptr;
4105 }
4106 
4108 {
4109  return ( trading_with.get() != nullptr );
4110 }
4111 
4113 {
4115 }
4116 
4117 void Character::trade_accepted( bool newvalue )
4118 {
4120 }
4121 
4123 {
4124  if ( trading_cont.get() == nullptr ) // FIXME hardcoded
4125  {
4126  Items::Item* cont = Items::Item::create( Core::settingsManager.extobj.secure_trade_container );
4127  cont->realm = realm;
4128  trading_cont.set( static_cast<Core::UContainer*>( cont ) );
4129  }
4130 }
4131 
4133 {
4134  return trading_cont.get();
4135 }
4136 
4137 // SkillValue removed for no use - MuadDib
4138 
4139 const char* Character::target_tag() const
4140 {
4141  return "mobile";
4142 }
4143 
4145 {
4146  for ( unsigned ai = 0; ai < Core::gamestate.numAttributes; ++ai )
4147  {
4148  Attribute* pAttr = Core::gamestate.attributes[ai];
4149  AttributeValue& av = attribute( ai );
4150 
4151  av = attribute( ai );
4152  av.cap( pAttr->default_cap );
4153  }
4154 }
4155 
4157 {
4158  return _last_textcolor;
4159 }
4160 
4162 {
4163  _last_textcolor = new_color;
4164 }
4165 
4166 unsigned int Character::guildid() const
4167 {
4168  auto g = guild();
4169  return ( g != nullptr ) ? g->guildid() : 0;
4170 }
4171 
4177 void Character::addBuff( u16 icon, u16 duration, u32 cl_name, u32 cl_descr,
4178  const std::vector<u32>& arguments )
4179 {
4180  // Icon is already present, must send a remove packet first or client will not update
4181  delBuff( icon );
4182 
4183  Core::gameclock_t end = Core::read_gameclock() + duration;
4184  buffs_[icon] = {end, cl_name, cl_descr, arguments};
4185 
4186  if ( client != nullptr )
4187  send_buff_message( this, icon, true, duration, cl_name, cl_descr, arguments );
4188 }
4189 
4197 {
4198  auto b = buffs_.find( icon );
4199 
4200  if ( b == buffs_.end() )
4201  return false;
4202 
4203  buffs_.erase( b );
4204  if ( client != nullptr )
4205  send_buff_message( this, icon, false );
4206  return true;
4207 }
4208 
4215 {
4216  for ( auto it = buffs_.begin(); it != buffs_.end(); ++it )
4217  delBuff( it->first );
4218 }
4219 
4225 {
4226  if ( client == nullptr )
4227  return;
4228 
4229  for ( auto it = buffs_.begin(); it != buffs_.end(); ++it )
4230  {
4231  int duration = it->second.end - Core::read_gameclock();
4232  if ( duration < 0 )
4233  duration = 0;
4234  else if ( duration > 0xFFFF )
4235  duration = 0xFFFF;
4236 
4237  send_buff_message( this, it->first, true, static_cast<u16>( duration ), it->second.cl_name,
4238  it->second.cl_descr, it->second.arguments );
4239  }
4240 }
4241 
4243 {
4244  size_t size = base::estimatedSize() + uclang.capacity() + privs.estimatedSize() +
4245  settings.estimatedSize() + sizeof( Core::AccountRef ) /*acct*/
4246  + sizeof( Network::Client* ) /*client*/
4247  + sizeof( u32 ) /*registered_house*/
4248  + sizeof( unsigned char ) /*cmdlevel_*/
4249  + sizeof( u8 ) /*dir*/
4250  + sizeof( u16 ) /*lastx*/
4251  + sizeof( u16 ) /*lasty*/
4252  + sizeof( s8 ) /*lastz*/
4253  + sizeof( MOVEREASON ) /*move_reason*/
4254  + sizeof( Core::MOVEMODE ) /*movemode*/
4255  + sizeof( time_t ) /*disable_regeneration_until*/
4256  + sizeof( u16 ) /*truecolor*/
4257  + sizeof( u32 ) /*trueobjtype*/
4258  + sizeof( Core::UGENDER ) /*gender*/
4259  + sizeof( Core::URACE ) /*race*/
4260  + sizeof( short ) /*gradual_boost*/
4261  + sizeof( u32 ) /*last_corpse*/
4262  + sizeof( GOTTEN_ITEM_TYPE ) /*gotten_item_source*/
4263  + sizeof( Core::TargetCursor* ) /*tcursor2*/
4264  + sizeof( weak_ptr<Core::Menu> ) /*menu*/
4265  + sizeof( u16 ) /*_last_textcolor*/
4266  + sizeof( ref_ptr<Core::WornItemsContainer> ) /*wornitems_ref*/
4267  + sizeof( unsigned short ) /*ar_*/
4268  + sizeof( Items::UWeapon* ) /*weapon*/
4269  + sizeof( Items::UArmor* ) /*shield*/
4270  + sizeof( unsigned char ) /*concealed_*/
4271  + sizeof( unsigned short ) /*stealthsteps_*/
4272  + sizeof( unsigned int ) /*mountedsteps_*/
4274  sizeof( Core::UOExecutor* ) /*script_ex*/
4275  + sizeof( Character* ) /*opponent_*/
4276  + sizeof( Core::polclock_t ) /*swing_timer_start_clock_*/
4277  + sizeof( Core::OneShotTask* ) /*swing_task*/
4278  + sizeof( Core::OneShotTask* ) /*spell_task*/
4279  + sizeof( Core::gameclock_t ) /*created_at*/
4280  + sizeof( Core::polclock_t ) /*criminal_until_*/
4281  + sizeof( Core::OneShotTask* ) /*repsys_task_*/
4282  + sizeof( Core::OneShotTask* ) /*party_decline_timeout_*/
4283  + sizeof( Core::AttributeFlags<PRIV_FLAGS> ) /*cached_settings*/
4284  + sizeof( Core::AttributeFlags<MOB_FLAGS> ) /*mob_flags_*/
4285  ;
4286 
4287  size += 3 * sizeof( AttributeValue* ) + attributes.capacity() * sizeof( AttributeValue );
4288  size += 3 * sizeof( VitalValue* ) + vitals.capacity() * sizeof( VitalValue );
4289  size += 3 * sizeof( Items::UArmor** ) + armor_.capacity() * sizeof( Items::UArmor* );
4290  size += 3 * sizeof( Core::ItemRef* ) + remote_containers_.capacity() * sizeof( Core::ItemRef );
4291 
4292  size += 3 * sizeof( void* ) + opponent_of.size() * ( sizeof( Character* ) + 3 * sizeof( void* ) );
4293 
4294  size += aggressor_to_.size() * ( sizeof( Core::CharacterRef ) + sizeof( Core::polclock_t ) +
4295  ( sizeof( void* ) * 3 + 1 ) / 2 );
4296  size += lawfully_damaged_.size() * ( sizeof( Core::CharacterRef ) + sizeof( Core::polclock_t ) +
4297  ( sizeof( void* ) * 3 + 1 ) / 2 );
4298 
4299  size +=
4300  3 * sizeof( void* ) + to_be_reportable_.size() * ( sizeof( USERIAL ) + 3 * sizeof( void* ) );
4301  size +=
4302  3 * sizeof( void* ) + reportable_.size() * ( sizeof( reportable_t ) + 3 * sizeof( void* ) );
4303 
4304  size +=
4305  3 * sizeof( void* ) + buffs_.size() * ( sizeof( u16 ) + sizeof( Buff ) + sizeof( void* ) );
4306 
4307  return size;
4308 }
4309 
4311 {
4312  if ( realm )
4314 }
4315 } // namespace Mobile
4316 } // namespace Pol
bool empty() const
Definition: scrdef.h:41
u16 strength() const
Definition: charactr.cpp:4075
Contents::const_iterator const_iterator
Definition: containr.h:115
unsigned char u8
Definition: rawtypes.h:25
const int VITAL_LOWEST_REGENRATE
Definition: vital.h:51
bool newbie() const
Definition: item.h:338
ToBeReportableList to_be_reportable_
Definition: charactr.h:847
bool poisoned() const
Definition: charactr.h:979
std::vector< unsigned short > layers
Definition: layers.h:23
#define UOBJ_GARGOYLE_FEMALE
Definition: objtype.h:136
void register_with_supporting_multi(Item *item)
Definition: ufunc.cpp:1875
std::vector< Items::Item * > Contents
Definition: containr.h:113
void check_weather_region_change(bool force=false)
Definition: charactr.cpp:3525
const unsigned int VITAL_LOWEST_MAX_HUNDREDTHS
Definition: vital.h:59
static Item * create(u32 objtype, u32 serial=0)
Definition: itemcr.cpp:53
void unload_armor_zones()
Definition: charactr.cpp:252
unsigned int gameclock_t
Definition: gameclck.h:14
virtual void walk_on(Mobile::Character *chr)
Definition: chrituse.cpp:90
Character * opponent_
Definition: charactr.h:828
virtual void walk_on(Mobile::Character *chr) POL_OVERRIDE
Definition: house.cpp:964
bool setting_enabled(const char *setting) const
Definition: charactr.cpp:1096
unsigned int tile_flags(unsigned short tilenum)
Definition: polfile2.cpp:49
#define UOBJ_GARGOYLE_MALE
Definition: objtype.h:135
bool can_add_to_slot(u8 &slotIndex)
Definition: containr.cpp:178
void select_opponent(u32 opp_serial)
Definition: charactr.cpp:3044
unsigned int guildid() const
Definition: charactr.cpp:4166
bool can_moveanydist() const
Definition: charactr.cpp:1256
Core::gameclock_t created_at
Definition: charactr.h:906
ref_ptr< Bscript::EScriptProgram > find_script2(const ScriptDef &script, bool complain_if_not_found, bool cache_script)
Definition: scrstore.cpp:83
static void swing_task_func(Character *chr)
Definition: charactr.cpp:2776
virtual size_t estimatedSize() const POL_OVERRIDE
Definition: charactr.cpp:4242
static const char custom_house_z_xlate_table[CUSTOM_HOUSE_NUM_PLANES]
Definition: customhouses.h:145
Vital * FindVital(const std::string &str)
Definition: vital.cpp:83
Character * get_attackable_opponent() const
Definition: charactr.cpp:2913
unsigned int find_sumof_objtype_noninuse(u32 objtype) const
Definition: containr.cpp:471
bool in_debugger_holdlist() const
Definition: osmod.cpp:869
#define UOBJ_DEATH_SHROUD
Definition: objtype.h:219
std::string remove_string(const char *propname)
Definition: cfgfile.cpp:381
bool is_murderer() const
Definition: repsys.cpp:776
bool has_active_prompt() const
Definition: charactr.cpp:453
u16 dexterity() const
Definition: charactr.cpp:4079
void set_opponent(Character *opponent, bool inform_old_opponent=true)
Definition: charactr.cpp:2994
virtual bool can_add(const Items::Item &item) const
Definition: containr.cpp:168
u8 get_flag1(Network::Client *other_client) const
Definition: charactr.cpp:1771
void add_item_to_world(Items::Item *item)
Definition: uworld.cpp:31
Core::UACTION mounted_anim() const
Definition: weapon.cpp:408
void send_remove_if_hidden_ghost(Character *chr, Network::Client *client)
Definition: charactr.cpp:2027
unsigned short default_light_level
Definition: ssopt.h:48
virtual void printSelfOn(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: charactr.cpp:758
void send_full_corpse(Client *client, const Item *item)
Definition: ufunc.cpp:660
void on_swing_failure(Character *attacker)
Definition: charactr.cpp:2952
unsigned char lock() const
Definition: charactr.h:174
unsigned char weathertype
Definition: miscrgn.h:69
Items::Item * create_backpack()
Definition: charactr.cpp:2009
ExternalObject extobj
Definition: settings.h:30
bool check_unequip_script()
Definition: item.cpp:1064
#define UPDATE_CHECKPOINT()
Definition: checkpnt.h:35
unsigned char concealed() const
Definition: charactr.h:955
const u8 CHAR_FLAG1_FLYING
Definition: pktdef.h:102
bool deafened() const
Definition: charactr.cpp:4051
void PropagateMove(Character *chr)
Definition: charactr.cpp:2683
SystemState systemstate
Definition: systemstate.cpp:12
bool call(Bscript::BObjectImp *p0)
Definition: syshook.cpp:44
void send_short_statmsg(Network::Client *client, Mobile::Character *chr)
Definition: statmsg.cpp:235
unsigned short cap() const
Definition: charactr.h:176
Network::Client * client
Definition: charactr.h:871
polclock_t polclock()
Definition: polclock.cpp:72
void revoke_privilege(const char *priv)
Definition: charactr.cpp:1193
Core::PolConfig config
Definition: systemstate.h:43
void extract(Contents &cnt)
Definition: containr.cpp:322
void seterror(bool err)
Definition: executor.h:437
bool party_can_loot() const
Definition: party.cpp:1492
std::map< u16, Buff > buffs_
Definition: charactr.h:910
MobileCont lawfully_damaged_
Definition: charactr.h:844
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
bool consume_projectile(Core::UContainer *cont) const
Definition: weapon.cpp:432
unsigned char tilelayer(unsigned short tilenum)
Definition: polfile2.cpp:22
void send_create_ghost(Character *chr, Network::Client *client)
Definition: charactr.cpp:2037
#define SOUND_EFFECT_FEMALE_DEFENSE
Definition: sfx.h:28
bool strong_enough_to_equip(const Items::Item *item) const
Definition: charactr.cpp:1350
unsigned short min_value(void) const
Definition: dice.cpp:178
bool is_intrinsic() const
Tells eather an equipment is intrinsic or not Intrinsic equipment is, by example, NPCs "natural" weap...
Definition: equipmnt.cpp:104
Core::UOExecutor * script_ex
Definition: charactr.h:867
ExportedFunction * combat_advancement_hook
Definition: syshook.h:70
time_t disable_regeneration_until
Definition: charactr.h:834
bool check_unequiptest_scripts(Mobile::Character *chr)
Definition: item.cpp:1142
unsigned int release()
Definition: refptr.h:126
Core::polclock_t polclock
Definition: charactr.h:276
unsigned short hit_sound() const
Definition: weapon.cpp:414
void add_remote_container(Items::Item *)
Definition: charactr.cpp:3981
#define UOBJ_ELF_FEMALE
Definition: objtype.h:131
std::string all_privs() const
Definition: charactr.cpp:1173
T * get() const
Definition: refptr.h:176
SystemHooks system_hooks
Definition: uvars.h:190
void grant_privilege(const char *priv)
Definition: charactr.cpp:1184
#define POLLOG_INFO
Definition: logfacility.h:213
bool target_cursor_busy() const
Definition: charactr.cpp:4088
void printWornItems(Clib::StreamWriter &sw_pc, Clib::StreamWriter &sw_equip) const
Definition: charactr.cpp:767
static const SkillStatCap DEFAULT
virtual unsigned short ar() const
Definition: armor.cpp:131
bool in_range(const Mobile::Character *wielder, const Mobile::Character *target) const
Definition: weapon.cpp:448
unsigned short base_str_req
Definition: itemdesc.h:125
MusicDef * musicdef
Definition: uvars.h:152
int charindex() const
Definition: charactr.cpp:533
void calc_single_attribute(const Attribute *pAttr)
Definition: charactr.cpp:1630
virtual void on_facing_changed() POL_OVERRIDE
Definition: charactr.cpp:1748
bool is_attackable(Character *who) const
Definition: charactr.cpp:2878
ExportedFunction * warmode_change
Definition: syshook.h:82
void notify_resurrected(Mobile::Character &whoressed)
Definition: realm.cpp:157
virtual void on_color_changed() POL_OVERRIDE
Definition: charactr.cpp:1676
Core::Dice damage_dice
Definition: wepntmpl.h:44
unsigned int remove_ulong(const char *propname)
Definition: cfgfile.cpp:461
void set(ENUM flag)
Definition: uobject.h:113
Items::Item * find(u32 serial) const
Definition: containr.cpp:620
bool is_visible_to_me(const Character *chr) const
Definition: charactr.cpp:2658
void heal_damage_hundredths(unsigned int damage)
When a Mobile is Healed.
Definition: charactr.cpp:1979
ref_ptr< Core::WornItemsContainer > wornitems
Definition: charactr.h:787
bool is_visible() const
Definition: charactr.h:936
void calc_vital_stuff(bool i_mod=true, bool v_mod=true)
Definition: charactr.cpp:1581
void add(const char *str)
Definition: strset.cpp:19
bool has_active_client() const
Definition: charactr.cpp:448
bool enforce_mount_objtype
Definition: polcfg.h:95
bool get(ENUM flag) const
Definition: uobject.h:108
Mobile::Character * get_character(int index)
Definition: account.cpp:169
bool inrange(const UObject *c1, unsigned short x, unsigned short y)
Definition: ufunc.cpp:454
void check_justice_region_change()
Definition: charactr.cpp:3438
virtual void get_hitscript_params(double damage, Items::UArmor **parmor, unsigned short *rawdamage)
Definition: charactr.cpp:1899
int remove_int(const char *propname)
Definition: cfgfile.cpp:340
unsigned char cmdlevel() const
Definition: charactr.h:993
Core::TargetCursor * tcursor2
Definition: charactr.h:861
unsigned short calc_thru_damage(double damage, unsigned short ar)
Definition: charactr.cpp:1847
Items::UWeapon * weapon
Definition: charactr.h:783
void attack(Character *opponent)
Definition: charactr.cpp:3223
bool frozen() const
Definition: charactr.h:969
#define THREAD_CHECKPOINT(thread, check)
Definition: polsig.h:48
Item * find_legal_item(const Character *chr, u32 serial, bool *additlegal, bool *isRemoteContainer)
Definition: ufunc.cpp:958
Core::WeatherRegion * weather_region
Definition: cgdata.h:75
std::string name
Definition: layers.h:21
virtual void Send(Client *client) POL_OVERRIDE
Definition: packetdefs.cpp:856
virtual void setfacing(u8 newfacing) POL_OVERRIDE
Definition: charactr.cpp:1766
virtual bool can_be_renamed_by(const Character *chr) const
Definition: charactr.cpp:1230
virtual void repsys_on_damage(Character *defender)
[14] Mobile (MA) Damages Mobile (MB)
Definition: repsys.cpp:872
bool use_insurance()
Returns current insurance value and resets it to false.
Definition: item.cpp:299
void send_light(Client *client, int lightlevel)
Definition: ufunc.cpp:766
Items::Item * create_death_shroud()
Definition: charactr.cpp:1997
bool connected() const
Definition: charactr.cpp:438
u32 USERIAL
Definition: utype.h:21
static const ExtStatBarFollowers DEFAULT
#define FUNCTION_CHECKPOINT(func, check)
Definition: polsig.h:50
bool logged_in() const
Definition: charactr.cpp:428
Clib::StringSet privs
Definition: charactr.h:892
virtual double armor_absorb_damage(double damage)
Definition: charactr.cpp:1874
std::string uclang
Definition: charactr.h:872
void remove(const char *str)
Definition: strset.cpp:23
std::vector< AttributeValue > attributes
Definition: charactr.h:835
virtual void register_object(UObject *obj)
Definition: multis.cpp:60
CmdLevels cmdlevels
Definition: uvars.h:137
Items::Item * create_death_robe()
Definition: charactr.cpp:2003
bool gflag_in_system_load
Definition: state.h:39
static void InRange(u16 x, u16 y, const Realms::Realm *realm, unsigned range, F &&f)
Definition: uworld.h:235
std::vector< Items::UArmor * > armor_
Definition: charactr.h:785
const Mobile::Attribute & attribute() const
Definition: weapon.cpp:360
void readfrom(const std::string &str)
Definition: strset.cpp:33
virtual Item * clone() const
Definition: item.cpp:56
void disable_regeneration_for(int seconds)
Definition: charactr.cpp:3060
unsigned short max_weapon_damage() const
Definition: charactr.cpp:3127
#define UOBJ_GOLD_COIN
Definition: objtype.h:209
virtual T * getregion(xcoord x, ycoord y, Realms::Realm *realm)
Definition: region.h:126
unsigned int mount
Definition: extobj.h:32
bool on_mount() const
Definition: charactr.cpp:1490
const short ATTRIBUTE_MIN_INTRINSIC_MOD
Definition: attribute.h:75
virtual size_t estimatedSize() const
Definition: uobject.cpp:106
bool is_projectile() const
Definition: weapon.cpp:377
const u8 CHAR_FLAG1_INVISIBLE
Definition: pktdef.h:98
LightDef * lightdef
Definition: uvars.h:150
int current_ones() const
Definition: charactr.h:193
MoveDelta move_delta[8]
Definition: ufacing.cpp:16
void play_moving_effect(const UObject *src, const UObject *dst, u16 effect, u8 speed, u8 loop, u8 explode)
Definition: ufunc.cpp:1039
virtual void add(Items::Item *item)
Definition: containr.cpp:194
static Core::MOVEMODE decode_movemode(const std::string &str)
Definition: charactr.cpp:772
virtual bool setgraphic(u16 newobjtype) POL_OVERRIDE
Definition: charactr.cpp:1659
const WeaponDesc & descriptor() const
Definition: weapon.cpp:426
void send_item_to_inrange(const Item *item)
Definition: ufunc.cpp:706
void set_setting(const char *setting, bool value)
Definition: charactr.cpp:1157
Mobile::Character * chr
Definition: client.h:182
bool can_rename(const Character *chr) const
Definition: charactr.cpp:1236
#define passert_r(exp, reason)
Definition: passert.h:66
bool can_freemove() const
Definition: charactr.cpp:1271
#define ITEM_ELEM_PTR(elem)
Definition: containr.h:39
Core::UGENDER gender
Definition: charactr.h:918
virtual bool script_isa(unsigned isatype) const POL_OVERRIDE
Definition: uoscrobj.cpp:4604
unsigned short delay() const
Definition: weapon.cpp:355
const u8 CHAR_FLAG1_POISONED
Definition: pktdef.h:101
void updateEquipableProperties(Items::Item *item)
Definition: charactr.cpp:2555
MOVEMODE
Definition: uconst.h:79
void send_item_move_failure(Network::Client *client, u8 reason)
Definition: ufunc.cpp:818
bool private_say_above(Character *chr, const UObject *obj, const char *text, unsigned short font, unsigned short color, unsigned int journal_print)
Definition: ufunc.cpp:1328
unsigned short projectile_sound() const
Definition: weapon.cpp:383
void addBuff(u16 icon, u16 duration, u32 cl_name, u32 cl_descr, const std::vector< u32 > &arguments)
Definition: charactr.cpp:4177
bool consume(const Core::Vital *pVital, VitalValue &vv, unsigned int amt)
Definition: charactr.cpp:1540
unsigned short getmidi() const
Definition: musicrgn.cpp:32
void undo_get_item(Mobile::Character *chr, Items::Item *item)
Definition: getitem.cpp:260
#define UOBJ_GARGOYLE_FEMALE_GHOST
Definition: objtype.h:138
double remove_double(const char *propname, double dflt)
Definition: cfgfile.cpp:448
void set_dirty()
Definition: uobject.h:291
int default_accessible_range
Definition: ssopt.h:47
bool no_drop() const
Definition: item.cpp:310
virtual bool script_isa(unsigned isatype) const POL_OVERRIDE
Definition: uoscrobj.cpp:4627
std::string hexint(unsigned short v)
Definition: strutil.cpp:23
bool valid_equip_layer(int layer)
Definition: item.h:383
const std::string & name() const
Definition: scrdef.h:45
ExportedFunction * get_maximum_func
Definition: vital.h:42
MobileCont aggressor_to_
Definition: charactr.h:843
unsigned short u16
Definition: rawtypes.h:26
unsigned int u32
Definition: rawtypes.h:27
void send_remove_character(Client *client, const Character *chr)
Definition: ufunc.cpp:355
#define UOBJ_HUMAN_MALE
Definition: objtype.h:125
void send_remove_character_to_nearby_cantsee(const Character *chr)
Definition: ufunc.cpp:389
#define RANGE_VISUAL
Definition: uconst.h:72
Core::UContainer * trade_container()
Definition: charactr.cpp:4132
virtual const char * classname() const POL_OVERRIDE
Definition: charactr.cpp:753
const u16 AOS
Definition: client.h:75
void clear()
Definition: refptr.h:283
POL_NORETURN void throw_error(const std::string &errmsg) const
Definition: cfgfile.cpp:285
void send_move_mobile_to_nearby_cansee(const Character *chr)
Definition: ufunc.cpp:1905
Multi::UMulti * system_find_multi(u32 serial)
Definition: fnsearch.cpp:50
void send_poisonhealthbar(Client *client, const Character *chr)
Definition: ufunc.cpp:186
bool consume(unsigned int hamt)
Definition: charactr.h:225
virtual void repsys_on_attack(Character *defender)
[13] Mobile (MA) Attacks Mobile (MA)
Definition: repsys.cpp:809
void spend_gold(unsigned int amount)
Definition: charactr.cpp:1321
void readCommonProperties(Clib::ConfigElem &elem)
Definition: charactr.cpp:802
void send_move(Client *client, const Character *chr)
Definition: ufunc.cpp:174
bool warmode() const
Definition: charactr.cpp:3067
void on_loggoff_party(Mobile::Character *chr)
Definition: party.cpp:878
virtual void cancel(void) POL_OVERRIDE
Definition: schedule.cpp:143
virtual void destroy()
Definition: uobject.cpp:122
void send_midi(Client *client, u16 midi)
Definition: ufunc.cpp:1867