bodyparts separated
[k8-i-v-a-n.git] / src / game / bodyparts / item_bodypart.cpp
blob3d2d5fa4bf21443f52ebfce5ccda3ce4b74d9300
1 #ifdef HEADER_PHASE
2 ITEM(bodypart, item)
4 public:
5 friend class corpse;
6 bodypart() : Master(0) { }
7 bodypart(const bodypart&);
8 virtual ~bodypart();
9 virtual void Save(outputfile&) const;
10 virtual void Load(inputfile&);
11 virtual int GetGraphicsContainerIndex() const;
12 character* GetMaster() const { return Master; }
13 humanoid* GetHumanoidMaster() const;
14 void SetMaster(character* What) { Master = What; }
15 virtual int GetStrengthValue() const;
16 int GetMaxHP() const { return MaxHP; }
17 void SetHP(int);
18 int GetHP() const { return HP; }
19 void EditHP(int, int);
20 void IncreaseHP();
21 virtual int GetTotalResistance(int) const { return 0; }
22 virtual truth ReceiveDamage(character*, int, int, int);
23 cfestring& GetOwnerDescription() const { return OwnerDescription; }
24 void SetOwnerDescription(cfestring& What) { OwnerDescription = What; }
25 truth IsUnique() const { return Flags & UNIQUE; }
26 void SetIsUnique(truth);
27 virtual void DropEquipment(stack* = 0) { }
28 virtual void InitSpecialAttributes() { }
29 virtual void SignalEquipmentAdd(gearslot*);
30 virtual void SignalEquipmentRemoval(gearslot*, citem*);
31 virtual void Mutate();
32 sLong GetBodyPartVolume() const { return BodyPartVolume; }
33 sLong GetCarriedWeight() const { return CarriedWeight; }
34 virtual item* GetEquipment(int) const { return 0; }
35 virtual int GetEquipments() const { return 0; }
36 virtual void CalculateVolumeAndWeight();
37 virtual void CalculateEmitation();
38 void CalculateMaxHP(feuLong = MAY_CHANGE_HPS|CHECK_USABILITY);
39 virtual void SignalVolumeAndWeightChange();
40 void FastRestoreHP();
41 void RestoreHP();
42 virtual void CalculateDamage() { }
43 virtual void CalculateToHitValue() { }
44 virtual void CalculateAPCost() { }
45 void CalculateAttackInfo();
46 double GetTimeToDie(int, double, double, truth, truth) const;
47 virtual double GetRoughChanceToHit(double, double) const;
48 cfestring& GetBodyPartName() const { return GetNameSingular(); }
49 void RandomizePosition();
50 void ResetPosition() { SpecialFlags &= ~0x7; }
51 virtual void SignalSpoil(material*);
52 virtual truth CanBePiledWith(citem*, ccharacter*) const;
53 truth IsAlive() const;
54 void SpillBlood(int);
55 void SpillBlood(int, v2);
56 virtual void Be();
57 int GetConditionColorIndex() const;
58 void SetBitmapPos(v2 What) { BitmapPos = What; }
59 void SetSpecialFlags(int What) { SpecialFlags = What; }
60 void SetWobbleData(int What) { WobbleData = What; }
61 void SetMaterialColorB(col16 What) { ColorB = What; }
62 void SetMaterialColorC(col16 What) { ColorC = What; }
63 void SetMaterialColorD(col16 What) { ColorD = What; }
64 virtual void SignalEnchantmentChange();
65 virtual void CalculateAttributeBonuses() { }
66 virtual void SignalSpoilLevelChange(material*);
67 virtual truth CanBeEatenByAI(ccharacter*) const;
68 virtual truth DamageArmor(character*, int, int) { return false; }
69 truth CanBeSevered(int) const;
70 virtual truth EditAllAttributes(int) { return false; }
71 virtual void Draw(blitdata&) const;
72 void SetSparkleFlags(int);
73 virtual int GetSpecialFlags() const;
74 virtual truth IsRepairable(ccharacter*) const;
75 truth IsWarm() const;
76 truth UseMaterialAttributes() const;
77 truth CanRegenerate() const;
78 truth CanHaveParasite() const;
79 virtual square* GetSquareUnder(int = 0) const;
80 virtual lsquare* GetLSquareUnder(int = 0) const;
81 virtual item* GetArmorToReceiveFluid(truth) const { return 0; }
82 virtual void SpillFluid(character*, liquid*, int = 0);
83 void StayOn(liquid*);
84 void SetBloodMaterial(int What) { BloodMaterial = What; }
85 int GetBloodMaterial() const { return BloodMaterial; }
86 liquid* CreateBlood(sLong) const;
87 virtual truth UpdateArmorPictures() { return false; }
88 virtual void DrawArmor(blitdata&) const { }
89 virtual void UpdatePictures();
90 item *GetExternalBodyArmor () const;
91 item *GetExternalCloak () const;
92 item *GetExternalHelmet () const;
93 item *GetExternalBelt () const;
94 virtual void ReceiveAcid(material*, cfestring&, sLong);
95 virtual truth ShowFluids() const { return false; }
96 virtual void TryToRust(sLong);
97 virtual truth AllowFluidBe() const;
98 virtual material* RemoveMaterial(material*);
99 virtual void CopyAttributes(const bodypart*) { }
100 virtual void DestroyBodyPart(stack*);
101 virtual void SetLifeExpectancy(int, int);
102 virtual void SpecialEatEffect(character*, int);
103 virtual character* GetBodyPartMaster() const { return Master; }
104 virtual truth AllowFluids() const { return true; }
105 truth IsBadlyHurt() const { return Flags & BADLY_HURT; }
106 truth IsStuck() const { return Flags & STUCK; }
107 truth IsUsable() const { return !(Flags & (BADLY_HURT|STUCK)); }
108 virtual void SignalPossibleUsabilityChange() { UpdateFlags(); }
109 void SetIsInfectedByLeprosy(truth);
110 virtual int GetSparkleFlags() const;
111 virtual truth MaterialIsChangeable(ccharacter*) const;
112 virtual void RemoveRust();
113 virtual item* Fix();
114 virtual sLong GetFixPrice() const;
115 virtual truth IsFixableBySmith(ccharacter*) const;
116 virtual truth IsFixableByTailor(ccharacter*) const;
117 virtual void SignalMaterialChange();
118 void SetNormalMaterial(int What) { NormalMaterial = What; }
119 virtual truth IsBroken() const { return HP < MaxHP; }
120 virtual truth IsDestroyable(ccharacter*) const;
121 void DrawScars(cblitdata&) const;
122 static truth DamageTypeCanScar(int);
123 void GenerateScar(int, int);
124 int CalculateScarAttributePenalty(int) const;
125 virtual truth IsBodyPart () const { return true; }
127 protected:
128 virtual alpha GetMaxAlpha() const;
129 virtual void GenerateMaterials() { }
130 virtual void AddPostFix(festring&, int) const;
131 virtual truth ShowMaterial() const;
132 virtual int GetArticleMode() const;
133 virtual col16 GetMaterialColorA(int) const;
134 virtual col16 GetMaterialColorB(int) const { return ColorB; }
135 virtual col16 GetMaterialColorC(int) const { return ColorC; }
136 virtual col16 GetMaterialColorD(int) const { return ColorD; }
137 virtual v2 GetBitmapPos(int) const { return BitmapPos; }
138 virtual int GetWobbleData() const { return WobbleData; }
139 void UpdateArmorPicture(graphicdata&, item*, int, v2 (item::*)(int) const, truth = false) const;
140 void DrawEquipment(const graphicdata&, blitdata&) const;
141 void UpdateFlags();
142 truth MasterIsAnimated() const;
143 void SignalAnimationStateChange(truth);
144 virtual truth AddAdjective(festring&, truth) const;
145 void RemoveDamageIDs(int);
146 void AddDamageID(int, int);
147 festring OwnerDescription;
148 character* Master;
149 sLong CarriedWeight;
150 sLong BodyPartVolume;
151 packv2 BitmapPos;
152 packcol16 ColorB;
153 packcol16 ColorC;
154 packcol16 ColorD;
155 uShort SpecialFlags;
156 short HP;
157 short MaxHP;
158 short BloodMaterial;
159 short NormalMaterial;
160 uChar SpillBloodCounter;
161 uChar WobbleData;
162 std::vector<scar> Scar;
163 std::deque<damageid> DamageID;
167 #else
171 int bodypart::GetGraphicsContainerIndex() const { return GR_HUMANOID; }
175 int bodypart::GetArticleMode() const { return IsUnique() ? FORCE_THE : 0; }
179 truth bodypart::IsAlive() const { return MainMaterial->GetBodyFlags() & IS_ALIVE; }
183 int bodypart::GetSpecialFlags() const { return SpecialFlags|ST_OTHER_BODYPART; }
187 col16 bodypart::GetMaterialColorA(int) const { return GetMainMaterial()->GetSkinColor(); }
191 truth bodypart::IsWarm() const { return MainMaterial->GetBodyFlags() & IS_WARM; }
195 truth bodypart::UseMaterialAttributes() const { return MainMaterial->GetBodyFlags() & USE_MATERIAL_ATTRIBUTES || !Master || Master->AlwaysUseMaterialAttributes(); }
199 truth bodypart::CanRegenerate() const { return MainMaterial->GetBodyFlags() & CAN_REGENERATE; }
203 truth bodypart::CanHaveParasite() const { return MainMaterial->GetBodyFlags() & CAN_HAVE_PARASITE; }
207 square* bodypart::GetSquareUnder(int I) const { return Master ? Slot[0]->GetSquareUnder(I) : Slot[I]->GetSquareUnder(); }
211 lsquare* bodypart::GetLSquareUnder(int I) const { return static_cast<lsquare*>(Master ? Slot[0]->GetSquareUnder(I) : Slot[I]->GetSquareUnder()); }
215 item* bodypart::GetExternalBodyArmor() const { return GetHumanoidMaster()->GetBodyArmor(); }
219 item* bodypart::GetExternalCloak() const { return GetHumanoidMaster()->GetCloak(); }
223 truth bodypart::AllowFluidBe() const { return !Master || !Master->IsPolymorphed(); }
227 item *bodypart::GetExternalHelmet () const { return GetHumanoidMaster()->GetHelmet(); }
231 item *bodypart::GetExternalBelt () const { return GetHumanoidMaster()->GetBelt(); }
235 void bodypart::Save(outputfile& SaveFile) const
237 item::Save(SaveFile);
238 SaveFile << BitmapPos << ColorB << ColorC << ColorD << SpecialFlags << WobbleData << HP;
239 SaveFile << OwnerDescription << BloodMaterial << NormalMaterial << Scar << DamageID;
244 void bodypart::Load(inputfile& SaveFile)
246 item::Load(SaveFile);
247 SaveFile >> BitmapPos >> ColorB >> ColorC >> ColorD >> SpecialFlags >> WobbleData >> HP;
248 SaveFile >> OwnerDescription >> BloodMaterial >> NormalMaterial >> Scar >> DamageID;
253 int bodypart::GetStrengthValue() const
255 if(!UseMaterialAttributes())
256 return sLong(GetStrengthModifier()) * Master->GetAttribute(ENDURANCE) / 2000;
257 else
258 return sLong(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000;
263 truth bodypart::ReceiveDamage(character* Damager, int Damage, int Type, int)
265 if(Master)
267 if(Type & POISON && !IsAlive())
268 return false;
270 int BHP = HP;
272 if(HP <= Damage && !CanBeSevered(Type))
273 Damage = GetHP() - 1;
275 if(!Damage)
276 return false;
278 EditHP(1, -Damage);
280 if(Type & DRAIN && IsAlive())
281 for(int c = 0; c < Damage; ++c)
282 Damager->HealHitPoint();
284 truth WasBadlyHurt = IsBadlyHurt();
286 if(HP <= 0)
287 return true;
289 if(DamageTypeCanScar(Type) && !(RAND_N(25 + 25 * HP / MaxHP)))
290 GenerateScar(Damage, Type);
292 if (Master->IsPlayer()) {
293 if (HP == 1 && BHP > 1) {
294 if (IsAlive()) ADD_MESSAGE("Your %s bleeds very badly.", GetBodyPartName().CStr());
295 else ADD_MESSAGE("Your %s is in very bad condition.", GetBodyPartName().CStr());
296 if (Master->BodyPartIsVital(GetBodyPartIndex())) game::AskForEscPress(CONST_S("Vital bodypart in serious danger!"));
297 } else if (IsBadlyHurt() && !WasBadlyHurt) {
298 if (IsAlive()) ADD_MESSAGE("Your %s bleeds.", GetBodyPartName().CStr());
299 else ADD_MESSAGE("Your %s is in bad condition.", GetBodyPartName().CStr());
300 if (Master->BodyPartIsVital(GetBodyPartIndex())) game::AskForEscPress(CONST_S("Vital bodypart in danger!"));
303 SignalPossibleUsabilityChange();
306 return false;
311 truth bodypart::CanBeSevered(int Type) const
313 if((HP == MaxHP && HP != 1 && !Master->IsExtraFragile())
314 || (Type & (POISON|SOUND) && GetBodyPartIndex() != TORSO_INDEX))
315 return false;
317 if(!Master->BodyPartIsVital(GetBodyPartIndex()) || Master->IsExtraFragile())
318 return true;
320 bodypart* Torso = Master->GetTorso();
321 return Torso->HP != Torso->MaxHP || Torso->HP == 1;
326 humanoid* bodypart::GetHumanoidMaster() const
328 return static_cast<humanoid*>(Master);
333 int bodypart::GetSparkleFlags() const
335 return (GetMainMaterial()->SkinColorIsSparkling() ? SPARKLING_A : 0)
336 | (Flags >> BODYPART_SPARKLE_SHIFT & (SPARKLING_B|SPARKLING_C|SPARKLING_D));
341 void bodypart::SignalEquipmentAdd(gearslot* Slot)
343 if(Master)
344 Master->SignalEquipmentAdd(Slot->GetEquipmentIndex());
349 void bodypart::SignalEquipmentRemoval(gearslot* Slot, citem* Item)
351 if(Master)
352 Master->SignalEquipmentRemoval(Slot->GetEquipmentIndex(), Item);
357 void bodypart::Mutate()
359 GetMainMaterial()->SetVolume(sLong(GetVolume() * (1.5 - (RAND() & 1023) / 1023.)));
364 alpha bodypart::GetMaxAlpha() const
366 if(Master && Master->StateIsActivated(INVISIBLE))
367 return 150;
368 else
369 return 255;
374 void bodypart::AddPostFix(festring& String, int) const
376 if(!OwnerDescription.IsEmpty())
377 String << ' ' << OwnerDescription;
382 void bodypart::CalculateVolumeAndWeight()
384 item::CalculateVolumeAndWeight();
385 CarriedWeight = 0;
386 BodyPartVolume = Volume;
388 for(int c = 0; c < GetEquipments(); ++c)
390 item* Equipment = GetEquipment(c);
392 if(Equipment)
394 Volume += Equipment->GetVolume();
395 CarriedWeight += Equipment->GetWeight();
399 Weight += CarriedWeight;
404 void bodypart::CalculateEmitation()
406 item::CalculateEmitation();
408 for(int c = 0; c < GetEquipments(); ++c)
410 item* Equipment = GetEquipment(c);
412 if(Equipment)
413 game::CombineLights(Emitation, Equipment->GetEmitation());
419 void bodypart::CalculateMaxHP(feuLong Flags)
421 int HPDelta = MaxHP - HP/*k8, OldMaxHP = MaxHP*/;
422 MaxHP = 0;
424 if(Master)
426 if(!UseMaterialAttributes())
428 sLong Endurance = Master->GetAttribute(ENDURANCE);
429 double DoubleHP = GetBodyPartVolume() * Endurance * Endurance / 200000;
431 for(unsigned int c = 0; c < Scar.size(); ++c)
432 DoubleHP *= (100. - Scar[c].Severity * 4) / 100;
434 MaxHP = int(DoubleHP);
436 else
438 sLong SV = GetMainMaterial()->GetStrengthValue();
439 MaxHP = (GetBodyPartVolume() * SV >> 4) * SV / 250000;
442 if(MaxHP < 1)
443 MaxHP = 1;
445 if(Flags & MAY_CHANGE_HPS)
447 if(MaxHP - HPDelta > 1)
448 HP = MaxHP - HPDelta;
449 else
450 HP = 1;
452 else
454 //OldMaxHP - MaxHP;
457 if(Flags & CHECK_USABILITY)
458 SignalPossibleUsabilityChange();
464 void bodypart::SignalVolumeAndWeightChange()
466 item::SignalVolumeAndWeightChange();
468 if(Master && !Master->IsInitializing())
470 CalculateMaxHP();
471 Master->CalculateHP();
472 Master->CalculateMaxHP();
473 Master->SignalBodyPartVolumeAndWeightChange();
474 square* SquareUnder = GetSquareUnder();
476 if(UpdateArmorPictures() && SquareUnder)
477 SquareUnder->SendNewDrawRequest();
483 void bodypart::SetHP(int What)
485 HP = What;
487 if(Master)
489 Master->CalculateHP();
490 SignalPossibleUsabilityChange();
496 void bodypart::EditHP(int SrcID, int What)
498 HP += What;
500 if(What < 0)
501 RemoveDamageIDs(-What);
502 else
503 AddDamageID(SrcID, What);
505 if(Master)
507 Master->CalculateHP();
508 SignalPossibleUsabilityChange();
514 void bodypart::CalculateAttackInfo()
516 CalculateDamage();
517 CalculateToHitValue();
518 CalculateAPCost();
523 double bodypart::GetTimeToDie(int Damage, double ToHitValue, double DodgeValue, truth AttackIsBlockable, truth UseMaxHP) const
525 double Durability;
526 int TotalResistance = GetTotalResistance(PHYSICAL_DAMAGE);
527 int Damage3 = (Damage << 1) + Damage;
528 int Damage5 = (Damage << 2) + Damage;
529 int TrueDamage = (19 * (Max((Damage3 >> 2) - TotalResistance, 0)
530 + Max((Damage5 >> 2) + 1 - (TotalResistance >> 1), 0))
531 + (Max(((Damage3 + (Damage3 >> 1)) >> 2) - TotalResistance, 0)
532 + Max(((Damage5 + (Damage5 >> 1)) >> 2) + 3 - (TotalResistance >> 1), 0))) / 40;
534 int HP = UseMaxHP ? GetMaxHP() : GetHP();
536 if(TrueDamage > 0)
538 double AverageDamage;
540 if(AttackIsBlockable)
542 blockvector Block;
543 Master->CreateBlockPossibilityVector(Block, ToHitValue);
545 if(Block.size())
547 double ChanceForNoBlock = 1.0;
548 AverageDamage = 0;
550 for(uInt c = 0; c < Block.size(); ++c)
552 ChanceForNoBlock -= Block[c].first;
554 if(TrueDamage - Block[c].second > 0)
555 AverageDamage += Block[c].first * (TrueDamage - Block[c].second);
558 AverageDamage += ChanceForNoBlock * TrueDamage;
560 else
561 AverageDamage = TrueDamage;
563 else
564 AverageDamage = TrueDamage;
566 Durability = HP / (AverageDamage * GetRoughChanceToHit(ToHitValue, DodgeValue));
568 if(Durability < 1)
569 Durability = 1;
571 if(Durability > 1000)
572 Durability = 1000;
574 else
575 Durability = 1000;
577 return Durability;
582 double bodypart::GetRoughChanceToHit(double ToHitValue, double DodgeValue) const
584 return GLOBAL_WEAK_BODYPART_HIT_MODIFIER * ToHitValue * GetBodyPartVolume() / ((DodgeValue / ToHitValue + 1) * DodgeValue * Master->GetBodyVolume() * 100);
589 void bodypart::RandomizePosition()
591 SpecialFlags |= 1 + RAND() % 7;
592 UpdatePictures();
597 void bodypart::SignalSpoil(material* Material)
599 if(Master)
600 Master->SignalSpoil();
601 else
602 item::SignalSpoil(Material);
607 truth bodypart::CanBePiledWith(citem* Item, ccharacter* Viewer) const
609 return item::CanBePiledWith(Item, Viewer)
610 && OwnerDescription == static_cast<const bodypart*>(Item)->OwnerDescription;
615 void bodypart::Be()
617 if(Master)
619 if(HP < MaxHP && ++SpillBloodCounter >= 4)
621 if(Master->IsEnabled())
623 if(IsBadlyHurt() && !Master->IsPolymorphed() && !(RAND() & 3))
624 SpillBlood(1);
626 else if(!Master->IsPolymorphed() && !(RAND() & 3))
628 SpillBlood(1);
629 HP += Max(((MaxHP - HP) >> 2), 1);
632 SpillBloodCounter = 0;
635 if(Master->AllowSpoil() || !Master->IsEnabled())
636 MainMaterial->Be(ItemFlags);
638 if (Exists() && LifeExpectancy) {
639 if (LifeExpectancy == 1) Master->SignalDisappearance(); else --LifeExpectancy;
642 else
644 if(HP < MaxHP && ++SpillBloodCounter >= 4)
646 if(!(RAND() & 3))
648 SpillBlood(1);
649 HP += Max(((MaxHP - HP) >> 2), 1);
652 SpillBloodCounter = 0;
655 item::Be();
661 void bodypart::SpillBlood(int HowMuch, v2 Pos)
663 if(HowMuch && (!Master || Master->SpillsBlood()) && (IsAlive() || MainMaterial->IsLiquid()) && !game::IsInWilderness())
664 GetNearLSquare(Pos)->SpillFluid(0, CreateBlood(sLong(HowMuch * sqrt(BodyPartVolume) / 2)), false, false);
669 void bodypart::SpillBlood(int HowMuch)
671 if(HowMuch && (!Master || Master->SpillsBlood()) && (IsAlive() || MainMaterial->IsLiquid()) && !game::IsInWilderness())
672 for(int c = 0; c < GetSquaresUnder(); ++c)
673 if(GetLSquareUnder(c))
674 GetLSquareUnder(c)->SpillFluid(0, CreateBlood(sLong(HowMuch * sqrt(BodyPartVolume) / 2)), false, false);
679 void bodypart::SignalEnchantmentChange()
681 if(Master && !Master->IsInitializing())
683 Master->CalculateAttributeBonuses();
684 Master->CalculateBattleInfo();
690 void bodypart::SignalSpoilLevelChange(material* Material)
692 if(Master)
693 Master->SignalSpoilLevelChange();
694 else
695 item::SignalSpoilLevelChange(Material);
700 truth bodypart::CanBeEatenByAI(ccharacter* Who) const
702 return item::CanBeEatenByAI(Who) && !(Who->IsPet() && PLAYER->HasHadBodyPart(this));
707 int bodypart::GetConditionColorIndex() const
709 if(HP <= 1 && MaxHP > 1)
710 return 0;
711 else if((HP << 1) + HP < MaxHP)
712 return 1;
713 else if((HP << 1) + HP < MaxHP << 1)
714 return 2;
715 else if(HP < MaxHP)
716 return 3;
717 else
718 return 4;
723 void bodypart::Draw(blitdata& BlitData) const
725 cint AF = GraphicData.AnimationFrames;
726 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1);
727 cbitmap* P = GraphicData.Picture[F];
729 if(BlitData.CustomData & ALLOW_ALPHA)
730 P->AlphaPriorityBlit(BlitData);
731 else
732 P->MaskedPriorityBlit(BlitData);
734 if(Fluid && ShowFluids())
735 DrawFluids(BlitData);
737 DrawArmor(BlitData);
742 truth bodypart::IsRepairable(ccharacter*) const
744 return !CanRegenerate() && (GetHP() < GetMaxHP() || IsRusted());
749 void bodypart::SpillFluid(character* Spiller, liquid* Liquid, int SquareIndex)
751 if(Master)
753 item* Armor = GetArmorToReceiveFluid(false);
755 if(Armor)
756 Armor->SpillFluid(Spiller, Liquid);
757 else if(GetMaster())
759 if(Liquid->GetVolume())
760 AddFluid(Liquid, "", SquareIndex, false);
761 else
762 delete Liquid;
765 else
766 item::SpillFluid(Spiller, Liquid, SquareIndex);
771 void bodypart::StayOn(liquid* Liquid)
773 item* Armor = GetArmorToReceiveFluid(true);
775 if(Armor)
776 Liquid->TouchEffect(Armor, CONST_S(""));
777 else if(GetMaster())
778 Liquid->TouchEffect(GetMaster(), GetBodyPartIndex());
783 liquid* bodypart::CreateBlood(sLong Volume) const
785 return liquid::Spawn(GetBloodMaterial(), Volume);
790 void bodypart::UpdateArmorPicture(graphicdata& GData, item* Armor, int SpecialFlags, v2 (item::*Retriever)(int) const, truth BodyArmor) const
792 if(Armor && Master)
794 Armor->UpdatePictures(GData,
795 ZERO_V2,
796 SpecialFlags|Armor->GetSpecialFlags(),
797 GetMaxAlpha(),
798 GR_HUMANOID,
799 static_cast<bposretriever>(Retriever));
800 Armor->CheckFluidGearPictures((Armor->*Retriever)(0), SpecialFlags, BodyArmor);
802 else
803 GData.Retire();
808 void bodypart::DrawEquipment(const graphicdata& GraphicData, blitdata& BlitData) const
810 int EAF = GraphicData.AnimationFrames;
812 if(EAF)
814 int F = !(BlitData.CustomData & ALLOW_ANIMATE) || EAF == 1 ? 0 : GET_TICK() & (EAF - 1);
815 GraphicData.Picture[F]->AlphaPriorityBlit(BlitData);
821 truth bodypart::MasterIsAnimated() const
823 return Master && !Master->IsInitializing() && Master->IsAnimated();
828 void bodypart::UpdatePictures()
830 truth WasAnimated = MasterIsAnimated();
832 item::UpdatePictures();
833 UpdateArmorPictures();
835 if(!WasAnimated != !MasterIsAnimated())
836 SignalAnimationStateChange(WasAnimated);
841 void bodypart::ReceiveAcid (material *Material, cfestring &LocationName, sLong Modifier) {
842 if (Master && MainMaterial->GetInteractionFlags() & CAN_DISSOLVE) {
843 sLong Tries = Modifier/1000;
844 Modifier -= Tries*1000; //opt%?
845 int Damage = 0;
846 for (sLong c = 0; c < Tries; ++c) if (!(RAND() % 100)) ++Damage;
847 if (Modifier && !(RAND()%100000/Modifier)) ++Damage;
848 if (Damage) {
849 feuLong Minute = game::GetTotalMinutes();
850 character *Master = this->Master;
851 if (Master->GetLastAcidMsgMin() != Minute && (Master->CanBeSeenByPlayer() || Master->IsPlayer())) {
852 Master->SetLastAcidMsgMin(Minute);
853 cchar *MName = Material->GetName(false, false).CStr();
854 if (Master->IsPlayer()) {
855 cchar *TName = LocationName.IsEmpty() ? GetBodyPartName().CStr() : LocationName.CStr();
856 ADD_MESSAGE("Acidous %s dissolves your %s.", MName, TName);
857 } else {
858 ADD_MESSAGE("Acidous %s dissolves %s.", MName, Master->CHAR_NAME(DEFINITE));
861 Master->ReceiveBodyPartDamage(0, Damage, ACID, GetBodyPartIndex(), YOURSELF, false, false, false);
862 feuLong DeathFlags = Material->IsStuckTo(Master) ? IGNORE_TRAPS : 0;
863 Master->CheckDeath(CONST_S("dissolved by ")+Material->GetName(), 0, DeathFlags);
870 void bodypart::TryToRust(sLong LiquidModifier)
872 if(MainMaterial->TryToRust(LiquidModifier << 4))
874 cchar* MoreMsg = MainMaterial->GetRustLevel() == NOT_RUSTED ? "" : " more";
876 if(Master)
878 if(Master->IsPlayer())
879 ADD_MESSAGE("Your %s rusts%s.", CHAR_NAME(UNARTICLED), MoreMsg);
880 else if(CanBeSeenByPlayer())
881 ADD_MESSAGE("The %s of %s rusts%s.", CHAR_NAME(UNARTICLED), Master->CHAR_NAME(DEFINITE), MoreMsg);
883 else if(CanBeSeenByPlayer())
884 ADD_MESSAGE("%s rusts%s.", CHAR_NAME(DEFINITE), MoreMsg);
886 MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1);
892 material* bodypart::RemoveMaterial(material* Material)
894 if(Master && GetBodyPartIndex() == TORSO_INDEX)
895 return Master->GetMotherEntity()->RemoveMaterial(Material); // gum
896 else
897 return item::RemoveMaterial(Material);
902 void bodypart::DestroyBodyPart(stack* Stack)
904 int Lumps = 1 + RAND() % 3;
905 sLong LumpVolume = Volume / Lumps >> 2;
907 if(LumpVolume >= 10)
908 for(int c = 0; c < Lumps; ++c)
910 item* Lump = GetMainMaterial()->CreateNaturalForm(LumpVolume + RAND() % LumpVolume);
911 Stack->AddItem(Lump);
914 SendToHell();
919 void bodypart::SetLifeExpectancy(int Base, int RandPlus)
921 LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base;
923 if(!Master)
924 Enable();
929 void bodypart::SpecialEatEffect(character* Eater, int Amount)
931 Amount >>= 6;
933 if(Amount && (!Master || Master->SpillsBlood()) && (IsAlive() || MainMaterial->IsLiquid()) && !game::IsInWilderness())
935 if(Eater->GetVirtualHead())
936 Eater->GetVirtualHead()->SpillFluid(Eater, CreateBlood(Amount));
938 Eater->GetTorso()->SpillFluid(Eater, CreateBlood(Amount));
944 void bodypart::UpdateFlags()
946 if((HP << 1) + HP < MaxHP || (HP == 1 && MaxHP != 1))
947 Flags |= BADLY_HURT;
948 else
949 Flags &= ~BADLY_HURT;
951 if(Master->BodyPartIsStuck(GetBodyPartIndex()))
952 Flags |= STUCK;
953 else
954 Flags &= ~STUCK;
959 void bodypart::IncreaseHP()
961 ++HP;
962 RemoveDamageIDs(1);
963 SignalPossibleUsabilityChange();
968 void bodypart::FastRestoreHP()
970 HP = MaxHP;
971 DamageID.clear();
972 SignalPossibleUsabilityChange();
977 void bodypart::RestoreHP()
979 HP = MaxHP;
980 DamageID.clear();
981 SignalPossibleUsabilityChange();
982 Master->CalculateHP();
987 void bodypart::SetIsUnique(truth What)
989 if(What)
990 Flags |= UNIQUE;
991 else
992 Flags &= ~UNIQUE;
997 void bodypart::SetIsInfectedByLeprosy(truth What)
999 MainMaterial->SetIsInfectedByLeprosy(What);
1004 void bodypart::SetSparkleFlags(int What)
1006 cint S = SPARKLING_B|SPARKLING_C|SPARKLING_D;
1007 Flags = (Flags & ~(S << BODYPART_SPARKLE_SHIFT)) | ((What & S) << BODYPART_SPARKLE_SHIFT);
1012 void bodypart::SignalAnimationStateChange(truth WasAnimated)
1014 if(WasAnimated)
1016 for(int c = 0; c < GetSquaresUnder(); ++c)
1018 square* Square = GetSquareUnder(c);
1020 if(Square)
1021 Square->DecAnimatedEntities();
1024 else
1026 for(int c = 0; c < GetSquaresUnder(); ++c)
1028 square* Square = GetSquareUnder(c);
1030 if(Square)
1031 Square->IncAnimatedEntities();
1038 truth bodypart::MaterialIsChangeable(ccharacter*) const
1040 return !Master || !Master->BodyPartIsVital(GetBodyPartIndex()) || UseMaterialAttributes();
1045 truth bodypart::AddAdjective(festring& String, truth Articled) const
1047 if(!Master)
1049 if(Articled)
1050 String << "a ";
1052 String << "severed ";
1053 return true;
1055 else
1056 return false;
1061 void bodypart::RemoveRust()
1063 item::RemoveRust();
1064 RestoreHP();
1069 sLong bodypart::GetFixPrice() const
1071 return GetMaxHP() - GetHP() + GetMainMaterial()->GetRustLevel() * 25;
1076 truth bodypart::IsFixableBySmith(ccharacter*) const
1078 return (GetMainMaterial()->GetCategoryFlags() & IS_METAL
1079 && (GetHP() < GetMaxHP() || IsRusted()));
1084 truth bodypart::IsFixableByTailor(ccharacter*) const
1086 return (GetMainMaterial()->GetCategoryFlags() & CAN_BE_TAILORED
1087 && GetHP() < GetMaxHP());
1092 item* bodypart::Fix()
1094 RestoreHP();
1095 return this;
1100 void bodypart::SignalMaterialChange()
1102 if(Master)
1103 RestoreHP();
1108 truth bodypart::ShowMaterial() const
1110 return MainMaterial->GetConfig() != NormalMaterial;
1115 truth bodypart::IsDestroyable(ccharacter*) const
1117 return !Master || !Master->BodyPartIsVital(GetBodyPartIndex());
1122 truth bodypart::DamageTypeCanScar(int Type)
1124 return !(Type == POISON || Type == DRAIN);
1129 void bodypart::GenerateScar(int Damage, int Type)
1131 Scar.push_back(scar());
1132 scar& NewScar = Scar.back();
1133 NewScar.Severity = 1 + RAND_N(1 + 5 * Damage / GetMaxHP());
1135 if(GetMaster()->IsPlayer())
1137 int ScarColor = MakeShadeColor(GetMainMaterial()->GetColor());
1138 NewScar.PanelBitmap = igraph::GenerateScarBitmap(GetBodyPartIndex(),
1139 NewScar.Severity,
1140 ScarColor);
1141 ADD_MESSAGE("Your %s is scarred.", CHAR_NAME(UNARTICLED));
1143 else
1144 NewScar.PanelBitmap = 0;
1146 CalculateMaxHP();
1147 GetMaster()->CalculateMaxHP();
1148 GetMaster()->CalculateAttributeBonuses();
1149 CalculateAttackInfo();
1154 void bodypart::DrawScars(cblitdata& B) const
1156 for(unsigned int c = 0; c < Scar.size(); ++c)
1158 if(!Scar[c].PanelBitmap)
1160 int ScarColor = MakeShadeColor(GetMainMaterial()->GetColor());
1161 Scar[c].PanelBitmap = igraph::GenerateScarBitmap(GetBodyPartIndex(),
1162 Scar[c].Severity,
1163 ScarColor);
1166 Scar[c].PanelBitmap->NormalMaskedBlit(B);
1172 int bodypart::CalculateScarAttributePenalty(int Attribute) const
1174 double DoubleAttribute = Attribute;
1176 for(unsigned int c = 0; c < Scar.size(); ++c)
1177 DoubleAttribute *= (100. - Scar[c].Severity * 4) / 100;
1179 return Min(Attribute - int(DoubleAttribute), Attribute - 1);
1184 bodypart::~bodypart()
1186 for(unsigned int c = 0; c < Scar.size(); ++c)
1187 delete Scar[c].PanelBitmap;
1192 bodypart::bodypart(const bodypart& B) : mybase(B), OwnerDescription(B.OwnerDescription), Master(B.Master), CarriedWeight(B.CarriedWeight), BodyPartVolume(B.BodyPartVolume), BitmapPos(B.BitmapPos), ColorB(B.ColorB), ColorC(B.ColorC), ColorD(B.ColorD), SpecialFlags(B.SpecialFlags), HP(B.HP), MaxHP(B.MaxHP), BloodMaterial(B.BloodMaterial), NormalMaterial(B.NormalMaterial), SpillBloodCounter(B.SpillBloodCounter), WobbleData(B.WobbleData), Scar(B.Scar)
1194 for(unsigned int c = 0; c < Scar.size(); ++c)
1195 if(Scar[c].PanelBitmap)
1196 Scar[c].PanelBitmap = new bitmap(Scar[c].PanelBitmap);
1201 void bodypart::RemoveDamageIDs(int Amount)
1203 /*while(Amount)
1205 damageid& D = DamageID.front();
1206 int CurrentAmount = D.Amount;
1208 if(Amount < CurrentAmount)
1210 D.Amount -= Amount;
1211 Amount = 0;
1213 else
1215 DamageID.pop_front();
1216 Amount -= CurrentAmount;
1223 void bodypart::AddDamageID(int SrcID, int Amount)
1225 /*damageid D = { SrcID, Amount };
1226 DamageID.push_back(D);*/
1228 #endif