1 /*
2  *
3  *  Iter Vehemens ad Necem (IVAN)
4  *  Copyright (C) Timo Kiviluoto
5  *  Released under the GNU General
6  *  Public License
7  *
8  *  See LICENSING which should be included
9  *  along with this file for more details
10  *
11  */
12 
13 /* Compiled through itemset.cpp */
14 
15 #include "dbgmsgproj.h"
16 
GetGraphicsContainerIndex() const17 int bodypart::GetGraphicsContainerIndex() const { return GR_HUMANOID; }
GetArticleMode() const18 int bodypart::GetArticleMode() const { return IsUnique() ? FORCE_THE : 0; }
IsAlive() const19 truth bodypart::IsAlive() const { return MainMaterial->GetBodyFlags() & IS_ALIVE; }
GetSpecialFlags() const20 int bodypart::GetSpecialFlags() const { return SpecialFlags|ST_OTHER_BODYPART; }
GetMaterialColorA(int) const21 col16 bodypart::GetMaterialColorA(int) const { return GetMainMaterial()->GetSkinColor(); }
IsWarm() const22 truth bodypart::IsWarm() const { return MainMaterial->GetBodyFlags() & IS_WARM || IsBurning(); }
IsWarmBlooded() const23 truth bodypart::IsWarmBlooded() const { return MainMaterial->GetBodyFlags() & IS_WARM_BLOODED; }
UseMaterialAttributes() const24 truth bodypart::UseMaterialAttributes() const
25 { return MainMaterial->GetBodyFlags() & USE_MATERIAL_ATTRIBUTES || !Master || Master->AlwaysUseMaterialAttributes(); }
CanRegenerate() const26 truth bodypart::CanRegenerate() const { return MainMaterial->GetBodyFlags() & CAN_REGENERATE; }
GetSquareUnder(int I) const27 square* bodypart::GetSquareUnder(int I) const
28 { return Master ? Slot[0]->GetSquareUnder(I) : Slot[I]->GetSquareUnder(); }
GetLSquareUnder(int I) const29 lsquare* bodypart::GetLSquareUnder(int I) const
30 { return static_cast<lsquare*>(Master ? Slot[0]->GetSquareUnder(I) : Slot[I]->GetSquareUnder()); }
GetExternalBodyArmor() const31 item* bodypart::GetExternalBodyArmor() const { return GetHumanoidMaster()->GetBodyArmor(); }
GetExternalCloak() const32 item* bodypart::GetExternalCloak() const { return GetHumanoidMaster()->GetCloak(); }
GetExternalHelmet() const33 item* bodypart::GetExternalHelmet() const { return GetHumanoidMaster()->GetHelmet(); }
GetExternalBelt() const34 item* bodypart::GetExternalBelt() const { return GetHumanoidMaster()->GetBelt(); }
AllowFluidBe() const35 truth bodypart::AllowFluidBe() const { return !Master || !Master->IsPolymorphed(); }
36 
GetBodyPartIndex() const37 int head::GetBodyPartIndex() const { return HEAD_INDEX; }
GetBiteMinDamage() const38 int head::GetBiteMinDamage() const { return int(BiteDamage * 0.75); }
GetBiteMaxDamage() const39 int head::GetBiteMaxDamage() const { return int(BiteDamage * 1.25 + 1); }
40 
GetBodyPartIndex() const41 int torso::GetBodyPartIndex() const { return TORSO_INDEX; }
42 
GetGraphicsContainerIndex() const43 int normaltorso::GetGraphicsContainerIndex() const { return GR_CHARACTER; }
44 
GetMinDamage() const45 int arm::GetMinDamage() const { return int(Damage * 0.75); }
GetMaxDamage() const46 int arm::GetMaxDamage() const { return int(Damage * 1.25 + 1); }
GetBlockValue() const47 double arm::GetBlockValue() const { return GetToHitValue() * GetWielded()->GetBlockModifier() / 10000; }
48 
GetBodyPartIndex() const49 int rightarm::GetBodyPartIndex() const { return RIGHT_ARM_INDEX; }
GetSpecialFlags() const50 int rightarm::GetSpecialFlags() const { return SpecialFlags|ST_RIGHT_ARM; }
51 
GetBodyPartIndex() const52 int leftarm::GetBodyPartIndex() const { return LEFT_ARM_INDEX; }
GetSpecialFlags() const53 int leftarm::GetSpecialFlags() const { return SpecialFlags|ST_LEFT_ARM; }
54 
GetBodyPartIndex() const55 int groin::GetBodyPartIndex() const { return GROIN_INDEX; }
GetSpecialFlags() const56 int groin::GetSpecialFlags() const { return SpecialFlags|ST_GROIN; }
57 
GetKickMinDamage() const58 int leg::GetKickMinDamage() const { return int(KickDamage * 0.75); }
GetKickMaxDamage() const59 int leg::GetKickMaxDamage() const { return int(KickDamage * 1.25 + 1); }
60 
GetBodyPartIndex() const61 int rightleg::GetBodyPartIndex() const { return RIGHT_LEG_INDEX; }
GetSpecialFlags() const62 int rightleg::GetSpecialFlags() const { return SpecialFlags|ST_RIGHT_LEG; }
63 
GetBodyPartIndex() const64 int leftleg::GetBodyPartIndex() const { return LEFT_LEG_INDEX; }
GetSpecialFlags() const65 int leftleg::GetSpecialFlags() const { return SpecialFlags|ST_LEFT_LEG; }
66 
GetBitmapPos(int Frame) const67 v2 eddytorso::GetBitmapPos(int Frame) const { return torso::GetBitmapPos(Frame) + v2((Frame&0x6) << 3, 0); }
68 
Behead()69 head* corpse::Behead() { return Deceased->Behead(); }
CanBeCloned() const70 truth corpse::CanBeCloned() const { return GetDeceased()->CanBeCloned(); }
GetAttachedGod() const71 int corpse::GetAttachedGod() const { return GetDeceased()->GetTorso()->GetAttachedGod(); }
72 
GetBitmapPos(int Frame) const73 v2 ennerhead::GetBitmapPos(int Frame) const
74 { return Frame & 16 ? head::GetBitmapPos(Frame) : head::GetBitmapPos(Frame) + v2(16, 0); }
75 
GetAlphaA(int Frame) const76 alpha blinkdogtorso::GetAlphaA(int Frame) const { return (Frame & 31) != 31 ? 255 : 0; }
77 
Save(outputfile & SaveFile) const78 void bodypart::Save(outputfile& SaveFile) const
79 {
80   item::Save(SaveFile);
81   SaveFile << BitmapPos << ColorB << ColorC << ColorD << SpecialFlags << WobbleData << HP;
82   SaveFile << OwnerDescription << BloodMaterial << NormalMaterial << Scar << DamageID;
83 }
84 
Load(inputfile & SaveFile)85 void bodypart::Load(inputfile& SaveFile)
86 {
87   item::Load(SaveFile);
88   SaveFile >> BitmapPos >> ColorB >> ColorC >> ColorD >> SpecialFlags >> WobbleData >> HP;
89   SaveFile >> OwnerDescription >> BloodMaterial >> NormalMaterial >> Scar >> DamageID;
90 }
91 
GetStrengthValue() const92 int bodypart::GetStrengthValue() const
93 {
94   if(!UseMaterialAttributes())
95     return long(GetStrengthModifier()) * Master->GetAttribute(ENDURANCE) / 2000;
96   else
97     return long(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000;
98 }
99 
GetTotalResistance(int Type) const100 int head::GetTotalResistance(int Type) const
101 {
102   if(Master)
103   {
104     int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type);
105 
106     if(GetHelmet())
107       Resistance += GetHelmet()->GetResistance(Type);
108 
109     if(GetExternalBodyArmor())
110       Resistance += GetExternalBodyArmor()->GetResistance(Type) >> 2;
111 
112     return Resistance;
113   }
114   else
115     return GetResistance(Type);
116 }
117 
GetTotalResistance(int Type) const118 int normaltorso::GetTotalResistance(int Type) const
119 {
120   if(Master)
121     return GetResistance(Type) + Master->GetGlobalResistance(Type);
122   else
123     return GetResistance(Type);
124 }
125 
GetTotalResistance(int Type) const126 int humanoidtorso::GetTotalResistance(int Type) const
127 {
128   if(Master)
129   {
130     int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type);
131 
132     if(GetBodyArmor())
133       Resistance += GetBodyArmor()->GetResistance(Type);
134 
135     if(GetBelt())
136       Resistance += GetBelt()->GetResistance(Type);
137 
138     return Resistance;
139   }
140   else
141     return GetResistance(Type);
142 }
143 
GetTotalResistance(int Type) const144 int arm::GetTotalResistance(int Type) const
145 {
146   if(Master)
147   {
148     int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type);
149 
150     if(GetExternalBodyArmor())
151       Resistance += GetExternalBodyArmor()->GetResistance(Type) >> 1;
152 
153     if(GetGauntlet())
154       Resistance += GetGauntlet()->GetResistance(Type);
155 
156     return Resistance;
157   }
158   else
159     return GetResistance(Type);
160 }
161 
GetTotalResistance(int Type) const162 int groin::GetTotalResistance(int Type) const
163 {
164   if(Master)
165   {
166     int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type);
167 
168     if(GetExternalBodyArmor())
169       Resistance += GetExternalBodyArmor()->GetResistance(Type);
170 
171     if(GetHumanoidMaster()->GetBelt())
172       Resistance += GetHumanoidMaster()->GetBelt()->GetResistance(Type);
173 
174     return Resistance;
175   }
176   else
177     return GetResistance(Type);
178 }
179 
GetTotalResistance(int Type) const180 int leg::GetTotalResistance(int Type) const
181 {
182   if(Master)
183   {
184     int Resistance = GetResistance(Type) + Master->GetGlobalResistance(Type);
185 
186     if(GetExternalBodyArmor())
187       Resistance += GetExternalBodyArmor()->GetResistance(Type) >> 1;
188 
189     if(GetBoot())
190       Resistance += GetBoot()->GetResistance(Type);
191 
192     return Resistance;
193   }
194   else
195     return GetResistance(Type);
196 }
197 
Save(outputfile & SaveFile) const198 void head::Save(outputfile& SaveFile) const
199 {
200   bodypart::Save(SaveFile);
201   SaveFile << BaseBiteStrength << BonusBiteStrength;
202   SaveFile << HelmetSlot << AmuletSlot;
203 }
204 
Load(inputfile & SaveFile)205 void head::Load(inputfile& SaveFile)
206 {
207   bodypart::Load(SaveFile);
208   SaveFile >> BaseBiteStrength >> BonusBiteStrength;
209   SaveFile >> HelmetSlot >> AmuletSlot;
210 }
211 
Save(outputfile & SaveFile) const212 void humanoidtorso::Save(outputfile& SaveFile) const
213 {
214   bodypart::Save(SaveFile);
215   SaveFile << BodyArmorSlot << CloakSlot << BeltSlot;
216 }
217 
Load(inputfile & SaveFile)218 void humanoidtorso::Load(inputfile& SaveFile)
219 {
220   bodypart::Load(SaveFile);
221   SaveFile >> BodyArmorSlot >> CloakSlot >> BeltSlot;
222 }
223 
Save(outputfile & SaveFile) const224 void arm::Save(outputfile& SaveFile) const
225 {
226   bodypart::Save(SaveFile);
227   SaveFile << BaseUnarmedStrength;
228   SaveFile << StrengthExperience << DexterityExperience;
229   SaveFile << WieldedSlot << GauntletSlot << RingSlot;
230   SaveFile << WieldedGraphicData;
231 }
232 
Load(inputfile & SaveFile)233 void arm::Load(inputfile& SaveFile)
234 {
235   bodypart::Load(SaveFile);
236   SaveFile >> BaseUnarmedStrength;
237   SaveFile >> StrengthExperience >> DexterityExperience;
238   SaveFile >> WieldedSlot >> GauntletSlot >> RingSlot;
239   SaveFile >> WieldedGraphicData;
240 }
241 
Save(outputfile & SaveFile) const242 void leg::Save(outputfile& SaveFile) const
243 {
244   bodypart::Save(SaveFile);
245   SaveFile << BaseKickStrength << StrengthExperience << AgilityExperience;
246   SaveFile << BootSlot;
247 }
248 
Load(inputfile & SaveFile)249 void leg::Load(inputfile& SaveFile)
250 {
251   bodypart::Load(SaveFile);
252   SaveFile >> BaseKickStrength >> StrengthExperience >> AgilityExperience;
253   SaveFile >> BootSlot;
254 }
255 
ReceiveDamage(character * Damager,int Damage,int Type,int Direction)256 truth bodypart::ReceiveDamage(character* Damager, int Damage, int Type, int Direction)
257 {DBG1(Damager);
258   if(Master)
259   {
260     if(Type & POISON && !IsAlive())
261       return false;
262 
263     int BHP = HP;
264 
265     if(HP <= Damage && !CanBeSevered(Type))
266       Damage = GetHP() - 1;
267 
268     if(!Damage)
269       return false;
270 
271     EditHP(1, -Damage);
272 
273     if(Damager!=NULL && (Type & DRAIN) && IsAlive())
274       for(int c = 0; c < Damage; ++c)
275         Damager->HealHitPoint();
276 
277     truth WasBadlyHurt = IsBadlyHurt();
278 
279     if(HP <= 0)
280       return true;
281 
282     if(DamageTypeCanScar(Type) && !(RAND_N(25 + 25 * HP / MaxHP)))
283       GenerateScar(Damage, Type);
284 
285     if(Master->IsPlayer())
286     {
287       if(HP == 1 && BHP > 1)
288       {
289         if(IsAlive())
290           ADD_MESSAGE("Your %s bleeds very badly.", GetBodyPartName().CStr());
291         else
292           ADD_MESSAGE("Your %s is in very bad condition.", GetBodyPartName().CStr());
293 
294         if(Master->BodyPartIsVital(GetBodyPartIndex()))
295           game::AskForKeyPress(CONST_S("Vital bodypart in serious danger! [press any key to continue]"));
296       }
297       else if(IsBadlyHurt() && !WasBadlyHurt)
298       {
299         if(IsAlive())
300           ADD_MESSAGE("Your %s bleeds.", GetBodyPartName().CStr());
301         else
302           ADD_MESSAGE("Your %s is in bad condition.", GetBodyPartName().CStr());
303 
304         if(Master->BodyPartIsVital(GetBodyPartIndex()))
305           game::AskForKeyPress(CONST_S("Vital bodypart in danger! [press any key to continue]"));
306       }
307     }
308 
309     SignalPossibleUsabilityChange();
310   }
311   else
312     return item::ReceiveDamage(Damager, Damage, Type, Direction);
313 
314   return false;
315 }
316 
CanBeSevered(int Type) const317 truth bodypart::CanBeSevered(int Type) const
318 {
319   if((HP == MaxHP && HP != 1 && !Master->IsExtraFragile())
320      || (Type & (POISON|SOUND) && GetBodyPartIndex() != TORSO_INDEX))
321     return false;
322 
323   if(!Master->BodyPartIsVital(GetBodyPartIndex()) || Master->IsExtraFragile())
324     return true;
325 
326   bodypart* Torso = Master->GetTorso();
327   return Torso->HP != Torso->MaxHP || Torso->HP == 1;
328 }
329 
GetUnarmedDamage() const330 double arm::GetUnarmedDamage() const
331 {
332   double WeaponStrength = GetBaseUnarmedStrength() * GetBaseUnarmedStrength();
333   item* Gauntlet = GetGauntlet();
334 
335   if(Gauntlet)
336     WeaponStrength += Gauntlet->GetWeaponStrength();
337 
338   double Base = sqrt(5e-5 * WeaponStrength);
339 
340   if(Gauntlet)
341     Base += Gauntlet->GetDamageBonus();
342 
343   double Damage = Base * sqrt(1e-7 * GetAttribute(ARM_STRENGTH))
344                   * GetHumanoidMaster()->GetCWeaponSkill(UNARMED)->GetBonus();
345 
346   return Damage;
347 }
348 
GetUnarmedToHitValue() const349 double arm::GetUnarmedToHitValue() const
350 {
351   double BonusMultiplier = 10.;
352   item* Gauntlet = GetGauntlet();
353 
354   if(Gauntlet)
355     BonusMultiplier += Gauntlet->GetTHVBonus();
356 
357   return GetAttribute(DEXTERITY)
358     * sqrt(2.5 * Master->GetAttribute(PERCEPTION))
359     * GetHumanoidMaster()->GetCWeaponSkill(UNARMED)->GetBonus()
360     * Master->GetMoveEase()
361     * BonusMultiplier / 5000000;
362 }
363 
GetUnarmedAPCost() const364 long arm::GetUnarmedAPCost() const
365 {
366   return long(10000000000. / (APBonus(GetAttribute(DEXTERITY)) * Master->GetMoveEase()
367                               * Master->GetCWeaponSkill(UNARMED)->GetBonus()));
368 }
369 
CalculateDamage()370 void arm::CalculateDamage()
371 {
372   if(!Master)
373     return;
374 
375   if(!IsUsable())
376     Damage = 0;
377   else if(GetWielded())
378     Damage = GetWieldedDamage();
379   else if(PairArmAllowsMelee())
380     Damage = GetUnarmedDamage();
381   else
382     Damage = 0;
383 }
384 
CalculateToHitValue()385 void arm::CalculateToHitValue()
386 {
387   if(!Master)
388     return;
389 
390   if(GetWielded())
391     ToHitValue = GetWieldedToHitValue();
392   else if(PairArmAllowsMelee())
393     ToHitValue = GetUnarmedToHitValue();
394   else
395     ToHitValue = 0;
396 }
397 
CalculateAPCost()398 void arm::CalculateAPCost()
399 {
400   if(!Master)
401     return;
402 
403   if(GetWielded())
404     APCost = GetWieldedAPCost();
405   else if(PairArmAllowsMelee())
406     APCost = GetUnarmedAPCost();
407   else return;
408 
409   if(APCost < 100)
410     APCost = 100;
411 }
412 
PairArmAllowsMelee() const413 truth arm::PairArmAllowsMelee() const
414 {
415   const arm* PairArm = GetPairArm();
416   return !PairArm || !PairArm->IsUsable() || !PairArm->GetWielded()
417     || PairArm->GetWielded()->IsShield(Master);
418 }
419 
GetWieldedDamage() const420 double arm::GetWieldedDamage() const
421 {
422   citem* Wielded = GetWielded();
423 
424   if(Wielded->IsShield(Master))
425     return 0;
426 
427   int HitStrength = GetAttribute(ARM_STRENGTH);
428   int Requirement = Wielded->GetStrengthRequirement();
429 
430   if(TwoHandWieldIsActive())
431   {
432     HitStrength += GetPairArm()->GetAttribute(ARM_STRENGTH);
433     Requirement >>= 2;
434   }
435 
436   if(HitStrength > Requirement)
437   {
438     /* I have no idea whether this works. It needs to be checked */
439 
440     return Wielded->GetBaseDamage() * sqrt(1e-13 * HitStrength)
441         * GetCurrentSWeaponSkillBonus()
442         * GetHumanoidMaster()->GetCWeaponSkill(Wielded->GetWeaponCategory())->GetBonus();
443   }
444   else
445     return 0;
446 }
447 
GetWieldedToHitValue() const448 double arm::GetWieldedToHitValue() const
449 {
450   int HitStrength = GetWieldedHitStrength();
451 
452   if(HitStrength <= 0)
453     return 0;
454 
455   citem* Wielded = GetWielded();
456 
457   double Base = 2e-11
458                 * Min(HitStrength, 10)
459                 * GetHumanoidMaster()->GetCWeaponSkill(Wielded->GetWeaponCategory())->GetBonus()
460                 * GetCurrentSWeaponSkillBonus()
461                 * Master->GetMoveEase()
462                 * (10000. / (1000 + Wielded->GetWeight()) + Wielded->GetTHVBonus());
463   double ThisToHit = GetAttribute(DEXTERITY) * sqrt(2.5 * Master->GetAttribute(PERCEPTION));
464   const arm* PairArm = GetPairArm();
465 
466   if(PairArm && PairArm->IsUsable())
467   {
468     citem* PairWielded = PairArm->GetWielded();
469 
470     if(!PairWielded)
471     {
472       if(Wielded->IsTwoHanded() && !Wielded->IsShield(Master))
473         return Base * (ThisToHit + PairArm->GetAttribute(DEXTERITY)
474                        * sqrt(2.5 * Master->GetAttribute(PERCEPTION))) / 2;
475     }
476     else if(!Wielded->IsShield(Master) && !PairWielded->IsShield(Master))
477       return Base * ThisToHit / (1.0 + (500.0 + PairWielded->GetWeight())
478                                  / (1000.0 + (Wielded->GetWeight() << 1)));
479   }
480 
481   return Base * ThisToHit;
482 }
483 
GetWieldedAPCost() const484 long arm::GetWieldedAPCost() const
485 {
486   citem* Wielded = GetWielded();
487 
488   if(Wielded->IsShield(Master))
489     return 0;
490 
491   int HitStrength = GetWieldedHitStrength();
492 
493   if(HitStrength <= 0)
494     return 0;
495 
496   return long(1 / (1e-14 * APBonus(GetAttribute(DEXTERITY)) * Master->GetMoveEase()
497                    * GetHumanoidMaster()->GetCWeaponSkill(Wielded->GetWeaponCategory())->GetBonus()
498                    * (GetCurrentSWeaponSkillBonus() * Min(HitStrength, 10))));
499 }
500 
CalculateDamage()501 void head::CalculateDamage()
502 {
503   if(!Master)
504     return;
505 
506   if(Master->StateIsActivated(VAMPIRISM))
507     BiteDamage = 7.07e-6 * (GetBaseBiteStrength() + GetBonusBiteStrength()) * GetHumanoidMaster()->GetCWeaponSkill(BITE)->GetBonus();
508   else
509     BiteDamage = 7.07e-6 * GetBaseBiteStrength() * GetHumanoidMaster()->GetCWeaponSkill(BITE)->GetBonus();
510 }
511 
CalculateToHitValue()512 void head::CalculateToHitValue()
513 {
514   if(!Master)
515     return;
516 
517   BiteToHitValue = Master->GetAttribute(AGILITY) * sqrt(2.5 * Master->GetAttribute(PERCEPTION))
518                    * GetHumanoidMaster()->GetCWeaponSkill(KICK)->GetBonus() * Master->GetMoveEase() / 1000000;
519 }
520 
CalculateAPCost()521 void head::CalculateAPCost()
522 {
523   if(!Master)
524     return;
525 
526   BiteAPCost = Max(long(10000000000. / (APBonus(Master->GetAttribute(AGILITY)) * Master->GetMoveEase()
527                                         * Master->GetCWeaponSkill(BITE)->GetBonus())), 100L);
528 }
529 
CalculateDamage()530 void leg::CalculateDamage()
531 {
532   if(!Master)
533     return;
534 
535   double WeaponStrength = GetBaseKickStrength() * GetBaseKickStrength();
536   item* Boot = GetBoot();
537 
538   if(Boot)
539     WeaponStrength += Boot->GetWeaponStrength();
540 
541   double Base = sqrt(5e-5 * WeaponStrength);
542 
543   if(Boot)
544     Base += Boot->GetDamageBonus();
545 
546   KickDamage = Base * sqrt(1e-7 * GetAttribute(LEG_STRENGTH))
547                * GetHumanoidMaster()->GetCWeaponSkill(KICK)->GetBonus();
548 }
549 
CalculateToHitValue()550 void leg::CalculateToHitValue()
551 {
552   if(!Master)
553     return;
554 
555   double BonusMultiplier = 10.;
556   item* Boot = GetBoot();
557 
558   if(Boot)
559     BonusMultiplier += Boot->GetTHVBonus();
560 
561   KickToHitValue = GetAttribute(AGILITY)
562                    * sqrt(2.5 * Master->GetAttribute(PERCEPTION))
563                    * GetHumanoidMaster()->GetCWeaponSkill(KICK)->GetBonus()
564                    * Master->GetMoveEase()
565                    * BonusMultiplier / 10000000;
566 }
567 
CalculateAPCost()568 void leg::CalculateAPCost()
569 {
570   if(!Master)
571     return;
572 
573   KickAPCost = Max(long(20000000000. / (APBonus(GetAttribute(AGILITY)) * Master->GetMoveEase()
574                                         * Master->GetCWeaponSkill(KICK)->GetBonus())), 100L);
575 }
576 
GetHumanoidMaster() const577 humanoid* bodypart::GetHumanoidMaster() const
578 {
579   return static_cast<humanoid*>(Master);
580 }
581 
Save(outputfile & SaveFile) const582 void corpse::Save(outputfile& SaveFile) const
583 {
584   item::Save(SaveFile);
585   SaveFile << Deceased;
586 }
587 
Load(inputfile & SaveFile)588 void corpse::Load(inputfile& SaveFile)
589 {
590   item::Load(SaveFile);
591   SaveFile >> Deceased;
592   Deceased->SetMotherEntity(this);
593   Enable();
594 }
595 
AddPostFix(festring & String,int) const596 void corpse::AddPostFix(festring& String, int) const
597 {
598   String << " of ";
599   GetDeceased()->AddName(String, INDEFINITE);
600 }
601 
GetOfferValue(int Receiver) const602 int corpse::GetOfferValue(int Receiver) const
603 {
604   int OfferValue = 0;
605 
606   for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c)
607   {
608     bodypart* BodyPart = GetDeceased()->GetBodyPart(c);
609 
610     if(BodyPart)
611       OfferValue += BodyPart->GetOfferValue(Receiver);
612   }
613 
614   return OfferValue;
615 }
616 
GetWeaponStrength() const617 double corpse::GetWeaponStrength() const
618 {
619   return GetFormModifier() * GetDeceased()->GetTorso()->GetMainMaterial()->GetStrengthValue()
620          * sqrt(GetDeceased()->GetTorso()->GetMainMaterial()->GetWeight());
621 }
622 
CanBeEatenByAI(ccharacter * Eater) const623 truth corpse::CanBeEatenByAI(ccharacter* Eater) const
624 {
625   for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c)
626   {
627     bodypart* BodyPart = GetDeceased()->GetBodyPart(c);
628 
629     if(BodyPart && !BodyPart->CanBeEatenByAI(Eater))
630       return false;
631   }
632 
633   return true;
634 }
635 
GetStrengthValue() const636 int corpse::GetStrengthValue() const
637 {
638   return long(GetStrengthModifier()) * GetDeceased()->GetTorso()->GetMainMaterial()->GetStrengthValue() / 2000;
639 }
640 
~corpse()641 corpse::~corpse()
642 {
643   delete Deceased;
644 }
645 
GetMaterialColorA(int) const646 col16 corpse::GetMaterialColorA(int) const
647 {
648   return GetDeceased()->GetTorso()->GetMainMaterial()->GetColor();
649 }
650 
GetAlphaA(int) const651 alpha corpse::GetAlphaA(int) const
652 {
653   return GetDeceased()->GetTorso()->GetMainMaterial()->GetAlpha();
654 }
655 
GetMaterialColorB(int) const656 col16 corpse::GetMaterialColorB(int) const
657 {
658   torso* Torso = GetDeceased()->GetTorso();
659 
660   if(Torso->IsAlive())
661     return material::GetDataBase(GetDeceased()->GetBloodMaterial())->Color;
662   else
663     return Torso->GetMainMaterial()->GetColor();
664 }
665 
GetAlphaB(int) const666 alpha corpse::GetAlphaB(int) const
667 {
668   torso* Torso = GetDeceased()->GetTorso();
669   return Torso->IsAlive() ? 175 : Torso->GetMainMaterial()->GetAlpha();
670 }
671 
GetSparkleFlags() const672 int corpse::GetSparkleFlags() const
673 {
674   torso* Torso = GetDeceased()->GetTorso();
675   material* Material = Torso->GetMainMaterial();
676   return Material->IsSparkling() ? SPARKLING_A|(!Torso->IsAlive() ? SPARKLING_B : 0) : 0;
677 }
678 
GetBitmapPos(int) const679 v2 corpse::GetBitmapPos(int) const
680 {
681   if(GetDeceased()->GetSize() < 50)
682     return v2(32, 64);
683   else if(GetDeceased()->GetSize() < 150)
684     return v2(16, 192);
685   else
686     return v2(16, 272);
687 }
688 
GetSize() const689 int corpse::GetSize() const
690 {
691   return GetDeceased()->GetSize();
692 }
693 
SetDeceased(character * What)694 void corpse::SetDeceased(character* What)
695 {
696   Deceased = What;
697   Deceased->SetMotherEntity(this);
698   SignalVolumeAndWeightChange();
699   SignalEmitationIncrease(Deceased->GetEmitation());
700   UpdatePictures();
701   Enable();
702 }
703 
DropEquipment(stack * Stack)704 void head::DropEquipment(stack* Stack)
705 {
706   if(Stack)
707   {
708     if(GetHelmet())
709       GetHelmet()->MoveTo(Stack);
710 
711     if(GetAmulet())
712       GetAmulet()->MoveTo(Stack);
713   }
714   else
715   {
716     if(GetHelmet())
717       GetSlot()->AddFriendItem(GetHelmet());
718 
719     if(GetAmulet())
720       GetSlot()->AddFriendItem(GetAmulet());
721   }
722 }
723 
DropEquipment(stack * Stack)724 void humanoidtorso::DropEquipment(stack* Stack)
725 {
726   if(Stack)
727   {
728     if(GetBodyArmor())
729       GetBodyArmor()->MoveTo(Stack);
730 
731     if(GetCloak())
732       GetCloak()->MoveTo(Stack);
733 
734     if(GetBelt())
735       GetBelt()->MoveTo(Stack);
736   }
737   else
738   {
739     if(GetBodyArmor())
740       GetSlot()->AddFriendItem(GetBodyArmor());
741 
742     if(GetCloak())
743       GetSlot()->AddFriendItem(GetCloak());
744 
745     if(GetBelt())
746       GetSlot()->AddFriendItem(GetBelt());
747   }
748 }
749 
DropEquipment(stack * Stack)750 void arm::DropEquipment(stack* Stack)
751 {
752   if(Stack)
753   {
754     if(GetWielded())
755       GetWielded()->MoveTo(Stack);
756 
757     if(GetGauntlet())
758       GetGauntlet()->MoveTo(Stack);
759 
760     if(GetRing())
761       GetRing()->MoveTo(Stack);
762   }
763   else
764   {
765     if(GetWielded())
766       GetSlot()->AddFriendItem(GetWielded());
767 
768     if(GetGauntlet())
769       GetSlot()->AddFriendItem(GetGauntlet());
770 
771     if(GetRing())
772       GetSlot()->AddFriendItem(GetRing());
773   }
774 }
775 
DropEquipment(stack * Stack)776 void leg::DropEquipment(stack* Stack)
777 {
778   if(Stack)
779   {
780     if(GetBoot())
781       GetBoot()->MoveTo(Stack);
782   }
783   else
784   {
785     if(GetBoot())
786       GetSlot()->AddFriendItem(GetBoot());
787   }
788 }
789 
~head()790 head::~head()
791 {
792   delete GetHelmet();
793   delete GetAmulet();
794 }
795 
~humanoidtorso()796 humanoidtorso::~humanoidtorso()
797 {
798   delete GetBodyArmor();
799   delete GetCloak();
800   delete GetBelt();
801 }
802 
~arm()803 arm::~arm()
804 {
805   delete GetWielded();
806   delete GetGauntlet();
807   delete GetRing();
808 }
809 
~leg()810 leg::~leg()
811 {
812   delete GetBoot();
813 }
814 
GetTruePrice() const815 long corpse::GetTruePrice() const
816 {
817   long Price = 0;
818 
819   for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c)
820   {
821     bodypart* BodyPart = GetDeceased()->GetBodyPart(c);
822 
823     if(BodyPart)
824       Price += BodyPart->GetTruePrice();
825   }
826 
827   return Price;
828 }
829 
GetMaterial(int I) const830 material* corpse::GetMaterial(int I) const
831 {
832   return GetDeceased()->GetTorso()->GetMaterial(I);
833 }
834 
GetSparkleFlags() const835 int bodypart::GetSparkleFlags() const
836 {
837   return (GetMainMaterial()->SkinColorIsSparkling() ? SPARKLING_A : 0)
838     | (Flags >> BODYPART_SPARKLE_SHIFT & (SPARKLING_B|SPARKLING_C|SPARKLING_D));
839 }
840 
RaiseTheDead(character * Summoner)841 truth corpse::RaiseTheDead(character* Summoner)
842 {
843   if(Summoner && Summoner->IsPlayer())
844     game::DoEvilDeed(50);
845 
846   GetDeceased()->Enable();
847 
848   if(GetDeceased()->TryToRiseFromTheDead())
849   {
850     v2 Pos = GetPos();
851     RemoveFromSlot();
852     GetDeceased()->SetMotherEntity(0);
853 
854     if(Summoner && GetDeceased()->CanTameWithResurrection(Summoner)
855                 && !GetDeceased()->IsPlayer())
856       GetDeceased()->ChangeTeam(Summoner->GetTeam());
857 
858     GetDeceased()->PutToOrNear(Pos);
859     GetDeceased()->SignalStepFrom(0);
860     Deceased = 0;
861     SendToHell();
862     return true;
863   }
864   else
865   {
866     GetDeceased()->Disable();
867     return false;
868   }
869 }
870 
head()871 head::head()
872 {
873   HelmetSlot.Init(this, HELMET_INDEX);
874   AmuletSlot.Init(this, AMULET_INDEX);
875 }
876 
humanoidtorso()877 humanoidtorso::humanoidtorso()
878 {
879   BodyArmorSlot.Init(this, BODY_ARMOR_INDEX);
880   CloakSlot.Init(this, CLOAK_INDEX);
881   BeltSlot.Init(this, BELT_INDEX);
882 }
883 
rightarm()884 rightarm::rightarm()
885 {
886   WieldedSlot.Init(this, RIGHT_WIELDED_INDEX);
887   GauntletSlot.Init(this, RIGHT_GAUNTLET_INDEX);
888   RingSlot.Init(this, RIGHT_RING_INDEX);
889 }
890 
leftarm()891 leftarm::leftarm()
892 {
893   WieldedSlot.Init(this, LEFT_WIELDED_INDEX);
894   GauntletSlot.Init(this, LEFT_GAUNTLET_INDEX);
895   RingSlot.Init(this, LEFT_RING_INDEX);
896 }
897 
rightleg()898 rightleg::rightleg()
899 {
900   BootSlot.Init(this, RIGHT_BOOT_INDEX);
901 }
902 
leftleg()903 leftleg::leftleg()
904 {
905   BootSlot.Init(this, LEFT_BOOT_INDEX);
906 }
907 
Hit(character * Enemy,v2 HitPos,int Direction,int Flags)908 void arm::Hit(character* Enemy, v2 HitPos, int Direction, int Flags)
909 {
910   long StrExp = 50, DexExp = 50;
911   truth THW = false;
912   item* Wielded = GetWielded();
913 
914   if(Wielded)
915   {
916     long Weight = Wielded->GetWeight();
917     StrExp = Limit(15 * Weight / 200L, 75L, 300L);
918     DexExp = Weight ? Limit(75000L / Weight, 75L, 300L) : 300;
919     THW = TwoHandWieldIsActive();
920   }
921 
922   switch(Enemy->TakeHit(Master, Wielded ? Wielded : GetGauntlet(), this, HitPos, GetTypeDamage(Enemy),
923                         GetToHitValue(), RAND() % 26 - RAND() % 26, Wielded ? WEAPON_ATTACK : UNARMED_ATTACK,
924                         Direction, !(RAND() % Master->GetCriticalModifier()), Flags & SADIST_HIT))
925   {
926    case HAS_HIT:
927    case HAS_BLOCKED:
928    case HAS_DIED:
929    case DID_NO_DAMAGE:
930     EditExperience(ARM_STRENGTH, StrExp, 1 << 9);
931 
932     if(THW && GetPairArm())
933       GetPairArm()->EditExperience(ARM_STRENGTH, StrExp, 1 << 9);
934 
935    case HAS_DODGED:
936     EditExperience(DEXTERITY, DexExp, 1 << 9);
937 
938     if(THW && GetPairArm())
939       GetPairArm()->EditExperience(DEXTERITY, DexExp, 1 << 9);
940   }
941 }
942 
GetAttribute(int Identifier,truth AllowBonus) const943 int arm::GetAttribute(int Identifier, truth AllowBonus) const
944 {
945   if(Identifier == ARM_STRENGTH)
946   {
947     int Base = !UseMaterialAttributes()
948                ? int(StrengthExperience * EXP_DIVISOR)
949                : GetMainMaterial()->GetStrengthValue();
950 
951     if(AllowBonus)
952       Base += StrengthBonus;
953 
954     return Max(!IsBadlyHurt() || !AllowBonus ? Base : Base / 3, 1);
955   }
956   else if(Identifier == DEXTERITY)
957   {
958     int Base = !UseMaterialAttributes()
959                ? int(DexterityExperience * EXP_DIVISOR)
960                : GetMainMaterial()->GetFlexibility() << 2;
961 
962     if(AllowBonus)
963       Base += DexterityBonus;
964 
965     return Max(IsUsable() || !AllowBonus ? Base : Base / 3, 1);
966   }
967   else
968   {
969     ABORT("Illegal arm attribute %d request!", Identifier);
970     return 0xACCA;
971   }
972 }
973 
EditAttribute(int Identifier,int Value)974 truth arm::EditAttribute(int Identifier, int Value)
975 {
976   if(!Master)
977     return false;
978 
979   if(Identifier == ARM_STRENGTH)
980   {
981     if(!UseMaterialAttributes()
982        && Master->RawEditAttribute(StrengthExperience, Value))
983     {
984       Master->CalculateBattleInfo();
985 
986       if(Master->IsPlayerKind())
987         UpdatePictures();
988 
989       return true;
990     }
991   }
992   else if(Identifier == DEXTERITY)
993     if(!UseMaterialAttributes()
994        && Master->RawEditAttribute(DexterityExperience, Value))
995     {
996       Master->CalculateBattleInfo();
997       return true;
998     }
999 
1000   return false;
1001 }
1002 
EditExperience(int Identifier,double Value,double Speed)1003 void arm::EditExperience(int Identifier, double Value, double Speed)
1004 {
1005   if(!Master)
1006     return;
1007 
1008   if(Identifier == ARM_STRENGTH)
1009   {
1010     if(!UseMaterialAttributes())
1011     {
1012       int Change = Master->RawEditExperience(StrengthExperience,
1013                                              Master->GetNaturalExperience(ARM_STRENGTH),
1014                                              Value, Speed);
1015 
1016       if(Change)
1017       {
1018         cchar* Adj = Change > 0 ? "stronger" : "weaker";
1019 
1020         if(Master->IsPlayer())
1021           ADD_MESSAGE("Your %s feels %s!", GetBodyPartName().CStr(), Adj);
1022         else if(Master->IsPet() && Master->CanBeSeenByPlayer())
1023           ADD_MESSAGE("Suddenly %s looks %s.", Master->CHAR_NAME(DEFINITE), Adj);
1024 
1025         Master->CalculateBattleInfo();
1026 
1027         if(Master->IsPlayerKind())
1028           UpdatePictures();
1029       }
1030     }
1031   }
1032   else if(Identifier == DEXTERITY)
1033   {
1034     if(!UseMaterialAttributes())
1035     {
1036       int Change = Master->RawEditExperience(DexterityExperience,
1037                                              Master->GetNaturalExperience(DEXTERITY),
1038                                              Value, Speed);
1039 
1040       if(Change)
1041       {
1042         cchar* Adj = Change > 0 ? "quite dextrous" : "clumsy";
1043 
1044         if(Master->IsPlayer())
1045           ADD_MESSAGE("Your %s feels %s!", GetBodyPartName().CStr(), Adj);
1046         else if(Master->IsPet() && Master->CanBeSeenByPlayer())
1047           ADD_MESSAGE("Suddenly %s looks %s.", Master->CHAR_NAME(DEFINITE), Adj);
1048 
1049         Master->CalculateBattleInfo();
1050       }
1051     }
1052   }
1053   else
1054     ABORT("Illegal arm attribute %d experience edit request!", Identifier);
1055 }
1056 
GetAttribute(int Identifier,truth AllowBonus) const1057 int leg::GetAttribute(int Identifier, truth AllowBonus) const
1058 {
1059   if(Identifier == LEG_STRENGTH)
1060   {
1061     int Base = !UseMaterialAttributes()
1062                ? int(StrengthExperience * EXP_DIVISOR)
1063                : GetMainMaterial()->GetStrengthValue();
1064 
1065     if(AllowBonus)
1066       Base += StrengthBonus;
1067 
1068     return Max(!IsBadlyHurt() || !AllowBonus ? Base : Base / 3, 1);
1069   }
1070   else if(Identifier == AGILITY)
1071   {
1072     int Base = !UseMaterialAttributes()
1073                ? int(AgilityExperience * EXP_DIVISOR)
1074                : GetMainMaterial()->GetFlexibility() << 2;
1075 
1076     if(AllowBonus)
1077       Base += AgilityBonus;
1078 
1079     return Max(IsUsable() || !AllowBonus ? Base : Base / 3, 1);
1080   }
1081   else
1082   {
1083     ABORT("Illegal leg attribute %d request!", Identifier);
1084     return 0xECCE;
1085   }
1086 }
1087 
EditAttribute(int Identifier,int Value)1088 truth leg::EditAttribute(int Identifier, int Value)
1089 {
1090   if(!Master)
1091     return false;
1092 
1093   if(Identifier == LEG_STRENGTH)
1094   {
1095     if(!UseMaterialAttributes()
1096        && Master->RawEditAttribute(StrengthExperience, Value))
1097     {
1098       Master->CalculateBurdenState();
1099       Master->CalculateBattleInfo();
1100       return true;
1101     }
1102   }
1103   else if(Identifier == AGILITY)
1104     if(!UseMaterialAttributes()
1105        && Master->RawEditAttribute(AgilityExperience, Value))
1106     {
1107       Master->CalculateBattleInfo();
1108       return true;
1109     }
1110 
1111   return false;
1112 }
1113 
EditExperience(int Identifier,double Value,double Speed)1114 void leg::EditExperience(int Identifier, double Value, double Speed)
1115 {
1116   if(!Master)
1117     return;
1118 
1119   if(Identifier == LEG_STRENGTH)
1120   {
1121     if(!UseMaterialAttributes())
1122     {
1123       int Change = Master->RawEditExperience(StrengthExperience,
1124                                              Master->GetNaturalExperience(LEG_STRENGTH),
1125                                              Value, Speed);
1126 
1127       if(Change)
1128       {
1129         cchar* Adj = Change > 0 ? "stronger" : "weaker";
1130 
1131         if(Master->IsPlayer())
1132           ADD_MESSAGE("Your %s feels %s!", GetBodyPartName().CStr(), Adj);
1133         else if(Master->IsPet() && Master->CanBeSeenByPlayer())
1134           ADD_MESSAGE("Suddenly %s looks %s.", Master->CHAR_NAME(DEFINITE), Adj);
1135 
1136         Master->CalculateBurdenState();
1137         Master->CalculateBattleInfo();
1138       }
1139     }
1140   }
1141   else if(Identifier == AGILITY)
1142   {
1143     if(!UseMaterialAttributes())
1144     {
1145       int Change = Master->RawEditExperience(AgilityExperience,
1146                                              Master->GetNaturalExperience(AGILITY),
1147                                              Value, Speed);
1148 
1149       if(Change)
1150       {
1151         cchar* Adj = Change > 0 ? "very agile" : "slower";
1152 
1153         if(Master->IsPlayer())
1154           ADD_MESSAGE("Your %s feels %s!", GetBodyPartName().CStr(), Adj);
1155         else if(Master->IsPet() && Master->CanBeSeenByPlayer())
1156           ADD_MESSAGE("Suddenly %s looks %s.", Master->CHAR_NAME(DEFINITE), Adj);
1157 
1158         Master->CalculateBattleInfo();
1159       }
1160     }
1161   }
1162   else
1163     ABORT("Illegal leg attribute %d experience edit request!", Identifier);
1164 }
1165 
InitSpecialAttributes()1166 void head::InitSpecialAttributes()
1167 {
1168   BaseBiteStrength = Master->GetBaseBiteStrength();
1169   BonusBiteStrength = Master->GetBonusBiteStrength();
1170 }
1171 
InitSpecialAttributes()1172 void arm::InitSpecialAttributes()
1173 {
1174   if(!Master->IsHuman() || Master->IsInitializing())
1175   {
1176     StrengthExperience = Master->GetNaturalExperience(ARM_STRENGTH);
1177     DexterityExperience = Master->GetNaturalExperience(DEXTERITY);
1178   }
1179   else
1180   {
1181     StrengthExperience = game::GetAveragePlayerArmStrengthExperience();
1182     DexterityExperience = game::GetAveragePlayerDexterityExperience();
1183   }
1184 
1185   LimitRef(StrengthExperience, MIN_EXP, MAX_EXP);
1186   LimitRef(DexterityExperience, MIN_EXP, MAX_EXP);
1187   BaseUnarmedStrength = Master->GetBaseUnarmedStrength();
1188 }
1189 
InitSpecialAttributes()1190 void leg::InitSpecialAttributes()
1191 {
1192   if(!Master->IsHuman() || Master->IsInitializing())
1193   {
1194     StrengthExperience = Master->GetNaturalExperience(LEG_STRENGTH);
1195     AgilityExperience = Master->GetNaturalExperience(AGILITY);
1196   }
1197   else
1198   {
1199     StrengthExperience = game::GetAveragePlayerLegStrengthExperience();
1200     AgilityExperience = game::GetAveragePlayerAgilityExperience();
1201   }
1202 
1203   LimitRef(StrengthExperience, MIN_EXP, MAX_EXP);
1204   LimitRef(AgilityExperience, MIN_EXP, MAX_EXP);
1205   BaseKickStrength = Master->GetBaseKickStrength();
1206 }
1207 
SignalEquipmentAdd(gearslot * Slot)1208 void bodypart::SignalEquipmentAdd(gearslot* Slot)
1209 {
1210   if(Master)
1211     Master->SignalEquipmentAdd(Slot->GetEquipmentIndex());
1212 }
1213 
SignalEquipmentRemoval(gearslot * Slot,citem * Item)1214 void bodypart::SignalEquipmentRemoval(gearslot* Slot, citem* Item)
1215 {
1216   if(Master)
1217     Master->SignalEquipmentRemoval(Slot->GetEquipmentIndex(), Item);
1218 }
1219 
Mutate()1220 void bodypart::Mutate()
1221 {
1222   GetMainMaterial()->SetVolume(long(GetVolume() * (1.5 - (RAND() & 1023) / 1023.)));
1223 }
1224 
Mutate()1225 void arm::Mutate()
1226 {
1227   bodypart::Mutate();
1228   StrengthExperience = StrengthExperience * (1.5 - (RAND() & 1023) / 1023.);
1229   DexterityExperience = DexterityExperience * (1.5 - (RAND() & 1023) / 1023.);
1230   LimitRef(StrengthExperience, MIN_EXP, MAX_EXP);
1231   LimitRef(DexterityExperience, MIN_EXP, MAX_EXP);
1232 }
1233 
Mutate()1234 void leg::Mutate()
1235 {
1236   bodypart::Mutate();
1237   StrengthExperience = StrengthExperience * (1.5 - (RAND() & 1023) / 1023.);
1238   AgilityExperience = AgilityExperience * (1.5 - (RAND() & 1023) / 1023.);
1239   LimitRef(StrengthExperience, MIN_EXP, MAX_EXP);
1240   LimitRef(AgilityExperience, MIN_EXP, MAX_EXP);
1241 }
1242 
GetPairArm() const1243 arm* rightarm::GetPairArm() const
1244 {
1245   return GetHumanoidMaster() ? GetHumanoidMaster()->GetLeftArm() : 0;
1246 }
1247 
GetPairArm() const1248 arm* leftarm::GetPairArm() const
1249 {
1250   return GetHumanoidMaster() ? GetHumanoidMaster()->GetRightArm() : 0;
1251 }
1252 
GetCurrentSWeaponSkill() const1253 sweaponskill** rightarm::GetCurrentSWeaponSkill() const
1254 {
1255   return &GetHumanoidMaster()->CurrentRightSWeaponSkill;
1256 }
1257 
GetCurrentSWeaponSkill() const1258 sweaponskill** leftarm::GetCurrentSWeaponSkill() const
1259 {
1260   return &GetHumanoidMaster()->CurrentLeftSWeaponSkill;
1261 }
1262 
GetMaxAlpha() const1263 alpha bodypart::GetMaxAlpha() const
1264 {
1265   if(Master && (Master->StateIsActivated(INVISIBLE) || !!Master->GhostCopyMaterials()))
1266     return 150;
1267   else
1268     return 255;
1269 }
1270 
AddPostFix(festring & String,int) const1271 void bodypart::AddPostFix(festring& String, int) const
1272 {
1273   if(!OwnerDescription.IsEmpty())
1274     String << ' ' << OwnerDescription;
1275 }
1276 
CalculateVolumeAndWeight()1277 void corpse::CalculateVolumeAndWeight()
1278 {
1279   Volume = Deceased->GetVolume();
1280   Weight = Deceased->GetWeight();
1281 }
1282 
GetEquipment(int I) const1283 item* head::GetEquipment(int I) const
1284 {
1285   switch(I)
1286   {
1287    case 0: return GetHelmet();
1288    case 1: return GetAmulet();
1289   }
1290 
1291   return 0;
1292 }
1293 
GetEquipment(int I) const1294 item* humanoidtorso::GetEquipment(int I) const
1295 {
1296   switch(I)
1297   {
1298    case 0: return GetBodyArmor();
1299    case 1: return GetCloak();
1300    case 2: return GetBelt();
1301   }
1302 
1303   return 0;
1304 }
1305 
GetEquipment(int I) const1306 item* arm::GetEquipment(int I) const
1307 {
1308   switch(I)
1309   {
1310    case 0: return GetWielded();
1311    case 1: return GetGauntlet();
1312    case 2: return GetRing();
1313   }
1314 
1315   return 0;
1316 }
1317 
GetEquipment(int I) const1318 item* leg::GetEquipment(int I) const
1319 {
1320   return !I ? GetBoot() : 0;
1321 }
1322 
CalculateVolumeAndWeight()1323 void bodypart::CalculateVolumeAndWeight()
1324 {
1325   item::CalculateVolumeAndWeight();
1326   CarriedWeight = 0;
1327   BodyPartVolume = Volume;
1328 
1329   for(int c = 0; c < GetEquipments(); ++c)
1330   {
1331     item* Equipment = GetEquipment(c);
1332 
1333     if(Equipment)
1334     {
1335       Volume += Equipment->GetVolume();
1336       CarriedWeight += Equipment->GetWeight();
1337     }
1338   }
1339 
1340   Weight += CarriedWeight;
1341 }
1342 
CalculateEmitation()1343 void corpse::CalculateEmitation()
1344 {
1345   Emitation = Deceased->GetEmitation();
1346 }
1347 
CalculateEmitation()1348 void bodypart::CalculateEmitation()
1349 {
1350   item::CalculateEmitation();
1351 
1352   for(int c = 0; c < GetEquipments(); ++c)
1353   {
1354     item* Equipment = GetEquipment(c);
1355 
1356     if(Equipment)
1357       game::CombineLights(Emitation, Equipment->GetEmitation());
1358   }
1359 }
1360 
CalculateMaxHP(ulong Flags)1361 void bodypart::CalculateMaxHP(ulong Flags)
1362 {
1363   int HPDelta = MaxHP - HP, OldMaxHP = MaxHP;
1364   MaxHP = 0;
1365   int BurnLevel = 0;
1366 
1367   if(Master)
1368   {
1369     if(!UseMaterialAttributes())
1370     {
1371       long Endurance = Master->GetAttribute(ENDURANCE);
1372       double DoubleHP = GetBodyPartVolume() * Endurance * Endurance / 200000;
1373 
1374       for(size_t c = 0; c < Scar.size(); ++c)
1375         DoubleHP *= (100. - Scar[c].Severity * 4) / 100;
1376 
1377       if(MainMaterial)
1378       {
1379         BurnLevel = MainMaterial->GetBurnLevel();
1380         DoubleHP *= 1. * (4 - BurnLevel) / 4;
1381       }
1382 
1383       MaxHP = int(DoubleHP);
1384     }
1385     else
1386     {
1387       long SV = GetMainMaterial()->GetStrengthValue();
1388       MaxHP = (GetBodyPartVolume() * SV >> 4) * SV / 250000;
1389     }
1390 
1391     if(MaxHP < 1)
1392       MaxHP = 1;
1393 
1394     if(Flags & MAY_CHANGE_HPS)
1395     {
1396       if(MaxHP - HPDelta > 1)
1397         HP = MaxHP - HPDelta;
1398       else
1399         HP = 1;
1400     }
1401     else
1402     {
1403       //OldMaxHP - MaxHP;
1404     }
1405 
1406     if(Flags & CHECK_USABILITY)
1407       SignalPossibleUsabilityChange();
1408   }
1409 }
1410 
SignalVolumeAndWeightChange()1411 void bodypart::SignalVolumeAndWeightChange()
1412 {
1413   item::SignalVolumeAndWeightChange();
1414 
1415   if(Master && !Master->IsInitializing())
1416   {
1417     CalculateMaxHP();
1418     Master->CalculateHP();
1419     Master->CalculateMaxHP();
1420     Master->SignalBodyPartVolumeAndWeightChange();
1421     square* SquareUnder = GetSquareUnder();
1422 
1423     if(UpdateArmorPictures() && SquareUnder)
1424       SquareUnder->SendNewDrawRequest();
1425   }
1426 }
1427 
1428 /*{
1429   for(;;)
1430   {
1431     damageid& D = DamageID.back();
1432     D.
1433   }
1434 }*/
1435 
SetHP(int What)1436 void bodypart::SetHP(int What)
1437 {
1438   HP = What;
1439 
1440   if(Master)
1441   {
1442     Master->CalculateHP();
1443     SignalPossibleUsabilityChange();
1444   }
1445 }
1446 
EditHP(int SrcID,int What)1447 void bodypart::EditHP(int SrcID, int What)
1448 {
1449   HP += What;
1450 
1451   if(What < 0)
1452     RemoveDamageIDs(-What);
1453   else
1454     AddDamageID(SrcID, What);
1455 
1456   if(Master)
1457   {
1458     Master->CalculateHP();
1459     SignalPossibleUsabilityChange();
1460   }
1461 }
1462 
SignalVolumeAndWeightChange()1463 void arm::SignalVolumeAndWeightChange()
1464 {
1465   bodypart::SignalVolumeAndWeightChange();
1466 
1467   if(Master && !Master->IsInitializing())
1468   {
1469     GetHumanoidMaster()->EnsureCurrentSWeaponSkillIsCorrect(*GetCurrentSWeaponSkill(), GetWielded());
1470     CalculateAttributeBonuses();
1471     CalculateAttackInfo();
1472     UpdateWieldedPicture();
1473 
1474     if(GetSquareUnder())
1475       GetSquareUnder()->SendNewDrawRequest();
1476   }
1477 }
1478 
SignalVolumeAndWeightChange()1479 void leg::SignalVolumeAndWeightChange()
1480 {
1481   bodypart::SignalVolumeAndWeightChange();
1482 
1483   if(Master && !Master->IsInitializing())
1484   {
1485     CalculateAttributeBonuses();
1486     CalculateAttackInfo();
1487   }
1488 }
1489 
SignalVolumeAndWeightChange()1490 void humanoidtorso::SignalVolumeAndWeightChange()
1491 {
1492   bodypart::SignalVolumeAndWeightChange();
1493 
1494   if(Master && !Master->IsInitializing())
1495   {
1496     humanoid* HumanoidMaster = GetHumanoidMaster();
1497 
1498     if(HumanoidMaster->GetRightArm())
1499       HumanoidMaster->GetRightArm()->CalculateAttributeBonuses();
1500 
1501     if(HumanoidMaster->GetLeftArm())
1502       HumanoidMaster->GetLeftArm()->CalculateAttributeBonuses();
1503 
1504     if(HumanoidMaster->GetRightLeg())
1505       HumanoidMaster->GetRightLeg()->CalculateAttributeBonuses();
1506 
1507     if(HumanoidMaster->GetLeftLeg())
1508       HumanoidMaster->GetLeftLeg()->CalculateAttributeBonuses();
1509   }
1510 }
1511 
CalculateAttackInfo()1512 void bodypart::CalculateAttackInfo()
1513 {
1514   CalculateDamage();
1515   CalculateToHitValue();
1516   CalculateAPCost();
1517 }
1518 
TwoHandWieldIsActive() const1519 truth arm::TwoHandWieldIsActive() const
1520 {
1521   citem* Wielded = GetWielded();
1522 
1523   if(Wielded->IsTwoHanded() && !Wielded->IsShield(Master))
1524   {
1525     arm* PairArm = GetPairArm();
1526     return PairArm && PairArm->IsUsable() && !PairArm->GetWielded();
1527   }
1528   else
1529     return false;
1530 }
1531 
GetTimeToDie(int Damage,double ToHitValue,double DodgeValue,truth AttackIsBlockable,truth UseMaxHP) const1532 double bodypart::GetTimeToDie(int Damage, double ToHitValue, double DodgeValue,
1533                               truth AttackIsBlockable, truth UseMaxHP) const
1534 {
1535   double Durability;
1536   int TotalResistance = GetTotalResistance(PHYSICAL_DAMAGE);
1537   int Damage3 = (Damage << 1) + Damage;
1538   int Damage5 = (Damage << 2) + Damage;
1539   int TrueDamage = (19 * (Max((Damage3 >> 2) - TotalResistance, 0)
1540                           + Max((Damage5 >> 2) + 1 - (TotalResistance >> 1), 0))
1541                     + (Max(((Damage3 + (Damage3 >> 1)) >> 2) - TotalResistance, 0)
1542                        + Max(((Damage5 + (Damage5 >> 1)) >> 2) + 3 - (TotalResistance >> 1), 0))) / 40;
1543 
1544   int HP = UseMaxHP ? GetMaxHP() : GetHP();
1545 
1546   if(TrueDamage > 0)
1547   {
1548     double AverageDamage;
1549 
1550     if(AttackIsBlockable)
1551     {
1552       blockvector Block;
1553       Master->CreateBlockPossibilityVector(Block, ToHitValue);
1554 
1555       if(Block.size())
1556       {
1557         double ChanceForNoBlock = 1.0;
1558         AverageDamage = 0;
1559 
1560         for(uint c = 0; c < Block.size(); ++c)
1561         {
1562           ChanceForNoBlock -= Block[c].first;
1563 
1564           if(TrueDamage - Block[c].second > 0)
1565             AverageDamage += Block[c].first * (TrueDamage - Block[c].second);
1566         }
1567 
1568         AverageDamage += ChanceForNoBlock * TrueDamage;
1569       }
1570       else
1571         AverageDamage = TrueDamage;
1572     }
1573     else
1574       AverageDamage = TrueDamage;
1575 
1576     Durability = HP / (AverageDamage * GetRoughChanceToHit(ToHitValue, DodgeValue));
1577 
1578     if(Durability < 1)
1579       Durability = 1;
1580 
1581     if(Durability > 1000)
1582       Durability = 1000;
1583   }
1584   else
1585     Durability = 1000;
1586 
1587   return Durability;
1588 }
1589 
GetRoughChanceToHit(double ToHitValue,double DodgeValue) const1590 double bodypart::GetRoughChanceToHit(double ToHitValue, double DodgeValue) const
1591 {
1592   return GLOBAL_WEAK_BODYPART_HIT_MODIFIER * ToHitValue * GetBodyPartVolume()
1593          / ((DodgeValue / ToHitValue + 1) * DodgeValue * Master->GetBodyVolume() * 100);
1594 }
1595 
GetRoughChanceToHit(double ToHitValue,double DodgeValue) const1596 double torso::GetRoughChanceToHit(double ToHitValue, double DodgeValue) const
1597 {
1598   return 1 / (DodgeValue / ToHitValue + 1);
1599 }
1600 
RandomizePosition()1601 void bodypart::RandomizePosition()
1602 {
1603   SpecialFlags |= 1 + RAND() % 7;
1604   UpdatePictures();
1605 }
1606 
GetBlockChance(double EnemyToHitValue) const1607 double arm::GetBlockChance(double EnemyToHitValue) const
1608 {
1609   citem* Wielded = GetWielded();
1610   return Wielded ? Min(1.0 / (1 + EnemyToHitValue / (GetToHitValue() * Wielded->GetBlockModifier()) * 10000), 1.0) : 0;
1611 }
1612 
GetBlockCapability() const1613 int arm::GetBlockCapability() const
1614 {
1615   citem* Wielded = GetWielded();
1616 
1617   if(!Wielded)
1618     return 0;
1619 
1620   int HitStrength = GetWieldedHitStrength();
1621 
1622   if(HitStrength <= 0)
1623     return 0;
1624 
1625   return Min(HitStrength, 10) * Wielded->GetStrengthValue()
1626          * GetHumanoidMaster()->GetCWeaponSkill(Wielded->GetWeaponCategory())->GetBonus()
1627          * (*GetCurrentSWeaponSkill())->GetBonus() / 10000000;
1628 }
1629 
WieldedSkillHit(int Hits)1630 void arm::WieldedSkillHit(int Hits)
1631 {
1632   item* Wielded = GetWielded();
1633 
1634   if(Master->GetCWeaponSkill(Wielded->GetWeaponCategory())->AddHit(Hits))
1635   {
1636     CalculateAttackInfo();
1637 
1638     if(Master->IsPlayer())
1639     {
1640       int Category = Wielded->GetWeaponCategory();
1641       GetHumanoidMaster()->GetCWeaponSkill(Category)->AddLevelUpMessage(Category);
1642     }
1643   }
1644 
1645   if((*GetCurrentSWeaponSkill())->AddHit(Hits))
1646   {
1647     CalculateAttackInfo();
1648 
1649     if(Master->IsPlayer())
1650       (*GetCurrentSWeaponSkill())->AddLevelUpMessage(Wielded->CHAR_NAME(UNARTICLED));
1651   }
1652 }
1653 
head(const head & Head)1654 head::head(const head& Head) : mybase(Head), BaseBiteStrength(Head.BaseBiteStrength), BonusBiteStrength(Head.BonusBiteStrength)
1655 {
1656   HelmetSlot.Init(this, HELMET_INDEX);
1657   AmuletSlot.Init(this, AMULET_INDEX);
1658 }
1659 
humanoidtorso(const humanoidtorso & Torso)1660 humanoidtorso::humanoidtorso(const humanoidtorso& Torso) : mybase(Torso)
1661 {
1662   BodyArmorSlot.Init(this, BODY_ARMOR_INDEX);
1663   CloakSlot.Init(this, CLOAK_INDEX);
1664   BeltSlot.Init(this, BELT_INDEX);
1665 }
1666 
arm(const arm & Arm)1667 arm::arm(const arm& Arm)
1668 : mybase(Arm),
1669   StrengthExperience(Arm.StrengthExperience),
1670   DexterityExperience(Arm.DexterityExperience),
1671   BaseUnarmedStrength(Arm.BaseUnarmedStrength)
1672 {
1673 }
1674 
rightarm(const rightarm & Arm)1675 rightarm::rightarm(const rightarm& Arm) : mybase(Arm)
1676 {
1677   WieldedSlot.Init(this, RIGHT_WIELDED_INDEX);
1678   GauntletSlot.Init(this, RIGHT_GAUNTLET_INDEX);
1679   RingSlot.Init(this, RIGHT_RING_INDEX);
1680 }
1681 
leftarm(const leftarm & Arm)1682 leftarm::leftarm(const leftarm& Arm) : mybase(Arm)
1683 {
1684   WieldedSlot.Init(this, LEFT_WIELDED_INDEX);
1685   GauntletSlot.Init(this, LEFT_GAUNTLET_INDEX);
1686   RingSlot.Init(this, LEFT_RING_INDEX);
1687 }
1688 
leg(const leg & Leg)1689 leg::leg(const leg& Leg)
1690 : mybase(Leg),
1691   StrengthExperience(Leg.StrengthExperience),
1692   AgilityExperience(Leg.AgilityExperience),
1693   BaseKickStrength(Leg.BaseKickStrength)
1694 {
1695 }
1696 
rightleg(const rightleg & Leg)1697 rightleg::rightleg(const rightleg& Leg) : mybase(Leg)
1698 {
1699   BootSlot.Init(this, RIGHT_BOOT_INDEX);
1700 }
1701 
leftleg(const leftleg & Leg)1702 leftleg::leftleg(const leftleg& Leg) : mybase(Leg)
1703 {
1704   BootSlot.Init(this, LEFT_BOOT_INDEX);
1705 }
1706 
corpse(const corpse & Corpse)1707 corpse::corpse(const corpse& Corpse) : mybase(Corpse)
1708 {
1709   Deceased = Corpse.Deceased->Duplicate();
1710   Deceased->SetMotherEntity(this);
1711 }
1712 
SignalSpoil(material * Material)1713 void bodypart::SignalSpoil(material* Material)
1714 {
1715   if(Master)
1716     Master->SignalSpoil();
1717   else
1718     item::SignalSpoil(Material);
1719 }
1720 
SignalSpoil(material *)1721 void corpse::SignalSpoil(material*)
1722 {
1723   GetDeceased()->Disappear(this, "spoil", &item::IsVeryCloseToSpoiling);
1724 }
1725 
TestActivationEnergy(int Damage)1726 truth bodypart::TestActivationEnergy(int Damage)
1727 {
1728 //  if(MainMaterial)
1729 //  {
1730 //    int molamola = ((GetMainMaterial()->GetStrengthValue() >> 1)
1731 //                    + 5 * MainMaterial->GetFireResistance() + GetResistance(FIRE));
1732 //    ADD_MESSAGE("%s is being tested (Damage is %d, AE is %d)", CHAR_NAME(DEFINITE), Damage, molamola);
1733 //  }
1734   if(Damage <= 0)
1735     return false;
1736 
1737   character* Owner = GetMaster();
1738   truth Success = false;
1739 
1740   if(Owner)
1741     if(Owner->BodyPartIsVital(GetBodyPartIndex()) || !CanBeBurned())
1742       return Success;
1743 
1744   if(MainMaterial)
1745   {
1746     int TestDamage = Damage + MainMaterial->GetTransientThermalEnergy();
1747     GetMainMaterial()->AddToTransientThermalEnergy(Damage);
1748     if((GetMainMaterial()->GetInteractionFlags() & CAN_BURN)
1749        && TestDamage >= ((GetMainMaterial()->GetStrengthValue() >> 1)
1750                          + 5 * MainMaterial->GetFireResistance() + GetResistance(FIRE)))
1751     {
1752       if(Owner)
1753       {
1754         if(Owner->IsPlayer())
1755           ADD_MESSAGE("Your %s catches fire!", GetBodyPartName().CStr());
1756         else if(Owner->CanBeSeenByPlayer())
1757           ADD_MESSAGE("The %s of %s catches fire!", GetBodyPartName().CStr(), Owner->CHAR_NAME(DEFINITE));
1758       }
1759       //ADD_MESSAGE("%s catches fire! (TestDamage was %d)", CHAR_NAME(DEFINITE), TestDamage); //CLEANUP
1760       Ignite();
1761       GetMainMaterial()->AddToSteadyStateThermalEnergy(Damage);
1762       Success = true;
1763     }
1764   }
1765   return Success;
1766 }
1767 
SignalBurn(material * Material)1768 void bodypart::SignalBurn(material* Material)
1769 {
1770   int BodyPartIndex = GetBodyPartIndex();
1771 
1772   if(Master)
1773   {
1774     character* Owner = GetMaster();
1775 
1776     if(Owner->IsPlayer())
1777       ADD_MESSAGE("Your %s burns away completely!", GetBodyPartName().CStr());
1778     else if(Owner->CanBeSeenByPlayer())
1779       ADD_MESSAGE("The %s of %s burns away completely!", GetBodyPartName().CStr(), Owner->CHAR_NAME(DEFINITE));
1780 
1781     /*GetBodyPart(BodyPartIndex)->*/DropEquipment(!game::IsInWilderness() ? Owner->GetStackUnder() : Owner->GetStack());
1782     /*item* Burnt = */Owner->SevereBodyPart(BodyPartIndex, true);
1783 /* // create lumps of (charred?) flesh... (note, remove 'true' from item* Burnt = SevereBodyPart(BodyPartIndex, true);
1784     if(Burnt)
1785       Burnt->DestroyBodyPart(!game::IsInWilderness() ? GetStackUnder() : GetStack());
1786 */
1787     Owner->SendNewDrawRequest();
1788 
1789     if(Owner->IsPlayer())
1790       game::AskForKeyPress(CONST_S("Bodypart destroyed! [press any key to continue]"));
1791   }
1792   else
1793     item::SignalBurn(Material);
1794 }
1795 
Extinguish(truth SendMessages)1796 void bodypart::Extinguish(truth SendMessages)
1797 {
1798   if(Master)
1799   {
1800     item::Extinguish(SendMessages); //Master->Extinguish();
1801     Master->UpdatePictures();
1802   }
1803   else
1804     item::Extinguish(SendMessages);
1805 }
1806 
SignalBurn(material *)1807 void corpse::SignalBurn(material*)
1808 {
1809   GetDeceased()->Disappear(this, "burn", &item::IsVeryCloseToBurning);
1810 }
1811 
AddExtinguishMessage()1812 void bodypart::AddExtinguishMessage()
1813 {
1814   if(Master)
1815   {
1816     character* Owner = GetMaster();
1817 
1818     if(Owner->IsPlayer())
1819       ADD_MESSAGE("The flames on your %s die away.", GetBodyPartName().CStr());
1820     else if(Owner->CanBeSeenByPlayer())
1821       ADD_MESSAGE("The flames on the %s of %s die away.", GetBodyPartName().CStr(), Owner->CHAR_NAME(DEFINITE));
1822   }
1823   else
1824     item::AddExtinguishMessage();
1825 }
1826 
AddSpecialExtinguishMessageForPF()1827 void bodypart::AddSpecialExtinguishMessageForPF()
1828 {
1829   if(Master)
1830   {
1831     character* Owner = GetMaster();
1832 
1833     if(Owner->IsPlayer())
1834       ADD_MESSAGE("Your %s burns even more! But lo', even as it does so, the ashes "
1835                   "peel away from your %s and it is made new by some innate virtue!",
1836                   CHAR_NAME(UNARTICLED), GetBodyPartName().CStr());
1837     else if(Owner->CanBeSeenByPlayer())
1838       ADD_MESSAGE("The %s of %s burns even more! But lo', even as it does so, the "
1839                   "ashes peel away from %s %s and it is made new by some innate virtue!",
1840                   GetBodyPartName().CStr(), Owner->CHAR_NAME(DEFINITE),
1841                   Owner->GetPossessivePronoun().CStr(), GetBodyPartName().CStr());
1842   }
1843   else
1844     item::AddSpecialExtinguishMessageForPF();
1845 }
1846 
Extinguish(truth SendMessages)1847 void corpse::Extinguish(truth SendMessages)
1848 {
1849   GetDeceased()->Extinguish(SendMessages);
1850 }
1851 
SignalDisappearance()1852 void corpse::SignalDisappearance()
1853 {
1854   GetDeceased()->Disappear(this, "disappear", &item::IsVeryCloseToDisappearance);
1855 }
1856 
CanBePiledWith(citem * Item,ccharacter * Viewer) const1857 truth bodypart::CanBePiledWith(citem* Item, ccharacter* Viewer) const
1858 {
1859   return item::CanBePiledWith(Item, Viewer)
1860     && OwnerDescription == static_cast<const bodypart*>(Item)->OwnerDescription;
1861 }
1862 
CanBePiledWith(citem * Item,ccharacter * Viewer) const1863 truth corpse::CanBePiledWith(citem* Item, ccharacter* Viewer) const
1864 {
1865   if(GetType() != Item->GetType()
1866      || GetConfig() != Item->GetConfig()
1867      || GetWeight() != Item->GetWeight()
1868      || Viewer->GetCWeaponSkillLevel(this) != Viewer->GetCWeaponSkillLevel(Item)
1869      || Viewer->GetSWeaponSkillLevel(this) != Viewer->GetSWeaponSkillLevel(Item))
1870     return false;
1871 
1872   const corpse* Corpse = static_cast<const corpse*>(Item);
1873 
1874   if(Deceased->GetBodyParts() != Corpse->Deceased->GetBodyParts())
1875     return false;
1876 
1877   for(int c = 0; c < Deceased->GetBodyParts(); ++c)
1878   {
1879     bodypart* BodyPart1 = Deceased->GetBodyPart(c);
1880     bodypart* BodyPart2 = Corpse->Deceased->GetBodyPart(c);
1881 
1882     if(!BodyPart1 && !BodyPart2)
1883       continue;
1884 
1885     if(!BodyPart1 || !BodyPart2 || !BodyPart1->CanBePiledWith(BodyPart2, Viewer))
1886       return false;
1887   }
1888 
1889   if(Deceased->GetName(UNARTICLED) != Corpse->Deceased->GetName(UNARTICLED))
1890     return false;
1891 
1892   return true;
1893 }
1894 
Be()1895 void bodypart::Be()
1896 {
1897   if(Master)
1898   {
1899     if(HP < MaxHP && ++SpillBloodCounter >= 4)
1900     {
1901       if(Master->IsEnabled())
1902       {
1903         if(IsBadlyHurt() && !Master->IsPolymorphed() && !(RAND() & 3))
1904           SpillBlood(1);
1905       }
1906       else if(!Master->IsPolymorphed() && !(RAND() & 3))
1907       {
1908         SpillBlood(1);
1909         HP += Max(((MaxHP - HP) >> 2), 1);
1910       }
1911 
1912       SpillBloodCounter = 0;
1913     }
1914     // Organics can have an active Be() function, if they are burning...
1915     // they will normally burn completely before they spoil
1916     if(Master->AllowSpoil() || !Master->IsEnabled() || !!IsBurning())
1917       MainMaterial->Be(ItemFlags);
1918 
1919     if(Exists() && LifeExpectancy)
1920     {
1921       if(LifeExpectancy == 1)
1922         Master->SignalDisappearance();
1923       else
1924         --LifeExpectancy;
1925     }
1926   }
1927   else
1928   {
1929     if(HP < MaxHP && ++SpillBloodCounter >= 4)
1930     {
1931       if(!(RAND() & 3))
1932       {
1933         SpillBlood(1);
1934         HP += Max(((MaxHP - HP) >> 2), 1);
1935       }
1936 
1937       SpillBloodCounter = 0;
1938     }
1939 
1940     item::Be();
1941   }
1942 }
1943 
SpillBlood(int HowMuch,v2 Pos)1944 void bodypart::SpillBlood(int HowMuch, v2 Pos)
1945 {
1946   if(HowMuch
1947      && (!Master || Master->SpillsBlood())
1948      && (IsAlive() || MainMaterial->IsLiquid())
1949      && !game::IsInWilderness())
1950     GetNearLSquare(Pos)->SpillFluid(0, CreateBlood(long(HowMuch * sqrt(BodyPartVolume) / 2)), false, false);
1951 }
1952 
SpillBlood(int HowMuch)1953 void bodypart::SpillBlood(int HowMuch)
1954 {
1955   if(HowMuch
1956      && (!Master || Master->SpillsBlood())
1957      && (IsAlive() || MainMaterial->IsLiquid())
1958      && !game::IsInWilderness())
1959     for(int c = 0; c < GetSquaresUnder(); ++c)
1960       if(GetLSquareUnder(c))
1961         GetLSquareUnder(c)->SpillFluid(0, CreateBlood(long(HowMuch * sqrt(BodyPartVolume) / 2)), false, false);
1962 }
1963 
SignalEnchantmentChange()1964 void bodypart::SignalEnchantmentChange()
1965 {
1966   if(Master && !Master->IsInitializing())
1967   {
1968     Master->CalculateAttributeBonuses();
1969     Master->CalculateBattleInfo();
1970   }
1971 }
1972 
SignalEquipmentAdd(gearslot * Slot)1973 void arm::SignalEquipmentAdd(gearslot* Slot)
1974 {
1975   int EquipmentIndex = Slot->GetEquipmentIndex();
1976 
1977   if(Master && !Master->IsInitializing())
1978   {
1979     item* Equipment = Slot->GetItem();
1980 
1981     if(Equipment->IsInCorrectSlot(EquipmentIndex))
1982       ApplyEquipmentAttributeBonuses(Equipment);
1983 
1984     if(EquipmentIndex == RIGHT_GAUNTLET_INDEX || EquipmentIndex == LEFT_GAUNTLET_INDEX)
1985       ApplyDexterityPenalty(Equipment);
1986 
1987     if(EquipmentIndex == RIGHT_WIELDED_INDEX || EquipmentIndex == LEFT_WIELDED_INDEX)
1988     {
1989       UpdateWieldedPicture();
1990       GetSquareUnder()->SendNewDrawRequest();
1991     }
1992   }
1993 
1994   if(Master)
1995     Master->SignalEquipmentAdd(EquipmentIndex);
1996 }
1997 
SignalEquipmentRemoval(gearslot * Slot,citem * Item)1998 void arm::SignalEquipmentRemoval(gearslot* Slot, citem* Item)
1999 {
2000   int EquipmentIndex = Slot->GetEquipmentIndex();
2001 
2002   if(Master && !Master->IsInitializing())
2003     if(EquipmentIndex == RIGHT_WIELDED_INDEX || EquipmentIndex == LEFT_WIELDED_INDEX)
2004     {
2005       UpdateWieldedPicture();
2006       square* Square = GetSquareUnder();
2007 
2008       if(Square)
2009         Square->SendNewDrawRequest();
2010     }
2011 
2012   if(Master)
2013     Master->SignalEquipmentRemoval(EquipmentIndex, Item);
2014 }
2015 
SignalEquipmentAdd(gearslot * Slot)2016 void leg::SignalEquipmentAdd(gearslot* Slot)
2017 {
2018   int EquipmentIndex = Slot->GetEquipmentIndex();
2019 
2020   if(Master && !Master->IsInitializing())
2021   {
2022     item* Equipment = Slot->GetItem();
2023 
2024     if(Equipment->IsInCorrectSlot(EquipmentIndex))
2025       ApplyEquipmentAttributeBonuses(Equipment);
2026 
2027     if(EquipmentIndex == RIGHT_BOOT_INDEX || EquipmentIndex == LEFT_BOOT_INDEX)
2028       ApplyAgilityPenalty(Equipment);
2029   }
2030 
2031   if(Master)
2032     Master->SignalEquipmentAdd(EquipmentIndex);
2033 }
2034 
ApplyEquipmentAttributeBonuses(item * Item)2035 void arm::ApplyEquipmentAttributeBonuses(item* Item)
2036 {
2037   if(Item->AffectsArmStrength())
2038     StrengthBonus += Item->GetEnchantment();
2039 
2040   if(Item->AffectsDexterity())
2041     DexterityBonus += Item->GetEnchantment();
2042 }
2043 
ApplyEquipmentAttributeBonuses(item * Item)2044 void leg::ApplyEquipmentAttributeBonuses(item* Item)
2045 {
2046   if(Item->AffectsLegStrength())
2047   {
2048     StrengthBonus += Item->GetEnchantment();
2049 
2050     if(Master)
2051       Master->CalculateBurdenState();
2052   }
2053 
2054   if(Item->AffectsAgility())
2055     AgilityBonus += Item->GetEnchantment();
2056 }
2057 
CalculateAttributeBonuses()2058 void arm::CalculateAttributeBonuses()
2059 {
2060   StrengthBonus = DexterityBonus = 0;
2061 
2062   for(int c = 0; c < GetEquipments(); ++c)
2063   {
2064     item* Equipment = GetEquipment(c);
2065 
2066     if(Equipment && Equipment->IsInCorrectSlot())
2067       ApplyEquipmentAttributeBonuses(Equipment);
2068   }
2069 
2070   ApplyDexterityPenalty(GetGauntlet());
2071 
2072   if(Master)
2073   {
2074     ApplyDexterityPenalty(GetExternalCloak());
2075     ApplyDexterityPenalty(GetExternalBodyArmor());
2076     ApplyStrengthBonus(GetExternalHelmet());
2077     ApplyStrengthBonus(GetExternalCloak());
2078     ApplyStrengthBonus(GetExternalBodyArmor());
2079     ApplyStrengthBonus(GetExternalBelt());
2080     ApplyDexterityBonus(GetExternalHelmet());
2081     ApplyDexterityBonus(GetExternalCloak());
2082     ApplyDexterityBonus(GetExternalBodyArmor());
2083     ApplyDexterityBonus(GetExternalBelt());
2084   }
2085 
2086   if(!UseMaterialAttributes())
2087   {
2088     StrengthBonus -= CalculateScarAttributePenalty(GetAttribute(ARM_STRENGTH, false));
2089     DexterityBonus -= CalculateScarAttributePenalty(GetAttribute(DEXTERITY, false));
2090 
2091     StrengthBonus -= CalculateBurnAttributePenalty(GetAttribute(ARM_STRENGTH, false));
2092     DexterityBonus -= CalculateBurnAttributePenalty(GetAttribute(DEXTERITY, false));
2093   }
2094 }
2095 
CalculateAttributeBonuses()2096 void leg::CalculateAttributeBonuses()
2097 {
2098   StrengthBonus = AgilityBonus = 0;
2099 
2100   for(int c = 0; c < GetEquipments(); ++c)
2101   {
2102     item* Equipment = GetEquipment(c);
2103 
2104     if(Equipment && Equipment->IsInCorrectSlot())
2105       ApplyEquipmentAttributeBonuses(Equipment);
2106   }
2107 
2108   ApplyAgilityPenalty(GetBoot());
2109 
2110   if(Master)
2111   {
2112     ApplyAgilityPenalty(GetExternalCloak());
2113     ApplyAgilityPenalty(GetExternalBodyArmor());
2114     ApplyStrengthBonus(GetExternalHelmet());
2115     ApplyStrengthBonus(GetExternalCloak());
2116     ApplyStrengthBonus(GetExternalBodyArmor());
2117     ApplyStrengthBonus(GetExternalBelt());
2118     ApplyAgilityBonus(GetExternalHelmet());
2119     ApplyAgilityBonus(GetExternalCloak());
2120     ApplyAgilityBonus(GetExternalBodyArmor());
2121     ApplyAgilityBonus(GetExternalBelt());
2122   }
2123 
2124   if(!UseMaterialAttributes())
2125   {
2126     StrengthBonus -= CalculateScarAttributePenalty(GetAttribute(LEG_STRENGTH, false));
2127     AgilityBonus -= CalculateScarAttributePenalty(GetAttribute(AGILITY, false));
2128 
2129     StrengthBonus -= CalculateBurnAttributePenalty(GetAttribute(LEG_STRENGTH, false));
2130     AgilityBonus -= CalculateBurnAttributePenalty(GetAttribute(AGILITY, false));
2131   }
2132 }
2133 
SignalEquipmentAdd(gearslot * Slot)2134 void humanoidtorso::SignalEquipmentAdd(gearslot* Slot)
2135 {
2136   if(!Master)
2137     return;
2138 
2139   humanoid* Master = GetHumanoidMaster();
2140   int EquipmentIndex = Slot->GetEquipmentIndex();
2141 
2142   if(!Master->IsInitializing()
2143      && (EquipmentIndex == CLOAK_INDEX || EquipmentIndex == BODY_ARMOR_INDEX))
2144   {
2145     item* Item = Slot->GetItem();
2146 
2147     if(Master->GetRightArm())
2148       Master->GetRightArm()->ApplyDexterityPenalty(Item);
2149 
2150     if(Master->GetLeftArm())
2151       Master->GetLeftArm()->ApplyDexterityPenalty(Item);
2152 
2153     if(Master->GetRightLeg())
2154       Master->GetRightLeg()->ApplyAgilityPenalty(Item);
2155 
2156     if(Master->GetLeftLeg())
2157       Master->GetLeftLeg()->ApplyAgilityPenalty(Item);
2158   }
2159 
2160   Master->SignalEquipmentAdd(EquipmentIndex);
2161 }
2162 
GetWieldedHitStrength() const2163 int arm::GetWieldedHitStrength() const
2164 {
2165   int HitStrength = GetAttribute(ARM_STRENGTH);
2166   int Requirement = GetWielded()->GetStrengthRequirement();
2167 
2168   if(TwoHandWieldIsActive())
2169   {
2170     HitStrength += GetPairArm()->GetAttribute(ARM_STRENGTH);
2171     Requirement >>= 2;
2172   }
2173 
2174   return HitStrength - Requirement;
2175 }
2176 
ApplyStrengthBonus(item * Item)2177 void arm::ApplyStrengthBonus(item* Item)
2178 {
2179   if(Item && Item->AffectsArmStrength())
2180     StrengthBonus += Item->GetEnchantment() / 2;
2181 }
2182 
ApplyDexterityBonus(item * Item)2183 void arm::ApplyDexterityBonus(item* Item)
2184 {
2185   if(Item && Item->AffectsDexterity())
2186     DexterityBonus += Item->GetEnchantment() / 2;
2187 }
2188 
ApplyStrengthBonus(item * Item)2189 void leg::ApplyStrengthBonus(item* Item)
2190 {
2191   if(Item && Item->AffectsLegStrength())
2192     StrengthBonus += Item->GetEnchantment() / 2;
2193 }
2194 
ApplyAgilityBonus(item * Item)2195 void leg::ApplyAgilityBonus(item* Item)
2196 {
2197   if(Item && Item->AffectsAgility())
2198     AgilityBonus += Item->GetEnchantment() / 2;
2199 }
2200 
ApplyDexterityPenalty(item * Item)2201 void arm::ApplyDexterityPenalty(item* Item)
2202 {
2203   if(Item)
2204     DexterityBonus -= Item->GetInElasticityPenalty(GetAttribute(DEXTERITY, false));
2205 }
2206 
ApplyAgilityPenalty(item * Item)2207 void leg::ApplyAgilityPenalty(item* Item)
2208 {
2209   if(Item)
2210     AgilityBonus -= Item->GetInElasticityPenalty(GetAttribute(AGILITY, false));
2211 }
2212 
GetSpoilLevel() const2213 int corpse::GetSpoilLevel() const
2214 {
2215   int FlyAmount = 0;
2216 
2217   for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c)
2218   {
2219     bodypart* BodyPart = GetDeceased()->GetBodyPart(c);
2220 
2221     if(BodyPart && FlyAmount < BodyPart->GetSpoilLevel())
2222       FlyAmount = BodyPart->GetSpoilLevel();
2223   }
2224 
2225   return FlyAmount;
2226 }
2227 
SignalSpoilLevelChange(material * Material)2228 void bodypart::SignalSpoilLevelChange(material* Material)
2229 {
2230   if(Master)
2231     Master->SignalSpoilLevelChange();
2232   else
2233     item::SignalSpoilLevelChange(Material);
2234 }
2235 
SignalBurnLevelChange()2236 void bodypart::SignalBurnLevelChange()
2237 {
2238 //  if(Master)
2239 //    Master->SignalBurnLevelChange();
2240 //  else
2241     item::SignalBurnLevelChange();
2242 }
2243 
SignalBurnLevelTransitionMessage()2244 void bodypart::SignalBurnLevelTransitionMessage()
2245 {
2246 cchar* MoreMsg = MainMaterial->GetBurnLevel() == NOT_BURNT ? "" : " more";
2247 
2248   if(Master)
2249   {
2250     if(Master->IsPlayer())
2251       ADD_MESSAGE("Your %s burns%s.", CHAR_NAME(UNARTICLED), MoreMsg);
2252     else if(CanBeSeenByPlayer())
2253       ADD_MESSAGE("The %s of %s burns%s.", CHAR_NAME(UNARTICLED), Master->CHAR_NAME(DEFINITE), MoreMsg);
2254   }
2255   else if(CanBeSeenByPlayer())
2256     ADD_MESSAGE("%s burns%s.", CHAR_NAME(DEFINITE), MoreMsg);
2257   else
2258     item::SignalBurnLevelTransitionMessage();
2259 }
2260 
DamageArmor(character * Damager,int Damage,int Type)2261 truth head::DamageArmor(character* Damager, int Damage, int Type)
2262 {
2263   long AV[3] = { 0, 0, 0 }, AVSum = 0;
2264   item* Armor[3];
2265 
2266   if((Armor[0] = GetHelmet()))
2267     AVSum += AV[0] = Max(Armor[0]->GetStrengthValue(), 1);
2268 
2269   if((Armor[1] = GetExternalBodyArmor()))
2270     AVSum += AV[1] = Max(Armor[1]->GetStrengthValue() >> 2, 1);
2271 
2272   if((Armor[2] = GetExternalCloak()))
2273     AVSum += AV[2] = Max(Armor[2]->GetStrengthValue(), 1);
2274 
2275   return AVSum ? Armor[femath::WeightedRand(AV, AVSum)]->ReceiveDamage(Damager, Damage, Type) : false;
2276 }
2277 
DamageArmor(character * Damager,int Damage,int Type)2278 truth humanoidtorso::DamageArmor(character* Damager, int Damage, int Type)
2279 {
2280   long AV[3] = { 0, 0, 0 }, AVSum = 0;
2281   item* Armor[3];
2282 
2283   if((Armor[0] = GetBodyArmor()))
2284     AVSum += AV[0] = Max(Armor[0]->GetStrengthValue(), 1);
2285 
2286   if((Armor[1] = GetBelt()))
2287     AVSum += AV[1] = Max(Armor[1]->GetStrengthValue(), 1);
2288 
2289   if((Armor[2] = GetCloak()))
2290     AVSum += AV[2] = Max(Armor[2]->GetStrengthValue(), 1);
2291 
2292   return AVSum ? Armor[femath::WeightedRand(AV, AVSum)]->ReceiveDamage(Damager, Damage, Type) : false;
2293 }
2294 
DamageArmor(character * Damager,int Damage,int Type)2295 truth arm::DamageArmor(character* Damager, int Damage, int Type)
2296 {
2297   long AV[3] = { 0, 0, 0 }, AVSum = 0;
2298   item* Armor[3];
2299 
2300   if((Armor[0] = GetGauntlet()))
2301     AVSum += AV[0] = Max(Armor[0]->GetStrengthValue(), 1);
2302 
2303   if((Armor[1] = GetExternalBodyArmor()))
2304     AVSum += AV[1] = Max(Armor[1]->GetStrengthValue() >> 1, 1);
2305 
2306   if((Armor[2] = GetExternalCloak()))
2307     AVSum += AV[2] = Max(Armor[2]->GetStrengthValue(), 1);
2308 
2309   return AVSum ? Armor[femath::WeightedRand(AV, AVSum)]->ReceiveDamage(Damager, Damage, Type) : false;
2310 }
2311 
DamageArmor(character * Damager,int Damage,int Type)2312 truth groin::DamageArmor(character* Damager, int Damage, int Type)
2313 {
2314   return Master->GetTorso()->DamageArmor(Damager, Damage, Type);
2315 }
2316 
DamageArmor(character * Damager,int Damage,int Type)2317 truth leg::DamageArmor(character* Damager, int Damage, int Type)
2318 {
2319   long AV[3] = { 0, 0, 0 }, AVSum = 0;
2320   item* Armor[3];
2321 
2322   if((Armor[0] = GetBoot()))
2323     AVSum += AV[0] = Max(Armor[0]->GetStrengthValue(), 1);
2324 
2325   if((Armor[1] = GetExternalBodyArmor()))
2326     AVSum += AV[1] = Max(Armor[1]->GetStrengthValue() >> 1, 1);
2327 
2328   if((Armor[2] = GetExternalCloak()))
2329     AVSum += AV[2] = Max(Armor[2]->GetStrengthValue(), 1);
2330 
2331   return AVSum ? Armor[femath::WeightedRand(AV, AVSum)]->ReceiveDamage(Damager, Damage, Type) : false;
2332 }
2333 
CanBeEatenByAI(ccharacter * Who) const2334 truth bodypart::CanBeEatenByAI(ccharacter* Who) const
2335 {
2336   return item::CanBeEatenByAI(Who) && !(Who->IsPet() && PLAYER->HasHadBodyPart(this));
2337 }
2338 
GetConditionColorIndex() const2339 int bodypart::GetConditionColorIndex() const
2340 {
2341   if(HP <= 1 && MaxHP > 1)
2342     return 0;
2343   else if((HP << 1) + HP < MaxHP)
2344     return 1;
2345   else if((HP << 1) + HP < MaxHP << 1)
2346     return 2;
2347   else if(HP < MaxHP)
2348     return 3;
2349   else
2350     return 4;
2351 }
2352 
CheckIfWeaponTooHeavy(cchar * WeaponDescription) const2353 truth arm::CheckIfWeaponTooHeavy(cchar* WeaponDescription) const
2354 {
2355   if(!IsUsable())
2356   {
2357     ADD_MESSAGE("%s %s is not usable.", Master->CHAR_POSSESSIVE_PRONOUN, GetBodyPartName().CStr());
2358     return !game::TruthQuestion(CONST_S("Continue anyway? [y/N]"));
2359   }
2360 
2361   int HitStrength = GetAttribute(ARM_STRENGTH);
2362   int Requirement = GetWielded()->GetStrengthRequirement();
2363 
2364   if(TwoHandWieldIsActive())
2365   {
2366     HitStrength += GetPairArm()->GetAttribute(ARM_STRENGTH);
2367     Requirement >>= 2;
2368 
2369     if(HitStrength - Requirement < 10)
2370     {
2371       if(HitStrength <= Requirement)
2372         ADD_MESSAGE("%s cannot use %s. Wielding it with two hands requires %d strength.",
2373                     Master->CHAR_DESCRIPTION(DEFINITE), WeaponDescription, (Requirement >> 1) + 1);
2374       else if(HitStrength - Requirement < 4)
2375         ADD_MESSAGE("Using %s even with two hands is extremely difficult for %s.",
2376                     WeaponDescription, Master->CHAR_DESCRIPTION(DEFINITE));
2377       else if(HitStrength - Requirement < 7)
2378         ADD_MESSAGE("%s %s much trouble using %s even with two hands.",
2379                     Master->CHAR_DESCRIPTION(DEFINITE), Master->IsPlayer() ? "have" : "has", WeaponDescription);
2380       else
2381         ADD_MESSAGE("It is somewhat difficult for %s to use %s even with two hands.",
2382                     Master->CHAR_DESCRIPTION(DEFINITE), WeaponDescription);
2383 
2384       return !game::TruthQuestion(CONST_S("Continue anyway? [y/N]"));
2385     }
2386   }
2387   else
2388   {
2389     if(HitStrength - Requirement < 10)
2390     {
2391       festring OtherHandInfo;
2392       cchar* HandInfo = "";
2393 
2394       if(GetWielded()->IsTwoHanded())
2395       {
2396         if(GetPairArm() && !GetPairArm()->IsUsable())
2397           OtherHandInfo = Master->GetPossessivePronoun() + " other arm is unusable. ";
2398 
2399         HandInfo = " with one hand";
2400       }
2401 
2402       if(HitStrength <= Requirement)
2403         ADD_MESSAGE("%s%s cannot use %s. Wielding it%s requires %d strength.", OtherHandInfo.CStr(),
2404                     Master->GetDescription(DEFINITE).CapitalizeCopy().CStr(),
2405                     WeaponDescription, HandInfo, Requirement + 1);
2406       else if(HitStrength - Requirement < 4)
2407         ADD_MESSAGE("%sUsing %s%s is extremely difficult for %s.", OtherHandInfo.CStr(),
2408                     WeaponDescription, HandInfo, Master->CHAR_DESCRIPTION(DEFINITE));
2409       else if(HitStrength - Requirement < 7)
2410         ADD_MESSAGE("%s%s %s much trouble using %s%s.", OtherHandInfo.CStr(),
2411                     Master->GetDescription(DEFINITE).CapitalizeCopy().CStr(),
2412                     Master->IsPlayer() ? "have" : "has", WeaponDescription, HandInfo);
2413       else
2414         ADD_MESSAGE("%sIt is somewhat difficult for %s to use %s%s.", OtherHandInfo.CStr(),
2415                     Master->CHAR_DESCRIPTION(DEFINITE), WeaponDescription, HandInfo);
2416       return !game::TruthQuestion(CONST_S("Continue anyway? [y/N]"));
2417     }
2418   }
2419 
2420   return false;
2421 }
2422 
GetArticleMode() const2423 int corpse::GetArticleMode() const
2424 {
2425   return Deceased->LeftOversAreUnique() ? FORCE_THE : 0;
2426 }
2427 
Behead()2428 head* head::Behead()
2429 {
2430   RemoveFromSlot();
2431   return this;
2432 }
2433 
EditAllAttributes(int Amount)2434 truth arm::EditAllAttributes(int Amount)
2435 {
2436   LimitRef(StrengthExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP);
2437   LimitRef(DexterityExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP);
2438   return (Amount < 0
2439           && (StrengthExperience != MIN_EXP || DexterityExperience != MIN_EXP))
2440     || (Amount > 0
2441         && (StrengthExperience != MAX_EXP || DexterityExperience != MAX_EXP));
2442 }
2443 
EditAllAttributes(int Amount)2444 truth leg::EditAllAttributes(int Amount)
2445 {
2446   LimitRef(StrengthExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP);
2447   LimitRef(AgilityExperience += Amount * EXP_MULTIPLIER, MIN_EXP, MAX_EXP);
2448   return (Amount < 0
2449           && (StrengthExperience != MIN_EXP || AgilityExperience != MIN_EXP))
2450     || (Amount > 0
2451         && (StrengthExperience != MAX_EXP || AgilityExperience != MAX_EXP));
2452 }
2453 
2454 #ifdef WIZARD
2455 
AddAttackInfo(felist & List) const2456 void arm::AddAttackInfo(felist& List) const
2457 {
2458   if(GetDamage())
2459   {
2460     festring Entry = CONST_S("   ");
2461 
2462     if(GetWielded())
2463     {
2464       GetWielded()->AddName(Entry, UNARTICLED);
2465 
2466       if(TwoHandWieldIsActive())
2467         Entry << " (b)";
2468     }
2469     else
2470       Entry << "melee attack";
2471 
2472     Entry.Resize(50);
2473     Entry << GetMinDamage() << '-' << GetMaxDamage();
2474     Entry.Resize(60);
2475     Entry << int(GetToHitValue());
2476     Entry.Resize(70);
2477     Entry << GetAPCost();
2478     List.AddEntry(Entry, LIGHT_GRAY);
2479   }
2480 }
2481 
AddDefenceInfo(felist & List) const2482 void arm::AddDefenceInfo(felist& List) const
2483 {
2484   if(GetWielded())
2485   {
2486     festring Entry = CONST_S("   ");
2487     GetWielded()->AddName(Entry, UNARTICLED);
2488     Entry.Resize(50);
2489     Entry << int(GetBlockValue());
2490     Entry.Resize(70);
2491     Entry << GetBlockCapability();
2492     List.AddEntry(Entry, LIGHT_GRAY);
2493   }
2494 }
2495 
2496 #else
2497 
AddAttackInfo(felist &) const2498 void arm::AddAttackInfo(felist&) const { }
AddDefenceInfo(felist &) const2499 void arm::AddDefenceInfo(felist&) const { }
2500 
2501 #endif
2502 
UpdateWieldedPicture()2503 void arm::UpdateWieldedPicture()
2504 {
2505   if(!Master || !Master->PictureUpdatesAreForbidden())
2506   {
2507     truth WasAnimated = MasterIsAnimated();
2508     item* Wielded = GetWielded();
2509 
2510     if(Wielded && Master)
2511     {
2512       int SpecialFlags = (IsRightArm() ? 0 : MIRROR)|ST_WIELDED|(Wielded->GetSpecialFlags()&~0x3F);
2513       Wielded->UpdatePictures(WieldedGraphicData,
2514                               Master->GetWieldedPosition(),
2515                               SpecialFlags,
2516                               GetMaxAlpha(),
2517                               GR_HUMANOID,
2518                               static_cast<bposretriever>(&item::GetWieldedBitmapPos));
2519 
2520       if(ShowFluids())
2521         Wielded->CheckFluidGearPictures(Wielded->GetWieldedBitmapPos(0), SpecialFlags, false);
2522     }
2523     else
2524       WieldedGraphicData.Retire();
2525 
2526     if(!WasAnimated != !MasterIsAnimated())
2527       SignalAnimationStateChange(WasAnimated);
2528   }
2529 }
2530 
DrawWielded(blitdata & BlitData) const2531 void arm::DrawWielded(blitdata& BlitData) const
2532 {
2533   DrawEquipment(WieldedGraphicData, BlitData);
2534 
2535   if(ShowFluids() && GetWielded())
2536     GetWielded()->DrawFluidGearPictures(BlitData, IsRightArm() ? 0 : MIRROR);
2537 }
2538 
UpdatePictures()2539 void arm::UpdatePictures()
2540 {
2541   bodypart::UpdatePictures();
2542   UpdateWieldedPicture();
2543 }
2544 
Draw(blitdata & BlitData) const2545 void bodypart::Draw(blitdata& BlitData) const
2546 {
2547   cint AF = GraphicData.AnimationFrames;
2548   cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1);
2549   cbitmap* P = GraphicData.Picture[F];
2550 
2551   if(BlitData.CustomData & ALLOW_ALPHA)
2552     P->AlphaPriorityBlit(BlitData);
2553   else
2554     P->MaskedPriorityBlit(BlitData);
2555 
2556   if(Fluid && ShowFluids())
2557     DrawFluids(BlitData);
2558 
2559   DrawArmor(BlitData);
2560 }
2561 
AddAttackInfo(felist & List) const2562 void leg::AddAttackInfo(felist& List) const
2563 {
2564   festring Entry = CONST_S("   kick attack");
2565   Entry.Resize(50);
2566   Entry << GetKickMinDamage() << '-' << GetKickMaxDamage();
2567   Entry.Resize(60);
2568   Entry << int(GetKickToHitValue());
2569   Entry.Resize(70);
2570   Entry << GetKickAPCost();
2571   List.AddEntry(Entry, LIGHT_GRAY);
2572 }
2573 
PreProcessForBone()2574 void corpse::PreProcessForBone()
2575 {
2576   item::PreProcessForBone();
2577 
2578   if(!Deceased->PreProcessForBone())
2579   {
2580     RemoveFromSlot();
2581     SendToHell();
2582   }
2583 }
2584 
PostProcessForBone()2585 void corpse::PostProcessForBone()
2586 {
2587   item::PostProcessForBone();
2588 
2589   if(!Deceased->PostProcessForBone())
2590   {
2591     RemoveFromSlot();
2592     SendToHell();
2593   }
2594 }
2595 
FinalProcessForBone()2596 void corpse::FinalProcessForBone()
2597 {
2598   item::FinalProcessForBone();
2599   Deceased->FinalProcessForBone();
2600 }
2601 
IsRepairable(ccharacter *) const2602 truth bodypart::IsRepairable(ccharacter*) const
2603 {
2604   return !CanRegenerate() && (GetHP() < GetMaxHP() || IsRusted() || IsBurnt());
2605 }
2606 
SuckSoul(character * Soul,character * Summoner)2607 truth corpse::SuckSoul(character* Soul, character* Summoner)
2608 {
2609   v2 Pos = Soul->GetPos();
2610 
2611   if(Deceased->SuckSoul(Soul))
2612   {
2613     Soul->Remove();
2614     character* Deceased = GetDeceased();
2615 
2616     if(RaiseTheDead(Summoner))
2617     {
2618       Soul->SendToHell();
2619       return true;
2620     }
2621     else
2622     {
2623       Deceased->SetSoulID(Soul->GetID());
2624       Soul->PutTo(Pos);
2625       return false;
2626     }
2627   }
2628   else
2629     return false;
2630 }
2631 
GetTypeDamage(ccharacter * Enemy) const2632 double arm::GetTypeDamage(ccharacter* Enemy) const
2633 {
2634   double ActualDamage = GetDamage();
2635 
2636   if(GetWielded())
2637   {
2638     if(GetWielded()->IsGoodWithPlants() && Enemy->IsPlant())
2639       ActualDamage *= 1.5;
2640     if(GetWielded()->IsGoodWithUndead() && Enemy->IsUndead())
2641       ActualDamage *= 1.5;
2642     if(HasSadistWeapon() && Enemy->IsMasochist())
2643       ActualDamage *= 0.75;
2644   }
2645 
2646   return ActualDamage;
2647 }
2648 
Draw(blitdata & BlitData) const2649 void largetorso::Draw(blitdata& BlitData) const
2650 {
2651   LargeDraw(BlitData);
2652 }
2653 
Draw(blitdata & BlitData) const2654 void largecorpse::Draw(blitdata& BlitData) const
2655 {
2656   LargeDraw(BlitData);
2657 }
2658 
SignalStackAdd(stackslot * StackSlot,void (stack::* AddHandler)(item *,truth))2659 void largetorso::SignalStackAdd(stackslot* StackSlot, void (stack::*AddHandler)(item*, truth))
2660 {
2661   if(!Slot[0])
2662   {
2663     Slot[0] = StackSlot;
2664     v2 Pos = GetPos();
2665     level* Level = GetLevel();
2666 
2667     for(int c = 1; c < 4; ++c)
2668       (Level->GetLSquare(Pos + game::GetLargeMoveVector(12 + c))->GetStack()->*AddHandler)(this, false);
2669   }
2670   else
2671     for(int c = 1; c < 4; ++c)
2672       if(!Slot[c])
2673       {
2674         Slot[c] = StackSlot;
2675         return;
2676       }
2677 }
2678 
GetSquareIndex(v2 Pos) const2679 int largetorso::GetSquareIndex(v2 Pos) const
2680 {
2681   v2 RelativePos = Pos - GetPos();
2682   return RelativePos.X + (RelativePos.Y << 1);
2683 }
2684 
SignalStackAdd(stackslot * StackSlot,void (stack::* AddHandler)(item *,truth))2685 void largecorpse::SignalStackAdd(stackslot* StackSlot, void (stack::*AddHandler)(item*, truth))
2686 {
2687   if(!Slot[0])
2688   {
2689     Slot[0] = StackSlot;
2690     v2 Pos = GetPos();
2691     level* Level = GetLevel();
2692 
2693     for(int c = 1; c < 4; ++c)
2694       (Level->GetLSquare(Pos + game::GetLargeMoveVector(12 + c))->GetStack()->*AddHandler)(this, false);
2695   }
2696   else
2697     for(int c = 1; c < 4; ++c)
2698       if(!Slot[c])
2699       {
2700         Slot[c] = StackSlot;
2701         return;
2702       }
2703 }
2704 
GetSquareIndex(v2 Pos) const2705 int largecorpse::GetSquareIndex(v2 Pos) const
2706 {
2707   v2 RelativePos = Pos - GetPos();
2708   return RelativePos.X + (RelativePos.Y << 1);
2709 }
2710 
TryNecromancy(character * Summoner)2711 character* corpse::TryNecromancy(character* Summoner)
2712 {
2713   if(Summoner && Summoner->IsPlayer())
2714     game::DoEvilDeed(50);
2715 
2716   character* Zombie = GetDeceased()->CreateZombie();
2717 
2718   if(Zombie)
2719   {
2720     v2 Pos = GetPos();
2721     RemoveFromSlot();
2722     Zombie->ChangeTeam(Summoner ? Summoner->GetTeam() : game::GetTeam(MONSTER_TEAM));
2723     Zombie->PutToOrNear(Pos);
2724     Zombie->SignalStepFrom(0);
2725     SendToHell();
2726     return Zombie;
2727   }
2728 
2729   return 0;
2730 }
2731 
GetArmorToReceiveFluid(truth) const2732 item* head::GetArmorToReceiveFluid(truth) const
2733 {
2734   item* Helmet = GetHelmet();
2735 
2736   if(Helmet && Helmet->GetCoverPercentile() > RAND() % 100)
2737     return Helmet;
2738   else
2739     return 0;
2740 }
2741 
GetArmorToReceiveFluid(truth) const2742 item* humanoidtorso::GetArmorToReceiveFluid(truth) const
2743 {
2744   item* Cloak = GetCloak();
2745 
2746   if(Cloak && !(RAND() % 3))
2747     return Cloak;
2748 
2749   item* Belt = GetBelt();
2750 
2751   if(Belt && !(RAND() % 10))
2752     return Belt;
2753 
2754   item* BodyArmor = GetBodyArmor();
2755   return BodyArmor ? BodyArmor : 0;
2756 }
2757 
GetArmorToReceiveFluid(truth) const2758 item* arm::GetArmorToReceiveFluid(truth) const
2759 {
2760   item* Cloak = GetExternalCloak();
2761 
2762   if(Cloak && !(RAND() % 3))
2763     return Cloak;
2764 
2765   item* Wielded = GetWielded();
2766 
2767   if(Wielded && !(RAND() % 3))
2768     return Wielded;
2769 
2770   item* Gauntlet = GetGauntlet();
2771 
2772   if(Gauntlet && RAND() & 1)
2773     return Gauntlet;
2774 
2775   item* BodyArmor = GetExternalBodyArmor();
2776   return BodyArmor ? BodyArmor : 0;
2777 }
2778 
GetArmorToReceiveFluid(truth) const2779 item* groin::GetArmorToReceiveFluid(truth) const
2780 {
2781   item* Cloak = GetExternalCloak();
2782 
2783   if(Cloak && !(RAND() % 3))
2784     return Cloak;
2785 
2786   item* BodyArmor = GetExternalBodyArmor();
2787   return BodyArmor ? BodyArmor : 0;
2788 }
2789 
GetArmorToReceiveFluid(truth SteppedOn) const2790 item* leg::GetArmorToReceiveFluid(truth SteppedOn) const
2791 {
2792   if(SteppedOn)
2793   {
2794     item* Boot = GetBoot();
2795     return Boot ? Boot : 0;
2796   }
2797   else
2798   {
2799     item* Cloak = GetExternalCloak();
2800 
2801     if(Cloak && !(RAND() % 3))
2802       return Cloak;
2803 
2804     item* Boot = GetBoot();
2805 
2806     if(Boot && RAND() & 1)
2807       return Boot;
2808 
2809     item* BodyArmor = GetExternalBodyArmor();
2810     return BodyArmor ? BodyArmor : 0;
2811   }
2812 }
2813 
SpillFluid(character * Spiller,liquid * Liquid,int SquareIndex)2814 void bodypart::SpillFluid(character* Spiller, liquid* Liquid, int SquareIndex)
2815 {
2816   if(Master)
2817   {
2818     item* Armor = GetArmorToReceiveFluid(false);
2819 
2820     if(Armor)
2821       Armor->SpillFluid(Spiller, Liquid);
2822     else if(GetMaster())
2823     {
2824       if(Liquid->GetVolume())
2825         AddFluid(Liquid, "", SquareIndex, false);
2826       else
2827         delete Liquid;
2828     }
2829   }
2830   else
2831     item::SpillFluid(Spiller, Liquid, SquareIndex);
2832 }
2833 
StayOn(liquid * Liquid)2834 void bodypart::StayOn(liquid* Liquid)
2835 {
2836   item* Armor = GetArmorToReceiveFluid(true);
2837 
2838   if(Armor)
2839     Liquid->TouchEffect(Armor, CONST_S(""));
2840   else if(GetMaster())
2841     Liquid->TouchEffect(GetMaster(), GetBodyPartIndex());
2842 }
2843 
CreateBlood(long Volume) const2844 liquid* bodypart::CreateBlood(long Volume) const
2845 {
2846   return liquid::Spawn(GetBloodMaterial(), Volume);
2847 }
2848 
GetRustDataA() const2849 int corpse::GetRustDataA() const
2850 {
2851   return Deceased->GetTorso()->GetMainMaterial()->GetRustData();
2852 }
2853 
GetBurnDataA() const2854 int corpse::GetBurnDataA() const
2855 {
2856   return Deceased->GetTorso()->GetMainMaterial()->GetBurnData();
2857 }
2858 
UpdateArmorPicture(graphicdata & GData,item * Armor,int SpecialFlags,v2 (item::* Retriever)(int)const,truth BodyArmor) const2859 void bodypart::UpdateArmorPicture(graphicdata& GData, item* Armor, int SpecialFlags,
2860                                   v2 (item::*Retriever)(int) const, truth BodyArmor) const
2861 {
2862   if(Armor && Master)
2863   {
2864     Armor->UpdatePictures(GData,
2865                           ZERO_V2,
2866                           SpecialFlags|Armor->GetSpecialFlags(),
2867                           GetMaxAlpha(),
2868                           GR_HUMANOID,
2869                           static_cast<bposretriever>(Retriever));
2870     Armor->CheckFluidGearPictures((Armor->*Retriever)(0), SpecialFlags, BodyArmor);
2871   }
2872   else
2873     GData.Retire();
2874 }
2875 
UpdateArmorPictures()2876 truth playerkindhead::UpdateArmorPictures()
2877 {
2878   UpdateHeadArmorPictures(HelmetGraphicData);
2879   return true;
2880 }
2881 
UpdateArmorPictures()2882 truth playerkindtorso::UpdateArmorPictures()
2883 {
2884   UpdateTorsoArmorPictures(TorsoArmorGraphicData,
2885                            CloakGraphicData,
2886                            BeltGraphicData);
2887   return true;
2888 }
2889 
UpdateArmorPictures()2890 truth playerkindrightarm::UpdateArmorPictures()
2891 {
2892   UpdateArmArmorPictures(ArmArmorGraphicData,
2893                          GauntletGraphicData,
2894                          ST_RIGHT_ARM);
2895   return true;
2896 }
2897 
UpdateArmorPictures()2898 truth playerkindleftarm::UpdateArmorPictures()
2899 {
2900   UpdateArmArmorPictures(ArmArmorGraphicData,
2901                          GauntletGraphicData,
2902                          ST_LEFT_ARM);
2903   return true;
2904 }
2905 
UpdateArmorPictures()2906 truth playerkindgroin::UpdateArmorPictures()
2907 {
2908   UpdateGroinArmorPictures(GroinArmorGraphicData);
2909   return true;
2910 }
2911 
UpdateArmorPictures()2912 truth playerkindrightleg::UpdateArmorPictures()
2913 {
2914   UpdateLegArmorPictures(LegArmorGraphicData,
2915                          BootGraphicData,
2916                          ST_RIGHT_LEG);
2917   return true;
2918 }
2919 
UpdateArmorPictures()2920 truth playerkindleftleg::UpdateArmorPictures()
2921 {
2922   UpdateLegArmorPictures(LegArmorGraphicData,
2923                          BootGraphicData,
2924                          ST_LEFT_LEG);
2925   return true;
2926 }
2927 
UpdateHeadArmorPictures(graphicdata & HelmetGraphicData) const2928 void head::UpdateHeadArmorPictures(graphicdata& HelmetGraphicData) const
2929 {
2930   if(!Master || !Master->PictureUpdatesAreForbidden())
2931   {
2932     UpdateArmorPicture(HelmetGraphicData,
2933                        GetHelmet(),
2934                        ST_OTHER_BODYPART,
2935                        &item::GetHelmetBitmapPos);
2936   }
2937 }
2938 
UpdateTorsoArmorPictures(graphicdata & TorsoArmorGraphicData,graphicdata & CloakGraphicData,graphicdata & BeltGraphicData) const2939 void humanoidtorso::UpdateTorsoArmorPictures(graphicdata& TorsoArmorGraphicData,
2940                                              graphicdata& CloakGraphicData,
2941                                              graphicdata& BeltGraphicData) const
2942 {
2943   if(!Master || !Master->PictureUpdatesAreForbidden())
2944   {
2945     UpdateArmorPicture(TorsoArmorGraphicData,
2946                        GetBodyArmor(),
2947                        ST_OTHER_BODYPART,
2948                        &item::GetTorsoArmorBitmapPos,
2949                        true);
2950     UpdateArmorPicture(CloakGraphicData,
2951                        GetCloak(),
2952                        ST_OTHER_BODYPART,
2953                        &item::GetCloakBitmapPos);
2954     UpdateArmorPicture(BeltGraphicData,
2955                        GetBelt(),
2956                        ST_OTHER_BODYPART,
2957                        &item::GetBeltBitmapPos);
2958   }
2959 }
2960 
UpdateArmArmorPictures(graphicdata & ArmArmorGraphicData,graphicdata & GauntletGraphicData,int SpecialFlags) const2961 void arm::UpdateArmArmorPictures(graphicdata& ArmArmorGraphicData,
2962                                  graphicdata& GauntletGraphicData,
2963                                  int SpecialFlags) const
2964 {
2965   if(!Master || !Master->PictureUpdatesAreForbidden())
2966   {
2967     UpdateArmorPicture(ArmArmorGraphicData,
2968                        Master ? GetExternalBodyArmor() : 0,
2969                        SpecialFlags,
2970                        GetAttribute(ARM_STRENGTH, false) >= 20 ?
2971                          &item::GetAthleteArmArmorBitmapPos : &item::GetArmArmorBitmapPos,
2972                        true);
2973     UpdateArmorPicture(GauntletGraphicData,
2974                        GetGauntlet(),
2975                        SpecialFlags,
2976                        &item::GetGauntletBitmapPos);
2977   }
2978 }
2979 
UpdateGroinArmorPictures(graphicdata & GroinArmorGraphicData) const2980 void groin::UpdateGroinArmorPictures(graphicdata& GroinArmorGraphicData) const
2981 {
2982   if(!Master || !Master->PictureUpdatesAreForbidden())
2983   {
2984     UpdateArmorPicture(GroinArmorGraphicData,
2985                        Master ? GetExternalBodyArmor() : 0,
2986                        ST_GROIN,
2987                        &item::GetLegArmorBitmapPos,
2988                        true);
2989   }
2990 }
2991 
UpdateLegArmorPictures(graphicdata & LegArmorGraphicData,graphicdata & BootGraphicData,int SpecialFlags) const2992 void leg::UpdateLegArmorPictures(graphicdata& LegArmorGraphicData,
2993                                  graphicdata& BootGraphicData,
2994                                  int SpecialFlags) const
2995 {
2996   if(!Master || !Master->PictureUpdatesAreForbidden())
2997   {
2998     UpdateArmorPicture(LegArmorGraphicData,
2999                        Master ? GetExternalBodyArmor() : 0,
3000                        SpecialFlags,
3001                        &item::GetLegArmorBitmapPos,
3002                        true);
3003     UpdateArmorPicture(BootGraphicData,
3004                        GetBoot(),
3005                        SpecialFlags,
3006                        &item::GetBootBitmapPos);
3007   }
3008 }
3009 
DrawEquipment(const graphicdata & GraphicData,blitdata & BlitData) const3010 void bodypart::DrawEquipment(const graphicdata& GraphicData, blitdata& BlitData) const
3011 {
3012   int EAF = GraphicData.AnimationFrames;
3013 
3014   if(EAF)
3015   {
3016     int F = !(BlitData.CustomData & ALLOW_ANIMATE) || EAF == 1 ? 0 : GET_TICK() & (EAF - 1);
3017     GraphicData.Picture[F]->AlphaPriorityBlit(BlitData);
3018   }
3019 }
3020 
DrawArmor(blitdata & BlitData) const3021 void playerkindhead::DrawArmor(blitdata& BlitData) const
3022 {
3023   DrawEquipment(HelmetGraphicData, BlitData);
3024 
3025   if(GetHelmet())
3026     GetHelmet()->DrawFluidGearPictures(BlitData);
3027 }
3028 
DrawArmor(blitdata & BlitData) const3029 void playerkindtorso::DrawArmor(blitdata& BlitData) const
3030 {
3031   DrawEquipment(TorsoArmorGraphicData, BlitData);
3032 
3033   if(GetBodyArmor())
3034     GetBodyArmor()->DrawFluidGearPictures(BlitData);
3035 
3036   DrawEquipment(CloakGraphicData, BlitData);
3037 
3038   if(GetCloak())
3039     GetCloak()->DrawFluidGearPictures(BlitData);
3040 
3041   DrawEquipment(BeltGraphicData, BlitData);
3042 
3043   if(GetBelt())
3044     GetBelt()->DrawFluidGearPictures(BlitData);
3045 }
3046 
DrawArmor(blitdata & BlitData) const3047 void playerkindrightarm::DrawArmor(blitdata& BlitData) const
3048 {
3049   DrawEquipment(ArmArmorGraphicData, BlitData);
3050 
3051   if(Master && GetExternalBodyArmor())
3052     GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_RIGHT_ARM);
3053 
3054   DrawEquipment(GauntletGraphicData, BlitData);
3055 
3056   if(GetGauntlet())
3057     GetGauntlet()->DrawFluidGearPictures(BlitData);
3058 }
3059 
DrawArmor(blitdata & BlitData) const3060 void playerkindleftarm::DrawArmor(blitdata& BlitData) const
3061 {
3062   DrawEquipment(ArmArmorGraphicData, BlitData);
3063 
3064   if(Master && GetExternalBodyArmor())
3065     GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_LEFT_ARM);
3066 
3067   DrawEquipment(GauntletGraphicData, BlitData);
3068 
3069   if(GetGauntlet())
3070     GetGauntlet()->DrawFluidGearPictures(BlitData);
3071 }
3072 
DrawArmor(blitdata & BlitData) const3073 void playerkindgroin::DrawArmor(blitdata& BlitData) const
3074 {
3075   DrawEquipment(GroinArmorGraphicData, BlitData);
3076 
3077   if(Master && GetExternalBodyArmor())
3078     GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_GROIN);
3079 }
3080 
DrawArmor(blitdata & BlitData) const3081 void playerkindrightleg::DrawArmor(blitdata& BlitData) const
3082 {
3083   DrawEquipment(LegArmorGraphicData, BlitData);
3084 
3085   if(Master && GetExternalBodyArmor())
3086     GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_RIGHT_LEG);
3087 
3088   DrawEquipment(BootGraphicData, BlitData);
3089 
3090   if(GetBoot())
3091     GetBoot()->DrawFluidGearPictures(BlitData);
3092 }
3093 
DrawArmor(blitdata & BlitData) const3094 void playerkindleftleg::DrawArmor(blitdata& BlitData) const
3095 {
3096   DrawEquipment(LegArmorGraphicData, BlitData);
3097 
3098   if(Master && GetExternalBodyArmor())
3099     GetExternalBodyArmor()->DrawFluidBodyArmorPictures(BlitData, ST_LEFT_LEG);
3100 
3101   DrawEquipment(BootGraphicData, BlitData);
3102 
3103   if(GetBoot())
3104     GetBoot()->DrawFluidGearPictures(BlitData);
3105 }
3106 
Save(outputfile & SaveFile) const3107 void playerkindhead::Save(outputfile& SaveFile) const
3108 {
3109   head::Save(SaveFile);
3110   SaveFile << HelmetGraphicData;
3111 }
3112 
Load(inputfile & SaveFile)3113 void playerkindhead::Load(inputfile& SaveFile)
3114 {
3115   head::Load(SaveFile);
3116   SaveFile >> HelmetGraphicData;
3117 }
3118 
Save(outputfile & SaveFile) const3119 void playerkindtorso::Save(outputfile& SaveFile) const
3120 {
3121   humanoidtorso::Save(SaveFile);
3122   SaveFile << TorsoArmorGraphicData << CloakGraphicData << BeltGraphicData;
3123 }
3124 
Load(inputfile & SaveFile)3125 void playerkindtorso::Load(inputfile& SaveFile)
3126 {
3127   humanoidtorso::Load(SaveFile);
3128   SaveFile >> TorsoArmorGraphicData >> CloakGraphicData >> BeltGraphicData;
3129 }
3130 
Save(outputfile & SaveFile) const3131 void playerkindrightarm::Save(outputfile& SaveFile) const
3132 {
3133   rightarm::Save(SaveFile);
3134   SaveFile << ArmArmorGraphicData << GauntletGraphicData;
3135 }
3136 
Load(inputfile & SaveFile)3137 void playerkindrightarm::Load(inputfile& SaveFile)
3138 {
3139   rightarm::Load(SaveFile);
3140   SaveFile >> ArmArmorGraphicData >> GauntletGraphicData;
3141 }
3142 
Save(outputfile & SaveFile) const3143 void playerkindleftarm::Save(outputfile& SaveFile) const
3144 {
3145   leftarm::Save(SaveFile);
3146   SaveFile << ArmArmorGraphicData << GauntletGraphicData;
3147 }
3148 
Load(inputfile & SaveFile)3149 void playerkindleftarm::Load(inputfile& SaveFile)
3150 {
3151   leftarm::Load(SaveFile);
3152   SaveFile >> ArmArmorGraphicData >> GauntletGraphicData;
3153 }
3154 
Save(outputfile & SaveFile) const3155 void playerkindgroin::Save(outputfile& SaveFile) const
3156 {
3157   groin::Save(SaveFile);
3158   SaveFile << GroinArmorGraphicData;
3159 }
3160 
Load(inputfile & SaveFile)3161 void playerkindgroin::Load(inputfile& SaveFile)
3162 {
3163   groin::Load(SaveFile);
3164   SaveFile >> GroinArmorGraphicData;
3165 }
3166 
Save(outputfile & SaveFile) const3167 void playerkindrightleg::Save(outputfile& SaveFile) const
3168 {
3169   rightleg::Save(SaveFile);
3170   SaveFile << LegArmorGraphicData << BootGraphicData;
3171 }
3172 
Load(inputfile & SaveFile)3173 void playerkindrightleg::Load(inputfile& SaveFile)
3174 {
3175   rightleg::Load(SaveFile);
3176   SaveFile >> LegArmorGraphicData >> BootGraphicData;
3177 }
3178 
Save(outputfile & SaveFile) const3179 void playerkindleftleg::Save(outputfile& SaveFile) const
3180 {
3181   leftleg::Save(SaveFile);
3182   SaveFile << LegArmorGraphicData << BootGraphicData;
3183 }
3184 
Load(inputfile & SaveFile)3185 void playerkindleftleg::Load(inputfile& SaveFile)
3186 {
3187   leftleg::Load(SaveFile);
3188   SaveFile >> LegArmorGraphicData >> BootGraphicData;
3189 }
3190 
MasterIsAnimated() const3191 truth bodypart::MasterIsAnimated() const
3192 {
3193   return Master && !Master->IsInitializing() && Master->IsAnimated();
3194 }
3195 
UpdatePictures()3196 void bodypart::UpdatePictures()
3197 {
3198   truth WasAnimated = MasterIsAnimated();
3199 
3200   item::UpdatePictures();
3201   UpdateArmorPictures();
3202 
3203   if(!WasAnimated != !MasterIsAnimated())
3204     SignalAnimationStateChange(WasAnimated);
3205 }
3206 
SignalVolumeAndWeightChange()3207 void playerkindtorso::SignalVolumeAndWeightChange()
3208 {
3209   humanoidtorso::SignalVolumeAndWeightChange();
3210 
3211   if(Master && !Master->IsInitializing())
3212     Master->UpdatePictures();
3213 }
3214 
ReceiveAcid(material * Material,cfestring & LocationName,long Modifier)3215 void bodypart::ReceiveAcid(material* Material, cfestring& LocationName, long Modifier)
3216 {
3217   if(Master && MainMaterial->GetInteractionFlags() & CAN_DISSOLVE)
3218   {
3219     long Tries = Modifier / 1000;
3220     Modifier -= Tries * 1000; //opt%?
3221     int Damage = 0;
3222 
3223     for(long c = 0; c < Tries; ++c)
3224       if(!(RAND() % 100))
3225         ++Damage;
3226 
3227     if(Modifier && !(RAND() % 100000 / Modifier))
3228       ++Damage;
3229 
3230     if(Damage)
3231     {
3232       ulong Minute = game::GetTotalMinutes();
3233       character* Master = this->Master;
3234 
3235       if(Master->GetLastAcidMsgMin() != Minute && (Master->CanBeSeenByPlayer() || Master->IsPlayer()))
3236       {
3237         Master->SetLastAcidMsgMin(Minute);
3238         cfestring MName = Material->GetName(false, false);
3239 
3240         if(Master->IsPlayer())
3241         {
3242           cchar* TName = LocationName.IsEmpty() ? GetBodyPartName().CStr() : LocationName.CStr();
3243           ADD_MESSAGE("Caustic %s dissolves your %s.", MName.CStr(), TName);
3244         }
3245         else
3246           ADD_MESSAGE("Caustic %s dissolves %s.", MName.CStr(), Master->CHAR_NAME(DEFINITE));
3247       }
3248 
3249       Master->ReceiveBodyPartDamage(0, Damage, ACID, GetBodyPartIndex(), YOURSELF, false, false, false);
3250       ulong DeathFlags = Material->IsStuckTo(Master) ? IGNORE_TRAPS : 0;
3251       Master->CheckDeath(CONST_S("dissolved by ") + Material->GetName(), 0, DeathFlags);
3252     }
3253   }
3254 }
3255 
ReceiveHeat(material * Material,cfestring & LocationName,long Modifier)3256 void bodypart::ReceiveHeat(material* Material, cfestring& LocationName, long Modifier)
3257 {
3258   if(Master && MainMaterial->GetInteractionFlags() & CAN_BURN)
3259   {
3260     long Tries = Modifier / 1000;
3261     Modifier -= Tries * 1000;
3262     int Damage = 0;
3263 
3264     for(long c = 0; c < Tries; ++c)
3265       if(!(RAND() % 100))
3266         ++Damage;
3267 
3268     if(Modifier && !(RAND() % 100000 / Modifier))
3269       ++Damage;
3270 
3271     if(Damage)
3272     {
3273       ulong Minute = game::GetTotalMinutes();
3274       character* Master = this->Master;
3275 
3276       if(Master->GetLastAcidMsgMin() != Minute && (Master->CanBeSeenByPlayer() || Master->IsPlayer()))
3277       {
3278         Master->SetLastAcidMsgMin(Minute); // I don't really think we need to track acid and heat damage messages separately.
3279         cfestring MName = Material->GetName(false, false);
3280 
3281         if(Master->IsPlayer())
3282         {
3283           cchar* TName = LocationName.IsEmpty() ? GetBodyPartName().CStr() : LocationName.CStr();
3284           ADD_MESSAGE("Scorching %s burns your %s.", MName.CStr(), TName);
3285         }
3286         else
3287           ADD_MESSAGE("Scorching %s burns %s.", MName.CStr(), Master->CHAR_NAME(DEFINITE));
3288       }
3289 
3290       Master->ReceiveBodyPartDamage(0, Damage, FIRE, GetBodyPartIndex(), YOURSELF, false, false, false);
3291       ulong DeathFlags = Material->IsStuckTo(Master) ? IGNORE_TRAPS : 0;
3292       Master->CheckDeath(CONST_S("burnt to death by ") + Material->GetName(), 0, DeathFlags);
3293     }
3294   }
3295 }
3296 
TryToRust(long LiquidModifier)3297 void bodypart::TryToRust(long LiquidModifier)
3298 {
3299   if(MainMaterial->TryToRust(LiquidModifier << 4))
3300   {
3301     cchar* MoreMsg = MainMaterial->GetRustLevel() == NOT_RUSTED ? "" : " more";
3302 
3303     if(Master)
3304     {
3305       if(Master->IsPlayer())
3306         ADD_MESSAGE("Your %s rusts%s.", CHAR_NAME(UNARTICLED), MoreMsg);
3307       else if(CanBeSeenByPlayer())
3308         ADD_MESSAGE("The %s of %s rusts%s.", CHAR_NAME(UNARTICLED), Master->CHAR_NAME(DEFINITE), MoreMsg);
3309     }
3310     else if(CanBeSeenByPlayer())
3311       ADD_MESSAGE("%s rusts%s.", CHAR_NAME(DEFINITE), MoreMsg);
3312 
3313     MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1);
3314   }
3315 }
3316 
GetConsumeMaterial(ccharacter * Consumer,materialpredicate Predicate) const3317 material* corpse::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const
3318 {
3319   for(int c = GetDeceased()->GetBodyParts() - 1; c; --c)
3320   {
3321     bodypart* BodyPart = GetDeceased()->GetBodyPart(c);
3322 
3323     if(BodyPart)
3324     {
3325       material* CM = BodyPart->GetConsumeMaterial(Consumer, Predicate);
3326 
3327       if(CM)
3328         return CM;
3329     }
3330   }
3331 
3332   return GetDeceased()->GetTorso()->GetConsumeMaterial(Consumer, Predicate);
3333 }
3334 
Cannibalize()3335 void corpse::Cannibalize()
3336 {
3337   item::Cannibalize();
3338 
3339   for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c)
3340   {
3341     bodypart* BodyPart = GetDeceased()->GetBodyPart(c);
3342 
3343     if(BodyPart)
3344       BodyPart->Cannibalize();
3345   }
3346 }
3347 
RemoveMaterial(material * Material)3348 material* bodypart::RemoveMaterial(material* Material)
3349 {
3350   if(Master && GetBodyPartIndex() == TORSO_INDEX)
3351     return Master->GetMotherEntity()->RemoveMaterial(Material); // gum
3352   else
3353     return item::RemoveMaterial(Material);
3354 }
3355 
CopyAttributes(const bodypart * BodyPart)3356 void arm::CopyAttributes(const bodypart* BodyPart)
3357 {
3358   const arm* Arm = static_cast<const arm*>(BodyPart);
3359   StrengthExperience = Arm->StrengthExperience;
3360   DexterityExperience = Arm->DexterityExperience;
3361 }
3362 
CopyAttributes(const bodypart * BodyPart)3363 void leg::CopyAttributes(const bodypart* BodyPart)
3364 {
3365   const leg* Leg = static_cast<const leg*>(BodyPart);
3366   StrengthExperience = Leg->StrengthExperience;
3367   AgilityExperience = Leg->AgilityExperience;
3368 }
3369 
DetectMaterial(const material * Material) const3370 truth corpse::DetectMaterial(const material* Material) const
3371 {
3372   return GetDeceased()->DetectMaterial(Material);
3373 }
3374 
DestroyBodyPart(stack * Stack)3375 void bodypart::DestroyBodyPart(stack* Stack)
3376 {
3377   int Lumps = 1 + RAND() % 3;
3378   long LumpVolume = Volume / Lumps >> 2;
3379 
3380   if(LumpVolume >= 10)
3381     for(int c = 0; c < Lumps; ++c)
3382     {
3383       item* Lump = GetMainMaterial()->CreateNaturalForm(LumpVolume + RAND() % LumpVolume);
3384       Stack->AddItem(Lump);
3385     }
3386 
3387   SendToHell();
3388 }
3389 
GetBitmapPos(int Frame) const3390 v2 magicmushroomtorso::GetBitmapPos(int Frame) const
3391 {
3392   v2 BasePos = torso::GetBitmapPos(Frame);
3393   Frame &= 0x3F;
3394 
3395   if(!(Frame & 0x30))
3396   {
3397     if(Frame <= 8)
3398       return v2(BasePos.X + 64 - (abs(Frame - 4) << 4), BasePos.Y);
3399     else
3400       return v2(BasePos.X + 64 - (abs(Frame - 12) << 4), BasePos.Y + 16);
3401   }
3402   else
3403     return BasePos;
3404 }
3405 
GetBitmapPos(int Frame) const3406 v2 dogtorso::GetBitmapPos(int Frame) const
3407 {
3408   v2 BasePos = torso::GetBitmapPos(Frame);
3409 
3410   if(Frame >= GraphicData.AnimationFrames >> 1)
3411     BasePos.X += 32;
3412 
3413   return v2(BasePos.X + ((Frame & 4) << 2), BasePos.Y);
3414 }
3415 
Draw(blitdata & BlitData) const3416 void dogtorso::Draw(blitdata& BlitData) const
3417 {
3418   cint AF = GraphicData.AnimationFrames >> 1;
3419   int Index = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1);
3420 
3421   if(GetHP() << 1 <= GetMaxHP())
3422     Index += AF;
3423 
3424   cbitmap* P = GraphicData.Picture[Index];
3425 
3426   if(BlitData.CustomData & ALLOW_ALPHA)
3427     P->AlphaPriorityBlit(BlitData);
3428   else
3429     P->MaskedPriorityBlit(BlitData);
3430 }
3431 
SetLifeExpectancy(int Base,int RandPlus)3432 void corpse::SetLifeExpectancy(int Base, int RandPlus)
3433 {
3434   Deceased->SetLifeExpectancy(Base, RandPlus);
3435 }
3436 
Be()3437 void corpse::Be()
3438 {
3439   for(int c = 0; c < Deceased->GetBodyParts(); ++c)
3440   {
3441     bodypart* BodyPart = Deceased->GetBodyPart(c);
3442 
3443     if(BodyPart)
3444       BodyPart->Be();
3445   }
3446 }
3447 
SetLifeExpectancy(int Base,int RandPlus)3448 void bodypart::SetLifeExpectancy(int Base, int RandPlus)
3449 {
3450   LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base;
3451 
3452   if(!Master)
3453     Enable();
3454 }
3455 
SpecialEatEffect(character * Eater,int Amount)3456 void bodypart::SpecialEatEffect(character* Eater, int Amount)
3457 {
3458   Amount >>= 6;
3459 
3460   if(Amount
3461      && (!Master || Master->SpillsBlood())
3462      && (IsAlive() || MainMaterial->IsLiquid())
3463      && !game::IsInWilderness())
3464   {
3465     if(Eater->GetVirtualHead())
3466       Eater->GetVirtualHead()->SpillFluid(Eater, CreateBlood(Amount));
3467 
3468     Eater->GetTorso()->SpillFluid(Eater, CreateBlood(Amount));
3469   }
3470 }
3471 
IsValuable() const3472 truth corpse::IsValuable() const
3473 {
3474   for(int c = 0; c < Deceased->GetBodyParts(); ++c)
3475   {
3476     bodypart* BodyPart = Deceased->GetBodyPart(c);
3477 
3478     if(BodyPart && BodyPart->IsValuable())
3479       return true;
3480   }
3481 
3482   return false;
3483 }
3484 
Necromancy(character * Necromancer)3485 truth corpse::Necromancy(character* Necromancer)
3486 {
3487   if(Necromancer && Necromancer->IsPlayer())
3488     game::DoEvilDeed(50);
3489 
3490   character* Zombie = GetDeceased()->CreateZombie();
3491 
3492   if(Zombie)
3493   {
3494     Zombie->ChangeTeam(Necromancer ? Necromancer->GetTeam() : game::GetTeam(MONSTER_TEAM));
3495     Zombie->PutToOrNear(GetPos());
3496     RemoveFromSlot();
3497     SendToHell();
3498 
3499     if(Zombie->CanBeSeenByPlayer())
3500       ADD_MESSAGE("%s rises back to cursed undead life.", Zombie->CHAR_DESCRIPTION(INDEFINITE));
3501 
3502     Zombie->SignalStepFrom(0);
3503     return true;
3504   }
3505   else
3506   {
3507     if(CanBeSeenByPlayer())
3508       ADD_MESSAGE("%s vibrates for some time.", CHAR_NAME(DEFINITE));
3509 
3510     return false;
3511   }
3512 }
3513 
GetOutlineAlpha(int Frame) const3514 alpha mysticfrogtorso::GetOutlineAlpha(int Frame) const
3515 {
3516   Frame &= 31;
3517   return Frame * (31 - Frame) >> 1;
3518 }
3519 
GetOutlineColor(int Frame) const3520 col16 mysticfrogtorso::GetOutlineColor(int Frame) const
3521 {
3522   switch((Frame&127) >> 5)
3523   {
3524    case 0: return BLUE;
3525    case 1: return GREEN;
3526    case 2: return RED;
3527    case 3: return YELLOW;
3528   }
3529 
3530   return TRANSPARENT_COLOR;
3531 }
3532 
UpdateFlags()3533 void bodypart::UpdateFlags()
3534 {
3535   if((HP << 1) + HP < MaxHP || (HP == 1 && MaxHP != 1))
3536     Flags |= BADLY_HURT;
3537   else
3538     Flags &= ~BADLY_HURT;
3539 
3540   if(Master->BodyPartIsStuck(GetBodyPartIndex()))
3541     Flags |= STUCK;
3542   else
3543     Flags &= ~STUCK;
3544 }
3545 
SignalPossibleUsabilityChange()3546 void head::SignalPossibleUsabilityChange()
3547 {
3548   ulong OldFlags = Flags;
3549   UpdateFlags();
3550 
3551   if(!Master->IsInitializing() && HP > 0
3552      && Flags & BADLY_HURT && !(OldFlags & BADLY_HURT))
3553     switch(RAND_N(8))
3554     {
3555      case 0:
3556      case 1:
3557      case 2: Master->LoseConsciousness(50 + RAND_N(50)); break;
3558      case 3:
3559      case 4:
3560      case 5: Master->BeginTemporaryState(CONFUSED, 500 + RAND_N(500)); break;
3561      case 6:
3562       if(Master->IsPlayer() && !RAND_N(3))
3563       {
3564         if(RAND_N(5))
3565         {
3566           ADD_MESSAGE("Your memory becomes blurred.");
3567           GetLevel()->Amnesia(25 + RAND_N(50));
3568           Master->EditExperience(INTELLIGENCE, -80, 1 << 13);
3569           game::SendLOSUpdateRequest();
3570         }
3571         else
3572         {
3573           ADD_MESSAGE("A terrible concussion garbles your consciousness.");
3574           Master->BeginTemporaryState(CONFUSED, 5000 + RAND_N(5000));
3575           Master->EditExperience(INTELLIGENCE, -100, 1 << 14);
3576           GetLevel()->BlurMemory();
3577           game::SendLOSUpdateRequest();
3578         }
3579       }
3580       else
3581         Master->EditExperience(INTELLIGENCE, -60, 1 << 12);
3582 
3583       break;
3584      case 7:
3585       Master->ForgetRandomThing();
3586     }
3587 }
3588 
SignalPossibleUsabilityChange()3589 void arm::SignalPossibleUsabilityChange()
3590 {
3591   ulong OldFlags = Flags;
3592   UpdateFlags();
3593 
3594   if(Flags != OldFlags && !Master->IsInitializing())
3595     Master->CalculateBattleInfo();
3596 }
3597 
SignalPossibleUsabilityChange()3598 void leg::SignalPossibleUsabilityChange()
3599 {
3600   ulong OldFlags = Flags;
3601   UpdateFlags();
3602 
3603   if(Flags != OldFlags && !Master->IsInitializing())
3604     Master->CalculateBattleInfo();
3605 }
3606 
IncreaseHP()3607 void bodypart::IncreaseHP()
3608 {
3609   ++HP;
3610   RemoveDamageIDs(1);
3611   SignalPossibleUsabilityChange();
3612 }
3613 
FastRestoreHP()3614 void bodypart::FastRestoreHP()
3615 {
3616   HP = MaxHP;
3617   DamageID.clear();
3618   SignalPossibleUsabilityChange();
3619 }
3620 
RestoreHP()3621 void bodypart::RestoreHP()
3622 {
3623   HP = MaxHP;
3624   DamageID.clear();
3625   SignalPossibleUsabilityChange();
3626   Master->CalculateHP();
3627 }
3628 
SetIsUnique(truth What)3629 void bodypart::SetIsUnique(truth What)
3630 {
3631   if(What)
3632     Flags |= UNIQUE;
3633   else
3634     Flags &= ~UNIQUE;
3635 }
3636 
SetIsInfectedByLeprosy(truth What)3637 void bodypart::SetIsInfectedByLeprosy(truth What)
3638 {
3639   MainMaterial->SetIsInfectedByLeprosy(What);
3640 }
3641 
SetSparkleFlags(int What)3642 void bodypart::SetSparkleFlags(int What)
3643 {
3644   cint S = SPARKLING_B|SPARKLING_C|SPARKLING_D;
3645   Flags = (Flags & ~(S << BODYPART_SPARKLE_SHIFT)) | ((What & S) << BODYPART_SPARKLE_SHIFT);
3646 }
3647 
IsAnimated() const3648 truth arm::IsAnimated() const
3649 {
3650   return (WieldedGraphicData.AnimationFrames > 1) || IsBurning();
3651 }
3652 
SignalAnimationStateChange(truth WasAnimated)3653 void bodypart::SignalAnimationStateChange(truth WasAnimated)
3654 {
3655   if(WasAnimated)
3656   {
3657     for(int c = 0; c < GetSquaresUnder(); ++c)
3658     {
3659       square* Square = GetSquareUnder(c);
3660 
3661       if(Square)
3662         Square->DecAnimatedEntities();
3663     }
3664   }
3665   else
3666   {
3667     for(int c = 0; c < GetSquaresUnder(); ++c)
3668     {
3669       square* Square = GetSquareUnder(c);
3670 
3671       if(Square)
3672         Square->IncAnimatedEntities();
3673     }
3674   }
3675 }
3676 
MaterialIsChangeable(ccharacter *) const3677 truth bodypart::MaterialIsChangeable(ccharacter*) const
3678 {
3679   return !Master || !Master->BodyPartIsVital(GetBodyPartIndex()) || UseMaterialAttributes();
3680 }
3681 
AddAdjective(festring & String,truth Articled) const3682 truth bodypart::AddAdjective(festring& String, truth Articled) const
3683 {
3684   if(!Master)
3685   {
3686     if(Articled)
3687       String << "a ";
3688 
3689     String << "severed ";
3690     return true;
3691   }
3692   else
3693     return false;
3694 }
3695 
RemoveRust()3696 void bodypart::RemoveRust()
3697 {
3698   item::RemoveRust();
3699   RestoreHP();
3700 }
3701 
RemoveBurns()3702 void bodypart::RemoveBurns()
3703 {
3704   item::RemoveBurns();
3705   RestoreHP();
3706 }
3707 
GetFixPrice() const3708 long bodypart::GetFixPrice() const
3709 {
3710   return GetMaxHP() - GetHP() + GetMainMaterial()->GetRustLevel() * 25 + GetMainMaterial()->GetBurnLevel() * 25;
3711 }
3712 
IsFixableBySmith(ccharacter *) const3713 truth bodypart::IsFixableBySmith(ccharacter*) const
3714 {
3715   return (GetMainMaterial()->GetCategoryFlags() & IS_METAL
3716           && (GetHP() < GetMaxHP() || IsRusted()));
3717 }
3718 
IsFixableByTailor(ccharacter *) const3719 truth bodypart::IsFixableByTailor(ccharacter*) const
3720 {
3721   return (GetMainMaterial()->GetCategoryFlags() & CAN_BE_TAILORED
3722           && GetHP() < GetMaxHP());
3723 }
3724 
Fix()3725 item* bodypart::Fix()
3726 {
3727   RestoreHP();
3728   return this;
3729 }
3730 
SignalMaterialChange()3731 void bodypart::SignalMaterialChange()
3732 {
3733   if(Master)
3734     RestoreHP();
3735 }
3736 
ShowMaterial() const3737 truth bodypart::ShowMaterial() const
3738 {
3739   return MainMaterial->GetConfig() != NormalMaterial;
3740 }
3741 
GetMaterialColorD(int Frame) const3742 col16 lobhsetorso::GetMaterialColorD(int Frame) const
3743 {
3744   Frame &= 31;
3745   int Modifier = Frame * (31 - Frame);
3746   return MakeRGB16(220 - (Modifier >> 2), 220 - (Modifier >> 1), 0);
3747 }
3748 
IsDestroyable(ccharacter *) const3749 truth bodypart::IsDestroyable(ccharacter*) const
3750 {
3751   return !Master || !Master->BodyPartIsVital(GetBodyPartIndex());
3752 }
3753 
DamageTypeCanScar(int Type)3754 truth bodypart::DamageTypeCanScar(int Type)
3755 {
3756   return !(Type == POISON || Type == DRAIN || Type == PSI);
3757 }
3758 
GenerateScar(int Damage,int Type)3759 void bodypart::GenerateScar(int Damage, int Type)
3760 {
3761   Scar.push_back(scar());
3762   scar& NewScar = Scar.back();
3763   NewScar.Severity = 1 + RAND_N(1 + 5 * Damage / GetMaxHP());
3764 
3765   if(GetMaster()->IsPlayer())
3766   {
3767     int ScarColor = MakeShadeColor(GetMainMaterial()->GetColor());
3768     NewScar.PanelBitmap = igraph::GenerateScarBitmap(GetBodyPartIndex(),
3769                                                      NewScar.Severity,
3770                                                      ScarColor);
3771     ADD_MESSAGE("Your %s is scarred.", CHAR_NAME(UNARTICLED));
3772   }
3773   else
3774     NewScar.PanelBitmap = 0;
3775 
3776   CalculateMaxHP();
3777   GetMaster()->CalculateMaxHP();
3778   GetMaster()->CalculateAttributeBonuses();
3779   CalculateAttackInfo();
3780 }
3781 
DrawScars(cblitdata & B) const3782 void bodypart::DrawScars(cblitdata& B) const
3783 {
3784   for(size_t c = 0; c < Scar.size(); ++c)
3785   {
3786     if(!Scar[c].PanelBitmap)
3787     {
3788       int ScarColor = MakeShadeColor(GetMainMaterial()->GetColor());
3789       Scar[c].PanelBitmap = igraph::GenerateScarBitmap(GetBodyPartIndex(),
3790                                                        Scar[c].Severity,
3791                                                        ScarColor);
3792     }
3793 
3794     Scar[c].PanelBitmap->NormalMaskedBlit(B);
3795   }
3796 }
3797 
operator <<(outputfile & SaveFile,const scar & Scar)3798 outputfile& operator<<(outputfile& SaveFile, const scar& Scar)
3799 {
3800   SaveFile << Scar.Severity << Scar.PanelBitmap;
3801   return SaveFile;
3802 }
3803 
operator >>(inputfile & SaveFile,scar & Scar)3804 inputfile& operator>>(inputfile& SaveFile, scar& Scar)
3805 {
3806   SaveFile >> Scar.Severity >> Scar.PanelBitmap;
3807   return SaveFile;
3808 }
3809 
CalculateScarAttributePenalty(int Attribute) const3810 int bodypart::CalculateScarAttributePenalty(int Attribute) const
3811 {
3812   double DoubleAttribute = Attribute;
3813 
3814   for(size_t c = 0; c < Scar.size(); ++c)
3815     DoubleAttribute *= (100. - Scar[c].Severity * 4) / 100;
3816 
3817   return Min(Attribute - int(DoubleAttribute), Attribute - 1);
3818 }
3819 
CalculateBurnAttributePenalty(int Attribute) const3820 int bodypart::CalculateBurnAttributePenalty(int Attribute) const
3821 {
3822   int BurnLevel = 0;
3823   double DoubleAttribute = Attribute;
3824 
3825   if(MainMaterial)
3826   {
3827     BurnLevel = MainMaterial->GetBurnLevel();
3828     DoubleAttribute *= (1. - BurnLevel * 0.05);
3829   }
3830 
3831   return BurnLevel ? Min(Attribute - int(DoubleAttribute), Attribute - 1) : 0;
3832 }
3833 
~bodypart()3834 bodypart::~bodypart()
3835 {
3836   for(size_t c = 0; c < Scar.size(); ++c)
3837     delete Scar[c].PanelBitmap;
3838 }
3839 
bodypart(const bodypart & B)3840 bodypart::bodypart(const bodypart& B)
3841 : mybase(B), OwnerDescription(B.OwnerDescription), Master(B.Master),
3842   CarriedWeight(B.CarriedWeight), BodyPartVolume(B.BodyPartVolume),
3843   BitmapPos(B.BitmapPos), ColorB(B.ColorB), ColorC(B.ColorC), ColorD(B.ColorD),
3844   SpecialFlags(B.SpecialFlags), HP(B.HP), MaxHP(B.MaxHP),
3845   BloodMaterial(B.BloodMaterial), NormalMaterial(B.NormalMaterial),
3846   SpillBloodCounter(B.SpillBloodCounter), WobbleData(B.WobbleData), Scar(B.Scar)
3847 {
3848   for(size_t c = 0; c < Scar.size(); ++c)
3849     if(Scar[c].PanelBitmap)
3850       Scar[c].PanelBitmap = new bitmap(Scar[c].PanelBitmap);
3851 }
3852 
3853 /* Amount should be > 0 */
3854 
RemoveDamageIDs(int Amount)3855 void bodypart::RemoveDamageIDs(int Amount)
3856 {
3857   /*while(Amount)
3858   {
3859     damageid& D = DamageID.front();
3860     int CurrentAmount = D.Amount;
3861 
3862     if(Amount < CurrentAmount)
3863     {
3864       D.Amount -= Amount;
3865       Amount = 0;
3866     }
3867     else
3868     {
3869       DamageID.pop_front();
3870       Amount -= CurrentAmount;
3871     }
3872   }*/
3873 }
3874 
3875 /* Amount should be > 0 */
3876 
AddDamageID(int SrcID,int Amount)3877 void bodypart::AddDamageID(int SrcID, int Amount)
3878 {
3879   /*damageid D = { SrcID, Amount };
3880   DamageID.push_back(D);*/
3881 }
3882 
GetCurrentSWeaponSkillBonus() const3883 int arm::GetCurrentSWeaponSkillBonus() const
3884 {
3885   return (*GetCurrentSWeaponSkill()
3886       ? (*GetCurrentSWeaponSkill())->GetBonus() : 1);
3887 }
3888 
GetBitmapPos(int Frame) const3889 v2 battorso::GetBitmapPos(int Frame) const
3890 {
3891   v2 BasePos = torso::GetBitmapPos(Frame);
3892   Frame &= 0xF;
3893   return v2(BasePos.X + ((Frame &~ 3) << 2), BasePos.Y);
3894 }
3895 
GetBitmapPos(int Frame) const3896 v2 spidertorso::GetBitmapPos(int Frame) const
3897 {
3898   v2 BasePos = torso::GetBitmapPos(Frame);
3899   Frame &= 0xF;
3900   return v2(BasePos.X + ((Frame &~ 7) << 1), BasePos.Y);
3901 }
3902 
GetBitmapPos(int Frame) const3903 v2 snaketorso::GetBitmapPos(int Frame) const
3904 {
3905   v2 BasePos = torso::GetBitmapPos(Frame);
3906 
3907   if(Frame<16)
3908     return v2(BasePos.X + ((Frame/2)%2)*TILE_SIZE, BasePos.Y);
3909   else
3910   if(Frame<24)
3911     return v2(BasePos.X + TILE_SIZE, BasePos.Y);
3912   return BasePos;
3913 }
3914 
GetBitmapPos(int Frame) const3915 v2 magpietorso::GetBitmapPos(int Frame) const
3916 {
3917   v2 BasePos = torso::GetBitmapPos(Frame);
3918   Frame &= 0xF;
3919 
3920   /**
3921    * GetClassAnimationFrames() is the total animation frames per second, so Frame is from 0 to 15 here
3922    * Magpie has 8 pictures, so it will draw the same picture for each 2 frames.
3923    * GetBitmapPos() method will be called only once and stored on the savegame file, therefore there is no need for speed.
3924    * TODO correct?
3925    */
3926   float fPicTot=8.0;
3927   float fDiv = GetClassAnimationFrames()/fPicTot; //each 2 frames
3928   int iPic=Frame/fDiv; DBG3(Frame,fDiv,iPic);
3929   return v2(BasePos.X + iPic*TILE_SIZE, BasePos.Y);
3930 }
3931 
HasSadistWeapon() const3932 truth arm::HasSadistWeapon() const
3933 {
3934   item* Wielded = GetWielded();
3935   return Wielded && Wielded->IsSadistWeapon();
3936 }
3937 
AddStateDescription(festring & Name,truth Articled) const3938 truth corpse::AddStateDescription(festring& Name, truth Articled) const
3939 {
3940   if(!Spoils())
3941     return false;
3942 
3943   truth Hasted = true, Slowed = true;
3944 
3945   for(int c = 0; c < GetDeceased()->GetBodyParts(); ++c)
3946   {
3947     bodypart* BodyPart = GetDeceased()->GetBodyPart(c);
3948 
3949     if(BodyPart)
3950     {
3951       if(!(BodyPart->ItemFlags & HASTE))
3952         Hasted = false;
3953 
3954       if(!(BodyPart->ItemFlags & SLOW))
3955         Slowed = false;
3956     }
3957   }
3958 
3959   if((Hasted | Slowed) && Articled)
3960     Name << "a ";
3961 
3962   if(Hasted)
3963     Name << "hasted ";
3964 
3965   if(Slowed)
3966     Name << "slowed ";
3967 
3968   return true;
3969 }
3970