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 charsset.cpp */
14 
15 #include "dbgmsgproj.h"
16 #include "whandler.h"
17 #include "devcons.h"
18 
19 cint humanoid::DrawOrder[] =
20 { TORSO_INDEX, GROIN_INDEX, RIGHT_LEG_INDEX, LEFT_LEG_INDEX, RIGHT_ARM_INDEX, LEFT_ARM_INDEX, HEAD_INDEX };
21 
BodyPartIsVital(int I) const22 truth humanoid::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX || I == GROIN_INDEX; }
BodyPartCanBeSevered(int I) const23 truth humanoid::BodyPartCanBeSevered(int I) const { return I != TORSO_INDEX && I != GROIN_INDEX; }
OpenMultiplier() const24 int humanoid::OpenMultiplier() const { return HasAUsableArm() ? 1 : 3; }
CloseMultiplier() const25 int humanoid::CloseMultiplier() const { return HasAUsableArm() ? 1 : 2; }
GetCarryingStrength() const26 int humanoid::GetCarryingStrength() const { return Max(GetAttribute(LEG_STRENGTH), 1) + CarryingBonus; }
CalculateBodyParts()27 void humanoid::CalculateBodyParts() { BodyParts = HUMANOID_BODYPARTS; }
CalculateAllowedWeaponSkillCategories()28 void humanoid::CalculateAllowedWeaponSkillCategories() { AllowedWeaponSkillCategories = WEAPON_SKILL_CATEGORIES; }
29 
GetHeadBitmapPos() const30 v2 farmer::GetHeadBitmapPos() const { return v2(96, (4 + (RAND() & 1)) << 4); }
GetRightArmBitmapPos() const31 v2 farmer::GetRightArmBitmapPos() const { return v2(64, (RAND() & 1) << 4); }
32 
SetWayPoints(const fearray<packv2> & What)33 void guard::SetWayPoints(const fearray<packv2>& What) { ArrayToVector(What, WayPoints); }
34 
FirstPersonBiteVerb() const35 cchar* oree::FirstPersonBiteVerb() const { return "vomit acidous blood at"; }
FirstPersonCriticalBiteVerb() const36 cchar* oree::FirstPersonCriticalBiteVerb() const { return "vomit very acidous blood at"; }
ThirdPersonBiteVerb() const37 cchar* oree::ThirdPersonBiteVerb() const { return "vomits acidous blood at"; }
ThirdPersonCriticalBiteVerb() const38 cchar* oree::ThirdPersonCriticalBiteVerb() const { return "vomits very acidous blood at"; }
BiteNoun() const39 cchar* oree::BiteNoun() const { return "liquid"; }
40 
BodyPartIsVital(int I) const41 truth skeleton::BodyPartIsVital(int I) const { return I == GROIN_INDEX || I == TORSO_INDEX; }
42 
ShowClassDescription() const43 truth communist::ShowClassDescription() const { return GetAssignedName() != "Ivan"; }
44 
GetHeadBitmapPos() const45 v2 housewife::GetHeadBitmapPos() const { return v2(112, (RAND() % 6) << 4); }
46 
BodyPartIsVital(int I) const47 truth zombie::BodyPartIsVital(int I) const { return I == GROIN_INDEX || I == TORSO_INDEX; }
GetZombieDescription() const48 festring zombie::GetZombieDescription() const { return Description; }
49 
BodyPartIsVital(int I) const50 truth ghost::BodyPartIsVital(int I) const { return I == GROIN_INDEX || I == TORSO_INDEX; }
GetGhostDescription() const51 festring ghost::GetGhostDescription() const { return Description; }
FirstPersonUnarmedHitVerb() const52 cchar* ghost::FirstPersonUnarmedHitVerb() const { return "touch"; }
FirstPersonCriticalUnarmedHitVerb() const53 cchar* ghost::FirstPersonCriticalUnarmedHitVerb() const
54 { return "critically touch"; }
ThirdPersonUnarmedHitVerb() const55 cchar* ghost::ThirdPersonUnarmedHitVerb() const { return "touches"; }
ThirdPersonCriticalUnarmedHitVerb() const56 cchar* ghost::ThirdPersonCriticalUnarmedHitVerb() const
57 { return "critically touches"; }
58 
BodyPartIsVital(int I) const59 truth angel::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX; }
60 
BodyPartIsVital(int I) const61 truth nihil::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX; }
62 
BodyPartIsVital(int I) const63 truth genie::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX; }
64 
CreateBodyPartMaterial(int,long Volume) const65 material* golem::CreateBodyPartMaterial(int, long Volume) const { return MAKE_MATERIAL(GetConfig(), Volume); }
66 
EquipmentIsAllowed(int I) const67 truth sumowrestler::EquipmentIsAllowed(int I) const { return I == BELT_INDEX; }
68 
SpecialEnemySightedReaction(character *)69 truth ghost::SpecialEnemySightedReaction(character*) { return !(Active = true); }
70 
~petrus()71 petrus::~petrus()
72 {
73   game::SetPetrus(0);
74 }
75 
Hit(character * Enemy,v2,int,int)76 truth ennerbeast::Hit(character* Enemy, v2, int, int)
77 {
78   if(CheckIfTooScaredToHit(Enemy))
79     return false;
80 
81   if(RAND() & 1)
82     ADD_MESSAGE("%s yells: UGH UGHAaaa!", CHAR_DESCRIPTION(DEFINITE));
83   else
84     ADD_MESSAGE("%s yells: Uga Ugar Ugade Ugat!", CHAR_DESCRIPTION(DEFINITE));
85 
86   rect Rect;
87   femath::CalculateEnvironmentRectangle(Rect, GetLevel()->GetBorder(), GetPos(), 30);
88 
89   for(int x = Rect.X1; x <= Rect.X2; ++x)
90     for(int y = Rect.Y1; y <= Rect.Y2; ++y)
91     {
92       int ScreamStrength = int(70 / (hypot(GetPos().X - x, GetPos().Y - y) + 1));
93 
94       if(ScreamStrength)
95       {
96         character* Char = GetNearSquare(x, y)->GetCharacter();
97 
98         if(Char && Char != this)
99         {
100           msgsystem::EnterBigMessageMode();
101 
102           if(Char->IsPlayer())
103             ADD_MESSAGE("You are hit by the horrible waves of high sound.");
104           else if(Char->CanBeSeenByPlayer())
105             ADD_MESSAGE("%s is hit by the horrible waves of high sound.", Char->CHAR_NAME(DEFINITE));
106 
107           Char->ReceiveDamage(this, ScreamStrength, SOUND, ALL, YOURSELF, true);
108           Char->CheckDeath(CONST_S("killed @bkp scream"), this);
109           msgsystem::LeaveBigMessageMode();
110         }
111 
112         GetNearLSquare(x, y)->GetStack()->ReceiveDamage(this, ScreamStrength, SOUND);
113       }
114     }
115 
116   EditNP(-100);
117   EditAP(-1000000 / GetCWeaponSkill(BITE)->GetBonus());
118   EditStamina(-1000, false);
119   return true;
120 }
121 
Hit(character * Enemy,v2,int,int)122 truth ennerchild::Hit(character* Enemy, v2, int, int)
123 {
124   if(CheckIfTooScaredToHit(Enemy))
125     return false;
126 
127   if(RAND() & 1)
128     ADD_MESSAGE("%s yells: UGH UGHAaaa!", CHAR_DESCRIPTION(DEFINITE));
129   else
130     ADD_MESSAGE("%s yells: Uga Ugar Ugade Ugat!", CHAR_DESCRIPTION(DEFINITE));
131 
132   rect Rect;
133   femath::CalculateEnvironmentRectangle(Rect, GetLevel()->GetBorder(), GetPos(), 30);
134 
135   // Enner girl is older
136   int BaseScreamStrength = 50;
137 
138   if(GetConfig() == BOY)
139     BaseScreamStrength = 40;
140 
141   for(int x = Rect.X1; x <= Rect.X2; ++x)
142     for(int y = Rect.Y1; y <= Rect.Y2; ++y)
143     {
144       int ScreamStrength = int(BaseScreamStrength / (hypot(GetPos().X - x, GetPos().Y - y) + 1));
145 
146       if(ScreamStrength)
147       {
148         character* Char = GetNearSquare(x, y)->GetCharacter();
149 
150         if(Char && Char != this)
151         {
152           msgsystem::EnterBigMessageMode();
153 
154           if(Char->IsPlayer())
155             ADD_MESSAGE("You are hit by the horrible waves of high sound.");
156           else if(Char->CanBeSeenByPlayer())
157             ADD_MESSAGE("%s is hit by the horrible waves of high sound.", Char->CHAR_NAME(DEFINITE));
158 
159           Char->ReceiveDamage(this, ScreamStrength, SOUND, ALL, YOURSELF, true);
160           Char->CheckDeath(CONST_S("killed @bkp scream"), this);
161           msgsystem::LeaveBigMessageMode();
162         }
163 
164         GetNearLSquare(x, y)->GetStack()->ReceiveDamage(this, ScreamStrength, SOUND);
165       }
166     }
167 
168   EditNP(-100);
169   EditAP(-1000000 / GetCWeaponSkill(BITE)->GetBonus());
170   EditStamina(-1000, false);
171   return true;
172 }
173 
ReceiveDamage(character * Damager,int Damage,int Type,int TargetFlags,int Direction,truth Divide,truth PenetrateArmor,truth Critical,truth ShowMsg)174 truth ennerchild::ReceiveDamage(character* Damager, int Damage, int Type, int TargetFlags, int Direction,
175                               truth Divide, truth PenetrateArmor, truth Critical, truth ShowMsg)
176 {
177   truth Success = false;
178 
179   if(Type != SOUND)
180   {
181     Success = humanoid::ReceiveDamage(Damager, Damage, Type, TargetFlags, Direction,
182                                           Divide, PenetrateArmor, Critical, ShowMsg);
183   }
184 
185   return Success;
186 }
187 
CreateCorpse(lsquare * Square)188 void skeleton::CreateCorpse(lsquare* Square)
189 {
190   if(GetHead())
191   {
192     item* Skull = SevereBodyPart(HEAD_INDEX, false, Square->GetStack());
193     Square->AddItem(Skull);
194   }
195 
196   int Amount = 2 + (RAND() & 3);
197 
198   for(int c = 0; c < Amount; ++c)
199     Square->AddItem(bone::Spawn());
200 
201   SendToHell();
202 }
203 
CreateCorpse(lsquare * Square)204 void petrus::CreateCorpse(lsquare* Square)
205 {
206   if(game::GetGloomyCaveStoryState() == 2)
207     game::GetTeam(ATTNAM_TEAM)->SetRelation(game::GetTeam(PLAYER_TEAM), FRIEND);
208 
209   Square->AddItem(leftnutofpetrus::Spawn());
210   SendToHell();
211 }
212 
Save(outputfile & SaveFile) const213 void humanoid::Save(outputfile& SaveFile) const
214 {
215   character::Save(SaveFile);
216   SaveFile << SWeaponSkill;
217 }
218 
Load(inputfile & SaveFile)219 void humanoid::Load(inputfile& SaveFile)
220 {
221   character::Load(SaveFile);
222   SaveFile >> SWeaponSkill;
223 
224   if(GetRightWielded())
225     for(sweaponskill* p : SWeaponSkill)
226       if(p->IsSkillOf(GetRightWielded()))
227       {
228         SetCurrentRightSWeaponSkill(p);
229         break;
230       }
231 
232   if(GetLeftWielded())
233     for(sweaponskill* p : SWeaponSkill)
234       if(p->IsSkillOf(GetLeftWielded()))
235       {
236         SetCurrentLeftSWeaponSkill(p);
237         break;
238       }
239 }
240 
MoveRandomly()241 truth golem::MoveRandomly()
242 {
243   if(!(RAND() % 500))
244   {
245     Engrave(CONST_S("Golem Needs Master"));
246     EditAP(-1000);
247     return true;
248   }
249   else
250     return character::MoveRandomly();
251 }
252 
GetAICommand()253 void ennerbeast::GetAICommand()
254 {
255   SeekLeader(GetLeader());
256 
257   if(StateIsActivated(PANIC) || !(RAND() % 3))
258     Hit(0, ZERO_V2, YOURSELF);
259 
260   if(CheckForEnemies(false, false, true))
261     return;
262 
263   if(FollowLeader(GetLeader()))
264     return;
265 
266   if(MoveRandomly())
267     return;
268 
269   EditAP(-1000);
270 }
271 
GetAICommand()272 void ennerchild::GetAICommand()
273 {
274   SeekLeader(GetLeader());
275 
276   if(StateIsActivated(PANIC) || !(RAND() % 3))
277     Hit(0, ZERO_V2, YOURSELF);
278 
279   if(CheckForEnemies(false, false, true))
280     return;
281 
282   if(FollowLeader(GetLeader()))
283     return;
284 
285   if(MoveRandomly())
286     return;
287 
288   EditAP(-1000);
289 }
290 
GetAICommand()291 void petrus::GetAICommand()
292 {
293   int Enemies = 0;
294 
295   for(int c = 0; c < game::GetTeams(); ++c)
296     if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
297       Enemies += game::GetTeam(c)->GetEnabledMembers();
298 
299   if(Enemies && !RAND_N(250 / Min(Enemies, 50)))
300   {
301     if(CanBeSeenByPlayer())
302       ADD_MESSAGE("%s shouts a magnificent prayer to Valpurus.", CHAR_NAME(DEFINITE));
303 
304     angel* Angel = angel::Spawn(VALPURUS);
305     Angel->SetLifeExpectancy(10000, 0);
306     v2 Where = GetLevel()->GetNearestFreeSquare(Angel, GetPos());
307 
308     if(Where == ERROR_V2)
309       Where = GetLevel()->GetRandomSquare(Angel);
310 
311     Angel->SetTeam(GetTeam());
312     Angel->PutTo(Where);
313 
314     if(Angel->CanBeSeenByPlayer())
315       ADD_MESSAGE("%s materializes.", Angel->CHAR_NAME(INDEFINITE));
316 
317     EditAP(-1000);
318     return;
319   }
320 
321   if(HP << 1 < MaxHP && (GetPos() - v2(28, 20)).GetLengthSquare() > 400 && !RAND_N(10))
322   {
323     if(CanBeSeenByPlayer())
324       ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE));
325 
326     GetLevel()->GetLSquare(28, 20)->KickAnyoneStandingHereAway();
327     Move(v2(28, 20), true);
328     EditAP(-1000);
329     return;
330   }
331 
332   if(!LastHealed || game::GetTick() - LastHealed > 16384)
333     for(int d = 0; d < GetNeighbourSquares(); ++d)
334     {
335       square* Square = GetNeighbourSquare(d);
336 
337       if(Square)
338       {
339         character* Char = Square->GetCharacter();
340 
341         if(Char && GetRelation(Char) == FRIEND && HealFully(Char))
342           return;
343       }
344     }
345 
346   StandIdleAI();
347 }
348 
HealFully(character * ToBeHealed)349 truth petrus::HealFully(character* ToBeHealed)
350 {
351   truth DidSomething = false;
352 
353   for(int c = 0; c < ToBeHealed->GetBodyParts(); ++c)
354     if(!ToBeHealed->GetBodyPart(c))
355     {
356       bodypart* BodyPart = 0;
357 
358       for(ulong ID : ToBeHealed->GetOriginalBodyPartID(c))
359       {
360         BodyPart = static_cast<bodypart*>(ToBeHealed->SearchForItem(ID));
361 
362         if(BodyPart)
363           break;
364       }
365 
366       if(!BodyPart || !BodyPart->CanRegenerate())
367         continue;
368 
369       BodyPart->RemoveFromSlot();
370       ToBeHealed->AttachBodyPart(BodyPart);
371       BodyPart->RestoreHP();
372       DidSomething = true;
373 
374       if(ToBeHealed->IsPlayer())
375         ADD_MESSAGE("%s attaches your old %s back and heals it.",
376                     CHAR_NAME(DEFINITE), BodyPart->GetBodyPartName().CStr());
377       else if(CanBeSeenByPlayer())
378         ADD_MESSAGE("%s attaches the old %s of %s back and heals it.",
379                     CHAR_NAME(DEFINITE), BodyPart->GetBodyPartName().CStr(), ToBeHealed->CHAR_DESCRIPTION(DEFINITE));
380     }
381 
382   if(ToBeHealed->IsInBadCondition())
383   {
384     ToBeHealed->RestoreLivingHP();
385     DidSomething = true;
386 
387     if(ToBeHealed->IsPlayer())
388       ADD_MESSAGE("%s heals you fully.", CHAR_DESCRIPTION(DEFINITE));
389     else if(CanBeSeenByPlayer())
390       ADD_MESSAGE("%s heals %s fully.", CHAR_DESCRIPTION(DEFINITE), ToBeHealed->CHAR_DESCRIPTION(DEFINITE));
391   }
392 
393   if(ToBeHealed->TemporaryStateIsActivated(POISONED))
394   {
395     ToBeHealed->DeActivateTemporaryState(POISONED);
396     DidSomething = true;
397 
398     if(ToBeHealed->IsPlayer())
399       ADD_MESSAGE("%s removes the foul poison in your body.", CHAR_DESCRIPTION(DEFINITE));
400     else if(CanBeSeenByPlayer())
401       ADD_MESSAGE("%s removes the foul poison in %s's body.",
402                   CHAR_DESCRIPTION(DEFINITE), ToBeHealed->CHAR_DESCRIPTION(DEFINITE));
403   }
404 
405   if(DidSomething)
406   {
407     LastHealed = game::GetTick();
408     DexterityAction(10);
409     return true;
410   }
411   else
412     return false;
413 }
414 
Save(outputfile & SaveFile) const415 void petrus::Save(outputfile& SaveFile) const
416 {
417   humanoid::Save(SaveFile);
418   SaveFile << LastHealed;
419 }
420 
Load(inputfile & SaveFile)421 void petrus::Load(inputfile& SaveFile)
422 {
423   humanoid::Load(SaveFile);
424   SaveFile >> LastHealed;
425   game::SetPetrus(this);
426 }
427 
GetMainWielded() const428 item* humanoid::GetMainWielded() const
429 {
430   if(GetMainArm())
431     if(GetMainArm()->GetWielded())
432       return GetMainArm()->GetWielded();
433     else
434       if(GetSecondaryArm())
435         return GetSecondaryArm()->GetWielded();
436       else
437         return 0;
438   else
439     if(GetSecondaryArm())
440       return GetSecondaryArm()->GetWielded();
441     else
442       return 0;
443 }
444 
GetSecondaryWielded() const445 item* humanoid::GetSecondaryWielded() const
446 {
447   if(GetMainArm() && GetMainArm()->GetWielded() && GetSecondaryArm())
448     return GetSecondaryArm()->GetWielded();
449   else
450     return 0;
451 }
452 
Hit(character * Enemy,v2 HitPos,int Direction,int Flags)453 truth humanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags)
454 {
455   if(CheckIfTooScaredToHit(Enemy))
456     return false;
457 
458   if(IsPlayer())
459   {
460     if(!(Enemy->IsMasochist() && GetRelation(Enemy) == FRIEND) && GetRelation(Enemy) != HOSTILE
461        && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]")))
462       return false;
463   }
464   else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit())
465     return false;
466 
467   if(GetBurdenState() == OVER_LOADED)
468   {
469     if(IsPlayer())
470       ADD_MESSAGE("You cannot fight while carrying so much.");
471 
472     return false;
473   }
474 
475   int c, AttackStyles;
476 
477   for(c = 0, AttackStyles = 0; c < 8; ++c)
478     if(GetAttackStyle() & (1 << c))
479       ++AttackStyles;
480 
481   int Chosen = RAND() % AttackStyles;
482 
483   for(c = 0, AttackStyles = 0; c < 8; ++c)
484     if(GetAttackStyle() & (1 << c) && AttackStyles++ == Chosen)
485     {
486       Chosen = 1 << c;
487       break;
488     }
489 
490   if(StateIsActivated(VAMPIRISM) && !(RAND() % 2))
491     {
492       if(Chosen == USE_ARMS && CanAttackWithAnArm())
493         if(!GetRightWielded() && !GetLeftWielded())
494           Chosen = USE_HEAD;
495 
496       if(Chosen == USE_LEGS && HasTwoUsableLegs())
497         Chosen = USE_HEAD;
498     }
499 
500   switch(Chosen)
501   {
502    case USE_ARMS:
503     if(CanAttackWithAnArm() && (!(Flags & SADIST_HIT) || HasSadistWeapon()))
504     {
505       msgsystem::EnterBigMessageMode();
506       Hostility(Enemy);
507       long FirstAPCost = 0, SecondAPCost = 0;
508       arm* FirstArm, * SecondArm;
509 
510       if(RAND() & 1)
511       {
512         FirstArm = GetRightArm();
513         SecondArm = GetLeftArm();
514       }
515       else
516       {
517         FirstArm = GetLeftArm();
518         SecondArm = GetRightArm();
519       }
520 
521       int Strength = Max(GetAttribute(ARM_STRENGTH), 1);
522 
523       if(FirstArm && FirstArm->GetDamage() && (!(Flags & SADIST_HIT) || FirstArm->HasSadistWeapon()))
524       {
525         FirstAPCost = FirstArm->GetAPCost();
526         FirstArm->Hit(Enemy, HitPos, Direction, Flags);
527 
528         if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE)))
529           DropBodyPart(FirstArm->GetBodyPartIndex());
530       }
531 
532       if(!GetAction() && IsEnabled() && Enemy->IsEnabled() && SecondArm && SecondArm->GetDamage()
533          && (!(Flags & SADIST_HIT) || SecondArm->HasSadistWeapon()))
534       {
535         SecondAPCost = SecondArm->GetAPCost();
536         SecondArm->Hit(Enemy, HitPos, Direction, Flags);
537 
538         if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE)))
539           DropBodyPart(SecondArm->GetBodyPartIndex());
540       }
541 
542       EditNP(-50);
543       EditAP(-Max(FirstAPCost, SecondAPCost));
544       EditStamina(GetAdjustedStaminaCost(-1000, Strength), false);
545       msgsystem::LeaveBigMessageMode();
546       return true;
547     }
548    case USE_LEGS:
549     if(HasTwoUsableLegs())
550     {
551       msgsystem::EnterBigMessageMode();
552       Hostility(Enemy);
553       Kick(GetNearLSquare(HitPos), Direction, Flags & SADIST_HIT);
554 
555       if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE)))
556         DropBodyPart(RAND_2 ? RIGHT_LEG_INDEX : LEFT_LEG_INDEX);
557 
558       msgsystem::LeaveBigMessageMode();
559       return true;
560     }
561    case USE_HEAD:
562     if(GetHead())
563     {
564       msgsystem::EnterBigMessageMode();
565       Hostility(Enemy);
566       Bite(Enemy, HitPos, Direction, Flags & SADIST_HIT);
567       msgsystem::LeaveBigMessageMode();
568       return true;
569     }
570    default:
571     if(IsPlayer())
572       ADD_MESSAGE("You are currently quite unable to damage anything.");
573 
574     return false;
575   }
576 }
577 
AddSpecialSkillInfo(felist & List) const578 truth humanoid::AddSpecialSkillInfo(felist& List) const
579 {
580   truth Something = false;
581 
582   if(CurrentRightSWeaponSkill && CurrentRightSWeaponSkill->GetHits() / 100)
583   {
584     List.AddEntry(CONST_S(""), LIGHT_GRAY);
585     festring Buffer = CONST_S("right accustomization");
586     Buffer.Resize(30);
587     Buffer << CurrentRightSWeaponSkill->GetLevel();
588     Buffer.Resize(40);
589     Buffer << CurrentRightSWeaponSkill->GetHits() / 100;
590     Buffer.Resize(50);
591 
592     if(CurrentRightSWeaponSkill->GetLevel() != 20)
593       Buffer << (CurrentRightSWeaponSkill->GetLevelMap(CurrentRightSWeaponSkill->GetLevel() + 1)
594                  - CurrentRightSWeaponSkill->GetHits()) / 100;
595     else
596       Buffer << '-';
597 
598     Buffer.Resize(60);
599     int Bonus = CurrentRightSWeaponSkill->GetBonus();
600     Buffer << '+' << (Bonus - 1000) / 10;
601 
602     if(Bonus %= 10)
603       Buffer << '.' << Bonus;
604 
605     Buffer << '%';
606     List.AddEntry(Buffer, WHITE);
607     Something = true;
608   }
609 
610   if(CurrentLeftSWeaponSkill && CurrentLeftSWeaponSkill->GetHits() / 100)
611   {
612     if(!Something)
613       List.AddEntry(CONST_S(""), LIGHT_GRAY);
614 
615     festring Buffer = CONST_S("left accustomization");
616     Buffer.Resize(30);
617     Buffer << CurrentLeftSWeaponSkill->GetLevel();
618     Buffer.Resize(40);
619     Buffer << CurrentLeftSWeaponSkill->GetHits() / 100;
620     Buffer.Resize(50);
621 
622     if(CurrentLeftSWeaponSkill->GetLevel() != 20)
623       Buffer << (CurrentLeftSWeaponSkill->GetLevelMap(CurrentLeftSWeaponSkill->GetLevel() + 1)
624                  - CurrentLeftSWeaponSkill->GetHits()) / 100;
625     else
626       Buffer << '-';
627 
628     Buffer.Resize(60);
629     int Bonus = CurrentLeftSWeaponSkill->GetBonus();
630     Buffer << '+' << (Bonus - 1000) / 10;
631 
632     if(Bonus %= 10)
633       Buffer << '.' << Bonus;
634 
635     Buffer << '%';
636     List.AddEntry(Buffer, WHITE);
637     Something = true;
638   }
639 
640   return Something;
641 }
642 
BeTalkedTo()643 void petrus::BeTalkedTo()
644 {
645   if(!GetPos().IsAdjacent(PLAYER->GetPos()))
646     return;
647 
648   if(GetRelation(PLAYER) == HOSTILE)
649   {
650     ADD_MESSAGE("Heretic! Dev/null is a place not worthy to receive thee!");
651     return;
652   }
653 
654   if(PLAYER->HasMuramasa() && PLAYER->HasMasamune())
655   {
656     if(game::TruthQuestion(CONST_S("Report your actions in the kingdom of Aslona? [y/N]"), REQUIRES_ANSWER))
657     {
658       game::PlayVictoryMusic();
659       game::TextScreen(CONST_S("\"Yes, citizen? Ah, it is thee. I was wondering were hast thou wandered off. Art thou\n"
660                                "coming back to beg for forgiveness and mercy?\"\n\n"
661                                "But Petrus' anger is quickly quelled when you fall to your knees and present to him\n"
662                                "the two regal swords of Aslona. He takes them and admires them in silence for a while,\n"
663                                "though interrupted by Sir Lancelyn who realizes your deeds, screams in rage and is\n"
664                                "promptly dragged away to a prison cell.\n\n"
665                                "Lost in thoughts, Petrus hands the swords to a servant and turns back to you again:\n\n"
666                                "\"I have decided thy fate, slave. I shan't have thee executed if thou canst prove thy worth.\n"
667                                "Sir Galladon hast telepathically informed me that our trainee guards are just about ready\n"
668                                "for their first war. Thou shalt join my army and help conquer Aslona while they are reeling.\n"
669                                "For the glory of Valpurus!\"\n\nYou are victorious!"));
670 
671       game::GetCurrentArea()->SendNewDrawRequest();
672       game::DrawEverything();
673       PLAYER->ShowAdventureInfo();
674       festring Msg = CONST_S("became an officer of the Attnamese army");
675       AddScoreEntry(Msg, 3, false);
676       game::End(Msg);
677       return;
678     }
679   }
680 
681   if(PLAYER->HasGoldenEagleShirt())
682   {
683     ADD_MESSAGE("Petrus smiles. \"Thou hast defeated Oree! Mayst thou be blessed by Valpurus for the rest of thy life! "
684                 "And thou possess the Shirt of the Golden Eagle, the symbol of Our status! Return it now, please.\"");
685 
686     if(game::TruthQuestion(CONST_S("Will you give the Shirt of the Golden Eagle to Petrus? [y/n]"), REQUIRES_ANSWER))
687     {
688       game::PlayVictoryMusic();
689       game::TextScreen(CONST_S("The Holy Shirt is returned to its old owner and you kneel down to receive your reward.\n"
690                                "Petrus taps your shoulder with the Justifier and raises you to nobility. Later you\n"
691                                "receive a small dukedom in the middle of tundra where you rule with justice till\n"
692                                "the end of your content life.\n\nYou are victorious!"));
693 
694       game::GetCurrentArea()->SendNewDrawRequest();
695       game::DrawEverything();
696       PLAYER->ShowAdventureInfo();
697       festring Msg = CONST_S("retrieved the Shirt of the Golden Eagle and was raised to nobility");
698       AddScoreEntry(Msg, 4, false);
699       game::End(Msg);
700       return;
701     }
702     else
703     {
704       ADD_MESSAGE("Petrus's face turns red. \"I see. Thy greed hath overcome thy wisdom. Then, "
705                   "we shall fight for the shiny shirt. May Valpurus bless him who is better.\"");
706 
707       /* And now we actually make his face change color ;-) */
708 
709       GetHead()->GetMainMaterial()->SetSkinColor(MakeRGB16(255, 75, 50));
710       GetHead()->UpdatePictures();
711       SendNewDrawRequest();
712       game::AskForKeyPress(CONST_S("You are attacked! [press any key to continue]"));
713       PLAYER->GetTeam()->Hostility(GetTeam());
714       game::SetGloomyCaveStoryState(2);
715       return;
716     }
717   }
718 
719   if(PLAYER->HasHeadOfElpuri())
720   {
721     game::PlayVictoryMusic();
722     game::TextScreen(CONST_S("You have slain Elpuri, and Petrus grants you the freedom you desire.\n"
723                              "You spend the next months in Attnam as an honored hero and when the\n"
724                              "sea finally melts, you board the first ship, leaving your past forever\n"
725                              "behind.\n\nYou are victorious!"));
726 
727     game::GetCurrentArea()->SendNewDrawRequest();
728     game::DrawEverything();
729     PLAYER->ShowAdventureInfo();
730     festring Msg = CONST_S("defeated Elpuri and continued to further adventures");
731     AddScoreEntry(Msg, 2, false);
732     game::End(Msg);
733   }
734   else if(game::GetGloomyCaveStoryState() == 1)
735   {
736     ADD_MESSAGE("Petrus says: \"Bring me the head of Elpuri and we'll talk.\"");
737     return;
738   }
739 
740   if(!game::GetStoryState())
741   {
742     if(PLAYER->RemoveEncryptedScroll())
743     {
744       game::TextScreen(CONST_S("You kneel down and bow before the high priest and hand him the encrypted scroll.\n"
745                                "Petrus raises his arm, the scroll glows yellow, and lo! The letters are clear and\n"
746                                "readable. Petrus asks you to voice them aloud. The first two thousand words praise\n"
747                                "Valpurus the Creator and all His manifestations and are followed by a canticle of\n"
748                                "Saint Petrus the Lion-Hearted lasting roughly three thousand words. Finally there\n"
749                                "are some sentences actually concerning your mission:\n\n"
750                                "\"Alas, I fear dirty tongues have spread lies to my Lord's ears. I assure all tales\n"
751                                "of treasures here in New Attnam are but mythic legends. There is nothing of value here.\n"
752                                "The taxes are already an unbearable burden and I can't possibly pay more. However I do\n"
753                                "not question the wisdom of the government's decisions. I will contribute what I can:\n"
754                                "the ostriches will deliver an extra 10000 bananas to the capital and additionally the\n"
755                                "slave that brought the message will henceforth be at Your disposal. I am certain this\n"
756                                "satisfies the crown's needs.\"\n\n"
757                                "\"Yours sincerely,\n"
758                                "Richel Decos, the viceroy of New Attnam\""));
759 
760       game::TextScreen(CONST_S("You almost expected the last bit. Petrus seems to be deep in his thoughts and you\n"
761                                "wonder what shape your destiny is taking in his mind. Suddenly he seems to return\n"
762                                "to this reality and talks to you.\n\n"
763                                "\"Oh, thou art still here. We were just discussing telepathically with Sir Galladon.\n"
764                                "We started doubting Decos's alleged poverty a while back when he bought a couple of\n"
765                                "medium-sized castles nearby. Thy brethren from New Attnam have also told Us about\n"
766                                "vast riches seized from them. Our law says all such stolen valuables belong to \n"
767                                "the Cathedral's treasury, so this is a severe claim. However, proof is needed,\n"
768                                "and even if such was provided, We couldn't send soldiers over the snow fields\n"
769                                "ere spring.\"\n\n"
770                                "\"However, since thou now servest Us, We ought to find thee something to do. In Our\n"
771                                "immesurable kindness and generosity, we giveth thee some time to see this majestic\n"
772                                "city of Ours and marvel at its glory. Return to Us once thou hast admired Our\n"
773                                "monumental Cathedral enough, and we shall have decided thy fate by then.\""));
774 
775       GetArea()->SendNewDrawRequest();
776       ADD_MESSAGE("\"The patrol guard will watch out for you, so don't thee think of running away.\"");
777       game::SetStoryState(1);
778     }
779     else
780       ADD_MESSAGE("\"Yes, citizen? We are quite busy now, thou shalt not disturb Us without proper cause.\"");
781   }
782   else if(game::GetStoryState() == 1)
783   {
784     game::TextScreen(CONST_S("\"Ah, here thou art, my brave warrior savage! Our agents hast informed Us that they\n"
785                              "witnessed thou leaving the dreaded underwater tunnel. This means thou most likely hast\n"
786                              "defeated Genetrix Vesana and art a talented monster slayer. We happen to have a task\n"
787                              "perfect for such a person.\"\n\n"
788                              "\"An evil dark frog named Elpuri who hates Valpurus and Attnam more than anything hath\n"
789                              "taken control over an abandoned mine nearby. It is pestering our fine city in many ways\n"
790                              "and reconnaissance has reported an army of monsters gathering in the cave. Our guards\n"
791                              "are not trained to fight underground and We dare not send them. To make things worse,\n"
792                              "someone hath recently stolen from Us the greatest armor in existence - the Shirt of the Golden Eagle.\n"
793                              "Elpuri cannot wear it but he who can is now nearly immortal.\"\n\n"
794                              "\"We have marked the location of the gloomy cave on thy map. We want you to dive\n"
795                              "into it and slay the vile frog. Bring Us its head and We reward thee with freedom.\n"
796                              "Shouldst thou also find the Shirt, We'll knight thee.\"\n\n"
797                              "\"Good luck, and return when thou hast succeeded.\""));
798 
799     game::LoadWorldMap();
800     v2 ElpuriCavePos = game::GetWorldMap()->GetEntryPos(0, ELPURI_CAVE);
801     game::GetWorldMap()->GetWSquare(ElpuriCavePos)->ChangeOWTerrain(elpuricave::Spawn());
802     game::GetWorldMap()->RevealEnvironment(ElpuriCavePos, 1);
803     game::SaveWorldMap();
804     GetArea()->SendNewDrawRequest();
805     ADD_MESSAGE("\"And by the way, visit the librarian. He might have advice for thee.\"");
806     game::SetGloomyCaveStoryState(1);
807     game::SetStoryState(2);
808   }
809   else // StoryState == 2
810     ADD_MESSAGE("\"Yes, citizen? We are quite busy now, thou shalt not disturb Us without proper cause.\"");
811 }
812 
BeTalkedTo()813 void priest::BeTalkedTo()
814 {
815   if(GetRelation(PLAYER) == HOSTILE)
816   {
817     character::BeTalkedTo();
818     return;
819   }
820   else if(!GetPos().IsAdjacent(PLAYER->GetPos()))
821   {
822     static long Said;
823 
824     if(GetConfig() != SILVA)
825       humanoid::BeTalkedTo();
826     else if(!game::TweraifIsFree())
827       ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 4)]);
828     else
829       ProcessAndAddMessage(GetFriendlyReplies()[4 + RandomizeReply(Said, 3)]);
830 
831     return;
832   }
833 
834   if(PLAYER->IsBurning())
835   {
836     long Price = GetConfig() == VALPURUS ? 25 : 5;
837 
838     if(PLAYER->GetMoney() >= Price)
839     {
840       ADD_MESSAGE("\"Good %s, you're on fire! Quick, hand over %ld gold!\"", GetMasterGod()->GetName(), Price);
841 
842       if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
843       {
844         PLAYER->Extinguish(true);
845         PLAYER->SetMoney(PLAYER->GetMoney() - Price);
846         SetMoney(GetMoney() + Price);
847         return;
848       }
849     }
850     else
851       ADD_MESSAGE("\"Good %s, you're on fire! Quick, go find %ld gold so that I can help!\"", GetMasterGod()->GetName(), Price);
852   }
853 
854   for(int c = 0; c < PLAYER->GetBodyParts(); ++c)
855   {
856     if(!PLAYER->GetBodyPart(c) && PLAYER->CanCreateBodyPart(c))
857     {
858       truth HasOld = false;
859 
860       for(ulong ID : PLAYER->GetOriginalBodyPartID(c))
861       {
862         bodypart* OldBodyPart = static_cast<bodypart*>(PLAYER->SearchForItem(ID));
863 
864         if(OldBodyPart)
865         {
866           HasOld = true;
867           long Price = GetConfig() == VALPURUS ? 50 : 10;
868 
869           if(PLAYER->GetMoney() >= Price)
870           {
871             if(!OldBodyPart->CanRegenerate())
872               ADD_MESSAGE("\"Sorry, I cannot put back bodyparts made of %s, not even your severed %s.\"",
873                           OldBodyPart->GetMainMaterial()->GetName(false, false).CStr(),
874                           PLAYER->GetBodyPartName(c).CStr());
875             else
876             {
877               ADD_MESSAGE("\"I could put your old %s back in exchange for %ld gold.\"",
878                           PLAYER->GetBodyPartName(c).CStr(), Price);
879 
880               if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
881               {
882                 OldBodyPart->SetHP(1);
883                 PLAYER->SetMoney(PLAYER->GetMoney() - Price);
884                 SetMoney(GetMoney() + Price);
885                 OldBodyPart->RemoveFromSlot();
886                 PLAYER->AttachBodyPart(OldBodyPart);
887                 return;
888               }
889             }
890           }
891           else
892             ADD_MESSAGE("\"Your %s is severed. Help yourself and get %ldgp and I'll help you too.\"",
893                         PLAYER->GetBodyPartName(c).CStr(), Price);
894         }
895       }
896 
897       long Price = GetConfig() == VALPURUS ? 100 : 20;
898 
899       if(PLAYER->GetMoney() >= Price)
900       {
901         if(HasOld)
902           ADD_MESSAGE("\"I could still summon up a new one for %ld gold.\"", Price);
903         else
904           ADD_MESSAGE("\"Since you don't seem to have your original %s with you, "
905                       "I could summon up a new one for %ld gold.\"",
906                       PLAYER->GetBodyPartName(c).CStr(), Price);
907 
908         if(game::TruthQuestion(CONST_S("Agreed? [y/N]")))
909         {
910           PLAYER->SetMoney(PLAYER->GetMoney() - Price);
911           SetMoney(GetMoney() + Price);
912           PLAYER->CreateBodyPart(c);
913           PLAYER->GetBodyPart(c)->SetHP(1);
914           return;
915         }
916       }
917       else if(!HasOld)
918         ADD_MESSAGE("\"You don't have your original %s with you. I could create you a new one, "
919                     "but my Divine Employer is not a communist and you need %ldgp first.\"",
920                     PLAYER->GetBodyPartName(c).CStr(), Price);
921     }
922 
923     if(PLAYER->GetBodyPart(c))
924     {
925       bodypart* BodyPart = PLAYER->GetBodyPart(c);
926 
927       if(BodyPart->CanRegenerate() && BodyPart->IsBurnt())
928       {
929         long Price = GetConfig() == VALPURUS ? 25 : 5;
930 
931         if(PLAYER->GetMoney() >= Price)
932         {
933             ADD_MESSAGE("\"I could cure the burns on your %s in exchange for %ld gold.\"",
934                         PLAYER->GetBodyPartName(c).CStr(), Price);
935 
936           if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
937           {
938             BodyPart->ResetBurning();
939             PLAYER->SetMoney(PLAYER->GetMoney() - Price);
940             SetMoney(GetMoney() + Price);
941             return;
942           }
943         }
944         else
945           ADD_MESSAGE("\"Your %s is burnt. Bring me %ld gold pieces and I'll make it all better.\"",
946                       PLAYER->GetBodyPartName(c).CStr(), Price);
947       }
948       else if(!BodyPart->CanRegenerate() && BodyPart->IsBurnt())
949         ADD_MESSAGE("\"Sorry, I cannot heal bodyparts made of %s, not even your burnt %s.\"",
950                           BodyPart->GetMainMaterial()->GetName(false, false).CStr(),
951                           PLAYER->GetBodyPartName(c).CStr());
952     }
953   }
954 
955   if(PLAYER->TemporaryStateIsActivated(POISONED))
956   {
957     long Price = GetConfig() == VALPURUS ? 25 : 5;
958 
959     if(PLAYER->GetMoney() >= Price)
960     {
961       ADD_MESSAGE("\"You seem to be rather ill. I could give you a "
962                   "small dose of antidote for %ld gold pieces.\"", Price);
963 
964       if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
965       {
966         ADD_MESSAGE("You feel better.");
967         PLAYER->DeActivateTemporaryState(POISONED);
968         PLAYER->SetMoney(PLAYER->GetMoney() - Price);
969         SetMoney(GetMoney() + Price);
970         return;
971       }
972     }
973     else
974       ADD_MESSAGE("\"You seem to be rather ill. Get %ld gold pieces and I'll fix that.\"", Price);
975   }
976 
977   if(PLAYER->TemporaryStateIsActivated(LEPROSY))
978   {
979     long Price = GetConfig() == VALPURUS ? 100 : 20;
980 
981     if(PLAYER->GetMoney() >= Price)
982     {
983       ADD_MESSAGE("\"You seem to have contracted the vile disease of leprosy. "
984                   "I could give you a small dose of medicine for %ld gold pieces.\"", Price);
985 
986       if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
987       {
988         ADD_MESSAGE("You feel better.");
989         PLAYER->DeActivateTemporaryState(LEPROSY);
990         PLAYER->SetMoney(PLAYER->GetMoney() - Price);
991         SetMoney(GetMoney() + Price);
992         return;
993       }
994     }
995     else
996       ADD_MESSAGE("\"You seem to be falling apart. Get %ld gold pieces and I'll fix that.\"", Price);
997   }
998 
999   if(PLAYER->TemporaryStateIsActivated(LYCANTHROPY))
1000   {
1001     long Price = GetConfig() == VALPURUS ? 100 : 20;
1002 
1003     if(PLAYER->GetMoney() >= Price)
1004     {
1005       ADD_MESSAGE("\"You seem to be turning into a werewolf quite frequently. Well, everyone has right to "
1006                   "little secret habits, but if you wish to donate %ldgp to %s, maybe I could pray to %s to "
1007                   "remove the canine blood from your veins, just so you don't scare our blessed youth.\"",
1008                   Price, GetMasterGod()->GetName(), GetMasterGod()->GetObjectPronoun());
1009 
1010       if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
1011       {
1012         ADD_MESSAGE("You feel better.");
1013         PLAYER->DeActivateTemporaryState(LYCANTHROPY);
1014         PLAYER->SetMoney(PLAYER->GetMoney() - Price);
1015         SetMoney(GetMoney() + Price);
1016         return;
1017       }
1018     }
1019     else
1020       ADD_MESSAGE("\"You seem to be lycanthropic. I might be able to do something "
1021                   "for that but I need %ldgp for the ritual materials first.\"", Price);
1022   }
1023 
1024   if(PLAYER->TemporaryStateIsActivated(VAMPIRISM))
1025   {
1026     long Price = GetConfig() == VALPURUS ? 100 : 20;
1027 
1028     if(PLAYER->GetMoney() >= Price)
1029     {
1030       ADD_MESSAGE("\"You seem to have an addiction to drinking blood. Well, everyone has right to "
1031                   "little secret habits, but if you wish to donate %ldgp to %s, maybe I could pray "
1032                   "%s to remove your vampiric urges, just so you don't victimize our besotted youth.\""
1033                   , Price, GetMasterGod()->GetName(), GetMasterGod()->GetObjectPronoun());
1034 
1035       if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
1036       {
1037         ADD_MESSAGE("You feel better.");
1038         PLAYER->DeActivateTemporaryState(VAMPIRISM);
1039         PLAYER->SetMoney(PLAYER->GetMoney() - Price);
1040         SetMoney(GetMoney() + Price);
1041         return;
1042       }
1043     }
1044     else
1045       ADD_MESSAGE("\"You seem to be vampiric. I might be able to do something for that but "
1046                   "I need %ldgp for the ritual materials first.\"", Price);
1047   }
1048 
1049   if(PLAYER->TemporaryStateIsActivated(PARASITE_TAPE_WORM))
1050   {
1051     long Price = GetConfig() == VALPURUS ? 100 : 20;
1052 
1053     if(PLAYER->GetMoney() >= Price)
1054     {
1055       ADD_MESSAGE("\"Ugh, there seems to be some other creature living in your body. I could try "
1056                   "to purge the parasite, that is if you wish to donate %ldgp to %s, of course.\""
1057                   , Price, GetMasterGod()->GetName(), GetMasterGod()->GetObjectPronoun());
1058 
1059       if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
1060       {
1061         ADD_MESSAGE("You feel better.");
1062         PLAYER->DeActivateTemporaryState(PARASITE_TAPE_WORM);
1063         PLAYER->SetMoney(PLAYER->GetMoney() - Price);
1064         SetMoney(GetMoney() + Price);
1065         return;
1066       }
1067     }
1068     else
1069       ADD_MESSAGE("\"You seem to have an unwanted guest in your guts. I can help but "
1070                   "I need %ldgp for a ritual of cleansing.\"", Price);
1071   }
1072 
1073   if(PLAYER->TemporaryStateIsActivated(PARASITE_MIND_WORM))
1074   {
1075     long Price = GetConfig() == VALPURUS ? 100 : 20;
1076 
1077     if(PLAYER->GetMoney() >= Price)
1078     {
1079       ADD_MESSAGE("\"Ugh, there seems to be some other creature living in your body. I could try "
1080                   "to purge the parasite, that is if you wish to donate %ldgp to %s, of course.\""
1081                   , Price, GetMasterGod()->GetName(), GetMasterGod()->GetObjectPronoun());
1082 
1083       if(game::TruthQuestion(CONST_S("Do you agree? [y/N]")))
1084       {
1085         ADD_MESSAGE("You feel better.");
1086         PLAYER->DeActivateTemporaryState(PARASITE_MIND_WORM);
1087         PLAYER->SetMoney(PLAYER->GetMoney() - Price);
1088         SetMoney(GetMoney() + Price);
1089         return;
1090       }
1091     }
1092     else
1093       ADD_MESSAGE("\"You seem to have an unwanted guest in your head. I can help but "
1094                   "I need %ldgp for a ritual of cleansing.\"", Price);
1095   }
1096 
1097   static long Said;
1098 
1099   if(GetConfig() != SILVA)
1100     humanoid::BeTalkedTo();
1101   else if(!game::TweraifIsFree())
1102     ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 4)]);
1103   else
1104     ProcessAndAddMessage(GetFriendlyReplies()[4 + RandomizeReply(Said, 3)]);
1105 }
1106 
BeTalkedTo()1107 void skeleton::BeTalkedTo()
1108 {
1109   if(GetHead())
1110     humanoid::BeTalkedTo();
1111   else if(GetPos().IsAdjacent(PLAYER->GetPos()))
1112     ADD_MESSAGE("The headless %s remains silent.", CHAR_DESCRIPTION(DEFINITE));
1113 }
1114 
BeTalkedTo()1115 void communist::BeTalkedTo()
1116 {
1117   if(GetRelation(PLAYER) != HOSTILE
1118      && GetTeam() != PLAYER->GetTeam()
1119      && PLAYER->GetRelativeDanger(this, true) > 0.1
1120      && GetPos().IsAdjacent(PLAYER->GetPos())
1121    )
1122   {
1123     ADD_MESSAGE("%s seems to be very friendly. \"%s make good communist. %s go with %s!\"",
1124                 CHAR_DESCRIPTION(DEFINITE), PLAYER->GetAssignedName().CStr(),
1125                 CHAR_NAME(UNARTICLED), PLAYER->GetAssignedName().CStr());
1126 
1127     for(character* Char : GetTeam()->GetMember())
1128     {
1129       if(Char != this)
1130         Char->ChangeTeam(PLAYER->GetTeam());
1131 
1132       if(GetTeam()->GetMembers() == 1) // Only Ivan left in Party
1133         break;
1134     }
1135 
1136     ChangeTeam(PLAYER->GetTeam());
1137   }
1138   else if(GetTeam() != PLAYER->GetTeam() && !(RAND() % 5))
1139     ADD_MESSAGE("\"You weak. Learn killing and come back.\"");
1140   else
1141     character::BeTalkedTo();
1142 }
1143 
BeTalkedTo()1144 void hunter::BeTalkedTo()
1145 {
1146   if(GetRelation(PLAYER) != HOSTILE && GetMainWielded() && !(RAND() % 10))
1147     ADD_MESSAGE("\"This is my %s. There are many like it but this one is mine. My %s is my best friend.\"",
1148                 GetMainWielded()->CHAR_NAME(UNARTICLED), GetMainWielded()->CHAR_NAME(UNARTICLED));
1149   else
1150     character::BeTalkedTo();
1151 }
1152 
BeTalkedTo()1153 void tourist::BeTalkedTo()
1154 {
1155   if(GetConfig() == CHILD && GetPos().IsAdjacent(PLAYER->GetPos()))
1156   {
1157     character* Spider = 0;
1158 
1159     // check all enabled members of PLAYER_TEAM
1160     for(character* p : game::GetTeam(PLAYER_TEAM)->GetMember())
1161       if(p->IsEnabled() && !p->IsPlayer() && p->IsSpider()
1162          && (p->GetConfig() != LARGE && p->GetConfig() != GIANT)) // check for lobh-se first
1163         Spider = p;
1164 
1165     if(!Spider) // lobh-se not found
1166     {
1167       for(character* p : game::GetTeam(PLAYER_TEAM)->GetMember())
1168         if(p->IsEnabled() && !p->IsPlayer() && p->IsSpider()) // check for any spider
1169           Spider = p;
1170     }
1171 
1172     static long Said;
1173 
1174     if(GetRelation(PLAYER) == HOSTILE) // hostile response
1175     {
1176       ADD_MESSAGE("\"Daddy!!! Hit this man!!! He teases me!!!\"");
1177       return;
1178     }
1179     else if(Spider && !game::ChildTouristHasSpider()) // implement truthquestion + proper dialogue  // quest fulfilled
1180     {
1181       ADD_MESSAGE("\"Wow, what a cool spider!!! Can I have it mister? Can I?\"");
1182       festring GiveSpider = CONST_S("Will you give ") + Spider->CHAR_NAME(DEFINITE) +
1183                             CONST_S(" to ") + CHAR_NAME(DEFINITE) + CONST_S("? [y/N]");
1184       if(game::TruthQuestion(GiveSpider))
1185       {
1186         ADD_MESSAGE("\"Thanks a lot mister!!! Here, you can have this.\"");
1187         item* Reward = 0; // create gift item
1188 
1189         if(Spider->GetConfig() != LARGE && Spider->GetConfig() != GIANT) // must be lobh-se
1190         {
1191           Reward = scrollofwishing::Spawn();
1192         }
1193         else if(Spider->GetConfig() == LARGE || Spider->GetConfig() == GIANT) // other spider
1194         {
1195           Reward = stick::Spawn();
1196           Reward->InitMaterials(MAKE_MATERIAL(BALSA_WOOD)); // balsa stick
1197         }
1198         else
1199           ABORT("Man, this ain't my spider; this is a cell phone!");
1200 
1201         PLAYER->GetStack()->AddItem(Reward); // add gift to player's inventory
1202         ADD_MESSAGE("%s hands you %s.", CHAR_NAME(DEFINITE), Reward->CHAR_NAME(INDEFINITE));
1203         team* Team = game::GetTeam(TOURIST_TEAM);
1204         Spider->ChangeTeam(Team); // change spider to tourist team
1205         game::SetTouristHasSpider(); // sets game::TouristHasSpider to true
1206       }
1207       else
1208         ADD_MESSAGE("\"Aw... you're no fun!!!\"");
1209     }
1210     else if(!Spider && !game::ChildTouristHasSpider()) // kid does not have spider; normal chat
1211       ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]);
1212     else if(game::ChildTouristHasSpider() && !(RAND() % 4))
1213       ADD_MESSAGE("\"My friends back home will be so jealous of my new pet spider!!!\"");
1214     else // kid has spider; normal chat (no spider request)
1215       ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size - 1)]);
1216   }
1217   else // not child tourist; normal chat
1218     character::BeTalkedTo();
1219 }
1220 
BeTalkedTo()1221 void slave::BeTalkedTo()
1222 {
1223   if(GetRelation(PLAYER) == HOSTILE)
1224   {
1225     ADD_MESSAGE("\"Yikes!\"");
1226     return;
1227   }
1228 
1229   if(GetPos().IsAdjacent(PLAYER->GetPos()))
1230   {
1231     room* Room = GetHomeRoom();
1232 
1233     if(Room && Room->MasterIsActive())
1234     {
1235       character* Master = Room->GetMaster();
1236 
1237       if(PLAYER->GetMoney() >= 50)
1238       {
1239         ADD_MESSAGE("%s talks: \"Do you want to buy me? 50 gold pieces. "
1240                     "I work very hard.\"", CHAR_DESCRIPTION(DEFINITE));
1241 
1242         if(game::TruthQuestion(CONST_S("Do you want to buy him? [y/N]")))
1243         {
1244           PLAYER->SetMoney(PLAYER->GetMoney() - 50);
1245           Master->SetMoney(Master->GetMoney() + 50);
1246           ChangeTeam(PLAYER->GetTeam());
1247           RemoveHomeData();
1248         }
1249       }
1250       else
1251         ADD_MESSAGE("\"Don't touch me! Master doesn't want people to touch "
1252                     "sale items. I'm worth 50 gold pieces, you know!\"");
1253 
1254       return;
1255     }
1256 
1257     if(GetTeam() == PLAYER->GetTeam())
1258     {
1259       if((PLAYER->GetMainWielded() && PLAYER->GetMainWielded()->IsWhip()) ||
1260          (PLAYER->GetSecondaryWielded() && PLAYER->GetSecondaryWielded()->IsWhip()))
1261         ADD_MESSAGE("\"Don't hit me! I work! I obey! I don't think!\"");
1262       else
1263         character::BeTalkedTo();
1264     }
1265     else
1266       ADD_MESSAGE("\"I'm free! Rejoice!\"");
1267   }
1268   else
1269     character::BeTalkedTo();
1270 }
1271 
GetAICommand()1272 void slave::GetAICommand()
1273 {
1274   SeekLeader(GetLeader()); DBG2(GetNameSingular().CStr(), GetLeader());
1275 
1276   if(CheckAIZapOpportunity()){ DBG1(GetNameSingular().CStr());
1277     return;
1278   }
1279 
1280   if(CheckForEnemies(true, true, true)){ DBG1(GetNameSingular().CStr());
1281     return;
1282   }
1283 
1284   if(CheckForUsefulItemsOnGround()){ DBG1(GetNameSingular().CStr());
1285     return;
1286   }
1287 
1288   if(FollowLeader(GetLeader()))
1289     return;
1290 
1291   if(CheckForDoors())
1292     return;
1293 
1294   if(!GetHomeRoom() || !GetHomeRoom()->MasterIsActive())
1295   {
1296     RemoveHomeData();
1297 
1298     if(MoveRandomly())
1299       return;
1300   }
1301   else if(MoveTowardsHomePos())
1302     return;
1303 
1304   EditAP(-1000);
1305 }
1306 
BeTalkedTo()1307 void librarian::BeTalkedTo()
1308 {
1309   if(GetRelation(PLAYER) == HOSTILE)
1310   {
1311     ADD_MESSAGE("\"The pen is mightier than the sword! Fall, unlearned one!\"");
1312     return;
1313   }
1314   else if(!GetPos().IsAdjacent(PLAYER->GetPos()))
1315     return;
1316 
1317   // TODO: Replies for TX and Aslona!
1318 
1319   static long Said;
1320 
1321   switch(RandomizeReply(Said, 12))
1322   {
1323    case 0:
1324     if(game::GetPetrus() && !game::GetStoryState())
1325       ADD_MESSAGE("\"Thou shouldst visit Petrus in his great Cathedral.\"");
1326     else if(game::GetPetrus() && game::GetStoryState() == 1)
1327       ADD_MESSAGE("\"Thou shouldst visit Petrus if thou art in need of further adventures.\"");
1328     else
1329       ADD_MESSAGE("\"They say a wand of polymorph hath dozens of uses.\"");
1330 
1331     break;
1332    case 1:
1333     if(game::GetPetrus() && game::GetGloomyCaveStoryState())
1334       ADD_MESSAGE("\"Thou art going to fight Elpuri? Beware! It is a powerful enemy. Other monsters "
1335                   "are very vulnerable if surrounded by thy party, but not that beast, for it may "
1336                   "slay a horde of thy friends at once with its horrendous tail attack.\"");
1337     else if(game::GetXinrochTombStoryState())
1338       ADD_MESSAGE("\"Thou art going to delve into the Tomb of Xinroch? Beware, for it is a place of horrific darkness and abundant necromancy.\"");
1339     /*else if(game::GetAslonaStoryState())
1340       ADD_MESSAGE("\"Elpuri the Dark Frog abhors light and resides in a level of eternal darkness.\"");*/
1341     else
1342       ADD_MESSAGE("\"Thou shalt remember: Scientia est potentia.\"");
1343 
1344     break;
1345    case 2:
1346     if(game::GetPetrus() && game::GetGloomyCaveStoryState())
1347       ADD_MESSAGE("\"Elpuri the Dark Frog abhors light and resides in a level of eternal darkness.\"");
1348     else if(game::GetXinrochTombStoryState())
1349       ADD_MESSAGE("\"The Tomb of Xinroch is a chilling place. It is said that a whole cavern of magical ice can be found when wandering its tunnels.\"");
1350     /*else if(game::GetAslonaStoryState())
1351       ADD_MESSAGE("\"Elpuri the Dark Frog abhors light and resides in a level of eternal darkness.\"");*/
1352     else
1353       ADD_MESSAGE("\"Shh! Thou shalt be silent in the library.\"");
1354 
1355     break;
1356    case 3:
1357     if(game::GetPetrus() && game::GetGloomyCaveStoryState())
1358       ADD_MESSAGE("\"Elpuri's attacks are so strong that they may shatter many of thy precious items.\"");
1359     else if(game::GetXinrochTombStoryState())
1360       ADD_MESSAGE("\"The Tomb of Xinroch is guarded by fanatical dark knights that once followed Xinroch and swore to protect him even in death.\"");
1361     /*else if(game::GetAslonaStoryState())
1362       ADD_MESSAGE("\"Elpuri the Dark Frog abhors light and resides in a level of eternal darkness.\"");*/
1363     else
1364       ADD_MESSAGE("\"Dost thou not smell all the knowledge floating around here?\"");
1365 
1366     break;
1367    case 4:
1368     if(!RAND_2)
1369       ADD_MESSAGE("\"It is said that Loricatus, the god of smithing, can upgrade thy weapons' materials.\"");
1370     else
1371       ADD_MESSAGE("\"It is said that Atavus, the god of support and charity, may bolster thy defenses.\"");
1372 
1373     break;
1374    case 5:
1375     if(game::GetPetrus() && game::GetGloomyCaveStoryState())
1376       ADD_MESSAGE("\"The Shirt of the Golden Eagle is a legendary artifact. Thou canst not find a better armor.\"");
1377     /*else if(game::GetXinrochTombStoryState())
1378       ADD_MESSAGE("\"The Tomb of Xinroch is guarded by fanatical dark knights that once followed Xinroch and swore to protect him even in death.\"");
1379     else if(game::GetAslonaStoryState())
1380       ADD_MESSAGE("\"Elpuri the Dark Frog abhors light and resides in a level of eternal darkness.\"");*/
1381     else
1382       ADD_MESSAGE("\"In this book they talk about Mortifer, the great chaos god. He hates us "
1383                   "mortals more than anything and will respond only to Champions of Evil.\"");
1384 
1385     break;
1386    case 6:
1387     ADD_MESSAGE("\"Attnam is traditionally ruled by the high priest of the Great Frog. He "
1388                 "holds the Shirt of the Golden Eagle and has always killed his predecessor.\"");
1389     break;
1390    case 7:
1391     ADD_MESSAGE("\"They say thou shouldst keep all the artifacts thou findst. "
1392                 "They shall make thee famous after thy retirement.\"");
1393     break;
1394    case 8:
1395     ADD_MESSAGE("\"If thou wilt ever encounter an enner beast, know this: It is a horrible foe. "
1396                 "It may shatter thy items and armor with its scream that penetrates iron and stone. "
1397                 "Thou shouldst not engage it in melee but rather kill it from afar.\"");
1398     break;
1399    case 9:
1400     if(game::GetPetrus() && game::GetGloomyCaveStoryState())
1401       ADD_MESSAGE("\"Thou art not alone in thy attempt to defeat Elpuri. A brave "
1402                   "adventurer called Ivan also diveth into its cave not long ago.\"");
1403     /*else if(game::GetXinrochTombStoryState())
1404       ADD_MESSAGE("\"The Tomb of Xinroch is guarded by fanatical dark knights that once followed Xinroch and swore to protect him even in death.\"");
1405     else if(game::GetAslonaStoryState())
1406       ADD_MESSAGE("\"Elpuri the Dark Frog abhors light and resides in a level of eternal darkness.\"");*/
1407     else
1408       ADD_MESSAGE("\"It is said that chaotic gods offer great power to their followers. But thou "
1409                   "must remember: unlike lawfuls, they shall not help thee when things go bad.\"");
1410 
1411     break;
1412    case 10:
1413     if(!RAND_2)
1414       ADD_MESSAGE("\"If a man cannot choose, he ceases to be a man.\"");
1415     else
1416       ADD_MESSAGE("\"It is said that Cruentus, the god of bloodshed, may empower thy weapons.\"");
1417 
1418     break;
1419    case 11:
1420     ADD_MESSAGE("%s sighs: \"The censorship laws in this town are really too strict...\"",
1421                 CHAR_DESCRIPTION(DEFINITE));
1422     break;
1423   }
1424 }
1425 
MoveRandomly()1426 truth communist::MoveRandomly()
1427 {
1428   switch(RAND() % 1000)
1429   {
1430    case 0:
1431     if(CanBeSeenByPlayer())
1432       ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED));
1433 
1434     Engrave(CONST_S("The bourgeois is a bourgeois -- for the benefit of the working class."));
1435     return true;
1436    case 1:
1437     if(CanBeSeenByPlayer())
1438       ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED));
1439 
1440     Engrave(CONST_S("Proletarians of all countries, unite!"));
1441     return true;
1442    case 2:
1443     if(CanBeSeenByPlayer())
1444       ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED));
1445 
1446     Engrave(CONST_S("Capital is therefore not only personal; it is a social power."));
1447     return true;
1448    default:
1449     return character::MoveRandomly();
1450   }
1451 }
1452 
BeTalkedTo()1453 void zombie::BeTalkedTo()
1454 {
1455   if(!HasHead() && GetPos().IsAdjacent(PLAYER->GetPos()))
1456   {
1457     ADD_MESSAGE("The headless %s remains silent.", CHAR_DESCRIPTION(DEFINITE));
1458   }
1459   else if(GetRelation(PLAYER) == HOSTILE && PLAYER->GetAttribute(INTELLIGENCE) > 5)
1460   {
1461     if(RAND() % 5)
1462     {
1463       if(GetHead())
1464         ADD_MESSAGE("\"Need brain!!\"");
1465       else
1466         ADD_MESSAGE("\"Need head with brain!!\"");
1467     }
1468     else
1469       ADD_MESSAGE("\"Redrum! Redrum! Redrum!\"");
1470   }
1471   else
1472     character::BeTalkedTo();
1473 }
1474 
Save(outputfile & SaveFile) const1475 void angel::Save(outputfile& SaveFile) const
1476 {
1477   humanoid::Save(SaveFile);
1478   SaveFile << LastHealed;
1479 }
1480 
Load(inputfile & SaveFile)1481 void angel::Load(inputfile& SaveFile)
1482 {
1483   humanoid::Load(SaveFile);
1484   SaveFile >> LastHealed;
1485 }
1486 
CreateInitialEquipment(int SpecialFlags)1487 void angel::CreateInitialEquipment(int SpecialFlags)
1488 {
1489   humanoid::CreateInitialEquipment(SpecialFlags);
1490   GetStack()->AddItem(holybook::Spawn(GetConfig(), SpecialFlags));
1491   armor* Equipment;
1492   meleeweapon* Weapon;
1493 
1494   switch(GetMasterGod()->GetBasicAlignment())
1495   {
1496    case GOOD:
1497     Equipment = bodyarmor::Spawn(PLATE_MAIL, SpecialFlags|NO_MATERIALS);
1498     Equipment->InitMaterials(MAKE_MATERIAL(ANGEL_HAIR), !(SpecialFlags & NO_PIC_UPDATE));
1499     Equipment->SetEnchantment(1);
1500     SetBodyArmor(Equipment);
1501     Weapon = meleeweapon::Spawn(LONG_SWORD, SpecialFlags|NO_MATERIALS);
1502     Weapon->InitMaterials(MAKE_MATERIAL(MITHRIL), MAKE_MATERIAL(MITHRIL), !(SpecialFlags & NO_PIC_UPDATE));
1503     Weapon->SetEnchantment(2);
1504     SetRightWielded(Weapon);
1505     Equipment = shield::Spawn(0, SpecialFlags|NO_MATERIALS);
1506     Equipment->InitMaterials(MAKE_MATERIAL(MITHRIL), !(SpecialFlags & NO_PIC_UPDATE));
1507     Equipment->SetEnchantment(2);
1508     SetLeftWielded(Equipment);
1509     GetCWeaponSkill(LARGE_SWORDS)->AddHit(20000);
1510     GetCWeaponSkill(SHIELDS)->AddHit(50000);
1511     GetCurrentRightSWeaponSkill()->AddHit(20000);
1512     GetCurrentLeftSWeaponSkill()->AddHit(20000);
1513     GetRightArm()->SetDexterity(40);
1514     GetLeftArm()->SetDexterity(40);
1515     break;
1516    case NEUTRAL:
1517     Equipment = cloak::Spawn(0, SpecialFlags|NO_MATERIALS);
1518     Equipment->InitMaterials(MAKE_MATERIAL(ANGEL_HAIR), !(SpecialFlags & NO_PIC_UPDATE));
1519     Equipment->SetEnchantment(1);
1520     SetCloak(Equipment);
1521     Weapon = meleeweapon::Spawn(WAR_HAMMER, SpecialFlags|NO_MATERIALS);
1522     Weapon->InitMaterials(MAKE_MATERIAL(MITHRIL), MAKE_MATERIAL(TEAK_WOOD), !(SpecialFlags & NO_PIC_UPDATE));
1523     Weapon->SetEnchantment(2);
1524     SetRightWielded(Weapon);
1525     Weapon = meleeweapon::Spawn(WAR_HAMMER, SpecialFlags|NO_MATERIALS);
1526     Weapon->InitMaterials(MAKE_MATERIAL(MITHRIL), MAKE_MATERIAL(TEAK_WOOD), !(SpecialFlags & NO_PIC_UPDATE));
1527     Weapon->SetEnchantment(2);
1528     SetLeftWielded(Weapon);
1529     GetCWeaponSkill(BLUNT_WEAPONS)->AddHit(50000);
1530     GetCurrentRightSWeaponSkill()->AddHit(20000);
1531     GetCurrentLeftSWeaponSkill()->AddHit(20000);
1532     SetEndurance(40);
1533     break;
1534    case EVIL:
1535     Weapon = meleeweapon::Spawn(HALBERD, SpecialFlags|NO_MATERIALS);
1536     Weapon->InitMaterials(MAKE_MATERIAL(MITHRIL), MAKE_MATERIAL(EBONY_WOOD), !(SpecialFlags & NO_PIC_UPDATE));
1537     Weapon->SetEnchantment(2);
1538     SetRightWielded(Weapon);
1539     Equipment = gauntlet::Spawn(0, SpecialFlags|NO_MATERIALS);
1540     Equipment->InitMaterials(MAKE_MATERIAL(ANGEL_HAIR), !(SpecialFlags & NO_PIC_UPDATE));
1541     Equipment->SetEnchantment(1);
1542     SetRightGauntlet(Equipment);
1543     Equipment = gauntlet::Spawn(0, SpecialFlags|NO_MATERIALS);
1544     Equipment->InitMaterials(MAKE_MATERIAL(ANGEL_HAIR), !(SpecialFlags & NO_PIC_UPDATE));
1545     Equipment->SetEnchantment(1);
1546     SetLeftGauntlet(Equipment);
1547     GetCWeaponSkill(POLE_ARMS)->AddHit(100000);
1548     GetCurrentRightSWeaponSkill()->AddHit(100000);
1549     GetRightArm()->SetStrength(40);
1550     GetLeftArm()->SetStrength(40);
1551   }
1552 }
1553 
CreateInitialEquipment(int SpecialFlags)1554 void kamikazedwarf::CreateInitialEquipment(int SpecialFlags)
1555 {
1556   humanoid::CreateInitialEquipment(SpecialFlags);
1557   SetRightWielded(holybook::Spawn(GetConfig(), SpecialFlags));
1558   GetCWeaponSkill(UNCATEGORIZED)->AddHit(GetWSkillHits());
1559   GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits());
1560 }
1561 
Hit(character * Enemy,v2 HitPos,int Direction,int Flags)1562 truth kamikazedwarf::Hit(character* Enemy, v2 HitPos, int Direction, int Flags)
1563 {
1564   if(!IsPlayer())
1565   {
1566     itemvector KamikazeWeapon;
1567     sortdata SortData(KamikazeWeapon, this, false, &item::IsKamikazeWeapon);
1568     SortAllItems(SortData);
1569 
1570     if(!KamikazeWeapon.empty())
1571     {
1572       if(IsElite() && RAND() & 1)
1573         ADD_MESSAGE("%s shouts: \"This time I won't fail, O Great %s!\"",
1574                     CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName());
1575       else if(RAND() & 1)
1576         ADD_MESSAGE("%s shouts: \"For %s!\"",
1577                     CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName());
1578       else
1579         ADD_MESSAGE("%s screams: \"%s, here I come!\"",
1580                     CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName());
1581 
1582       if(KamikazeWeapon[RAND_N(KamikazeWeapon.size())]->Apply(this))
1583         return true;
1584     }
1585   }
1586 
1587   return humanoid::Hit(Enemy, HitPos, Direction, Flags);
1588 }
1589 
GetAICommand()1590 void kamikazedwarf::GetAICommand()
1591 {
1592   if(GetHomeRoom())
1593     StandIdleAI();
1594   else
1595   {
1596     if(!RAND_N(50))
1597     {
1598       SingRandomSong();
1599       return;
1600     }
1601 
1602     character::GetAICommand();
1603   }
1604 }
1605 
GetSize() const1606 int humanoid::GetSize() const
1607 {
1608   int Size = 0;
1609 
1610   if(GetHead())
1611     Size += GetHead()->GetSize();
1612 
1613   if(GetTorso())
1614     Size += GetTorso()->GetSize();
1615 
1616   leg* RightLeg = GetRightLeg();
1617   leg* LeftLeg = GetLeftLeg();
1618 
1619   if(LeftLeg && RightLeg)
1620     Size += Max(LeftLeg->GetSize(), RightLeg->GetSize());
1621   else if(LeftLeg)
1622     Size += LeftLeg->GetSize();
1623   else if(RightLeg)
1624     Size += RightLeg->GetSize();
1625 
1626   return Size;
1627 }
1628 
GetBodyPartSize(int I,int TotalSize) const1629 long humanoid::GetBodyPartSize(int I, int TotalSize) const
1630 {
1631   switch(I)
1632   {
1633    case HEAD_INDEX: return 20;
1634    case TORSO_INDEX: return ((TotalSize - 20) << 1) / 5;
1635    case RIGHT_ARM_INDEX:
1636    case LEFT_ARM_INDEX: return (TotalSize - 20) * 3 / 5;
1637    case GROIN_INDEX: return (TotalSize - 20) / 3;
1638    case RIGHT_LEG_INDEX:
1639    case LEFT_LEG_INDEX: return (TotalSize - 20) * 3 / 5;
1640   }
1641 
1642   ABORT("Illegal humanoid bodypart size request!");
1643   return 0;
1644 }
1645 
GetBodyPartVolume(int I) const1646 long humanoid::GetBodyPartVolume(int I) const
1647 {
1648   switch(I)
1649   {
1650    case HEAD_INDEX: return 4000;
1651    case TORSO_INDEX: return (GetTotalVolume() - 4000) * 13 / 30;
1652    case RIGHT_ARM_INDEX:
1653    case LEFT_ARM_INDEX: return (GetTotalVolume() - 4000) / 10;
1654    case GROIN_INDEX: return (GetTotalVolume() - 4000) / 10;
1655    case RIGHT_LEG_INDEX:
1656    case LEFT_LEG_INDEX: return ((GetTotalVolume() - 4000) << 1) / 15;
1657   }
1658 
1659   ABORT("Illegal humanoid bodypart volume request!");
1660   return 0;
1661 }
1662 
MakeBodyPart(int I) const1663 bodypart* humanoid::MakeBodyPart(int I) const
1664 {
1665   switch(I)
1666   {
1667    case TORSO_INDEX: return humanoidtorso::Spawn(0, NO_MATERIALS);
1668    case HEAD_INDEX: return head::Spawn(0, NO_MATERIALS);
1669    case RIGHT_ARM_INDEX: return rightarm::Spawn(0, NO_MATERIALS);
1670    case LEFT_ARM_INDEX: return leftarm::Spawn(0, NO_MATERIALS);
1671    case GROIN_INDEX: return groin::Spawn(0, NO_MATERIALS);
1672    case RIGHT_LEG_INDEX: return rightleg::Spawn(0, NO_MATERIALS);
1673    case LEFT_LEG_INDEX: return leftleg::Spawn(0, NO_MATERIALS);
1674   }
1675 
1676   ABORT("Weird bodypart to make for a humanoid. It must be your fault!");
1677   return 0;
1678 }
1679 
ReceiveDamage(character * Damager,int Damage,int Type,int TargetFlags,int Direction,truth Divide,truth PenetrateArmor,truth Critical,truth ShowMsg)1680 truth humanoid::ReceiveDamage(character* Damager, int Damage, int Type, int TargetFlags, int Direction,
1681                               truth Divide, truth PenetrateArmor, truth Critical, truth ShowMsg)
1682 {
1683   int ChooseFrom[MAX_BODYPARTS], BodyParts = 0;
1684 
1685   if(TargetFlags & RIGHT_ARM && GetRightArm())
1686     ChooseFrom[BodyParts++] = 2;
1687 
1688   if(TargetFlags & LEFT_ARM && GetLeftArm())
1689     ChooseFrom[BodyParts++] = 3;
1690 
1691   if(TargetFlags & RIGHT_LEG && GetRightLeg())
1692     ChooseFrom[BodyParts++] = 5;
1693 
1694   if(TargetFlags & LEFT_LEG && GetLeftLeg())
1695     ChooseFrom[BodyParts++] = 6;
1696 
1697   if(TargetFlags & HEAD && GetHead())
1698     ChooseFrom[BodyParts++] = 1;
1699 
1700   if(TargetFlags & TORSO && GetTorso())
1701     ChooseFrom[BodyParts++] = 0;
1702 
1703   if(TargetFlags & GROIN && GetGroin())
1704     ChooseFrom[BodyParts++] = 4;
1705 
1706   if(!BodyParts)
1707     return false;
1708 
1709   truth Affected = false;
1710 
1711   if(Divide)
1712   {
1713     int c;
1714     long TotalVolume = 0;
1715 
1716     for(c = 0; c < BodyParts; ++c)
1717       TotalVolume += GetBodyPart(ChooseFrom[c])->GetBodyPartVolume();
1718 
1719     for(c = 0; c < BodyParts; ++c)
1720       if(ReceiveBodyPartDamage(Damager, long(Damage) * GetBodyPart(ChooseFrom[c])->GetBodyPartVolume() / TotalVolume,
1721                                Type, ChooseFrom[c], Direction, PenetrateArmor, Critical, false))
1722         Affected = true;
1723   }
1724   else
1725   {
1726     long Possibility[MAX_BODYPARTS], PossibilitySum = 0;
1727     int Index = 0;
1728 
1729     for(int c = 0; c < BodyParts; ++c)
1730       PossibilitySum += Possibility[Index++] = GetBodyPart(ChooseFrom[c])->GetBodyPartVolume();
1731 
1732     Index = femath::WeightedRand(Possibility, PossibilitySum);
1733     Affected = ReceiveBodyPartDamage(Damager, Damage, Type, ChooseFrom[Index],
1734                                      Direction, PenetrateArmor, Critical, false);
1735   }
1736 
1737   if(!Affected && ShowMsg)
1738   {
1739     if(IsPlayer())
1740       ADD_MESSAGE("You are not hurt.");
1741     else if(CanBeSeenByPlayer())
1742       ADD_MESSAGE("%s is not hurt.", GetPersonalPronoun().CStr());
1743   }
1744 
1745   if(DamageTypeAffectsInventory(Type))
1746   {
1747     for(int c = 0; c < GetEquipments(); ++c)
1748     {
1749       item* Equipment = GetEquipment(c);
1750 
1751       if(Equipment)
1752         Equipment->ReceiveDamage(Damager, Damage, Type);
1753     }
1754 
1755     GetStack()->ReceiveDamage(Damager, Damage, Type);
1756   }
1757 
1758   return Affected;
1759 }
1760 
GetMainArm() const1761 arm* humanoid::GetMainArm() const
1762 {
1763   return GetRightArm() ? GetRightArm() : GetLeftArm();
1764 }
1765 
GetSecondaryArm() const1766 arm* humanoid::GetSecondaryArm() const
1767 {
1768   return GetRightArm() ? GetLeftArm() : 0;
1769 }
1770 
GetEquipmentName(int I) const1771 cchar* humanoid::GetEquipmentName(int I) const // convert to array
1772 {
1773   switch(I)
1774   {
1775    case HELMET_INDEX: return "helmet";
1776    case AMULET_INDEX: return "amulet";
1777    case CLOAK_INDEX: return "cloak";
1778    case BODY_ARMOR_INDEX: return "body armor";
1779    case BELT_INDEX: return "belt";
1780    case RIGHT_WIELDED_INDEX: return "right hand wielded";
1781    case LEFT_WIELDED_INDEX: return "left hand wielded";
1782    case RIGHT_RING_INDEX: return "right ring";
1783    case LEFT_RING_INDEX: return "left ring";
1784    case RIGHT_GAUNTLET_INDEX: return "right gauntlet";
1785    case LEFT_GAUNTLET_INDEX: return "left gauntlet";
1786    case RIGHT_BOOT_INDEX: return "right boot";
1787    case LEFT_BOOT_INDEX: return "left boot";
1788   }
1789 
1790   return "forbidden piece of cloth";
1791 }
1792 
EquipmentSorter(int I) const1793 sorter humanoid::EquipmentSorter(int I) const
1794 {
1795   switch(I)
1796   {
1797    case HELMET_INDEX: return &item::IsHelmet;
1798    case AMULET_INDEX: return &item::IsAmulet;
1799    case CLOAK_INDEX: return &item::IsCloak;
1800    case BODY_ARMOR_INDEX: return &item::IsBodyArmor;
1801    case BELT_INDEX: return &item::IsBelt;
1802    case RIGHT_WIELDED_INDEX:
1803    case LEFT_WIELDED_INDEX: return 0;
1804    case RIGHT_RING_INDEX:
1805    case LEFT_RING_INDEX: return &item::IsRing;
1806    case RIGHT_GAUNTLET_INDEX:
1807    case LEFT_GAUNTLET_INDEX: return &item::IsGauntlet;
1808    case RIGHT_BOOT_INDEX:
1809    case LEFT_BOOT_INDEX: return &item::IsBoot;
1810   }
1811 
1812   return 0;
1813 }
1814 
GetBodyPartOfEquipment(int I) const1815 bodypart* humanoid::GetBodyPartOfEquipment(int I) const
1816 {
1817   switch(I)
1818   {
1819    case HELMET_INDEX:
1820    case AMULET_INDEX:
1821     return GetHead();
1822    case CLOAK_INDEX:
1823    case BODY_ARMOR_INDEX:
1824    case BELT_INDEX:
1825     return GetTorso();
1826    case RIGHT_WIELDED_INDEX:
1827    case RIGHT_RING_INDEX:
1828    case RIGHT_GAUNTLET_INDEX:
1829     return GetRightArm();
1830    case LEFT_WIELDED_INDEX:
1831    case LEFT_RING_INDEX:
1832    case LEFT_GAUNTLET_INDEX:
1833     return GetLeftArm();
1834    case RIGHT_BOOT_INDEX:
1835     return GetRightLeg();
1836    case LEFT_BOOT_INDEX:
1837     return GetLeftLeg();
1838   }
1839 
1840   return 0;
1841 }
1842 
GetEquipment(int I) const1843 item* humanoid::GetEquipment(int I) const
1844 {
1845   switch(I)
1846   {
1847    case HELMET_INDEX: return GetHelmet();
1848    case AMULET_INDEX: return GetAmulet();
1849    case CLOAK_INDEX: return GetCloak();
1850    case BODY_ARMOR_INDEX: return GetBodyArmor();
1851    case BELT_INDEX: return GetBelt();
1852    case RIGHT_WIELDED_INDEX: return GetRightWielded();
1853    case LEFT_WIELDED_INDEX: return GetLeftWielded();
1854    case RIGHT_RING_INDEX: return GetRightRing();
1855    case LEFT_RING_INDEX: return GetLeftRing();
1856    case RIGHT_GAUNTLET_INDEX: return GetRightGauntlet();
1857    case LEFT_GAUNTLET_INDEX: return GetLeftGauntlet();
1858    case RIGHT_BOOT_INDEX: return GetRightBoot();
1859    case LEFT_BOOT_INDEX: return GetLeftBoot();
1860   }
1861 
1862   return 0;
1863 }
1864 
SetEquipment(int I,item * What)1865 void humanoid::SetEquipment(int I, item* What)
1866 {
1867   if(ivanconfig::GetRotateTimesPerSquare() > 0)
1868     if(What)What->ResetFlyingThrownStep();
1869 
1870   switch(I)
1871   {
1872    case HELMET_INDEX: SetHelmet(What); break;
1873    case AMULET_INDEX: SetAmulet(What); break;
1874    case CLOAK_INDEX: SetCloak(What); break;
1875    case BODY_ARMOR_INDEX: SetBodyArmor(What); break;
1876    case BELT_INDEX: SetBelt(What); break;
1877    case RIGHT_WIELDED_INDEX: SetRightWielded(What); break;
1878    case LEFT_WIELDED_INDEX: SetLeftWielded(What); break;
1879    case RIGHT_RING_INDEX: SetRightRing(What); break;
1880    case LEFT_RING_INDEX: SetLeftRing(What); break;
1881    case RIGHT_GAUNTLET_INDEX: SetRightGauntlet(What); break;
1882    case LEFT_GAUNTLET_INDEX: SetLeftGauntlet(What); break;
1883    case RIGHT_BOOT_INDEX: SetRightBoot(What); break;
1884    case LEFT_BOOT_INDEX: SetLeftBoot(What); break;
1885   }
1886 }
1887 
SwitchToCraft(recipedata rpd)1888 truth humanoid::SwitchToCraft(recipedata rpd)
1889 {DBGLN;
1890   craft* Craft = craft::Spawn(this);DBGLN;
1891   DBG4(rpd.GetTool(),rpd.GetTool2(),GetRightArm()?GetRightArm()->IsUsable():0,GetLeftArm()?GetLeftArm()->IsUsable():0);
1892 
1893   bool b1OK=false;
1894   bool b2OK=false;
1895   item* it;
1896   if(rpd.GetTool()){
1897     if(
1898       (GetRightArm() && GetRightArm()->IsUsable() && GetRightWielded() == rpd.GetTool()) ||
1899       (GetLeftArm()  && GetLeftArm()->IsUsable()  && GetLeftWielded()  == rpd.GetTool())
1900     ){
1901       b1OK=true;
1902       Craft->SetMoveCraftTool(false);
1903     }
1904 
1905     if(!b1OK && GetRightArm() && GetRightArm()->IsUsable()){
1906       if((it = GetRightWielded())){
1907         Craft->SetRightBackupID(it->GetID());
1908         it->MoveTo(GetStack());
1909       }
1910 
1911       rpd.GetTool()->RemoveFromSlot();
1912       SetRightWielded(rpd.GetTool());
1913 
1914       b1OK=true;
1915       Craft->SetMoveCraftTool(true);
1916     }
1917 
1918     if(!b1OK && GetLeftArm() && GetLeftArm()->IsUsable()){
1919       if((it = GetLeftWielded())){
1920         Craft->SetLeftBackupID(it->GetID());
1921         it->MoveTo(GetStack());
1922       }
1923 
1924       rpd.GetTool()->RemoveFromSlot();
1925       SetLeftWielded(rpd.GetTool());
1926 
1927       b1OK=true;
1928       Craft->SetMoveCraftTool(true);
1929     }
1930 
1931   }else{
1932     b1OK=true; //can craft somethings w/o tools
1933   }
1934 
1935   //TODO let the GetTool2() be equipped too?
1936 
1937   if(b1OK){
1938     Craft->SetCraftWhat(rpd);DBGLN;
1939     SetAction(Craft);DBGLN;
1940     return true;
1941   }
1942 
1943   ADD_MESSAGE("You have no usable arm.");
1944   rpd.SetAlreadyExplained();
1945   return false;
1946 }
1947 
SwitchToDig(item * DigItem,v2 Square)1948 void humanoid::SwitchToDig(item* DigItem, v2 Square)
1949 {
1950   if(IsPlayer())
1951     ADD_MESSAGE("You start digging.");
1952 
1953   dig* Dig = dig::Spawn(this);
1954 
1955   if(GetRightArm())
1956   {
1957     item* Item = GetRightArm()->GetWielded();
1958 
1959     if(Item && Item != DigItem)
1960     {
1961       Dig->SetRightBackupID(GetRightArm()->GetWielded()->GetID());
1962       GetRightArm()->GetWielded()->MoveTo(GetStack());
1963     }
1964   }
1965 
1966   if(GetLeftArm())
1967   {
1968     item* Item = GetLeftArm()->GetWielded();
1969 
1970     if(Item && Item != DigItem)
1971     {
1972       Dig->SetLeftBackupID(GetLeftArm()->GetWielded()->GetID());
1973       GetLeftArm()->GetWielded()->MoveTo(GetStack());
1974     }
1975   }
1976 
1977   if(GetMainWielded() != DigItem)
1978   {
1979     Dig->SetMoveDigger(true);
1980     DigItem->RemoveFromSlot();
1981 
1982     if(GetMainArm() && GetMainArm()->IsUsable())
1983       GetMainArm()->SetWielded(DigItem);
1984     else
1985       GetSecondaryArm()->SetWielded(DigItem);
1986   }
1987   else
1988     Dig->SetMoveDigger(false);
1989 
1990   Dig->SetSquareDug(Square);
1991   SetAction(Dig);
1992 }
1993 
CheckKick() const1994 truth humanoid::CheckKick() const
1995 {
1996   if(!CanKick())
1997   {
1998     if(IsPlayer())
1999       ADD_MESSAGE("This race can't kick.");
2000 
2001     return false;
2002   }
2003 
2004   if(GetUsableLegs() < 2)
2005   {
2006     if(IsPlayer())
2007       ADD_MESSAGE("How are you going to do that with %s?",
2008                   GetUsableLegs() ? "only one usable leg" : "no usable legs");
2009 
2010     return false;
2011   }
2012   else
2013     return true;
2014 }
2015 
GetUsableLegs() const2016 int humanoid::GetUsableLegs() const
2017 {
2018   int Legs = 0;
2019 
2020   if(RightLegIsUsable())
2021     ++Legs;
2022 
2023   if(LeftLegIsUsable())
2024     ++Legs;
2025 
2026   return Legs;
2027 }
2028 
GetUsableArms() const2029 int humanoid::GetUsableArms() const
2030 {
2031   int Arms = 0;
2032 
2033   if(RightArmIsUsable())
2034     ++Arms;
2035 
2036   if(LeftArmIsUsable())
2037     ++Arms;
2038 
2039   return Arms;
2040 }
2041 
CheckThrow() const2042 truth humanoid::CheckThrow() const
2043 {
2044   if(!character::CheckThrow())
2045     return false;
2046 
2047   if(HasAUsableArm())
2048     return true;
2049   else
2050   {
2051     ADD_MESSAGE("You don't have a usable arm to do that!");
2052     return false;
2053   }
2054 }
2055 
CheckOffer() const2056 truth humanoid::CheckOffer() const
2057 {
2058   if(HasAUsableArm())
2059     return true;
2060   else
2061   {
2062     ADD_MESSAGE("You need a usable arm to offer.");
2063     return false;
2064   }
2065 }
2066 
GetEquipmentPanelPos(int I) const2067 v2 humanoid::GetEquipmentPanelPos(int I) const // convert to array
2068 {
2069   switch(I)
2070   {
2071    case HELMET_INDEX: return v2(34, -22);
2072    case AMULET_INDEX: return v2(14, -22);
2073    case CLOAK_INDEX: return v2(-6, -22);
2074    case BODY_ARMOR_INDEX: return v2(54, -22);
2075    case BELT_INDEX: return v2(24, 70);
2076    case RIGHT_WIELDED_INDEX: return v2(-14, 4);
2077    case LEFT_WIELDED_INDEX: return v2(62, 4);
2078    case RIGHT_RING_INDEX: return v2(-14, 44);
2079    case LEFT_RING_INDEX: return v2(62, 44);
2080    case RIGHT_GAUNTLET_INDEX: return v2(-14, 24);
2081    case LEFT_GAUNTLET_INDEX: return v2(62, 24);
2082    case RIGHT_BOOT_INDEX: return v2(4, 70);
2083    case LEFT_BOOT_INDEX: return v2(44, 70);
2084   }
2085 
2086   return v2(24, 12);
2087 }
2088 
2089 v2 humanoid::SilhouetteWhere=v2(0,0); //zeroed because wont init properly here.. TODO explain why.
2090 v2 humanoid::SilhouetteWhereDefault=v2(0,0); //zeroed because wont init properly here.. TODO explain why.
DrawSilhouette(truth AnimationDraw) const2091 void humanoid::DrawSilhouette(truth AnimationDraw) const
2092 {
2093   int c;
2094   blitdata B1 = { DOUBLE_BUFFER,
2095                   { 0, 0 },
2096                   { 0, 0 },
2097                   { TILE_SIZE, TILE_SIZE },
2098                   { ivanconfig::GetContrastLuminance() },
2099                   TRANSPARENT_COLOR,
2100                   ALLOW_ANIMATE };
2101 
2102   cint Equipments = GetEquipments();
2103 
2104   if(SilhouetteWhereDefault.Is0())SilhouetteWhereDefault={RES.X - SILHOUETTE_SIZE.X - 39, 53};
2105   if(SilhouetteWhere.Is0())SilhouetteWhere=SilhouetteWhereDefault;
2106 
2107   item* eqMHover = NULL;
2108   if(CanUseEquipment())
2109     for(c = 0; c < Equipments; ++c)
2110       if(GetBodyPartOfEquipment(c) && EquipmentIsAllowed(c))
2111       {
2112         v2 Pos = SilhouetteWhereDefault + GetEquipmentPanelPos(c);
2113 
2114         item* Equipment = GetEquipment(c);
2115 
2116         if(Equipment && globalwindowhandler::IsMouseAtRect(Pos,TILE_V2)){
2117           eqMHover = Equipment;
2118           static item* eqMHoverPrevious = NULL;
2119           if(eqMHoverPrevious != eqMHover){ //prevent spam
2120             festring fs;
2121             Equipment->AddInventoryEntry(PLAYER,fs,1,true); //to show AV DAM weight volume
2122             ADD_MESSAGE("Your %s is %s.",GetEquipmentName(c),fs.CStr());
2123             msgsystem::Draw();
2124             eqMHoverPrevious = eqMHover;
2125           }
2126         }
2127 
2128         if(!AnimationDraw || eqMHover!=NULL)
2129           DOUBLE_BUFFER->DrawRectangle(Pos + v2(-1, -1), Pos + TILE_V2, DARK_GRAY);
2130 //            eqMHover==Equipment ? LIGHT_GRAY : DARK_GRAY);
2131 
2132         if(Equipment && (!AnimationDraw || Equipment->IsAnimated() || eqMHover!=NULL))
2133         {
2134           igraph::BlitBackGround(Pos, TILE_V2);
2135           B1.Dest = Pos;
2136 
2137           if(Equipment->AllowAlphaEverywhere())
2138             B1.CustomData |= ALLOW_ALPHA;
2139 
2140           Equipment->Draw(B1);
2141           B1.CustomData &= ~ALLOW_ALPHA;
2142 
2143 //          if(eqMHover==Equipment){
2144 //            v2 v2M = globalwindowhandler::GetMouseLocation();
2145 //            if(ivanconfig::GetSilhouetteScale()>=2)
2146 //              DOUBLE_BUFFER->DrawLine(v2M,v2M+v2(1,1),WHITE,true); //mouse "dot"
2147 //          }
2148         }
2149       }
2150 
2151   if(!AnimationDraw)
2152   {
2153     blitdata B2 = { DOUBLE_BUFFER,
2154                     { 0, 0 },
2155                     { SilhouetteWhere.X + 8, SilhouetteWhere.Y },
2156                     { SILHOUETTE_SIZE.X, SILHOUETTE_SIZE.Y },
2157                     { 0 },
2158                     0,
2159                     0 };
2160 
2161     for(int c = 0; c < BodyParts; ++c)
2162     {
2163       bodypart* BodyPart = GetBodyPart(c);
2164 
2165       if(BodyPart)
2166       {
2167         int Type = BodyPart->IsUsable() ? SILHOUETTE_NORMAL : SILHOUETTE_INTER_LACED;
2168         bitmap* Cache = igraph::GetSilhouetteCache(c, BodyPart->GetConditionColorIndex(), Type);
2169         Cache->NormalMaskedBlit(B2);
2170         BodyPart->DrawScars(B2);
2171       }
2172     }
2173   }
2174 }
2175 
GetGlobalResistance(int Type) const2176 int humanoid::GetGlobalResistance(int Type) const
2177 {
2178   int Resistance = GetResistance(Type);
2179 
2180   if(GetCloak())
2181     Resistance += GetCloak()->GetResistance(Type);
2182 
2183   if(GetRightWielded())
2184   {
2185     if(GetRightWielded()->IsShield(this))
2186       Resistance += GetRightWielded()->GetResistance(Type);
2187   }
2188 
2189   if(GetLeftWielded())
2190   {
2191     if(GetLeftWielded()->IsShield(this))
2192       Resistance += GetLeftWielded()->GetResistance(Type);
2193   }
2194 
2195   if(!(Type & PHYSICAL_DAMAGE))
2196   {
2197     if(GetAmulet())
2198       Resistance += GetAmulet()->GetResistance(Type);
2199 
2200     if(GetRightRing())
2201       Resistance += GetRightRing()->GetResistance(Type);
2202 
2203     if(GetLeftRing())
2204       Resistance += GetLeftRing()->GetResistance(Type);
2205   }
2206 
2207   return Resistance;
2208 }
2209 
TryToRiseFromTheDead()2210 truth humanoid::TryToRiseFromTheDead()
2211 {
2212   int c;
2213 
2214   for(c = 0; c < BodyParts; ++c)
2215     if(!GetBodyPart(c))
2216     {
2217       bodypart* BodyPart = SearchForOriginalBodyPart(c);
2218 
2219       if(BodyPart)
2220       {
2221         BodyPart->RemoveFromSlot();
2222         AttachBodyPart(BodyPart);
2223         BodyPart->SetHP(1);
2224       }
2225     }
2226 
2227   for(c = 0; c < BodyParts; ++c)
2228   {
2229     bodypart* BodyPart = GetBodyPart(c);
2230 
2231     if(BodyPartIsVital(c) && !BodyPart)
2232       if(!HandleNoBodyPart(c))
2233         return false;
2234 
2235     if(BodyPart)
2236     {
2237       if(BodyPart->IsBurning())
2238       {
2239         BodyPart->Extinguish(false);
2240         BodyPart->ResetBurning();
2241         BodyPart->ResetThermalEnergies();
2242         BodyPart->ResetSpoiling();
2243         BodyPart->SignalEmitationDecrease(MakeRGB24(150, 120, 90)); // gum solution
2244       }
2245 
2246       if(BodyPart->CanRegenerate() || BodyPart->GetHP() < 1)
2247         BodyPart->SetHP(1);
2248     }
2249   }
2250 
2251   ResetStates();
2252   return true;
2253 }
2254 
HandleNoBodyPart(int I)2255 truth humanoid::HandleNoBodyPart(int I)
2256 {
2257   switch(I)
2258   {
2259    case HEAD_INDEX:
2260     if(CanBeSeenByPlayer())
2261       ADD_MESSAGE("The headless body of %s vibrates violently.", CHAR_NAME(DEFINITE));
2262 
2263     return false;
2264    case GROIN_INDEX:
2265     if(CanBeSeenByPlayer())
2266       ADD_MESSAGE("The groinless body of %s vibrates violently.", CHAR_NAME(DEFINITE));
2267 
2268     return false;
2269    case TORSO_INDEX:
2270     ABORT("The corpse does not have a torso.");
2271    default:
2272     return true;
2273   }
2274 }
2275 
GetBodyPartBitmapPos(int I,truth) const2276 v2 humanoid::GetBodyPartBitmapPos(int I, truth) const
2277 {
2278   switch(I)
2279   {
2280    case TORSO_INDEX: return GetTorsoBitmapPos();
2281    case HEAD_INDEX: return GetHeadBitmapPos();
2282    case RIGHT_ARM_INDEX: return GetRightArmBitmapPos();
2283    case LEFT_ARM_INDEX: return GetLeftArmBitmapPos();
2284    case GROIN_INDEX: return GetGroinBitmapPos();
2285    case RIGHT_LEG_INDEX: return GetRightLegBitmapPos();
2286    case LEFT_LEG_INDEX: return GetLeftLegBitmapPos();
2287   }
2288 
2289   ABORT("Weird bodypart BitmapPos request for a humanoid!");
2290   return v2();
2291 }
2292 
GetBodyPartColorB(int I,truth) const2293 col16 humanoid::GetBodyPartColorB(int I, truth) const
2294 {
2295   switch(I)
2296   {
2297    case TORSO_INDEX: return GetTorsoMainColor();
2298    case HEAD_INDEX: return GetCapColor();
2299    case RIGHT_ARM_INDEX:
2300    case LEFT_ARM_INDEX: return GetArmMainColor();
2301    case GROIN_INDEX:
2302    case RIGHT_LEG_INDEX:
2303    case LEFT_LEG_INDEX: return GetLegMainColor();
2304   }
2305 
2306   ABORT("Weird bodypart col B request for a humanoid!");
2307   return 0;
2308 }
2309 
GetBodyPartColorC(int I,truth) const2310 col16 humanoid::GetBodyPartColorC(int I, truth) const
2311 {
2312   switch(I)
2313   {
2314    case TORSO_INDEX: return GetBeltColor();
2315    case HEAD_INDEX: return GetHairColor();
2316    case RIGHT_ARM_INDEX:
2317    case LEFT_ARM_INDEX: return GetGauntletColor();
2318    case GROIN_INDEX:
2319    case RIGHT_LEG_INDEX:
2320    case LEFT_LEG_INDEX: return GetBootColor();
2321   }
2322 
2323   ABORT("Weird bodypart col C request for a humanoid!");
2324   return 0;
2325 }
2326 
GetBodyPartColorD(int I,truth) const2327 col16 humanoid::GetBodyPartColorD(int I, truth) const
2328 {
2329   switch(I)
2330   {
2331    case TORSO_INDEX: return GetTorsoSpecialColor();
2332    case HEAD_INDEX: return GetEyeColor();
2333    case RIGHT_ARM_INDEX:
2334    case LEFT_ARM_INDEX: return GetArmSpecialColor();
2335    case GROIN_INDEX:
2336    case RIGHT_LEG_INDEX:
2337    case LEFT_LEG_INDEX: return GetLegSpecialColor();
2338   }
2339 
2340   ABORT("Weird bodypart col D request for a humanoid!");
2341   return 0;
2342 }
2343 
GetBodyPartSparkleFlags(int I) const2344 int humanoid::GetBodyPartSparkleFlags(int I) const
2345 {
2346   truth Sparkling = false;
2347   int SparkleFlags = GetNaturalSparkleFlags() & SKIN_COLOR ? SPARKLING_A : 0;
2348 
2349   switch(I)
2350   {
2351    case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & TORSO_MAIN_COLOR; break;
2352    case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & CAP_COLOR; break;
2353    case RIGHT_ARM_INDEX:
2354    case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & ARM_MAIN_COLOR; break;
2355    case GROIN_INDEX:
2356    case RIGHT_LEG_INDEX:
2357    case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & LEG_MAIN_COLOR; break;
2358   }
2359 
2360   SparkleFlags |= Sparkling ? SPARKLING_B : 0;
2361   Sparkling = false;
2362 
2363   switch(I)
2364   {
2365    case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & BELT_COLOR; break;
2366    case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & HAIR_COLOR; break;
2367    case RIGHT_ARM_INDEX:
2368    case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & GAUNTLET_COLOR; break;
2369    case GROIN_INDEX:
2370    case RIGHT_LEG_INDEX:
2371    case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & BOOT_COLOR; break;
2372   }
2373 
2374   SparkleFlags |= Sparkling ? SPARKLING_C : 0;
2375   Sparkling = false;
2376 
2377   switch(I)
2378   {
2379    case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & TORSO_SPECIAL_COLOR; break;
2380    case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & EYE_COLOR; break;
2381    case RIGHT_ARM_INDEX:
2382    case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & ARM_SPECIAL_COLOR; break;
2383    case GROIN_INDEX:
2384    case RIGHT_LEG_INDEX:
2385    case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & LEG_SPECIAL_COLOR; break;
2386   }
2387 
2388   SparkleFlags |= Sparkling ? SPARKLING_D : 0;
2389   return SparkleFlags;
2390 }
2391 
playerkind()2392 playerkind::playerkind() : SoulID(0), IsBonePlayer(false), IsClone(false)
2393 {
2394 }
2395 
petrus()2396 petrus::petrus() : LastHealed(0)
2397 {
2398   game::SetPetrus(this);
2399 }
2400 
shopkeeper()2401 shopkeeper::shopkeeper()
2402 {
2403   if(!game::IsLoading())
2404     Money = GetMoney() + RAND() % 2001;
2405 }
2406 
Bite(character * Enemy,v2 HitPos,int Direction,truth ForceHit)2407 void humanoid::Bite(character* Enemy, v2 HitPos, int Direction, truth ForceHit)
2408 {
2409   /* This function ought not to be called without a head */
2410 
2411   EditNP(-50);
2412   EditAP(-GetHead()->GetBiteAPCost());
2413   EditExperience(AGILITY, 150, 1 << 9);
2414   EditStamina(GetAdjustedStaminaCost(-1000, GetAttribute(AGILITY)), false);
2415   Enemy->TakeHit(this, 0, GetHead(), HitPos, GetHead()->GetBiteDamage(), GetHead()->GetBiteToHitValue(),
2416                  RAND() % 26 - RAND() % 26, BITE_ATTACK, Direction, !(RAND() % GetCriticalModifier()), ForceHit);
2417 }
2418 
Kick(lsquare * Square,int Direction,truth ForceHit)2419 void humanoid::Kick(lsquare* Square, int Direction, truth ForceHit)
2420 {
2421   leg* KickLeg = RAND_2 ? GetRightLeg() : GetLeftLeg();
2422   item* Boot = KickLeg->GetBoot();
2423   EditNP(-50);
2424   EditAP(-KickLeg->GetKickAPCost());
2425   EditStamina(GetAdjustedStaminaCost(-1000, GetAttribute(LEG_STRENGTH)), false);
2426 
2427   if(Square->BeKicked(this, Boot, KickLeg, KickLeg->GetKickDamage(), KickLeg->GetKickToHitValue(),
2428                       RAND() % 26 - RAND() % 26, Direction, !(RAND() % GetCriticalModifier()), ForceHit))
2429   {
2430     KickLeg->EditExperience(LEG_STRENGTH, 75, 1 << 9);
2431     KickLeg->EditExperience(AGILITY, 75, 1 << 9);
2432   }
2433 }
2434 
2435 /* Returns the average number of APs required to kill Enemy */
2436 
GetTimeToKill(ccharacter * Enemy,truth UseMaxHP) const2437 double humanoid::GetTimeToKill(ccharacter* Enemy, truth UseMaxHP) const
2438 {
2439   double Effectivity = 0;
2440   int AttackStyles = 0;
2441 
2442   if(IsUsingArms())
2443   {
2444     arm* RightArm = GetRightArm();
2445 
2446     if(RightArm)
2447     {
2448       double Damage = RightArm->GetDamage();
2449 
2450       if(Damage)
2451         Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Damage) + 1, RightArm->GetToHitValue(),
2452                                                 AttackIsBlockable(GetRightWielded() ? WEAPON_ATTACK : UNARMED_ATTACK),
2453                                                 UseMaxHP) * RightArm->GetAPCost());
2454     }
2455 
2456     arm* LeftArm = GetLeftArm();
2457 
2458     if(LeftArm)
2459     {
2460       double Damage = LeftArm->GetDamage();
2461 
2462       if(Damage)
2463         Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Damage) + 1, LeftArm->GetToHitValue(),
2464                                                 AttackIsBlockable(GetLeftWielded() ? WEAPON_ATTACK : UNARMED_ATTACK),
2465                                                 UseMaxHP) * LeftArm->GetAPCost());
2466     }
2467 
2468     ++AttackStyles;
2469   }
2470 
2471   if(IsUsingLegs())
2472   {
2473     leg* RightLeg = GetRightLeg();
2474     leg* LeftLeg = GetLeftLeg();
2475     double TimeToDie = Enemy->GetTimeToDie(this, int(RightLeg->GetKickDamage()) + 1, RightLeg->GetKickToHitValue(),
2476                                            AttackIsBlockable(KICK_ATTACK), UseMaxHP) * RightLeg->GetKickAPCost()
2477                        + Enemy->GetTimeToDie(this, int(LeftLeg->GetKickDamage()) + 1, LeftLeg->GetKickToHitValue(),
2478                                              AttackIsBlockable(KICK_ATTACK), UseMaxHP) * LeftLeg->GetKickAPCost();
2479     Effectivity += 2 / TimeToDie;
2480     ++AttackStyles;
2481   }
2482 
2483   if(IsUsingHead())
2484   {
2485     head* Head = GetHead();
2486     Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Head->GetBiteDamage()) + 1, Head->GetBiteToHitValue(),
2487                                             AttackIsBlockable(BITE_ATTACK), UseMaxHP) * Head->GetBiteAPCost());
2488     ++AttackStyles;
2489   }
2490 
2491   if(StateIsActivated(HASTE))
2492     Effectivity *= 2;
2493 
2494   if(StateIsActivated(SLOW))
2495     Effectivity /= 2;
2496 
2497   return AttackStyles ? AttackStyles / Effectivity : 10000000;
2498 }
2499 
GetAttribute(int Identifier,truth AllowBonus) const2500 int humanoid::GetAttribute(int Identifier, truth AllowBonus) const
2501 {
2502   if(Identifier < BASE_ATTRIBUTES)
2503     return character::GetAttribute(Identifier, AllowBonus);
2504   else
2505   {
2506     int Attrib = 0;
2507 
2508     if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY)
2509     {
2510       arm* RightArm = GetRightArm();
2511 
2512       if(RightArm)
2513         Attrib += RightArm->GetAttribute(Identifier, AllowBonus);
2514 
2515       arm* LeftArm = GetLeftArm();
2516 
2517       if(LeftArm)
2518         Attrib += LeftArm->GetAttribute(Identifier, AllowBonus);
2519     }
2520     else if(Identifier == LEG_STRENGTH || Identifier == AGILITY)
2521     {
2522       leg* RightLeg = GetRightLeg();
2523 
2524       if(RightLeg)
2525         Attrib += RightLeg->GetAttribute(Identifier, AllowBonus);
2526 
2527       leg* LeftLeg = GetLeftLeg();
2528 
2529       if(LeftLeg)
2530         Attrib += LeftLeg->GetAttribute(Identifier, AllowBonus);
2531     }
2532     else
2533     {
2534       ABORT("Illegal humanoid attribute %d request!", Identifier);
2535       return 0xEBBA;
2536     }
2537 
2538     return Attrib >> 1;
2539   }
2540 }
2541 
EditAttribute(int Identifier,int Value)2542 truth humanoid::EditAttribute(int Identifier, int Value)
2543 {
2544   if(Identifier < BASE_ATTRIBUTES)
2545     return character::EditAttribute(Identifier, Value);
2546   else if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY)
2547   {
2548     truth Success = false;
2549 
2550     if(GetRightArm() && GetRightArm()->EditAttribute(Identifier, Value))
2551       Success = true;
2552 
2553     if(GetLeftArm() && GetLeftArm()->EditAttribute(Identifier, Value))
2554       Success = true;
2555 
2556     return Success;
2557   }
2558   else if(Identifier == LEG_STRENGTH || Identifier == AGILITY)
2559   {
2560     truth Success = false;
2561 
2562     if(GetRightLeg() && GetRightLeg()->EditAttribute(Identifier, Value))
2563       Success = true;
2564 
2565     if(GetLeftLeg() && GetLeftLeg()->EditAttribute(Identifier, Value))
2566       Success = true;
2567 
2568     return Success;
2569   }
2570   else
2571   {
2572     ABORT("Illegal humanoid attribute %d edit request!", Identifier);
2573     return false;
2574   }
2575 }
2576 
EditExperience(int Identifier,double Value,double Speed)2577 void humanoid::EditExperience(int Identifier, double Value, double Speed)
2578 {
2579   if(!AllowExperience())
2580     return;
2581 
2582   if(Identifier < BASE_ATTRIBUTES)
2583     character::EditExperience(Identifier, Value, Speed);
2584   else if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY)
2585   {
2586     if(GetRightArm())
2587       GetRightArm()->EditExperience(Identifier, Value, Speed);
2588 
2589     if(GetLeftArm())
2590       GetLeftArm()->EditExperience(Identifier, Value, Speed);
2591   }
2592   else if(Identifier == LEG_STRENGTH || Identifier == AGILITY)
2593   {
2594     if(GetRightLeg())
2595       GetRightLeg()->EditExperience(Identifier, Value, Speed);
2596 
2597     if(GetLeftLeg())
2598       GetLeftLeg()->EditExperience(Identifier, Value, Speed);
2599   }
2600   else
2601     ABORT("Illegal humanoid attribute %d experience edit request!", Identifier);
2602 }
2603 
DrawStats(truth AnimationDraw) const2604 int humanoid::DrawStats(truth AnimationDraw) const
2605 {
2606   DrawSilhouette(AnimationDraw);
2607 
2608   if(AnimationDraw)
2609     return 15;
2610 
2611   int PanelPosX = RES.X - 96, PanelPosY = 15;
2612   PrintAttribute("AStr", ARM_STRENGTH, PanelPosX, PanelPosY++);
2613   PrintAttribute("LStr", LEG_STRENGTH, PanelPosX, PanelPosY++);
2614   PrintAttribute("Dex", DEXTERITY, PanelPosX, PanelPosY++);
2615   PrintAttribute("Agi", AGILITY, PanelPosX, PanelPosY++);
2616   return PanelPosY;
2617 }
2618 
GetRandomStepperBodyPart() const2619 int humanoid::GetRandomStepperBodyPart() const
2620 {
2621   int Possible = 0, PossibleArray[3];
2622 
2623   if(GetRightLeg())
2624     PossibleArray[Possible++] = RIGHT_LEG_INDEX;
2625 
2626   if(GetLeftLeg())
2627     PossibleArray[Possible++] = LEFT_LEG_INDEX;
2628 
2629   if(Possible)
2630     return PossibleArray[RAND_N(Possible)];
2631 
2632   if(GetRightArm())
2633     PossibleArray[Possible++] = RIGHT_ARM_INDEX;
2634 
2635   if(GetLeftArm())
2636     PossibleArray[Possible++] = LEFT_ARM_INDEX;
2637 
2638   if(Possible)
2639     return PossibleArray[RAND_N(Possible)];
2640 
2641   if(GetHead())
2642     PossibleArray[Possible++] = HEAD_INDEX;
2643 
2644   if(GetGroin())
2645     PossibleArray[Possible++] = GROIN_INDEX;
2646 
2647   PossibleArray[Possible++] = TORSO_INDEX;
2648   return PossibleArray[RAND_N(Possible)];
2649 }
2650 
CheckForBlock(character * Enemy,item * Weapon,double ToHitValue,int Damage,int Success,int Type)2651 int humanoid::CheckForBlock(character* Enemy, item* Weapon, double ToHitValue, int Damage, int Success, int Type)
2652 {
2653   if(GetAction())
2654     return Damage;
2655 
2656   if(GetRightWielded())
2657     Damage = CheckForBlockWithArm(Enemy, Weapon, GetRightArm(), ToHitValue, Damage, Success, Type);
2658 
2659   if(Damage && GetLeftWielded() && (!Weapon || Weapon->Exists()))
2660     Damage = CheckForBlockWithArm(Enemy, Weapon, GetLeftArm(), ToHitValue, Damage, Success, Type);
2661 
2662   return Damage;
2663 }
2664 
CanWield() const2665 truth humanoid::CanWield() const
2666 {
2667   return CanUseEquipment(RIGHT_WIELDED_INDEX) || CanUseEquipment(LEFT_WIELDED_INDEX);
2668 }
2669 
2670 /* return true if still in balance */
2671 
CheckBalance(double KickDamage)2672 truth humanoid::CheckBalance(double KickDamage)
2673 {
2674   return !CanMove()
2675     || IsStuck()
2676     || !KickDamage
2677     || (GetUsableLegs() != 1
2678         && !IsFlying()
2679         && KickDamage * 5 < RAND() % GetSize());
2680 }
2681 
GetMoveAPRequirement(int Difficulty) const2682 long humanoid::GetMoveAPRequirement(int Difficulty) const
2683 {
2684   if(IsFlying())
2685     return (!StateIsActivated(PANIC) ? 10000000 : 8000000) * Difficulty
2686            / (APBonus(GetAttribute(AGILITY)) * GetMoveEase());
2687 
2688   switch(GetUsableLegs())
2689   {
2690    case 0:
2691     return (!StateIsActivated(PANIC) ? 20000000 : 16000000) * Difficulty
2692            / (APBonus(GetAttribute(AGILITY)) * GetMoveEase());
2693    case 1:
2694     return (!StateIsActivated(PANIC) ? 13333333 : 10666667) * Difficulty
2695            / (APBonus(GetAttribute(AGILITY)) * GetMoveEase());
2696    case 2:
2697     return (!StateIsActivated(PANIC) ? 10000000 : 8000000) * Difficulty
2698            / (APBonus(GetAttribute(AGILITY)) * GetMoveEase());
2699   }
2700 
2701   ABORT("A %d legged humanoid invaded the dungeon!", GetUsableLegs());
2702   return 0;
2703 }
2704 
CreateBodyParts(int SpecialFlags)2705 void hunter::CreateBodyParts(int SpecialFlags)
2706 {
2707   for(int c = 0; c < BodyParts; ++c)
2708     if(c != LEFT_ARM_INDEX)
2709       CreateBodyPart(c, SpecialFlags);
2710     else
2711       SetBodyPart(LEFT_ARM_INDEX, 0);
2712 }
2713 
EquipmentEasilyRecognized(int I) const2714 truth humanoid::EquipmentEasilyRecognized(int I) const
2715 {
2716   if(GetRelation(PLAYER) != HOSTILE)
2717     return true;
2718 
2719   switch(I)
2720   {
2721    case AMULET_INDEX:
2722    case RIGHT_RING_INDEX:
2723    case LEFT_RING_INDEX:
2724    case BELT_INDEX:
2725     return false;
2726   }
2727 
2728   return true;
2729 }
2730 
SignalEquipmentAdd(int EquipmentIndex)2731 void humanoid::SignalEquipmentAdd(int EquipmentIndex)
2732 {
2733   character::SignalEquipmentAdd(EquipmentIndex);
2734 
2735   if(EquipmentIndex == RIGHT_WIELDED_INDEX)
2736     EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, GetRightWielded());
2737   else if(EquipmentIndex == LEFT_WIELDED_INDEX)
2738     EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, GetLeftWielded());
2739 
2740   if(!IsInitializing())
2741     CalculateBattleInfo();
2742 }
2743 
SignalEquipmentRemoval(int EquipmentIndex,citem * Item)2744 void humanoid::SignalEquipmentRemoval(int EquipmentIndex, citem* Item)
2745 {
2746   character::SignalEquipmentRemoval(EquipmentIndex, Item);
2747 
2748   if(EquipmentIndex == RIGHT_WIELDED_INDEX)
2749     EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0);
2750   else if(EquipmentIndex == LEFT_WIELDED_INDEX)
2751     EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0);
2752 
2753   if(!IsInitializing())
2754     CalculateBattleInfo();
2755 }
2756 
SWeaponSkillTick()2757 void humanoid::SWeaponSkillTick()
2758 {
2759   for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end();)
2760   {
2761     if((*i)->Tick() && IsPlayer())
2762     {
2763       item* Item = SearchForItem(*i);
2764 
2765       if(Item)
2766         (*i)->AddLevelDownMessage(Item->CHAR_NAME(UNARTICLED));
2767     }
2768 
2769     if(!(*i)->GetHits() && *i != GetCurrentRightSWeaponSkill() && *i != GetCurrentLeftSWeaponSkill())
2770     {
2771       std::list<sweaponskill*>::iterator Dirt = i++;
2772       SWeaponSkill.erase(Dirt);
2773     }
2774     else
2775       ++i;
2776   }
2777 }
2778 
GetAICommand()2779 void angel::GetAICommand()
2780 {
2781   if((LastHealed || game::GetTick() - LastHealed > 10000) && AttachBodyPartsOfFriendsNear())
2782     return;
2783 
2784   humanoid::GetAICommand();
2785 }
2786 
2787 /* Returns true if the angel finds somebody near to heal else false */
2788 
AttachBodyPartsOfFriendsNear()2789 truth angel::AttachBodyPartsOfFriendsNear()
2790 {
2791   character* HurtOne = 0;
2792   bodypart* SeveredOne = 0;
2793 
2794   for(int d = 0; d < GetNeighbourSquares(); ++d)
2795   {
2796     square* Square = GetNeighbourSquare(d);
2797 
2798     if(Square)
2799     {
2800       character* Char = Square->GetCharacter();
2801 
2802       if(Char && (!HurtOne || Char->IsPlayer()) && GetRelation(Char) == FRIEND && !Char->HasAllBodyParts())
2803       {
2804         bodypart* BodyPart = Char->FindRandomOwnBodyPart(false);
2805 
2806         if(BodyPart)
2807         {
2808           HurtOne = Char;
2809           SeveredOne = BodyPart;
2810         }
2811       }
2812     }
2813   }
2814 
2815   if(HurtOne)
2816   {
2817     if(HurtOne->IsPlayer())
2818       ADD_MESSAGE("%s puts your %s back to its place.",
2819                   CHAR_DESCRIPTION(DEFINITE), SeveredOne->GetBodyPartName().CStr());
2820     else if(CanBeSeenByPlayer())
2821       ADD_MESSAGE("%s helps %s by putting %s %s in its old place.",
2822                   CHAR_DESCRIPTION(DEFINITE), HurtOne->CHAR_DESCRIPTION(DEFINITE),
2823                   HurtOne->GetPossessivePronoun().CStr(), SeveredOne->GetBodyPartName().CStr());
2824 
2825     SeveredOne->SetHP(1);
2826     SeveredOne->RemoveFromSlot();
2827     HurtOne->AttachBodyPart(SeveredOne);
2828     LastHealed = game::GetTick();
2829     DexterityAction(10);
2830     return true;
2831   }
2832   else
2833     return false;
2834 }
2835 
DrawBodyParts(blitdata & BlitData) const2836 void humanoid::DrawBodyParts(blitdata& BlitData) const
2837 {
2838   bitmap* TileBuffer = igraph::GetTileBuffer();
2839   bitmap* RealBitmap = BlitData.Bitmap;
2840   blitdata B = { TileBuffer,
2841                  { BlitData.Dest.X, BlitData.Dest.Y },
2842                  { 0, 0 },
2843                  { TILE_SIZE, TILE_SIZE },
2844                  { 0 },
2845                  TRANSPARENT_COLOR,
2846                  BlitData.CustomData };
2847 
2848   RealBitmap->NormalBlit(B);
2849   TileBuffer->FillPriority(0);
2850   B.Src.X = B.Src.Y = 0;
2851   B.Luminance = BlitData.Luminance;
2852 
2853   for(int c = 0; c < BodyParts; ++c)
2854   {
2855     bodypart* BodyPart = GetBodyPart(DrawOrder[c]);
2856 
2857     if(BodyPart)
2858       BodyPart->Draw(B);
2859   }
2860 
2861   arm* LeftArm = GetLeftArm();
2862 
2863   if(LeftArm)
2864     LeftArm->DrawWielded(B);
2865 
2866   arm* RightArm = GetRightArm();
2867 
2868   if(RightArm)
2869     RightArm->DrawWielded(B);
2870 
2871   TileBuffer->FastBlit(RealBitmap, BlitData.Dest);
2872 }
2873 
GetTorsoMainColor() const2874 col16 angel::GetTorsoMainColor() const
2875 {
2876   return GetMasterGod()->GetColor();
2877 }
2878 
GetArmMainColor() const2879 col16 angel::GetArmMainColor() const
2880 {
2881   return GetMasterGod()->GetColor();
2882 }
2883 
GetTorsoMainColor() const2884 col16 kamikazedwarf::GetTorsoMainColor() const
2885 {
2886   return GetMasterGod()->GetColor();
2887 }
2888 
GetGauntletColor() const2889 col16 kamikazedwarf::GetGauntletColor() const
2890 {
2891   return GetMasterGod()->GetColor();
2892 }
2893 
GetLegMainColor() const2894 col16 kamikazedwarf::GetLegMainColor() const
2895 {
2896   return GetMasterGod()->GetColor();
2897 }
2898 
GetHairColor() const2899 col16 housewife::GetHairColor() const
2900 {
2901   static col16 HouseWifeHairColor[] = { MakeRGB16(48, 40, 8), MakeRGB16(60, 48, 24), MakeRGB16(200, 0, 0) };
2902   return HouseWifeHairColor[RAND() % 3];
2903 }
2904 
GetAttribute(int Identifier,truth AllowBonus) const2905 int angel::GetAttribute(int Identifier, truth AllowBonus) const // temporary until wings are bodyparts
2906 {
2907   if(Identifier == LEG_STRENGTH)
2908     return GetDefaultLegStrength();
2909   else if(Identifier == AGILITY)
2910     return GetDefaultAgility();
2911   else
2912     return humanoid::GetAttribute(Identifier, AllowBonus);
2913 }
2914 
GetAttribute(int Identifier,truth AllowBonus) const2915 int nihil::GetAttribute(int Identifier, truth AllowBonus) const
2916 {
2917   if(Identifier == LEG_STRENGTH)
2918     return GetDefaultLegStrength();
2919   else if(Identifier == AGILITY)
2920     return GetDefaultAgility();
2921   else
2922     return humanoid::GetAttribute(Identifier, AllowBonus);
2923 }
2924 
GetAttribute(int Identifier,truth AllowBonus) const2925 int genie::GetAttribute(int Identifier, truth AllowBonus) const // temporary until someone invents a better way of doing this
2926 {
2927   if(Identifier == LEG_STRENGTH)
2928     return GetDefaultLegStrength();
2929   else if(Identifier == AGILITY)
2930     return GetDefaultAgility();
2931   else
2932     return humanoid::GetAttribute(Identifier, AllowBonus);
2933 }
2934 
CanUseStethoscope(truth PrintReason) const2935 truth humanoid::CanUseStethoscope(truth PrintReason) const
2936 {
2937   if(!GetUsableArms())
2938   {
2939     if(PrintReason)
2940       ADD_MESSAGE("You need a usable arm to use a stethoscope.");
2941 
2942     return false;
2943   }
2944 
2945   if(!GetHead())
2946   {
2947     if(PrintReason)
2948       ADD_MESSAGE("You need a head to use stethoscope.");
2949 
2950     return false;
2951   }
2952 
2953   return true;
2954 }
2955 
IsUsingArms() const2956 truth humanoid::IsUsingArms() const
2957 {
2958   return GetAttackStyle() & USE_ARMS && CanAttackWithAnArm();
2959 }
2960 
IsUsingLegs() const2961 truth humanoid::IsUsingLegs() const
2962 {
2963   return (GetAttackStyle() & USE_LEGS
2964           || (GetAttackStyle() & USE_ARMS && !CanAttackWithAnArm()))
2965     && HasTwoUsableLegs();
2966 }
2967 
IsUsingHead() const2968 truth humanoid::IsUsingHead() const
2969 {
2970   return (GetAttackStyle() & USE_HEAD
2971           || ((GetAttackStyle() & USE_LEGS
2972                || (GetAttackStyle() & USE_ARMS && !CanAttackWithAnArm()))
2973               && !HasTwoUsableLegs()))
2974     && GetHead();
2975 }
2976 
CalculateBattleInfo()2977 void humanoid::CalculateBattleInfo()
2978 {
2979   CalculateDodgeValue();
2980   doforbodyparts()(this, &bodypart::CalculateAttackInfo);
2981 }
2982 
SevereBodyPart(int BodyPartIndex,truth ForceDisappearance,stack * EquipmentDropStack)2983 item* skeleton::SevereBodyPart(int BodyPartIndex, truth ForceDisappearance, stack* EquipmentDropStack)
2984 {
2985   if(BodyPartIndex == RIGHT_ARM_INDEX)
2986     EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0);
2987   else if(BodyPartIndex == LEFT_ARM_INDEX)
2988     EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0);
2989 
2990   item* BodyPart = GetBodyPart(BodyPartIndex);
2991   item* Bone = 0;
2992 
2993   if(!ForceDisappearance)
2994   {
2995     if(BodyPartIndex == HEAD_INDEX)
2996     {
2997       if(GetConfig() == WAR_LORD)
2998         Bone = skullofxinroch::Spawn(0, NO_MATERIALS);
2999       else
3000         Bone = skull::Spawn(0, NO_MATERIALS);
3001     }
3002     else
3003       Bone = bone::Spawn(0, NO_MATERIALS);
3004 
3005     material* OldMaterial = BodyPart->GetMainMaterial();
3006     Bone->InitMaterials(OldMaterial);
3007     BodyPart->DropEquipment(EquipmentDropStack);
3008     BodyPart->RemoveFromSlot();
3009     BodyPart->SetMainMaterial(0, NO_PIC_UPDATE|NO_SIGNALS);
3010   }
3011   else
3012   {
3013     BodyPart->DropEquipment(EquipmentDropStack);
3014     BodyPart->RemoveFromSlot();
3015   }
3016 
3017   BodyPart->SendToHell();
3018   CalculateAttributeBonuses();
3019   CalculateBattleInfo();
3020   SignalPossibleTransparencyChange();
3021   RemoveTraps(BodyPartIndex);
3022   return Bone;
3023 }
3024 
CreateBodyParts(int SpecialFlags)3025 void zombie::CreateBodyParts(int SpecialFlags)
3026 {
3027   bool Anyway = false;
3028   if((GetConfig() == ZOMBIE_OF_KHAZ_ZADM) || !!(SpecialFlags & NO_SEVERED_LIMBS))
3029   {
3030     Anyway = true;
3031   } // Khaz-Zadm needs her hands...
3032 
3033   for(int c = 0; c < BodyParts; ++c)
3034     if(Anyway || BodyPartIsVital(c) || RAND_N(3) || (c == HEAD_INDEX && !RAND_N(3)))
3035     {
3036       bodypart* BodyPart = CreateBodyPart(c, SpecialFlags|NO_PIC_UPDATE);
3037       BodyPart->GetMainMaterial()->SetSpoilCounter(2000 + RAND_N(1000));
3038     }
3039 }
3040 
AddName(festring & String,int Case) const3041 void ghost::AddName(festring& String, int Case) const
3042 {
3043   if(OwnerSoul.IsEmpty() || Case & PLURAL)
3044     character::AddName(String, Case);
3045   else
3046   {
3047     character::AddName(String, (Case|ARTICLE_BIT)&~INDEFINE_BIT);
3048     String << " of " << OwnerSoul;
3049   }
3050 }
3051 
Save(outputfile & SaveFile) const3052 void ghost::Save(outputfile& SaveFile) const
3053 {
3054   humanoid::Save(SaveFile);
3055   SaveFile << OwnerSoul << Active << Description;
3056 }
3057 
Load(inputfile & SaveFile)3058 void ghost::Load(inputfile& SaveFile)
3059 {
3060   humanoid::Load(SaveFile);
3061   SaveFile >> OwnerSoul >> Active >> Description;
3062 }
3063 
Save(outputfile & SaveFile) const3064 void bonesghost::Save(outputfile& SaveFile) const
3065 {
3066   humanoid::Save(SaveFile);
3067   SaveFile << OwnerSoul << Active << Description << EyeColor << HairColor;
3068 }
3069 
Load(inputfile & SaveFile)3070 void bonesghost::Load(inputfile& SaveFile)
3071 {
3072   humanoid::Load(SaveFile);
3073   SaveFile >> OwnerSoul >> Active >> Description >> EyeColor >> HairColor;
3074 }
3075 
RaiseTheDead(character * Summoner)3076 truth ghost::RaiseTheDead(character* Summoner)
3077 {
3078   itemvector ItemVector;
3079   GetStackUnder()->FillItemVector(ItemVector);
3080 
3081   for(uint c = 0; c < ItemVector.size(); ++c)
3082     if(ItemVector[c]->SuckSoul(this, Summoner))
3083       return true;
3084 
3085   if(IsPlayer())
3086     ADD_MESSAGE("You shudder.");
3087   else if(CanBeSeenByPlayer())
3088     ADD_MESSAGE("%s shudders.", CHAR_NAME(DEFINITE));
3089 
3090   return false;
3091 }
3092 
ReceiveBodyPartDamage(character * Damager,int Damage,int Type,int BodyPartIndex,int Direction,truth PenetrateResistance,truth Critical,truth ShowNoDamageMsg,truth CaptureBodyPart)3093 int ghost::ReceiveBodyPartDamage(character* Damager, int Damage, int Type, int BodyPartIndex,
3094                                  int Direction, truth PenetrateResistance, truth Critical,
3095                                  truth ShowNoDamageMsg, truth CaptureBodyPart)
3096 {
3097   if(Type != SOUND)
3098   {
3099     Active = true;
3100     return character::ReceiveBodyPartDamage(Damager, Damage, Type, BodyPartIndex, Direction,
3101                                             PenetrateResistance, Critical, ShowNoDamageMsg, CaptureBodyPart);
3102   }
3103   else
3104     return 0;
3105 }
3106 
GetAICommand()3107 void ghost::GetAICommand()
3108 {
3109   if(Active)
3110     character::GetAICommand();
3111   else
3112   {
3113     if(CheckForEnemies(false, false, false))
3114       return;
3115 
3116     EditAP(-1000);
3117   }
3118 }
3119 
AddSpecialEquipmentInfo(festring & String,int I) const3120 void humanoid::AddSpecialEquipmentInfo(festring& String, int I) const
3121 {
3122   if((I == RIGHT_WIELDED_INDEX && GetRightArm()->TwoHandWieldIsActive()) ||
3123      (I == LEFT_WIELDED_INDEX && GetLeftArm()->TwoHandWieldIsActive()))
3124     String << " (in both hands)";
3125 }
3126 
3127 /* Yes, this is evil. */
3128 
3129 #define INSTANTIATE(name)\
3130 if(DataBase->name.IsValid() && (Item = DataBase->name.Instantiate(SpecialFlags)))\
3131   Set##name(Item);
3132 
CreateInitialEquipment(int SpecialFlags)3133 void humanoid::CreateInitialEquipment(int SpecialFlags)
3134 {
3135   character::CreateInitialEquipment(SpecialFlags);
3136   item* Item;
3137 
3138   INSTANTIATE(Helmet);
3139   INSTANTIATE(Amulet);
3140   INSTANTIATE(Cloak);
3141   INSTANTIATE(BodyArmor);
3142   INSTANTIATE(Belt);
3143   INSTANTIATE(RightWielded);
3144   INSTANTIATE(LeftWielded);
3145   INSTANTIATE(RightRing);
3146   INSTANTIATE(LeftRing);
3147   INSTANTIATE(RightGauntlet);
3148   INSTANTIATE(LeftGauntlet);
3149   INSTANTIATE(RightBoot);
3150   INSTANTIATE(LeftBoot);
3151 
3152   if(CurrentRightSWeaponSkill)
3153     CurrentRightSWeaponSkill->AddHit(GetRightSWeaponSkillHits() * 100);
3154 
3155   if(CurrentLeftSWeaponSkill)
3156     CurrentLeftSWeaponSkill->AddHit(GetLeftSWeaponSkillHits() * 100);
3157 }
3158 
GetBodyPartName(int I,truth Articled) const3159 festring humanoid::GetBodyPartName(int I, truth Articled) const
3160 {
3161   festring Article;
3162 
3163   if(Articled)
3164     Article << 'a';
3165 
3166   switch(I)
3167   {
3168    case HEAD_INDEX: return Article + "head";
3169    case TORSO_INDEX: return Article + "torso";
3170    case RIGHT_ARM_INDEX: return Article + "right arm";
3171    case LEFT_ARM_INDEX: return Article + "left arm";
3172    case GROIN_INDEX: return Article + "groin";
3173    case RIGHT_LEG_INDEX: return Article + "right leg";
3174    case LEFT_LEG_INDEX: return Article + "left leg";
3175   }
3176 
3177   ABORT("Illegal humanoid bodypart name request!");
3178   return "";
3179 }
3180 
CreateBlockPossibilityVector(blockvector & Vector,double ToHitValue) const3181 void humanoid::CreateBlockPossibilityVector(blockvector& Vector, double ToHitValue) const
3182 {
3183   double RightBlockChance = 0;
3184   int RightBlockCapability = 0;
3185   double LeftBlockChance = 0;
3186   int LeftBlockCapability = 0;
3187   arm* RightArm = GetRightArm();
3188   arm* LeftArm = GetLeftArm();
3189 
3190   if(RightArm)
3191   {
3192     RightBlockChance = RightArm->GetBlockChance(ToHitValue);
3193     RightBlockCapability = RightArm->GetBlockCapability();
3194   }
3195 
3196   if(LeftArm)
3197   {
3198     LeftBlockChance = LeftArm->GetBlockChance(ToHitValue);
3199     LeftBlockCapability = LeftArm->GetBlockCapability();
3200   }
3201 
3202   /* Double block */
3203 
3204   if(RightBlockCapability + LeftBlockCapability)
3205     Vector.push_back(std::make_pair(RightBlockChance * LeftBlockChance, RightBlockCapability + LeftBlockCapability));
3206 
3207   /* Right block */
3208 
3209   if(RightBlockCapability)
3210     Vector.push_back(std::make_pair(RightBlockChance * (1 - LeftBlockChance), RightBlockCapability));
3211 
3212   /* Left block */
3213 
3214   if(LeftBlockCapability)
3215     Vector.push_back(std::make_pair(LeftBlockChance * (1 - RightBlockChance), LeftBlockCapability));
3216 }
3217 
SevereBodyPart(int BodyPartIndex,truth ForceDisappearance,stack * EquipmentDropStack)3218 item* humanoid::SevereBodyPart(int BodyPartIndex, truth ForceDisappearance, stack* EquipmentDropStack)
3219 {
3220   if(BodyPartIndex == RIGHT_ARM_INDEX)
3221     EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0);
3222   else if(BodyPartIndex == LEFT_ARM_INDEX)
3223     EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0);
3224 
3225   return character::SevereBodyPart(BodyPartIndex, ForceDisappearance, EquipmentDropStack);
3226 }
3227 
humanoid(const humanoid & Humanoid)3228 humanoid::humanoid(const humanoid& Humanoid)
3229 : mybase(Humanoid), CurrentRightSWeaponSkill(0), CurrentLeftSWeaponSkill(0)
3230 {
3231   SWeaponSkill.resize(Humanoid.SWeaponSkill.size());
3232   std::list<sweaponskill*>::const_iterator i2 = Humanoid.SWeaponSkill.begin();
3233 
3234   for(sweaponskill*& p : SWeaponSkill)
3235     p = new sweaponskill(**i2++);
3236 }
3237 
GetDeathMessage() const3238 cfestring& humanoid::GetDeathMessage() const
3239 {
3240   static festring HeadlessDeathMsg = CONST_S("@Dd dies without a sound.");
3241 
3242   if(GetHead() || character::GetDeathMessage() != "@Dd dies screaming.")
3243     return character::GetDeathMessage();
3244   else
3245     return HeadlessDeathMsg;
3246 }
3247 
GetSWeaponSkillLevel(citem * Item) const3248 int humanoid::GetSWeaponSkillLevel(citem* Item) const
3249 {
3250   for(sweaponskill* p : SWeaponSkill)
3251     if(p->IsSkillOf(Item))
3252       return p->GetLevel();
3253 
3254   for(idholder* I = Item->GetCloneMotherID(); I; I = I->Next)
3255     for(sweaponskill* p : SWeaponSkill)
3256       if(p->IsSkillOfCloneMother(Item, I->ID))
3257         return p->GetLevel();
3258 
3259   return 0;
3260 }
3261 
UseMaterialAttributes() const3262 truth humanoid::UseMaterialAttributes() const
3263 {
3264   return combinebodypartpredicates()(this, &bodypart::UseMaterialAttributes, 0);
3265 }
3266 
GetBaseEmitation() const3267 col24 angel::GetBaseEmitation() const
3268 {
3269   switch(GetMasterGod()->GetBasicAlignment())
3270   {
3271    case GOOD: return MakeRGB24(150, 150, 150);
3272    case NEUTRAL: return MakeRGB24(120, 120, 150);
3273    case EVIL: return MakeRGB24(150, 110, 110);
3274   }
3275 
3276   return 0;
3277 }
3278 
BeTalkedTo()3279 void bananagrower::BeTalkedTo()
3280 {
3281   if(!GetPos().IsAdjacent(PLAYER->GetPos()))
3282     return;
3283 
3284   static long Said;
3285 
3286   if(GetRelation(PLAYER) == HOSTILE)
3287     ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]);
3288   else if(!game::TweraifIsFree())
3289   {
3290     if(GetRelation(PLAYER) != HOSTILE
3291        && Profession.Find("president", 0) != festring::NPos && !(RAND() % 7))
3292       ADD_MESSAGE("\"I'm glad Petrus spared my life even though I was the president.\"");
3293 
3294     ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 6)]);
3295   }
3296   else
3297     ProcessAndAddMessage(GetFriendlyReplies()[6 + RandomizeReply(Said, 3)]);
3298 }
3299 
RandomizeProfession()3300 void bananagrower::RandomizeProfession()
3301 {
3302   switch(RAND_N(12))
3303   {
3304    case 0:
3305     Profession = CONST_S("the president of Tweraif");
3306     break;
3307    case 1:
3308     Profession = CONST_S("a diplomat");
3309     break;
3310    case 2:
3311     Profession = CONST_S("a teacher");
3312     break;
3313    case 3:
3314     Profession = CONST_S("a philosopher");
3315     break;
3316    case 4:
3317     Profession = CONST_S("a journalist");
3318     break;
3319    case 5:
3320     Profession = CONST_S("an alchemist");
3321     break;
3322    case 6:
3323     Profession = CONST_S("a renowned mathematician");
3324     break;
3325    case 7:
3326     Profession = CONST_S("a priest of Silva");
3327     break;
3328    case 8:
3329    case 9:
3330    case 10:
3331    case 11:
3332     Profession = CONST_S("a professor of ");
3333     AddRandomScienceName(Profession);
3334     break;
3335   }
3336 }
3337 
PostConstruct()3338 void bananagrower::PostConstruct()
3339 {
3340   Stamina = MaxStamina / 5;
3341   RandomizeProfession();
3342   HasDroppedBananas = FeedingSumo = false;
3343 }
3344 
Save(outputfile & SaveFile) const3345 void bananagrower::Save(outputfile& SaveFile) const
3346 {
3347   humanoid::Save(SaveFile);
3348   SaveFile << Profession << HasDroppedBananas << FeedingSumo;
3349 }
3350 
Load(inputfile & SaveFile)3351 void bananagrower::Load(inputfile& SaveFile)
3352 {
3353   humanoid::Load(SaveFile);
3354   SaveFile >> Profession >> HasDroppedBananas >> FeedingSumo;
3355 }
3356 
BeTalkedTo()3357 void smith::BeTalkedTo()
3358 {
3359   if(!GetPos().IsAdjacent(PLAYER->GetPos()))
3360     return;
3361 
3362   if(GetRelation(PLAYER) == HOSTILE)
3363   {
3364     ADD_MESSAGE("\"You talkin' to me? You talkin' to me? You talkin' to me? Then who "
3365                 "the hell else are you talkin' to? You talkin' to me? Well I'm the "
3366                 "only one here. Who do you think you're talking to? Oh yeah? Huh? Ok.\"");
3367     return;
3368   }
3369 
3370   if(!GetMainWielded() || !GetMainWielded()->CanBeUsedBySmith())
3371   {
3372     ADD_MESSAGE("\"Sorry, I need an intact hammer to practise the art of smithing.\"");
3373     return;
3374   }
3375 
3376   if(PLAYER->PossessesItem(&item::IsFixableBySmith))
3377   {
3378     item* Item = PLAYER->SelectFromPossessions(CONST_S("\"What do you want me to fix?\""), &item::IsFixableBySmith);
3379 
3380     if(!Item)
3381       return;
3382 
3383     if(!(Item->GetMainMaterial()->GetCategoryFlags() & IS_METAL))
3384     {
3385       ADD_MESSAGE("\"I only fix items made of metal.\"");
3386       return;
3387     }
3388 
3389     /** update messages */
3390 
3391     long FixPrice = Item->GetFixPrice();
3392 
3393     if(PLAYER->GetMoney() < FixPrice)
3394     {
3395       ADD_MESSAGE("\"Getting that fixed costs you %ld gold pieces. Get the money and we'll talk.\"", FixPrice);
3396       return;
3397     }
3398 
3399     ADD_MESSAGE("\"I can fix your %s, but it'll cost you %ld gold pieces.\"", Item->CHAR_NAME(UNARTICLED), FixPrice);
3400 
3401     if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]")))
3402     {
3403       Item->RemoveRust();
3404       Item->Fix();
3405       PLAYER->EditMoney(-FixPrice);
3406       ADD_MESSAGE("%s fixes %s in no time.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(DEFINITE));
3407     }
3408   }
3409   else
3410     ADD_MESSAGE("\"Come back when you have some weapons or armor I can fix.\"");
3411 }
3412 
CalculateDodgeValue()3413 void humanoid::CalculateDodgeValue()
3414 {
3415   DodgeValue = 0.05 * GetMoveEase() * GetAttribute(AGILITY) / sqrt(GetSize());
3416 
3417   if(IsFlying())
3418     DodgeValue *= 2;
3419   else
3420   {
3421     if(!HasAUsableLeg())
3422       DodgeValue *= 0.50;
3423     if(!HasTwoUsableLegs())
3424       DodgeValue *= 0.75;
3425   }
3426 
3427   if(DodgeValue < 1)
3428     DodgeValue = 1;
3429 }
3430 
CheckZap()3431 truth humanoid::CheckZap()
3432 {
3433   if(!GetUsableArms())
3434   {
3435     ADD_MESSAGE("You need at least one usable arm to zap.");
3436     return false;
3437   }
3438   else
3439     return character::CheckZap();
3440 }
3441 
GetAICommand()3442 void bananagrower::GetAICommand()
3443 {
3444   if(game::TweraifIsFree() ||
3445      (GetDungeon()->GetIndex() != NEW_ATTNAM)
3446    ) // Behave normally outside of New Attnam.
3447   {
3448     humanoid::GetAICommand();
3449     return;
3450   }
3451 
3452   if(CheckForEnemies(false, false, true, true))
3453     return;
3454 
3455   if(!IsEnabled())
3456     return;
3457 
3458   cv2 BananaTarget = FeedingSumo ? SUMO_ROOM_POS + v2(1, 2) : v2(45, 45);
3459 
3460   if(GetPos() == BananaTarget)
3461   {
3462     itemvector ItemVector;
3463     GetStack()->FillItemVector(ItemVector);
3464     int BananasDropped = 0;
3465     uint c;
3466 
3467     for(c = 0; c < ItemVector.size(); ++c)
3468       if(ItemVector[c]->IsBanana())
3469       {
3470         ItemVector[c]->MoveTo(GetStackUnder());
3471         ++BananasDropped;
3472       }
3473 
3474     if(BananasDropped)
3475     {
3476       if(CanBeSeenByPlayer())
3477         ADD_MESSAGE("%s drops %s.", CHAR_NAME(DEFINITE), BananasDropped == 1 ? "a banana" : "some bananas");
3478 
3479       return;
3480     }
3481 
3482     ItemVector.clear();
3483     GetStackUnder()->FillItemVector(ItemVector);
3484     int PeelsPickedUp = 0;
3485 
3486     for(c = 0; c < ItemVector.size(); ++c)
3487       if(ItemVector[c]->IsBananaPeel())
3488       {
3489         ItemVector[c]->MoveTo(GetStack());
3490         ++PeelsPickedUp;
3491       }
3492 
3493     if(PeelsPickedUp)
3494     {
3495       if(CanBeSeenByPlayer())
3496         ADD_MESSAGE("%s picks up %s.", CHAR_NAME(DEFINITE),
3497                     PeelsPickedUp == 1 ? "a banana peel" : "some banana peels");
3498 
3499       return;
3500     }
3501 
3502     HasDroppedBananas = true;
3503   }
3504 
3505   if(!HasDroppedBananas)
3506   {
3507     SetGoingTo(BananaTarget);
3508 
3509     if(MoveTowardsTarget(true))
3510       return;
3511   }
3512   else if(GetPos().X == 54)
3513   {
3514     if(CanBeSeenByPlayer())
3515       ADD_MESSAGE("%s leaves the town to gather more bananas.", CHAR_NAME(DEFINITE));
3516 
3517     GetStack()->Clean();
3518     character* Sumo = game::GetSumo();
3519     FeedingSumo = Sumo && Sumo->GetNP() < (SATIATED_LEVEL + BLOATED_LEVEL) >> 1 && !(RAND() % 15);
3520     int Bananas = FeedingSumo ? 3 : 10;
3521 
3522     for(int c = 0; c < Bananas; ++c)
3523       GetStack()->AddItem(banana::Spawn());
3524 
3525     v2 Where = GetLevel()->GetNearestFreeSquare(this, v2(0, 45));
3526 
3527     if(Where == ERROR_V2)
3528       Where = GetLevel()->GetRandomSquare(this, NOT_IN_ROOM); // this is odd but at least it doesn't crash
3529 
3530     Move(Where, true);
3531     RandomizeProfession();
3532     RestoreBodyParts();
3533     RestoreHP();
3534     Stamina = MaxStamina / 5;
3535     ResetStates();
3536     TemporaryState = 0;
3537 
3538     if(CanBeSeenByPlayer())
3539       ADD_MESSAGE("%s enters the town.", CHAR_NAME(INDEFINITE));
3540 
3541     HasDroppedBananas = false;
3542   }
3543   else
3544   {
3545     SetGoingTo(v2(54, 45));
3546 
3547     if(MoveTowardsTarget(true))
3548       return;
3549   }
3550 
3551   EditAP(-1000);
3552 }
3553 
CheckTalk()3554 truth humanoid::CheckTalk()
3555 {
3556   if(!character::CheckTalk())
3557     return false;
3558 
3559   if(!GetHead())
3560   {
3561     ADD_MESSAGE("You need a head to talk.");
3562     return false;
3563   }
3564 
3565   return true;
3566 }
3567 
CanCreateBodyPart(int I) const3568 truth angel::CanCreateBodyPart(int I) const
3569 {
3570   return I == TORSO_INDEX || I == HEAD_INDEX || I == RIGHT_ARM_INDEX || I == LEFT_ARM_INDEX;
3571 }
3572 
CanCreateBodyPart(int I) const3573 truth nihil::CanCreateBodyPart(int I) const
3574 {
3575   return I == TORSO_INDEX || I == HEAD_INDEX || I == RIGHT_ARM_INDEX || I == LEFT_ARM_INDEX;
3576 }
3577 
CanCreateBodyPart(int I) const3578 truth genie::CanCreateBodyPart(int I) const
3579 {
3580   return I == TORSO_INDEX || I == HEAD_INDEX || I == RIGHT_ARM_INDEX || I == LEFT_ARM_INDEX;
3581 }
3582 
HandleCharacterBlockingTheWay(character * Char,v2 Pos,int Dir)3583 truth bananagrower::HandleCharacterBlockingTheWay(character* Char, v2 Pos, int Dir)
3584 {
3585   return Char->GetPos() == v2(45, 45) && (Displace(Char, true) || Hit(Char, Pos, Dir));
3586 }
3587 
ProcessMessage(festring & Msg) const3588 festring& bananagrower::ProcessMessage(festring& Msg) const
3589 {
3590   character::ProcessMessage(Msg);
3591   SEARCH_N_REPLACE(Msg, "@pd", GetProfession());
3592   SEARCH_N_REPLACE(Msg, "@Pd", GetProfession().CapitalizeCopy());
3593   return Msg;
3594 }
3595 
CreateBodyParts(int SpecialFlags)3596 void elder::CreateBodyParts(int SpecialFlags)
3597 {
3598   for(int c = 0; c < BodyParts; ++c)
3599     if(c != LEFT_LEG_INDEX)
3600       CreateBodyPart(c, SpecialFlags);
3601     else
3602       SetBodyPart(LEFT_LEG_INDEX, 0);
3603 }
3604 
3605 /*void encourager::GetAICommand()
3606 {
3607   if(CheckForEnemies(true, true, true))
3608     return;
3609 
3610   if(CheckForUsefulItemsOnGround())
3611     return;
3612 
3613   if(CheckForDoors())
3614     return;
3615 
3616   if(game::GetTick() - LastHit > 200)
3617   {
3618     static int NotDiagonal[] = { 1, 3, 4, 6 };
3619 
3620     for(int d = 0; d < 4; ++d)
3621     {
3622       square* Square = GetNeighbourSquare(NotDiagonal[d]);
3623 
3624       if(Square)
3625       {
3626         character* Char = Square->GetCharacter();
3627 
3628         if(Char && Char->IsBananaGrower() && Hit(Char, Square->GetPos(), NotDiagonal[d], true))
3629         {
3630           LastHit = game::GetTick();
3631           TerminateGoingTo();
3632           return;
3633         }
3634       }
3635     }
3636   }
3637 
3638   if(MoveTowardsHomePos())
3639     return;
3640 
3641   EditAP(-1000);
3642 }*/
3643 
3644 /*void encourager::Save(outputfile& SaveFile) const
3645 {
3646   humanoid::Save(SaveFile);
3647   SaveFile << LastHit;
3648 }
3649 
3650 void encourager::Load(inputfile& SaveFile)
3651 {
3652   humanoid::Load(SaveFile);
3653   SaveFile >> LastHit;
3654 }*/
3655 
GetBodyPartVolume(int I) const3656 long skeleton::GetBodyPartVolume(int I) const
3657 {
3658   switch(I)
3659   {
3660    case HEAD_INDEX: return 600;
3661    case TORSO_INDEX: return (GetTotalVolume() - 600) * 13 / 30;
3662    case RIGHT_ARM_INDEX:
3663    case LEFT_ARM_INDEX: return (GetTotalVolume() - 600) / 10;
3664    case GROIN_INDEX: return (GetTotalVolume() - 600) / 10;
3665    case RIGHT_LEG_INDEX:
3666    case LEFT_LEG_INDEX: return ((GetTotalVolume() - 600) << 1) / 15;
3667   }
3668 
3669   ABORT("Illegal humanoid bodypart volume request!");
3670   return 0;
3671 }
3672 
AutoPlayAIequip()3673 truth humanoid::AutoPlayAIequip()
3674 {
3675   item* iL = GetEquipment(LEFT_WIELDED_INDEX);
3676   item* iR = GetEquipment(RIGHT_WIELDED_INDEX);
3677 
3678   //every X turns remove all equipments
3679   bool bTryWieldNow=false;
3680   static int iLastReEquipAllTurn=-1;
3681   if(game::GetTurn()>(iLastReEquipAllTurn+150)){ DBG2(game::GetTurn(),iLastReEquipAllTurn);
3682     iLastReEquipAllTurn=game::GetTurn();
3683     for(int i=0;i<MAX_EQUIPMENT_SLOTS;i++){
3684       item* eq = GetEquipment(i);
3685       if(eq){eq->MoveTo(GetStack());SetEquipment(i,NULL);} //eq is moved to end of stack!
3686       if(iL==eq)iL=NULL;
3687       if(iR==eq)iR=NULL;
3688     }
3689 //        if(iL!=NULL){iL->MoveTo(GetStack());iL=NULL;SetEquipment(LEFT_WIELDED_INDEX ,NULL);DBGLN;}
3690 //        if(iR!=NULL){iR->MoveTo(GetStack());iR=NULL;SetEquipment(RIGHT_WIELDED_INDEX,NULL);DBGLN;}
3691     bTryWieldNow=true;
3692   }
3693 
3694   //wield some weapon from the inventory as the NPC AI is not working for the player TODO why?
3695   //every X turns try to wield
3696   static int iLastTryToWieldTurn=-1;
3697   if(bTryWieldNow || game::GetTurn()>(iLastTryToWieldTurn+10)){ DBG2(game::GetTurn(),iLastTryToWieldTurn);
3698     iLastTryToWieldTurn=game::GetTurn();
3699     bool bDoneLR=false;
3700     bool bL2H = iL && iL->IsTwoHanded();
3701     bool bR2H = iR && iR->IsTwoHanded();
3702 
3703     //2handed
3704     static int iTryWieldWhat=0; iTryWieldWhat++; DBG1(iTryWieldWhat);
3705     if(iTryWieldWhat%2==0){ //will try 2handed first, alternating. If player has only 2handeds, the 1handeds will not be wielded and it will use punches, what is good too for tests.
3706       if( !bDoneLR &&
3707           iL==NULL && GetBodyPartOfEquipment(LEFT_WIELDED_INDEX )!=NULL &&
3708           iR==NULL && GetBodyPartOfEquipment(RIGHT_WIELDED_INDEX)!=NULL
3709       ){
3710         static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW);
3711         for(uint c = 0; c < vitEqW.size(); ++c){
3712           if(vitEqW[c]->IsWeapon(this) && vitEqW[c]->IsTwoHanded()){  DBG1(vitEqW[c]->GetNameSingular().CStr());
3713             vitEqW[c]->RemoveFromSlot();
3714             SetEquipment(clock()%2==0 ? LEFT_WIELDED_INDEX : RIGHT_WIELDED_INDEX, vitEqW[c]); //DBG3("Wield",iEqIndex,vitEqW[c]->GetName(DEFINITE).CStr());
3715             bDoneLR=true;
3716             break;
3717           }
3718         }
3719       }
3720     }
3721 
3722     //dual 1handed (if not 2hd already)
3723     if(!bDoneLR){
3724       for(int i=0;i<2;i++){
3725         int iChk=-1;
3726         if(i==0)iChk=LEFT_WIELDED_INDEX;
3727         if(i==1)iChk=RIGHT_WIELDED_INDEX;
3728 
3729         if(
3730             !bDoneLR &&
3731             (
3732               (iChk==LEFT_WIELDED_INDEX  && iL==NULL && GetBodyPartOfEquipment(LEFT_WIELDED_INDEX ) && !bR2H)
3733               ||
3734               (iChk==RIGHT_WIELDED_INDEX && iR==NULL && GetBodyPartOfEquipment(RIGHT_WIELDED_INDEX) && !bL2H)
3735             )
3736         ){
3737           static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW);
3738           for(uint c = 0; c < vitEqW.size(); ++c){
3739             if(
3740                 (vitEqW[c]->IsWeapon(this) && !vitEqW[c]->IsTwoHanded())
3741                 ||
3742                 vitEqW[c]->IsShield(this)
3743             ){ DBG1(vitEqW[c]->GetNameSingular().CStr());
3744               vitEqW[c]->RemoveFromSlot();
3745               SetEquipment(iChk, vitEqW[c]);
3746               bDoneLR=true;
3747               break;
3748             }
3749           }
3750         }
3751       }
3752     }
3753 
3754   }
3755 
3756   //every X turns try to use stuff from inv
3757   static int iLastTryToUseInvTurn=-1;
3758   if(game::GetTurn()>(iLastTryToUseInvTurn+5)){ DBG2(game::GetTurn(),iLastTryToUseInvTurn);
3759     iLastTryToUseInvTurn=game::GetTurn();
3760 
3761     //////////////////////////////// consume food/drink
3762     { //TODO let this happen for non-human too?
3763       static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW);DBGLN;
3764       for(uint c = 0; c < vitEqW.size(); ++c){DBGLN;
3765         if(clock()%3!=0 && GetHungerState() >= BLOATED)break;DBGLN; //randomly let it vomit and activate all related flows *eew* xD
3766 
3767         //if(TryToConsume(vitEqW[c]))
3768         material* ConsumeMaterial = vitEqW[c]->GetConsumeMaterial(this);
3769         if(
3770           ConsumeMaterial!=NULL &&
3771           vitEqW[c]->IsConsumable() &&
3772           ConsumeItem(vitEqW[c], vitEqW[c]->GetConsumeMaterial(this)->GetConsumeVerb())
3773         ){
3774           DBG2("AutoPlayConsumed",vitEqW[c]->GetNameSingular().CStr());
3775           return true;
3776         }DBGLN;
3777       }
3778     }
3779 
3780     //////////////////////////////// equip
3781     {DBGLN;
3782       static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW);DBGLN;
3783       for(uint c = 0; c < vitEqW.size(); ++c){DBGLN;
3784         if(TryToEquip(vitEqW[c],true)){ DBG1(vitEqW[c]->GetNameSingular().CStr());
3785           return true;
3786         }else{DBGLN;
3787           vitEqW[c]->MoveTo(GetStack()); //was dropped, get back, will be in the end of the stack! :)
3788         }
3789       }
3790     }
3791 
3792     //////////////////////////////// zap
3793     static int iLastZapTurn=-1;
3794     if(game::GetTurn()>(iLastZapTurn+100)){ DBG2(game::GetTurn(),iLastZapTurn); //every X turns try to use stuff from inv
3795       iLastZapTurn=game::GetTurn();
3796 
3797       int iDir=clock()%(8+1); // index 8 is the macro YOURSELF already... if(iDir==8)iDir=YOURSELF;
3798       static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW);
3799       for(uint c = 0; c < vitEqW.size(); ++c){
3800         if(!vitEqW[c]->IsZappable(this))continue;
3801 
3802         if(vitEqW[c]->Zap(this, GetPos(), iDir)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs
3803           return true;
3804         }
3805 
3806         if(vitEqW[c]->Apply(this)){ DBG1(vitEqW[c]->GetNameSingular().CStr());
3807           return true;
3808         }
3809       }
3810     }
3811 
3812     //////////////////////////////// read book
3813     static int iLastReadTurn=-1;
3814     if(game::GetTurn()>(iLastReadTurn+50)){ DBG2(game::GetTurn(),iLastReadTurn); //every X turns try to use stuff from inv
3815       iLastReadTurn=game::GetTurn();
3816 
3817       static itemvector vitEqW;vitEqW.clear();GetStack()->FillItemVector(vitEqW);
3818       for(uint c = 0; c < vitEqW.size(); ++c){
3819         if(!vitEqW[c]->IsReadable(this))continue;
3820         static holybook* hb;hb = dynamic_cast<holybook*>(vitEqW[c]);
3821         if(hb==NULL)continue;
3822 
3823         if(vitEqW[c]->Read(this)){ DBG1(vitEqW[c]->GetNameSingular().CStr()); //TODO try to aim at NPCs
3824           return true;
3825         }
3826       }
3827     }
3828   }
3829 
3830   return false;
3831 }
3832 
CheckIfEquipmentIsNotUsable(int I) const3833 truth humanoid::CheckIfEquipmentIsNotUsable(int I) const
3834 {
3835   return (I == RIGHT_WIELDED_INDEX && GetRightArm()->CheckIfWeaponTooHeavy("this item"))
3836       || (I == LEFT_WIELDED_INDEX && GetLeftArm()->CheckIfWeaponTooHeavy("this item"))
3837       || (I == RIGHT_WIELDED_INDEX && GetLeftWielded() && GetLeftWielded()->IsTwoHanded()
3838           && GetLeftArm()->CheckIfWeaponTooHeavy(festring(GetPossessivePronoun() + " other wielded item").CStr()))
3839       || (I == LEFT_WIELDED_INDEX && GetRightWielded() && GetRightWielded()->IsTwoHanded()
3840           && GetRightArm()->CheckIfWeaponTooHeavy(festring(GetPossessivePronoun() + " other wielded item").CStr()));
3841 }
3842 
TakeHit(character * Enemy,item * Weapon,bodypart * EnemyBodyPart,v2 HitPos,double Damage,double ToHitValue,int Success,int Type,int Direction,truth Critical,truth ForceHit)3843 int mistress::TakeHit(character* Enemy, item* Weapon, bodypart* EnemyBodyPart, v2 HitPos, double Damage,
3844                       double ToHitValue, int Success, int Type, int Direction, truth Critical, truth ForceHit)
3845 {
3846   int Return = humanoid::TakeHit(Enemy, Weapon, EnemyBodyPart, HitPos, Damage,
3847                                  ToHitValue, Success, Type, Direction, Critical, ForceHit);
3848 
3849   if(Return == HAS_HIT && Critical)
3850   {
3851     if(IsPlayer())
3852       ADD_MESSAGE("Aahhh. The pain feels unbelievably good.");
3853     else if(CanBeSeenByPlayer())
3854       ADD_MESSAGE("%s screams: \"Oh the delightful pain!\"", CHAR_NAME(DEFINITE));
3855     else
3856       ADD_MESSAGE("You hear someone screaming: \"Oh the delightful pain!\"");
3857   }
3858 
3859   return Return;
3860 }
3861 
SpecialEnemySightedReaction(character * Char)3862 truth petrusswife::SpecialEnemySightedReaction(character* Char)
3863 {
3864   item* Weapon = Char->GetMainWielded();
3865 
3866   if(!(GetConfig() == 5) && Weapon && Weapon->IsWeapon(Char) && !(RAND() % 20))
3867     ADD_MESSAGE("%s screams: \"Oh my Frog, %s's got %s %s!\"",
3868                 CHAR_DESCRIPTION(DEFINITE), Char->CHAR_PERSONAL_PRONOUN_THIRD_PERSON_VIEW,
3869                 Weapon->GetArticle(), Weapon->GetNameSingular().CStr());
3870 
3871   return false;
3872 }
3873 
SpecialEnemySightedReaction(character * Char)3874 truth housewife::SpecialEnemySightedReaction(character* Char)
3875 {
3876   item* Weapon = Char->GetMainWielded();
3877 
3878   if(Weapon && Weapon->IsWeapon(Char) && !(RAND() % 5))
3879     ADD_MESSAGE("%s screams: \"Oh my Frog, %s's got %s %s!\"",
3880                 CHAR_DESCRIPTION(DEFINITE), Char->CHAR_PERSONAL_PRONOUN_THIRD_PERSON_VIEW,
3881                 Weapon->GetArticle(), Weapon->GetNameSingular().CStr());
3882 
3883   return false;
3884 }
3885 
CreateInitialEquipment(int SpecialFlags)3886 void housewife::CreateInitialEquipment(int SpecialFlags)
3887 {
3888   humanoid::CreateInitialEquipment(SpecialFlags);
3889   meleeweapon* Weapon;
3890 
3891   if(GetConfig() == CULTIST)
3892   {
3893     Weapon = meleeweapon::Spawn(SCYTHE);
3894     SetRightWielded(Weapon);
3895     GetCWeaponSkill(POLE_ARMS)->AddHit(100);
3896     GetCurrentRightSWeaponSkill()->AddHit(50);
3897     return;
3898   }
3899   else switch(RAND() % 4)
3900   {
3901    case 0:
3902     Weapon = meleeweapon::Spawn(ROLLING_PIN);
3903     SetRightWielded(Weapon);
3904     GetCWeaponSkill(BLUNT_WEAPONS)->AddHit(50);
3905     GetCurrentRightSWeaponSkill()->AddHit(10);
3906     break;
3907    case 1:
3908     Weapon = meleeweapon::Spawn(FRYING_PAN);
3909     SetRightWielded(Weapon);
3910     GetCWeaponSkill(BLUNT_WEAPONS)->AddHit(50);
3911     GetCurrentRightSWeaponSkill()->AddHit(10);
3912     break;
3913    case 2:
3914     Weapon = meleeweapon::Spawn(MEAT_CLEAVER);
3915     SetRightWielded(Weapon);
3916     GetCWeaponSkill(SMALL_SWORDS)->AddHit(50);
3917     GetCurrentRightSWeaponSkill()->AddHit(10);
3918     break;
3919    default: break;
3920   }
3921 }
3922 
Save(outputfile & SaveFile) const3923 void guard::Save(outputfile& SaveFile) const
3924 {
3925   humanoid::Save(SaveFile);
3926   SaveFile << WayPoints << NextWayPoint;
3927 }
3928 
Load(inputfile & SaveFile)3929 void guard::Load(inputfile& SaveFile)
3930 {
3931   humanoid::Load(SaveFile);
3932   SaveFile >> WayPoints >> NextWayPoint;
3933 }
3934 
GetAICommand()3935 void guard::GetAICommand()
3936 {
3937   if(GetConfig() == MASTER && HP << 1 < MaxHP && (GetPos() - v2(30, 17)).GetLengthSquare() > 9)
3938   {
3939     if(CanBeSeenByPlayer())
3940       ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE));
3941 
3942     GetLevel()->GetLSquare(30, 16)->KickAnyoneStandingHereAway();
3943     Move(v2(30, 16), true);
3944     EditAP(-1000);
3945     return;
3946   }
3947 
3948   if(WayPoints.size() && !IsGoingSomeWhere())
3949   {
3950     if(GetPos() == WayPoints[NextWayPoint])
3951     {
3952       if(NextWayPoint < WayPoints.size() - 1)
3953         ++NextWayPoint;
3954       else
3955         NextWayPoint = 0;
3956     }
3957 
3958     GoingTo = WayPoints[NextWayPoint];
3959   }
3960 
3961   SeekLeader(GetLeader());
3962 
3963   if(CheckForEnemies(true, true, true))
3964     return;
3965 
3966   if(CheckForUsefulItemsOnGround())
3967     return;
3968 
3969   if(FollowLeader(GetLeader()))
3970     return;
3971 
3972   if(CheckForDoors())
3973     return;
3974 
3975   if(MoveTowardsHomePos())
3976     return;
3977 
3978   if(CheckSadism())
3979     return;
3980 
3981   if(CheckForBeverage())
3982     return;
3983 
3984   EditAP(-1000);
3985 }
3986 
ReceiveDamage(character * Damager,int Damage,int Type,int TargetFlags,int Direction,truth Divide,truth PenetrateArmor,truth Critical,truth ShowMsg)3987 truth mistress::ReceiveDamage(character* Damager, int Damage, int Type, int TargetFlags, int Direction,
3988                               truth Divide, truth PenetrateArmor, truth Critical, truth ShowMsg)
3989 {
3990   truth Success = humanoid::ReceiveDamage(Damager, Damage, Type, TargetFlags, Direction,
3991                                           Divide, PenetrateArmor, Critical, ShowMsg);
3992 
3993   if(Type & SOUND && Success && !(RAND() & 7))
3994   {
3995     if(IsPlayer())
3996       ADD_MESSAGE("Aahhh. The pain feels unbelievably good.");
3997     else if(CanBeSeenByPlayer())
3998       ADD_MESSAGE("%s screams: \"Oh the delightful pain!\"", CHAR_NAME(DEFINITE));
3999     else
4000       ADD_MESSAGE("You hear someone screaming: \"Oh the delightful pain!\"");
4001   }
4002 
4003   return Success;
4004 }
4005 
AddSpecialStethoscopeInfo(felist & Info) const4006 void humanoid::AddSpecialStethoscopeInfo(felist& Info) const
4007 {
4008   Info.AddEntry(CONST_S("Arm strength: ") + GetAttribute(ARM_STRENGTH), LIGHT_GRAY);
4009   Info.AddEntry(CONST_S("Leg strength: ") + GetAttribute(LEG_STRENGTH), LIGHT_GRAY);
4010   Info.AddEntry(CONST_S("Dexterity:    ") + GetAttribute(DEXTERITY), LIGHT_GRAY);
4011   Info.AddEntry(CONST_S("Agility:      ") + GetAttribute(AGILITY), LIGHT_GRAY);
4012 }
4013 
GetPairEquipment(int I) const4014 item* humanoid::GetPairEquipment(int I) const
4015 {
4016   switch(I)
4017   {
4018    case RIGHT_WIELDED_INDEX: return GetLeftWielded();
4019    case LEFT_WIELDED_INDEX: return GetRightWielded();
4020    case RIGHT_GAUNTLET_INDEX: return GetLeftGauntlet();
4021    case LEFT_GAUNTLET_INDEX: return GetRightGauntlet();
4022    case RIGHT_BOOT_INDEX: return GetLeftBoot();
4023    case LEFT_BOOT_INDEX: return GetRightBoot();
4024   }
4025 
4026   return 0;
4027 }
4028 
GetStandVerb() const4029 cfestring& humanoid::GetStandVerb() const
4030 {
4031   if(ForceCustomStandVerb())
4032     return DataBase->StandVerb;
4033 
4034   static festring HasntFeet = CONST_S("crawling");
4035   static festring Hovering = CONST_S("hovering");
4036   static festring Swimming = CONST_S("swimming");
4037 
4038   if(StateIsActivated(LEVITATION))
4039     return Hovering;
4040 
4041   if(IsSwimming())
4042     return Swimming;
4043 
4044   return HasAUsableLeg() ? DataBase->StandVerb : HasntFeet;
4045 }
4046 
GetAICommand()4047 void darkmage::GetAICommand()
4048 {
4049   SeekLeader(GetLeader());
4050 
4051   if(FollowLeader(GetLeader()))
4052     return;
4053 
4054   character* NearestEnemy = 0;
4055   long NearestEnemyDistance = 0x7FFFFFFF;
4056   character* RandomFriend = 0;
4057   charactervector Friend;
4058   v2 Pos = GetPos();
4059 
4060   for(int c = 0; c < game::GetTeams(); ++c)
4061   {
4062     if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
4063     {
4064       for(character* p : game::GetTeam(c)->GetMember())
4065         if(p->IsEnabled())
4066         {
4067           long ThisDistance = Max<long>(abs(p->GetPos().X - Pos.X), abs(p->GetPos().Y - Pos.Y));
4068 
4069           if((ThisDistance < NearestEnemyDistance
4070               || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && p->CanBeSeenBy(this))
4071           {
4072             NearestEnemy = p;
4073             NearestEnemyDistance = ThisDistance;
4074           }
4075         }
4076     }
4077     else if(GetTeam()->GetRelation(game::GetTeam(c)) == FRIEND)
4078     {
4079       for(character* p : game::GetTeam(c)->GetMember())
4080         if(p->IsEnabled() && p->CanBeSeenBy(this))
4081           Friend.push_back(p);
4082     }
4083   }
4084 
4085   if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos))
4086   {
4087     if(NearestEnemy->IsSmall()
4088        && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit()
4089        && !(RAND() % 5)
4090        && Hit(NearestEnemy, NearestEnemy->GetPos(),
4091               game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos())))
4092       return;
4093     else if((GetConfig() == ARCH_MAGE && RAND() & 1)
4094             || (GetConfig() == ELDER && !(RAND() & 3)))
4095     {
4096       if(CanBeSeenByPlayer())
4097         ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE));
4098 
4099       TeleportRandomly(true);
4100       EditAP(-GetSpellAPCost());
4101       return;
4102     }
4103   }
4104 
4105   if(NearestEnemy
4106      && ((GetConfig() != APPRENTICE && NearestEnemyDistance < 10) || StateIsActivated(PANIC))
4107      && RAND() & 3)
4108   {
4109     SetGoingTo((Pos << 1) - NearestEnemy->GetPos());
4110 
4111     if(MoveTowardsTarget(true))
4112       return;
4113   }
4114 
4115   if(Friend.size() && !(RAND() & 3))
4116   {
4117     RandomFriend = Friend[RAND() % Friend.size()];
4118     NearestEnemy = 0;
4119   }
4120 
4121   beamdata Beam
4122     (
4123       this,
4124       CONST_S("killed by the spells of ") + GetName(INDEFINITE),
4125       YOURSELF,
4126       0
4127     );
4128 
4129   if(NearestEnemy)
4130   {
4131     lsquare* Square = NearestEnemy->GetLSquareUnder();
4132     EditAP(-GetSpellAPCost());
4133 
4134     if(CanBeSeenByPlayer())
4135       ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE));
4136 
4137     switch(GetConfig())
4138     {
4139      case APPRENTICE:
4140       Square->DrawLightning(v2(8, 8), WHITE, YOURSELF);
4141       Square->Lightning(Beam);
4142       break;
4143      case BATTLE_MAGE:
4144       if(RAND() % 20)
4145       {
4146         Square->DrawLightning(v2(8, 8), WHITE, YOURSELF);
4147         Square->Lightning(Beam);
4148       }
4149       else
4150       {
4151         Square->DrawParticles(RED);
4152         Square->LowerEnchantment(Beam);
4153       }
4154 
4155       break;
4156      case ELDER:
4157       switch(RAND() % 20)
4158       {
4159        case 0:
4160        case 1:
4161        case 2: Square->DrawParticles(RED); Square->Strike(Beam); break;
4162        case 3: Square->DrawParticles(RED); Square->FireBall(Beam); break;
4163        case 4:
4164        case 5:
4165        case 6: Square->DrawParticles(RED); Square->Slow(Beam); break;
4166        case 7: Square->DrawParticles(RED); Square->Teleport(Beam); break;
4167        case 8:
4168        case 9:
4169        case 10: Square->DrawParticles(RED); Square->LowerEnchantment(Beam); break;
4170        default: Square->DrawLightning(v2(8, 8), WHITE, YOURSELF); Square->Lightning(Beam); break;
4171       }
4172 
4173       break;
4174      case ARCH_MAGE:
4175       switch(RAND() % 20)
4176       {
4177        case 0:
4178        case 1:
4179        case 2: Square->DrawParticles(RED); Square->FireBall(Beam); break;
4180        case 3:
4181         {
4182           character* Char = NearestEnemy->DuplicateToNearestSquare(this,
4183             CHANGE_TEAM|MIRROR|(1000 << LE_BASE_SHIFT)|(1000 << LE_RAND_SHIFT));
4184 
4185           if(Char)
4186           {
4187             if(Char->CanBeSeenByPlayer())
4188               ADD_MESSAGE("%s materializes!", Char->CHAR_NAME(INDEFINITE));
4189 
4190             break;
4191           }
4192         }
4193        case 4:
4194        case 5: Square->DrawParticles(RED); Square->Slow(Beam); break;
4195        case 6: Square->DrawParticles(RED); Square->Teleport(Beam); break;
4196        case 7:
4197        case 8:
4198        case 9: Square->DrawParticles(RED); Square->LowerEnchantment(Beam); break;
4199        case 10:
4200         {
4201           golem* Golem = golem::Spawn(RAND() % 3 ? ARCANITE : OCTIRON);
4202           v2 Where = GetLevel()->GetNearestFreeSquare(Golem, Square->GetPos());
4203 
4204           if(Where == ERROR_V2)
4205           {
4206             if(CanBeSeenByPlayer())
4207               ADD_MESSAGE("Nothing happens.");
4208 
4209             delete Golem;
4210           }
4211           else
4212           {
4213             Golem->SetGenerationDanger(GetGenerationDanger());
4214             Golem->SetTeam(GetTeam());
4215             Golem->PutTo(Where);
4216 
4217             if(Golem->CanBeSeenByPlayer())
4218               ADD_MESSAGE("Suddenly %s materializes!", Golem->CHAR_NAME(INDEFINITE));
4219 
4220             Golem->GetLSquareUnder()->DrawParticles(RED);
4221           }
4222 
4223           break;
4224         }
4225        default: Square->DrawParticles(RED); Square->Strike(Beam); break;
4226       }
4227 
4228       break;
4229     }
4230 
4231     if(CanBeSeenByPlayer())
4232       NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE)
4233                                               + CONST_S(" interrupts you."));
4234     else
4235       NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you."));
4236 
4237     return;
4238   }
4239 
4240   if(RandomFriend)
4241   {
4242     lsquare* Square = RandomFriend->GetLSquareUnder();
4243     EditAP(-GetSpellAPCost());
4244     Square->DrawParticles(RED);
4245 
4246     switch(GetConfig())
4247     {
4248      case APPRENTICE:
4249      case BATTLE_MAGE:
4250       Square->Haste(Beam);
4251       break;
4252      case ARCH_MAGE:
4253       if(!(RAND() & 31))
4254       {
4255         RandomFriend->DuplicateToNearestSquare(this, CHANGE_TEAM);
4256         return;
4257       }
4258      case ELDER:
4259       if(RAND() & 1)
4260         Square->Invisibility(Beam);
4261       else
4262         Square->Haste(Beam);
4263 
4264       break;
4265     }
4266 
4267     return;
4268   }
4269 
4270   if(CheckForDoors())
4271     return;
4272 
4273   if(CheckSadism())
4274     return;
4275 
4276   if(MoveRandomly())
4277     return;
4278 
4279   EditAP(-1000);
4280 }
4281 
GetAICommand()4282 void zombie::GetAICommand()
4283 {
4284   if(!GetHead())
4285   {
4286     for(stackiterator i = GetLSquareUnder()->GetStack()->GetBottom(); i.HasItem(); ++i)
4287     {
4288       head* Head = i->Behead();
4289 
4290       if(Head)
4291       {
4292         if(CanBeSeenByPlayer())
4293           ADD_MESSAGE("%s takes %s and attaches it to its torso.",
4294                       CHAR_NAME(DEFINITE), Head->CHAR_NAME(INDEFINITE));
4295 
4296         Head->RemoveFromSlot();
4297         AttachBodyPart(Head);
4298         Head->SetHP(1);
4299         DexterityAction(10);
4300         return;
4301       }
4302     }
4303   }
4304 
4305   humanoid::GetAICommand();
4306 }
4307 
Behead()4308 head* humanoid::Behead()
4309 {
4310   head* Head = GetHead();
4311 
4312   if(Head)
4313     SevereBodyPart(HEAD_INDEX);
4314 
4315   return Head;
4316 }
4317 
BoundToUse(citem * Item,int I) const4318 truth communist::BoundToUse(citem* Item, int I) const
4319 {
4320   return Item && Item->IsGorovitsFamilyRelic() && Item->IsInCorrectSlot(I);
4321 }
4322 
GetKillName() const4323 festring werewolfwolf::GetKillName() const
4324 {
4325   if(GetPolymorphBackup() && GetPolymorphBackup()->GetType() == werewolfhuman::ProtoType.GetIndex())
4326     return GetName(INDEFINITE);
4327 
4328   return humanoid::GetKillName();
4329 }
4330 
SpecialBiteEffect(character * Victim,v2 HitPos,int BodyPartIndex,int Direction,truth BlockedByArmour,truth Critical,int DoneDamage)4331 truth werewolfwolf::SpecialBiteEffect(character* Victim, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour, truth Critical, int DoneDamage)
4332 {
4333   if(!BlockedByArmour && Victim->IsWarmBlooded() && (!(RAND() % 2) || Critical) && !Victim->AllowSpoil())
4334   {
4335     // Werewolf wolf gives lycanthropy
4336     if(Victim->IsHumanoid() && !Victim->StateIsActivated(VAMPIRISM) && !Victim->StateIsActivated(LYCANTHROPY) && !Victim->StateIsActivated(DISEASE_IMMUNITY))
4337       Victim->BeginTemporaryState(LYCANTHROPY, 6000 + RAND_N(2000));
4338 
4339     // Werewolves do double damage against vampires and this is a drain attack
4340     if(Victim->StateIsActivated(VAMPIRISM) && (DoneDamage >= 1))
4341     {
4342       if(IsPlayer())
4343         ADD_MESSAGE("You drain some life force from %s!", Victim->CHAR_DESCRIPTION(DEFINITE));
4344       else if(Victim->IsPlayer() || Victim->CanBeSeenByPlayer() || CanBeSeenByPlayer())
4345         ADD_MESSAGE("%s drains some life force from %s!", CHAR_DESCRIPTION(DEFINITE), Victim->CHAR_DESCRIPTION(DEFINITE));
4346 
4347       return Victim->ReceiveBodyPartDamage(this, DoneDamage, DRAIN, BodyPartIndex, Direction);
4348     }
4349     else
4350       return false;
4351   }
4352   else
4353     return false;
4354 }
4355 
GetRandomApplyBodyPart() const4356 int humanoid::GetRandomApplyBodyPart() const
4357 {
4358   if(RightArmIsUsable())
4359   {
4360     if(LeftArmIsUsable())
4361       return RAND_2 ? RIGHT_ARM_INDEX : LEFT_ARM_INDEX;
4362     else
4363       return RIGHT_ARM_INDEX;
4364   }
4365   else if(LeftArmIsUsable())
4366     return LEFT_ARM_INDEX;
4367 
4368   if(GetHead())
4369     return HEAD_INDEX;
4370 
4371   return TORSO_INDEX;
4372 }
4373 
BeTalkedTo()4374 void golem::BeTalkedTo()
4375 {
4376   static long Said;
4377 
4378   if(GetRelation(PLAYER) == HOSTILE)
4379     Engrave(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]);
4380   else
4381     Engrave(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]);
4382 
4383   if(CanBeSeenByPlayer())
4384     ADD_MESSAGE("%s engraves something.", CHAR_NAME(DEFINITE));
4385 }
4386 
4387 #ifdef WIZARD
4388 
AddAttributeInfo(festring & Entry) const4389 void humanoid::AddAttributeInfo(festring& Entry) const
4390 {
4391   Entry.Resize(42);
4392   Entry << GetAttribute(ARM_STRENGTH);
4393   Entry.Resize(45);
4394   Entry << GetAttribute(LEG_STRENGTH);
4395   Entry.Resize(48);
4396   Entry << GetAttribute(DEXTERITY);
4397   Entry.Resize(51);
4398   Entry << GetAttribute(AGILITY);
4399   character::AddAttributeInfo(Entry);
4400 }
4401 
AddAttackInfo(felist & List) const4402 void humanoid::AddAttackInfo(felist& List) const
4403 {
4404   if(GetAttackStyle() & USE_ARMS)
4405   {
4406     if(GetRightArm())
4407       GetRightArm()->AddAttackInfo(List);
4408 
4409     if(GetLeftArm())
4410       GetLeftArm()->AddAttackInfo(List);
4411   }
4412 
4413   festring Entry;
4414 
4415   if(IsUsingLegs())
4416   {
4417     GetRightLeg()->AddAttackInfo(List);
4418     GetLeftLeg()->AddAttackInfo(List);
4419   }
4420 
4421   if(IsUsingHead())
4422   {
4423     Entry = CONST_S("   bite attack");
4424     Entry.Resize(50);
4425     Entry << GetHead()->GetBiteMinDamage() << '-' << GetHead()->GetBiteMaxDamage();
4426     Entry.Resize(60);
4427     Entry << int(GetHead()->GetBiteToHitValue());
4428     Entry.Resize(70);
4429     Entry << GetHead()->GetBiteAPCost();
4430     List.AddEntry(Entry, LIGHT_GRAY);
4431   }
4432 }
4433 
AddDefenceInfo(felist & List) const4434 void humanoid::AddDefenceInfo(felist& List) const
4435 {
4436   character::AddDefenceInfo(List);
4437 
4438   if(GetRightArm())
4439     GetRightArm()->AddDefenceInfo(List);
4440 
4441   if(GetLeftArm())
4442     GetLeftArm()->AddDefenceInfo(List);
4443 }
4444 
DetachBodyPart()4445 void humanoid::DetachBodyPart()
4446 {
4447   int ToBeDetached;
4448 
4449   switch(game::KeyQuestion(CONST_S("What limb? (l)eft arm, (r)ight arm, (L)eft leg, (R)ight leg, (h)ead?"),
4450                            KEY_ESC, 5, 'l', 'r', 'L', 'R', 'h'))
4451   {
4452    case 'l':
4453     ToBeDetached = LEFT_ARM_INDEX;
4454     break;
4455    case 'r':
4456     ToBeDetached = RIGHT_ARM_INDEX;
4457     break;
4458    case 'L':
4459     ToBeDetached = LEFT_LEG_INDEX;
4460     break;
4461    case 'R':
4462     ToBeDetached = RIGHT_LEG_INDEX;
4463     break;
4464    case 'h':
4465     ToBeDetached = HEAD_INDEX;
4466     break;
4467    default:
4468     return;
4469   }
4470 
4471   if(GetBodyPart(ToBeDetached))
4472   {
4473     item* ToDrop = SevereBodyPart(ToBeDetached);
4474     SendNewDrawRequest();
4475 
4476     if(ToDrop)
4477     {
4478       GetStack()->AddItem(ToDrop);
4479       ToDrop->DropEquipment();
4480     }
4481 
4482     ADD_MESSAGE("Bodypart detached!");
4483   }
4484   else
4485     ADD_MESSAGE("That bodypart has already been detached.");
4486 
4487   CheckDeath(CONST_S("removed one of his vital bodyparts"), 0);
4488 }
4489 
SetFireToBodyPart()4490 void humanoid::SetFireToBodyPart()
4491 {
4492   int ToBeSetFireTo;
4493 
4494   switch(game::KeyQuestion(CONST_S("What limb? (l)eft arm, (r)ight arm, (L)eft leg, (R)ight leg, (h)ead?"),
4495                            KEY_ESC, 5, 'l', 'r', 'L', 'R', 'h'))
4496   {
4497    case 'l':
4498     ToBeSetFireTo = LEFT_ARM_INDEX;
4499     break;
4500    case 'r':
4501     ToBeSetFireTo = RIGHT_ARM_INDEX;
4502     break;
4503    case 'L':
4504     ToBeSetFireTo = LEFT_LEG_INDEX;
4505     break;
4506    case 'R':
4507     ToBeSetFireTo = RIGHT_LEG_INDEX;
4508     break;
4509    case 'h':
4510     ToBeSetFireTo = HEAD_INDEX;
4511     break;
4512    default:
4513     return;
4514   }
4515 
4516   if(GetBodyPart(ToBeSetFireTo))
4517   {
4518     IgniteBodyPart(ToBeSetFireTo, game::NumberQuestion(CONST_S("How much fire damage?"), PINK));
4519     SendNewDrawRequest();
4520 
4521   }
4522   else
4523     ADD_MESSAGE("That bodypart has previously been detached.");
4524 
4525   CheckDeath(CONST_S("burnt off one of his vital bodyparts"), 0);
4526 }
4527 
4528 #else
4529 
AddAttributeInfo(festring &) const4530 void humanoid::AddAttributeInfo(festring&) const { }
AddAttackInfo(felist &) const4531 void humanoid::AddAttackInfo(felist&) const { }
AddDefenceInfo(felist &) const4532 void humanoid::AddDefenceInfo(felist&) const { }
DetachBodyPart()4533 void humanoid::DetachBodyPart() { }
SetFireToBodyPart()4534 void humanoid::SetFireToBodyPart() { }
4535 
4536 #endif
4537 
MustBeRemovedFromBone() const4538 truth ennerbeast::MustBeRemovedFromBone() const
4539 {
4540   return !IsEnabled()
4541     || GetTeam()->GetID() != MONSTER_TEAM
4542     || GetDungeon()->GetIndex() != ELPURI_CAVE
4543     || GetLevel()->GetIndex() != ENNER_BEAST_LEVEL;
4544 }
4545 
MustBeRemovedFromBone() const4546 truth ennerchild::MustBeRemovedFromBone() const
4547 {
4548   return !IsEnabled()
4549     || GetTeam()->GetID() != MONSTER_TEAM
4550     || GetDungeon()->GetIndex() != XINROCH_TOMB
4551     || GetLevel()->GetIndex() != DUAL_ENNER_BEAST_LEVEL;
4552 }
4553 
MustBeRemovedFromBone() const4554 truth child::MustBeRemovedFromBone() const
4555 {
4556   if(GetConfig() != KING)
4557     return false;
4558 
4559   return !IsEnabled()
4560     || GetTeam()->GetID() != ASLONA_TEAM
4561     || GetDungeon()->GetIndex() != GOBLIN_FORT
4562     || GetLevel()->GetIndex() != KING_LEVEL;
4563 }
4564 
MustBeRemovedFromBone() const4565 truth communist::MustBeRemovedFromBone() const
4566 {
4567   return !IsEnabled()
4568     || GetTeam()->GetID() != IVAN_TEAM
4569     || GetDungeon()->GetIndex() != ELPURI_CAVE
4570     || GetLevel()->GetIndex() != IVAN_LEVEL;
4571 }
4572 
PreProcessForBone()4573 truth humanoid::PreProcessForBone()
4574 {
4575   for(sweaponskill* p : SWeaponSkill)
4576     p->PreProcessForBone();
4577 
4578   return character::PreProcessForBone();
4579 }
4580 
FinalProcessForBone()4581 void humanoid::FinalProcessForBone()
4582 {
4583   character::FinalProcessForBone();
4584 
4585   for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end();)
4586   {
4587     boneidmap::iterator BI = game::GetBoneItemIDMap().find(-(*i)->GetID());
4588 
4589     if(BI == game::GetBoneItemIDMap().end())
4590     {
4591       std::list<sweaponskill*>::iterator Dirt = i++;
4592       SWeaponSkill.erase(Dirt);
4593     }
4594     else
4595     {
4596       (*i)->SetID(BI->second);
4597       ++i;
4598     }
4599   }
4600 }
4601 
FinalProcessForBone()4602 void petrus::FinalProcessForBone()
4603 {
4604   humanoid::FinalProcessForBone();
4605   LastHealed = 0;
4606 }
4607 
FinalProcessForBone()4608 void angel::FinalProcessForBone()
4609 {
4610   humanoid::FinalProcessForBone();
4611   LastHealed = 0;
4612 }
4613 
4614 /*void encourager::FinalProcessForBone()
4615 {
4616   humanoid::FinalProcessForBone();
4617   LastHit = 0;
4618 }*/
4619 
Save(outputfile & SaveFile) const4620 void playerkind::Save(outputfile& SaveFile) const
4621 {
4622   humanoid::Save(SaveFile);
4623   SaveFile << SoulID << HairColor << EyeColor << Talent << Weakness << IsBonePlayer << IsClone;
4624 }
4625 
Load(inputfile & SaveFile)4626 void playerkind::Load(inputfile& SaveFile)
4627 {
4628   humanoid::Load(SaveFile);
4629   SaveFile >> SoulID >> HairColor >> EyeColor >> Talent >> Weakness >> IsBonePlayer >> IsClone;
4630 }
4631 
SetSoulID(ulong What)4632 void playerkind::SetSoulID(ulong What)
4633 {
4634   SoulID = What;
4635 
4636   if(GetPolymorphBackup())
4637     GetPolymorphBackup()->SetSoulID(What);
4638 }
4639 
SuckSoul(character * Soul)4640 truth playerkind::SuckSoul(character* Soul)
4641 {
4642   if(Soul->GetID() == SoulID)
4643   {
4644     SoulID = 0;
4645     return true;
4646   }
4647 
4648   return false;
4649 }
4650 
TryToRiseFromTheDead()4651 truth playerkind::TryToRiseFromTheDead()
4652 {
4653   if(humanoid::TryToRiseFromTheDead())
4654   {
4655     if(IsEnabled() && SoulID)
4656     {
4657       ADD_MESSAGE("The soulless body of %s wobbles for a moment.", CHAR_NAME(DEFINITE));
4658       return false;
4659     }
4660 
4661     return true;
4662   }
4663   else
4664     return false;
4665 }
4666 
FinalProcessForBone()4667 void playerkind::FinalProcessForBone()
4668 {
4669   humanoid::FinalProcessForBone();
4670   IsBonePlayer = true;
4671 
4672   if(SoulID)
4673   {
4674     boneidmap::iterator BI = game::GetBoneCharacterIDMap().find(SoulID);
4675 
4676     if(BI != game::GetBoneCharacterIDMap().end())
4677       SoulID = BI->second;
4678     else
4679       SoulID = 0;
4680   }
4681 }
4682 
playerkind(const playerkind & Char)4683 playerkind::playerkind(const playerkind& Char)
4684 : mybase(Char), SoulID(Char.SoulID), HairColor(Char.HairColor), EyeColor(Char.EyeColor),
4685   Talent(Char.Talent), Weakness(Char.Weakness), IsBonePlayer(Char.IsBonePlayer), IsClone(true)
4686 {
4687 }
4688 
BeTalkedTo()4689 void playerkind::BeTalkedTo()
4690 {
4691   if(IsClone && IsBonePlayer)
4692   {
4693     if(GetRelation(PLAYER) == HOSTILE)
4694     {
4695       ADD_MESSAGE("Oh no, you too! Why does everyone bully me!");
4696       return;
4697     }
4698 
4699     static long Said;
4700 
4701     switch(RandomizeReply(Said, 4))
4702     {
4703      case 0:
4704       ADD_MESSAGE("\"I'd like to write a memoir, but alas I doubt anyone would believe it.\"");
4705       break;
4706      case 1:
4707       ADD_MESSAGE("\"Then that damned clone appeared, took all my equipment and claimed I was his slave...\"");
4708       break;
4709      case 2:
4710       ADD_MESSAGE("\"The level was a catastrophe for the party, but luckily you saved the day.\"");
4711       break;
4712      case 3:
4713       ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\"");
4714       break;
4715     }
4716   }
4717   else if(IsClone)
4718   {
4719     if(GetRelation(PLAYER) == HOSTILE)
4720     {
4721       ADD_MESSAGE("%s seems extremely irritated. \"Vanish, you foul mirror image!\"", CHAR_DESCRIPTION(DEFINITE));
4722       return;
4723     }
4724 
4725     static long Said;
4726 
4727     switch(RandomizeReply(Said, 4))
4728     {
4729      case 0:
4730       ADD_MESSAGE("\"Hey, those clothes are mine! Give them back!\"");
4731       break;
4732      case 1:
4733       ADD_MESSAGE("\"What, you summoned me? What a coincidence, I remember summoning you, too.\"");
4734       break;
4735      case 2:
4736       ADD_MESSAGE("\"I'm leading this party, not you, Mr. copy guy!\"");
4737       break;
4738      case 3:
4739       ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\"");
4740       break;
4741     }
4742   }
4743   else
4744   {
4745     if(GetRelation(PLAYER) == HOSTILE)
4746     {
4747       ADD_MESSAGE("Let's finish what my ghost failed to do!");
4748       return;
4749     }
4750 
4751     static long Said;
4752 
4753     switch(RandomizeReply(Said, 4))
4754     {
4755      case 0:
4756       ADD_MESSAGE("\"What was it like? Death, you mean? Well, just like New Attnam. Very hot and whips everywhere.\"");
4757       break;
4758      case 1:
4759       ADD_MESSAGE("\"Stop it already! I *don't* want to know how my corpse smelled!\"");
4760       break;
4761      case 2:
4762       ADD_MESSAGE("\"I'm sorry about that ghost thing. That YASD was just a bit too much to handle, so I lost myself.\"");
4763       break;
4764      case 3:
4765       ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\"");
4766       break;
4767     }
4768   }
4769 }
4770 
EnsureCurrentSWeaponSkillIsCorrect(sweaponskill * & Skill,citem * Wielded)4771 void humanoid::EnsureCurrentSWeaponSkillIsCorrect(sweaponskill*& Skill, citem* Wielded)
4772 {
4773   if(Wielded)
4774   {
4775     if(!Skill || !Skill->IsSkillOf(Wielded))
4776     {
4777       if(Skill)
4778         EnsureCurrentSWeaponSkillIsCorrect(Skill, 0);
4779 
4780       for(sweaponskill* p : SWeaponSkill)
4781         if(p->IsSkillOf(Wielded))
4782         {
4783           Skill = p;
4784           return;
4785         }
4786 
4787       for(idholder* I = Wielded->GetCloneMotherID(); I; I = I->Next)
4788         for(sweaponskill* p : SWeaponSkill)
4789           if(p->IsSkillOfCloneMother(Wielded, I->ID))
4790           {
4791             Skill = new sweaponskill(*p);
4792             Skill->SetID(Wielded->GetID());
4793             SWeaponSkill.push_back(Skill);
4794             return;
4795           }
4796 
4797       Skill = new sweaponskill(Wielded);
4798       SWeaponSkill.push_back(Skill);
4799     }
4800   }
4801   else if(Skill)
4802   {
4803     if(!Skill->GetHits() && (CurrentRightSWeaponSkill != Skill || CurrentLeftSWeaponSkill != Skill))
4804       for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
4805         if(*i == Skill)
4806         {
4807           delete *i;
4808           SWeaponSkill.erase(i);
4809           break;
4810         }
4811 
4812     Skill = 0;
4813   }
4814 }
4815 
~humanoid()4816 humanoid::~humanoid()
4817 {
4818   for(sweaponskill* p : SWeaponSkill)
4819     delete p;
4820 }
4821 
MoveTowardsHomePos()4822 truth petrus::MoveTowardsHomePos()
4823 {
4824   if(GetPos() != v2(28, 20))
4825   {
4826     if(CanBeSeenByPlayer())
4827       ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE));
4828 
4829     GetLevel()->GetLSquare(28, 20)->KickAnyoneStandingHereAway();
4830     Move(v2(28, 20), true);
4831 
4832     if(CanBeSeenByPlayer())
4833       ADD_MESSAGE("%s appears.", CHAR_NAME(DEFINITE));
4834 
4835     EditAP(-1000);
4836     return true;
4837   }
4838   else
4839     return false;
4840 }
4841 
MoveTowardsHomePos()4842 truth guard::MoveTowardsHomePos()
4843 {
4844   if(GetConfig() == MASTER && GetPos() != v2(30, 16))
4845   {
4846     if(CanBeSeenByPlayer())
4847       ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE));
4848 
4849     GetLevel()->GetLSquare(30, 16)->KickAnyoneStandingHereAway();
4850     Move(v2(30, 16), true);
4851 
4852     if(CanBeSeenByPlayer())
4853       ADD_MESSAGE("%s appears.", CHAR_NAME(DEFINITE));
4854 
4855     EditAP(-1000);
4856     return true;
4857   }
4858   else
4859     return humanoid::MoveTowardsHomePos();
4860 }
4861 
MakeBodyPart(int I) const4862 bodypart* ennerbeast::MakeBodyPart(int I) const
4863 {
4864   if(I == HEAD_INDEX)
4865     return ennerhead::Spawn(0, NO_MATERIALS);
4866   else
4867     return humanoid::MakeBodyPart(I);
4868 }
4869 
MakeBodyPart(int I) const4870 bodypart* ennerchild::MakeBodyPart(int I) const
4871 {
4872   if(I == HEAD_INDEX)
4873     return ennerhead::Spawn(0, NO_MATERIALS);
4874   else
4875     return humanoid::MakeBodyPart(I);
4876 }
4877 
GetSumOfAttributes() const4878 int humanoid::GetSumOfAttributes() const
4879 {
4880   return character::GetSumOfAttributes() + GetAttribute(LEG_STRENGTH) + GetAttribute(DEXTERITY) ;
4881 }
4882 
CheckConsume(cfestring & Verb) const4883 truth humanoid::CheckConsume(cfestring& Verb) const
4884 {
4885   if(!HasHead())
4886   {
4887     if(IsPlayer())
4888       ADD_MESSAGE("You need a head to %s.", Verb.CStr());
4889 
4890     return false;
4891   }
4892 
4893   return character::CheckConsume(Verb);
4894 }
4895 
CanConsume(material * Material) const4896 truth humanoid::CanConsume(material* Material) const
4897 {
4898   return character::CanConsume(Material) && HasHead();
4899 }
4900 
BeTalkedTo()4901 void femaleslave::BeTalkedTo()
4902 {
4903   static long Said;
4904 
4905   if(GetConfig() != NEW_ATTNAM || GetRelation(PLAYER) == HOSTILE)
4906     humanoid::BeTalkedTo();
4907   else if(!game::TweraifIsFree())
4908     ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 4)]);
4909   else
4910     ProcessAndAddMessage(GetFriendlyReplies()[4 + RandomizeReply(Said, 3)]);
4911 }
4912 
GetAICommand()4913 void necromancer::GetAICommand()
4914 {
4915   SeekLeader(GetLeader());
4916 
4917   if(FollowLeader(GetLeader()))
4918     return;
4919 
4920   character* NearestEnemy = 0;
4921   long NearestEnemyDistance = 0x7FFFFFFF;
4922   v2 Pos = GetPos();
4923 
4924   for(int c = 0; c < game::GetTeams(); ++c)
4925     if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
4926     {
4927       for(character* p : game::GetTeam(c)->GetMember())
4928         if(p->IsEnabled())
4929         {
4930           long ThisDistance = Max<long>(abs(p->GetPos().X - Pos.X), abs(p->GetPos().Y - Pos.Y));
4931 
4932           if((ThisDistance < NearestEnemyDistance
4933               || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && p->CanBeSeenBy(this))
4934           {
4935             NearestEnemy = p;
4936             NearestEnemyDistance = ThisDistance;
4937           }
4938         }
4939     }
4940 
4941   if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos))
4942   {
4943     if(GetConfig() != APPRENTICE_NECROMANCER && !(RAND() & 3))
4944     {
4945       if(CanBeSeenByPlayer())
4946         ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE));
4947 
4948       TeleportRandomly(true);
4949       EditAP(-GetSpellAPCost());
4950       return;
4951     }
4952     else if(NearestEnemy->IsSmall()
4953             && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit()
4954             && !(RAND() & 3)
4955             && Hit(NearestEnemy,
4956                    NearestEnemy->GetPos(),
4957                    game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos())))
4958       return;
4959   }
4960 
4961   if(!NearestEnemy)
4962   {
4963     if(!RAND_N(3) && TryToRaiseZombie())
4964       return;
4965   }
4966   else
4967   {
4968     if(!RAND_N(6) && TryToRaiseZombie())
4969       return;
4970   }
4971 
4972   if(NearestEnemy && !(RAND() % (GetConfig() == APPRENTICE_NECROMANCER ? 3 : 2)))
4973   {
4974     lsquare* Square = NearestEnemy->GetLSquareUnder();
4975     EditAP(-GetSpellAPCost());
4976 
4977     if(CanBeSeenByPlayer())
4978       ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE));
4979 
4980     truth Interrupt = false;
4981 
4982     switch(GetConfig())
4983     {
4984      case APPRENTICE_NECROMANCER:
4985       RaiseSkeleton();
4986       break;
4987      default:
4988       if(RAND() % 5)
4989         RaiseSkeleton();
4990       else
4991       {
4992         Square->DrawLightning(v2(8, 8), WHITE, YOURSELF);
4993 
4994         beamdata Beam
4995           (
4996             this,
4997             CONST_S("killed by the spells of ") + GetName(INDEFINITE),
4998             YOURSELF,
4999             0
5000           );
5001 
5002         Square->Lightning(Beam);
5003         Interrupt = true;
5004       }
5005 
5006       break;
5007     }
5008 
5009     if(Interrupt)
5010     {
5011       if(CanBeSeenByPlayer())
5012         NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE)
5013                                                 + CONST_S(" interrupts you."));
5014       else
5015         NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you."));
5016     }
5017 
5018     return;
5019   }
5020 
5021   if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3)
5022   {
5023     SetGoingTo((Pos << 1) - NearestEnemy->GetPos());
5024 
5025     if(MoveTowardsTarget(true))
5026       return;
5027   }
5028 
5029   if(GetConfig() == IMPRISONED_NECROMANCER && !(GetRelation(PLAYER) == HOSTILE))
5030   {
5031     humanoid::MoveTowardsHomePos();
5032     return;
5033   }
5034 
5035   if(CheckForDoors())
5036     return;
5037 
5038   if(CheckSadism())
5039     return;
5040 
5041   if(MoveRandomly())
5042     return;
5043 
5044   EditAP(-1000);
5045 }
5046 
TryToRaiseZombie()5047 truth necromancer::TryToRaiseZombie()
5048 {
5049   for(int c = 0; c < game::GetTeams(); ++c)
5050     for(character* p : game::GetTeam(c)->GetMember())
5051       if(!p->IsEnabled() && p->GetMotherEntity()
5052          && p->GetMotherEntity()->Exists()
5053          && (GetConfig() == MASTER_NECROMANCER
5054              || p->GetMotherEntity()->GetSquareUnderEntity()->CanBeSeenBy(this)))
5055       {
5056         character* Zombie = p->GetMotherEntity()->TryNecromancy(this);
5057 
5058         if(Zombie)
5059         {
5060           if(Zombie->CanBeSeenByPlayer())
5061             ADD_MESSAGE("%s calls %s back to cursed undead life.",
5062                         CHAR_DESCRIPTION(DEFINITE), Zombie->CHAR_NAME(INDEFINITE));
5063           else if(CanBeSeenByPlayer())
5064             ADD_MESSAGE("%s casts a spell, but you notice no effect.",
5065                         CHAR_NAME(DEFINITE));
5066 
5067           EditAP(-GetSpellAPCost());
5068           return true;
5069         }
5070       }
5071 
5072   return false;
5073 }
5074 
RaiseSkeleton()5075 void necromancer::RaiseSkeleton()
5076 {
5077   /* Gum solution */
5078 
5079   const database* WarLordDataBase;
5080   databasecreator<character>::FindDataBase(WarLordDataBase, &skeleton::ProtoType, WAR_LORD);
5081   skeleton* Skeleton;
5082 
5083   if(GetConfig() == MASTER_NECROMANCER && !(WarLordDataBase->Flags & HAS_BEEN_GENERATED) && !(game::GetCurrentDungeonIndex() == XINROCH_TOMB) && !(RAND() % 250))
5084   {
5085     Skeleton = skeleton::Spawn(WAR_LORD);
5086     Skeleton->SetTeam(GetTeam());
5087     Skeleton->PutNear(GetPos());
5088     Skeleton->SignalGeneration();
5089 
5090     if(Skeleton->CanBeSeenByPlayer())
5091       ADD_MESSAGE("The whole area trembles terribly as %s emerges from the ground.", Skeleton->CHAR_NAME(DEFINITE));
5092     else if(CanBeSeenByPlayer())
5093       ADD_MESSAGE("%s casts a powerful spell which makes the whole area tremble.", CHAR_NAME(DEFINITE));
5094     else
5095       ADD_MESSAGE("You feel the presence of an ancient evil being awakened from its long slumber. You shiver.");
5096   }
5097   else
5098   {
5099     Skeleton = skeleton::Spawn(GetConfig() == APPRENTICE_NECROMANCER ? 0 : WARRIOR, NO_EQUIPMENT);
5100     Skeleton->SetTeam(GetTeam());
5101     Skeleton->PutNear(GetPos());
5102 
5103     if(Skeleton->CanBeSeenByPlayer())
5104       ADD_MESSAGE("The ground shakes and %s emerges from it.", Skeleton->CHAR_NAME(INDEFINITE));
5105     else if(CanBeSeenByPlayer())
5106       ADD_MESSAGE("%s casts a spell, but you notice no effect.", CHAR_NAME(DEFINITE));
5107   }
5108 
5109   Skeleton->SetGenerationDanger(GetGenerationDanger());
5110   EditAP(-GetSpellAPCost());
5111 }
5112 
BeTalkedTo()5113 void necromancer::BeTalkedTo()
5114 {
5115   if(GetConfig() != IMPRISONED_NECROMANCER || !GetPos().IsAdjacent(PLAYER->GetPos()))
5116   {
5117     humanoid::BeTalkedTo();
5118     return;
5119   }
5120 
5121   /* From here we are talking to the necromancer in the attnamese catacombs */
5122   if(GetRelation(PLAYER) == HOSTILE)
5123   {
5124     ADD_MESSAGE("\"I will bury you in the catacombs with all the others!\"");
5125     return;
5126   }
5127 
5128   if(game::GetXinrochTombStoryState() == 1)
5129   {
5130     if(PLAYER->HasShadowVeil() && PLAYER->RemoveShadowVeil(this))
5131     {
5132       game::TextScreen(CONST_S("\"At last I can make my escape from Petrus' wretched clutches!\"\n\n"
5133                                "Anmah takes the shadow veil from you and seems completely lost in\n"
5134                                "thoughts for a while. Suddenly, he looks up:\n\n"
5135                                "\"Oh, you are still here. Good! Pray tell me, what did you find in the Tomb?\n"
5136                                "A portal? Did you traverse it? Of course not! You can't do so bodily,\n"
5137                                "unless you were...\n\n...changed in some way.\"\n\n"
5138                                "Before you can stop him, he reaches for your face."));
5139 
5140       game::TextScreen(CONST_S("You feel a cold, tingling sensation in the middle of your forehead.\n\n"
5141                                "\"Here, I give you the seal of the undead. You will be able to traverse\n"
5142                                "the portal without the use of the shadow veil. You can still retrieve\n"
5143                                "the lost flaming ruby sword. If you go beyond the portal, you will find\n"
5144                                "the one who carries this lost sword. But be warned, he is a terrible foe!\""));
5145 
5146       GetArea()->SendNewDrawRequest();
5147       ADD_MESSAGE("\"May Infuscor guide you towards the darkest of secrets.\"");
5148       game::SetXinrochTombStoryState(2);
5149     }
5150     else
5151     {
5152       ADD_MESSAGE("%s says: \"Bring me the shadow veil and we'll talk.\"", CHAR_NAME(DEFINITE));
5153       return;
5154     }
5155   }
5156 
5157   if(PLAYER->HasLostRubyFlamingSword())
5158   {
5159     ADD_MESSAGE("%s exclaims: \"What are you still doing down here? That sword belongs to the Champion of Infuscor!\"", CHAR_NAME(DEFINITE));
5160     return;
5161   }
5162   else if(game::GetXinrochTombStoryState() == 2)
5163     ADD_MESSAGE("%s says: \"I am just preparing to leave. Have you found that flaming ruby sword yet?\"", CHAR_NAME(DEFINITE));
5164 
5165   if(game::GetStoryState() == 1)
5166   {
5167     ADD_MESSAGE("%s looks up: \"Would you mind helping me with a little problem?\"", CHAR_NAME(DEFINITE));
5168 
5169     if(game::TruthQuestion(CONST_S("Do you accept the quest? [y/N]"), REQUIRES_ANSWER))
5170     {
5171       /*game::TextScreen(CONST_S("The necromancer takes the scroll and mutters an incantation in a low voice.\n"
5172                                "To your surprise, the words rearrange themselves on the page,\n"
5173                                "revealing a previously inscrutable message.\n"
5174                                "The necromancer scans the page from left to right several times. His face contorts:\n"
5175                                "\"Bah! A canticle of Saint Petrus the Lion-Hearted!\"\n"
5176                                "He continues down the page. His eyes widen:\n"
5177                                "\"O ho! 10 000 bananas? It sounds bad out in the colonies. I'm sorry to hear about it.\"\n"
5178                                "\n"
5179                                "The necromancer allows the scroll to burn and the ashes wither away in his hands.\n"
5180                                "\"Alas, no news about my trial. But thank you for sharing.\"\n\n"
5181       */
5182       game::TextScreen(CONST_S("\"You might be asking what am I doing down here? Lets just say I had spent some time\n"
5183                                "arranging... things in the catacombs below. I was the undertaker for the city of Attnam,\n"
5184                                "you see. Well, curiosity got the better of me and I admit I dabbled in some necromancy.\n"
5185                                "223 years later, and I was still down here, drinking blood, eating bones, and generally \n"
5186                                "trying all the old life-extension tricks. Finally I got caught by that meddling Haedlac.\n"
5187                                "He's got nothing better to do these days, I guess. He sent me here to the Cellar, agonizingly\n"
5188                                "close to my minions, but still unable to escape. That stupid floating eye hovers by here\n"
5189                                "every now and again to check up on me.\""));
5190 
5191       game::TextScreen(CONST_S("\"Wait, don't go yet! It gets lonely here, with no one to talk to but the punishers.\n"
5192                                "Keep me company a little longer, please... Maybe I can tell you a story? I can relate\n"
5193                                "the history of dark knighthood to you.\"\n\n"
5194                                "\"Long ago, there lived a powerful warrior, Xinroch, who rose up the ranks\n"
5195                                "of the fearsome order of the dark knights, to become the grand master dark knight. \n\n"
5196                                "His soul dwells within his mausoleum, not far from here. He doesn't stand a chance\n"
5197                                "of returning to us; not without a piece of his soul getting out. There is a cadre\n"
5198                                "of devoted dark knights, called the Templars. Being eager to protect the resting place\n"
5199                                "of their legendary master, they may obstruct your entry to the tomb. Little do they know\n"
5200                                "that in order for their master to be reborn, his spirit must be freed from the place.\n"
5201                                "Of course, disturbing such a restless soul would be dangerous. You may need to subdue it\n"
5202                                "by force to gain what you need. Legend has it Xinroch's spirit is able to wield weapons,\n"
5203                                "and possesses a cloak of unimaginable usefulness: The Shadow Veil.\""));
5204 
5205       game::TextScreen(CONST_S("The necromancer suddenly looks at you intently.\n\n"
5206                                "\"Okay, we can talk now. The cardinals are not listening to our thoughts. I need you to\n"
5207                                "bring me the shadow veil, it will surely allow me to escape from Attnam. It has certain\n"
5208                                "properties conducive to getting away unnoticed.\"\n\n"
5209                                "\"It will take all your wits to survive the powers of the Tomb of Xinroch, but I believe\n"
5210                                "in you. You are my only hope. Oh, how I wish to taste fresh blood again!\"\n\n"
5211                                "\"Lastly, there is the matter of Xinroch's lost sword. Its power lies in its symbolism.\n"
5212                                "If you were to gain it somehow, then I imagine most believers would be convinced that\n"
5213                                "you were Xinroch himself, returned to the flesh. Although you would need to prove this\n"
5214                                "with the help of our god, Infuscor... ...it might require some offering, perhaps? I have\n"
5215                                "a feeling that if you find anything belonging to Xinroch, it will help you greatly\n"
5216                                "in your quest.\"\n\n"
5217                                "\"I cannot say what trial would await you to retrieve the lost sword, but I'm sure\n"
5218                                "a mighty adventurer like you would like to lead a whole order of dark knights?\""));
5219 
5220       game::LoadWorldMap();
5221       v2 XinrochTombPos = game::GetWorldMap()->GetEntryPos(0, XINROCH_TOMB);
5222       game::GetWorldMap()->GetWSquare(XinrochTombPos)->ChangeOWTerrain(xinrochtomb::Spawn());
5223       game::GetWorldMap()->RevealEnvironment(XinrochTombPos, 1);
5224       game::SaveWorldMap();
5225       GetArea()->SendNewDrawRequest();
5226       ADD_MESSAGE("\"And don't worry about the patrol guard, he's not really paying attention to who leaves the Attnam or if they should be leaving.\"");
5227       game::SetXinrochTombStoryState(1);
5228       game::SetStoryState(2);
5229       return;
5230     }
5231     else
5232     {
5233       ADD_MESSAGE("%s looks downcast. \"I see. I guess I shall have to wait for another adventurer then.\"", CHAR_NAME(DEFINITE));
5234       return;
5235     }
5236   }
5237   else
5238     ADD_MESSAGE("%s says: \"Come back when you are on no other quests.\"", CHAR_NAME(DEFINITE));
5239 }
5240 
StayOn(liquid * Liquid)5241 void humanoid::StayOn(liquid* Liquid)
5242 {
5243   if(IsFlying())
5244     return;
5245 
5246   truth Standing = false;
5247 
5248   if(GetRightLeg())
5249   {
5250     GetRightLeg()->StayOn(Liquid);
5251     Standing = true;
5252   }
5253 
5254   if(IsEnabled() && GetLeftLeg())
5255   {
5256     GetLeftLeg()->StayOn(Liquid);
5257     Standing = true;
5258   }
5259 
5260   if(!Standing)
5261   {
5262     bodypart* BodyPart[MAX_BODYPARTS];
5263     int Index = 0;
5264 
5265     for(int c = 0; c < BodyParts; ++c)
5266       if(GetBodyPart(c))
5267         BodyPart[Index++] = GetBodyPart(c);
5268 
5269     BodyPart[RAND() % Index]->StayOn(Liquid);
5270   }
5271 }
5272 
MakeBodyPart(int I) const5273 bodypart* playerkind::MakeBodyPart(int I) const
5274 {
5275   switch(I)
5276   {
5277    case TORSO_INDEX: return playerkindtorso::Spawn(0, NO_MATERIALS);
5278    case HEAD_INDEX: return playerkindhead::Spawn(0, NO_MATERIALS);
5279    case RIGHT_ARM_INDEX: return playerkindrightarm::Spawn(0, NO_MATERIALS);
5280    case LEFT_ARM_INDEX: return playerkindleftarm::Spawn(0, NO_MATERIALS);
5281    case GROIN_INDEX: return playerkindgroin::Spawn(0, NO_MATERIALS);
5282    case RIGHT_LEG_INDEX: return playerkindrightleg::Spawn(0, NO_MATERIALS);
5283    case LEFT_LEG_INDEX: return playerkindleftleg::Spawn(0, NO_MATERIALS);
5284   }
5285 
5286   ABORT("Weird bodypart to make for a playerkind. It must be your fault!");
5287   return 0;
5288 }
5289 
AddAdjective(festring & String,truth Articled) const5290 truth golem::AddAdjective(festring& String, truth Articled) const
5291 {
5292   int TotalRustLevel = sumbodypartproperties()(this, &bodypart::GetMainMaterialRustLevel);
5293 
5294   if(!TotalRustLevel)
5295     return humanoid::AddAdjective(String, Articled);
5296   else
5297   {
5298     if(Articled)
5299       String << "a ";
5300 
5301     if(TotalRustLevel <= GetBodyParts())
5302       String << "slightly rusted ";
5303     else if(TotalRustLevel <= GetBodyParts() << 1)
5304       String << "rusted ";
5305     else
5306       String << "very rusted ";
5307 
5308     String << GetAdjective() << ' ';
5309     return true;
5310   }
5311 }
5312 
Bite(character * Enemy,v2 HitPos,int,truth)5313 void oree::Bite(character* Enemy, v2 HitPos, int, truth)
5314 {
5315   if(IsPlayer())
5316     ADD_MESSAGE("You vomit acidous blood at %s.", Enemy->CHAR_DESCRIPTION(DEFINITE));
5317   else if(Enemy->IsPlayer() || CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer())
5318     ADD_MESSAGE("%s vomits acidous blood at %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE));
5319 
5320   Vomit(HitPos, 500 + RAND() % 500, false);
5321 }
5322 
SpecialBiteEffect(character * Victim,v2 HitPos,int BodyPartIndex,int Direction,truth BlockedByArmour,truth Critical,int DoneDamage)5323 truth vampire::SpecialBiteEffect(character* Victim, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour, truth Critical, int DoneDamage)
5324 {
5325   if(!BlockedByArmour && Victim->IsWarmBlooded() && (!(RAND() % 2) || Critical) && !Victim->AllowSpoil())
5326   {
5327     if(IsPlayer())
5328       ADD_MESSAGE("You drain some precious lifeblood from %s!", Victim->CHAR_DESCRIPTION(DEFINITE));
5329     else if(Victim->IsPlayer() || Victim->CanBeSeenByPlayer() || CanBeSeenByPlayer())
5330       ADD_MESSAGE("%s drains some precious lifeblood from %s!", CHAR_DESCRIPTION(DEFINITE), Victim->CHAR_DESCRIPTION(DEFINITE));
5331 
5332     if(Victim->IsHumanoid() && !Victim->StateIsActivated(VAMPIRISM) && !Victim->StateIsActivated(LYCANTHROPY) && !Victim->StateIsActivated(DISEASE_IMMUNITY))
5333       Victim->BeginTemporaryState(VAMPIRISM, 5000 + RAND_N(2500));
5334 
5335       // HP recieved is about half the damage done; against werewolves this is full
5336       int DrainDamage = (DoneDamage >> 1) + 1;
5337       if(Victim->StateIsActivated(LYCANTHROPY))
5338         DrainDamage = DoneDamage + 1;
5339 
5340     if(IsPlayer())
5341       game::DoEvilDeed(10);
5342 
5343     return Victim->ReceiveBodyPartDamage(this, DrainDamage, DRAIN, BodyPartIndex, Direction);
5344   }
5345   else
5346     return false;
5347 }
5348 
SpecialBiteEffect(character * Victim,v2 HitPos,int BodyPartIndex,int Direction,truth BlockedByArmour,truth Critical,int DoneDamage)5349 truth humanoid::SpecialBiteEffect(character* Victim, v2 HitPos, int BodyPartIndex, int Direction, truth BlockedByArmour, truth Critical, int DoneDamage)
5350 {
5351   if(StateIsActivated(VAMPIRISM))
5352   {
5353     if(!BlockedByArmour && Victim->IsWarmBlooded() && (!(RAND() % 2) || Critical) && !Victim->AllowSpoil())
5354     {
5355       if(IsPlayer())
5356         ADD_MESSAGE("You drain some precious lifeblood from %s!", Victim->CHAR_DESCRIPTION(DEFINITE));
5357       else if(Victim->IsPlayer() || Victim->CanBeSeenByPlayer() || CanBeSeenByPlayer())
5358         ADD_MESSAGE("%s drains some precious lifeblood from %s!", CHAR_DESCRIPTION(DEFINITE), Victim->CHAR_DESCRIPTION(DEFINITE));
5359 
5360       if(Victim->IsHumanoid() && !Victim->StateIsActivated(VAMPIRISM) && !Victim->StateIsActivated(LYCANTHROPY) && !Victim->StateIsActivated(DISEASE_IMMUNITY))
5361         Victim->BeginTemporaryState(VAMPIRISM, 2000 + RAND_N(500));
5362 
5363       // HP recieved is about half the damage done; against werewolves this is full
5364       int DrainDamage = (DoneDamage >> 1) + 1;
5365       if(Victim->StateIsActivated(LYCANTHROPY))
5366         DrainDamage = DoneDamage + 1;
5367 
5368       // To perpetuate vampirism, simply keep doing drain attacks
5369       BeginTemporaryState(VAMPIRISM, 50*DrainDamage);
5370       if(IsPlayer())
5371         game::DoEvilDeed(10);
5372 
5373       return Victim->ReceiveBodyPartDamage(this, DrainDamage, DRAIN, BodyPartIndex, Direction);
5374     }
5375     else
5376       return false;
5377   }
5378   else
5379     return false;
5380 }
5381 
FixSumoWrestlerHouse(festring fsCmdParams)5382 void FixSumoWrestlerHouse(festring fsCmdParams)
5383 {
5384   sumowrestler* SM = NULL;
5385   characteridmap map = game::GetCharacterIDMapCopy();
5386   for(characteridmap::iterator itr = map.begin();itr!=map.end();itr++){
5387     character* C = itr->second;
5388     if(dynamic_cast<sumowrestler*>(C)){
5389       SM=(sumowrestler*)C;
5390       break;
5391     }
5392   }
5393 
5394   if(SM){
5395     for(int d = 0; d < SM->GetNeighbourSquares(); ++d)
5396     {
5397       lsquare* Square = SM->GetNeighbourLSquare(d);
5398 
5399       if(Square){
5400         character* C2 = Square->GetCharacter();
5401         if(C2 && dynamic_cast<bananagrower*>(C2)){
5402           C2->TeleportRandomly(true);
5403         }
5404       }
5405     }
5406   }
5407 }
5408 
GetAICommand()5409 void sumowrestler::GetAICommand()
5410 {
5411   static bool bInitDummy = [](){
5412     devcons::AddDevCmd("FixSumoHouse",FixSumoWrestlerHouse,
5413       "BugFix sumo wrestler house in case banana growers over crowd it.");
5414     return true;}();
5415 
5416   EditNP(-25);
5417 
5418   SeekLeader(GetLeader());
5419 
5420   if(CheckForEnemies(true, true, true))
5421     return;
5422 
5423   if(CheckForUsefulItemsOnGround())
5424     return;
5425 
5426   if(CheckForFood(4))
5427     return;
5428 
5429   if(FollowLeader(GetLeader()))
5430     return;
5431 
5432   if(CheckForDoors())
5433     return;
5434 
5435   if(MoveTowardsHomePos())
5436     return;
5437 
5438   EditAP(-1000);
5439 }
5440 
BeTalkedTo()5441 void sumowrestler::BeTalkedTo()
5442 {
5443   static long Said;
5444 
5445   if(GetRelation(PLAYER) == HOSTILE)
5446     ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]);
5447   else if(!game::TweraifIsFree())
5448     ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 6)]);
5449   else
5450     ProcessAndAddMessage(GetFriendlyReplies()[6 + RandomizeReply(Said, 3)]);
5451 }
5452 
GetLeader() const5453 character* tourist::GetLeader() const
5454 {
5455   character* Guide = game::GetTeam(TOURIST_GUIDE_TEAM)->GetLeader();
5456   return Guide && Guide->GetRelation(this) != HOSTILE ? Guide : GetTeam()->GetLeader();
5457 }
5458 
GetAICommand()5459 void elder::GetAICommand()
5460 {
5461   /* Select a place to guide the tourists to */
5462 
5463   if(!(RAND() % 10))
5464     SetGoingTo(GetLevel()->GetRandomSquare());
5465 
5466   humanoid::GetAICommand();
5467 }
5468 
GetAICommand()5469 void tourist::GetAICommand()
5470 {
5471   if(game::IsSumoWrestling() && !(RAND() % 10))
5472   {
5473     if(GetConfig() == HUSBAND)
5474     {
5475       if(RAND() & 1)
5476         ADD_MESSAGE("%s shouts: \"Show that skinny wimp what you've got, Huang!\"", CHAR_DESCRIPTION(DEFINITE));
5477       else
5478         ADD_MESSAGE("%s screams: \"Go for it, Huang!\"", CHAR_DESCRIPTION(DEFINITE));
5479     }
5480     else if(GetConfig() == WIFE)
5481     {
5482       if(RAND() & 1)
5483         ADD_MESSAGE("%s encourages you: \"Knock him out, %s!\"",
5484                     CHAR_DESCRIPTION(DEFINITE), game::GetPlayerName().CStr());
5485       else
5486         ADD_MESSAGE("%s cheers you: \"A handsome guy like you can't lose to that banana ball!\"",
5487                     CHAR_DESCRIPTION(DEFINITE));
5488     }
5489     else if(GetConfig() == CHILD)
5490     {
5491       if(RAND() & 1)
5492         ADD_MESSAGE("%s yells: \"More blood on the ring!!!\"", CHAR_DESCRIPTION(DEFINITE));
5493       else
5494         ADD_MESSAGE("%s cries: \"Kill him, Pong!!!\"", CHAR_DESCRIPTION(DEFINITE));
5495     }
5496   }
5497 
5498   humanoid::GetAICommand();
5499 }
5500 
BeTalkedTo()5501 void imperialist::BeTalkedTo()
5502 {
5503   if(GetConfig() == VICE_ROY && GetPos().IsAdjacent(PLAYER->GetPos()))
5504   {
5505     if(GetRelation(PLAYER) != HOSTILE)
5506     {
5507       decosadshirt* Shirt = static_cast<decosadshirt*>(PLAYER->SearchForItem(this, &item::IsDecosAdShirt));
5508 
5509       if(Shirt)
5510       {
5511         ulong Reward = Shirt->GetEquippedTicks() / 500;
5512 
5513         if(Reward)
5514         {
5515           ADD_MESSAGE("%s smiles. \"I see you have advertised our company diligently. "
5516                       "Here's %ldgp as a token of my gratitude.\"", CHAR_NAME(DEFINITE), Reward);
5517           PLAYER->EditMoney(Reward);
5518           Shirt->SetEquippedTicks(0);
5519           return;
5520         }
5521         else if(!(RAND() % 5))
5522         {
5523           ADD_MESSAGE("\"Come back when you've worn the shirt for some time and I'll reward you generously!\"");
5524           return;
5525         }
5526       }
5527     }
5528 
5529     static long Said;
5530 
5531     if(GetRelation(PLAYER) == HOSTILE)
5532       ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]);
5533     else if(!game::PlayerIsSumoChampion())
5534       ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]);
5535     else
5536       ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size - 1)]);
5537   }
5538   else if(GetConfig() == HOARD_MASTER && (PLAYER->GetMoney() >= 50000) &&
5539           game::TweraifIsFree() && !(GetRelation(PLAYER) == HOSTILE) &&
5540           GetPos().IsAdjacent(PLAYER->GetPos())
5541          )
5542   {
5543     if(game::TruthQuestion(CONST_S("Do you want to bribe the hoardmaster? [y/n]"), REQUIRES_ANSWER))
5544     {
5545       game::TextScreen(CONST_S("\"Hmm, so you're saying you 'freed' New Attnam, right? And when you say\n"
5546                                "'freed', you mean 'slaughtered our soldiers and the viceroy'. Hmm...\"\n"
5547                                "\n"
5548                                "\"And what makes you think I won't call the guards this very instant, hmm?\n"
5549                                "I could hand you over to the master torturer. Did you know he and\n"
5550                                "the late viceroy were brothers? Not especially close brothers, but I think\n"
5551                                "he'll still want to roast you very slowly over a firepit; flay and cut and\n"
5552                                "tear and break you; and then have the priests heal you up to start over again.\n"
5553                                "So why did you come to tell me this, hmm?\""));
5554 
5555       game::TextScreen(CONST_S("\"Fifty thousand gold?! Oh, sorry I cried out. Hmm, yes, now I can see your point.\"\n"
5556                                "\n"
5557                                "\"But you do realize that the high priest will just send a war ship or two\n"
5558                                "in the Spring, and your precious New Attnam will have a new viceroy and\n"
5559                                "a new squad of soldiers? Even if there were no plantations, we cannot have\n"
5560                                "bits of the Empire just breaking free. It sets a bad example.\"\n"
5561                                "\n"
5562                                "\"Hmm, so fifty thousand gold pieces a year. And of course, you will\n"
5563                                "provide a steady supply of bananas. Hmm...\""));
5564 
5565       game::TextScreen(CONST_S("\"Hmm...\""));
5566 
5567       game::TextScreen(CONST_S("\"Very well! In that case, trouble yourself not with the master torturer,\n"
5568                                "nor with the high priest. I will handle things here, as long as you handle\n"
5569                                "things in your village. We have a deal. Hmm...\"\n"
5570                                "\n"
5571                                "\"Congratulations, mister. It's nice to meet the new viceroy of Tweraif.\""));
5572 
5573       game::PlayVictoryMusic();
5574       game::TextScreen(CONST_S("You are victorious!"));
5575 
5576       game::GetCurrentArea()->SendNewDrawRequest();
5577       game::DrawEverything();
5578       PLAYER->ShowAdventureInfo();
5579       festring Msg = CONST_S("became the new viceroy of Tweraif and worked hard for the well-being of his people");
5580       AddScoreEntry(Msg, 2, false);
5581       game::End(Msg);
5582       return;
5583     }
5584   }
5585   else
5586     humanoid::BeTalkedTo();
5587 }
5588 
CreateZombie() const5589 character* humanoid::CreateZombie() const
5590 {
5591   if(!TorsoIsAlive())
5592     return 0;
5593 
5594   humanoid* Zombie = zombie::Spawn();
5595   int c;
5596 
5597   for(c = 0; c < BodyParts; ++c)
5598   {
5599     bodypart* BodyPart = GetBodyPart(c);
5600 
5601     if(!BodyPart)
5602     {
5603       BodyPart = SearchForOriginalBodyPart(c);
5604 
5605       if(BodyPart)
5606       {
5607         BodyPart->RemoveFromSlot();
5608         BodyPart->SendToHell();
5609       }
5610     }
5611 
5612     if(BodyPart)
5613     {
5614       bodypart* ZombieBodyPart = Zombie->GetBodyPart(c);
5615 
5616       if(!ZombieBodyPart)
5617         ZombieBodyPart = Zombie->CreateBodyPart(c);
5618 
5619       material* M = BodyPart->GetMainMaterial()->Duplicate();
5620       M->SetSpoilCounter(2000 + RAND() % 1000);
5621       M->SetSkinColor(Zombie->GetSkinColor());
5622       delete ZombieBodyPart->SetMainMaterial(M);
5623       ZombieBodyPart->CopyAttributes(BodyPart);
5624     }
5625     else if(!Zombie->BodyPartIsVital(c))
5626     {
5627       bodypart* ZombieBodyPart = Zombie->GetBodyPart(c);
5628 
5629       if(ZombieBodyPart)
5630       {
5631         ZombieBodyPart->RemoveFromSlot();
5632         ZombieBodyPart->SendToHell();
5633       }
5634     }
5635   }
5636 
5637   for(c = 0; c < Zombie->AllowedWeaponSkillCategories; ++c)
5638     Zombie->CWeaponSkill[c] = CWeaponSkill[c];
5639 
5640   Zombie->SWeaponSkill.resize(SWeaponSkill.size());
5641   std::list<sweaponskill*>::iterator i = Zombie->SWeaponSkill.begin();
5642 
5643   for(sweaponskill* p2 : SWeaponSkill)
5644     *i++ = new sweaponskill(*p2);
5645 
5646   memcpy(Zombie->BaseExperience,
5647          BaseExperience,
5648          BASE_ATTRIBUTES * sizeof(*BaseExperience));
5649   Zombie->CalculateAll();
5650   Zombie->RestoreHP();
5651   Zombie->RestoreStamina();
5652   static_cast<zombie*>(Zombie)->SetDescription(GetZombieDescription());
5653   Zombie->GenerationDanger = GenerationDanger;
5654   return Zombie;
5655 }
5656 
AddPostFix(festring & String,int Case) const5657 void zombie::AddPostFix(festring& String, int Case) const
5658 {
5659   if(!Description.IsEmpty())
5660     String << Description;
5661   else
5662     humanoid::AddPostFix(String, Case);
5663 }
5664 
Save(outputfile & SaveFile) const5665 void zombie::Save(outputfile& SaveFile) const
5666 {
5667   humanoid::Save(SaveFile);
5668   SaveFile << Description;
5669 }
5670 
Load(inputfile & SaveFile)5671 void zombie::Load(inputfile& SaveFile)
5672 {
5673   humanoid::Load(SaveFile);
5674   SaveFile >> Description;
5675 }
5676 
ModifyBodyPartHitPreference(int I,int Modifier) const5677 int darkknight::ModifyBodyPartHitPreference(int I, int Modifier) const
5678 {
5679   return IsLimbIndex(I) ? Modifier << 1 : Modifier;
5680 }
5681 
ModifyBodyPartToHitChance(int I,int Chance) const5682 int darkknight::ModifyBodyPartToHitChance(int I, int Chance) const
5683 {
5684   return IsLimbIndex(I) ? Chance << 1 : Chance;
5685 }
5686 
SpecialBodyPartSeverReaction()5687 void darkknight::SpecialBodyPartSeverReaction()
5688 {
5689   if(!IsPlayer())
5690   {
5691     if(!(GetConfig() == MASTER))
5692     {
5693       if(IsUsingHead())
5694         ADD_MESSAGE("%s screams: \"I'll do you for that! I'll bite your legs off!\"", CHAR_DESCRIPTION(DEFINITE));
5695       else if(!(RAND() % 5))
5696         switch(RAND() % 3)
5697         {
5698          case 0:
5699           ADD_MESSAGE("%s states calmly: \"'Tis but a scratch.\"", CHAR_DESCRIPTION(DEFINITE)); break;
5700          case 1:
5701           ADD_MESSAGE("%s states calmly: \"Just a flesh wound.\"", CHAR_DESCRIPTION(DEFINITE)); break;
5702          case 2:
5703           ADD_MESSAGE("%s shouts: \"I'm invincible!\"", CHAR_DESCRIPTION(DEFINITE)); break;
5704         }
5705     }
5706     else if((GetConfig() == MASTER) && HasHead())
5707     {
5708       character* Called = 0;
5709       Called = darkknight::Spawn(ELITE);
5710       Called->SetTeam(GetTeam());
5711       Called->PutNear(GetPos());
5712       Called->SignalGeneration();
5713 
5714       if(CanBeSeenByPlayer())
5715       {
5716         ADD_MESSAGE("%s screams a profane incantation to Infuscor before disappearing.", CHAR_NAME(DEFINITE));
5717       }
5718 
5719       if(Called->CanBeSeenByPlayer())
5720         ADD_MESSAGE("The whole area trembles terribly as %s emerges from the shadows.", Called->CHAR_NAME(INDEFINITE));
5721       }
5722       else
5723         ADD_MESSAGE("You feel the sudden presence of a violent enemy nearby.");
5724 
5725       TeleportRandomly(true);
5726   }
5727 }
5728 
LeprosyHandler()5729 void humanoid::LeprosyHandler()
5730 {
5731   if(IsImmuneToLeprosy())
5732   {
5733     return;
5734   }
5735 
5736   if(!RAND_N(1000 * GetAttribute(ENDURANCE)))
5737     DropRandomNonVitalBodypart();
5738 
5739   if(!game::IsInWilderness())
5740   {
5741     for(int d = 0; d < GetNeighbourSquares(); ++d)
5742     {
5743       lsquare* Square = GetNeighbourLSquare(d);
5744 
5745       if(Square && Square->GetCharacter())
5746         Square->GetCharacter()->TryToInfectWithLeprosy(this);
5747     }
5748   }
5749 
5750   character::LeprosyHandler();
5751 }
5752 
DropRandomNonVitalBodypart()5753 void humanoid::DropRandomNonVitalBodypart()
5754 {
5755   int BodyPartIndexToDrop = GetRandomNonVitalBodyPart();
5756 
5757   if(BodyPartIndexToDrop != NONE_INDEX)
5758     DropBodyPart(BodyPartIndexToDrop);
5759 }
5760 
DropBodyPart(int Index)5761 void humanoid::DropBodyPart(int Index)
5762 {
5763   if(!GetBodyPart(Index)->IsAlive())
5764     return;
5765 
5766   festring NameOfDropped = GetBodyPart(Index)->GetBodyPartName();
5767   item* Dropped = SevereBodyPart(Index);
5768 
5769   if(Dropped)
5770   {
5771     GetStack()->AddItem(Dropped);
5772     Dropped->DropEquipment();
5773 
5774     if(IsPlayer())
5775     {
5776       ADD_MESSAGE("You feel very ill. Your %s snaps off.", NameOfDropped.CStr());
5777       game::AskForKeyPress(CONST_S("Bodypart severed! [press any key to continue]"));
5778       DeActivateVoluntaryAction();
5779     }
5780     else if(CanBeSeenByPlayer())
5781       ADD_MESSAGE("Suddenly %s's %s snaps off.", CHAR_NAME(DEFINITE), NameOfDropped.CStr());
5782   }
5783   else
5784   {
5785     if(IsPlayer())
5786     {
5787       ADD_MESSAGE("You feel very ill. Your %s disappears.", NameOfDropped.CStr());
5788       game::AskForKeyPress(CONST_S("Bodypart destroyed! [press any key to continue]"));
5789       DeActivateVoluntaryAction();
5790     }
5791     else if(CanBeSeenByPlayer())
5792       ADD_MESSAGE("Suddenly %s's %s disappears.", CHAR_NAME(DEFINITE), NameOfDropped.CStr());
5793   }
5794 }
5795 
DuplicateEquipment(character * Receiver,ulong Flags)5796 void humanoid::DuplicateEquipment(character* Receiver, ulong Flags)
5797 {
5798   character::DuplicateEquipment(Receiver, Flags);
5799   EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, GetRightWielded());
5800   EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, GetLeftWielded());
5801 }
5802 
CanHear() const5803 truth character::CanHear() const
5804 {
5805   return DataBase->CanHear && HasHead();
5806 }
5807 
GetTorsoMainColor() const5808 col16 veterankamikazedwarf::GetTorsoMainColor() const
5809 {
5810   return GetMasterGod()->GetEliteColor();
5811 }
5812 
GetGauntletColor() const5813 col16 veterankamikazedwarf::GetGauntletColor() const
5814 {
5815   return GetMasterGod()->GetEliteColor();
5816 }
5817 
GetLegMainColor() const5818 col16 veterankamikazedwarf::GetLegMainColor() const
5819 {
5820   return GetMasterGod()->GetEliteColor();
5821 }
5822 
GetTorsoMainColor() const5823 col16 archangel::GetTorsoMainColor() const
5824 {
5825   return GetMasterGod()->GetEliteColor();
5826 }
5827 
GetArmMainColor() const5828 col16 archangel::GetArmMainColor() const
5829 {
5830   return GetMasterGod()->GetEliteColor();
5831 }
5832 
CreateInitialEquipment(int SpecialFlags)5833 void archangel::CreateInitialEquipment(int SpecialFlags)
5834 {
5835   humanoid::CreateInitialEquipment(SpecialFlags);
5836   GetStack()->AddItem(holybook::Spawn(GetConfig(), SpecialFlags));
5837 
5838   switch(GetMasterGod()->GetBasicAlignment())
5839   {
5840    case GOOD:
5841     GetRightArm()->SetDexterity(70);
5842     GetLeftArm()->SetDexterity(70);
5843     break;
5844    case NEUTRAL:
5845     SetEndurance(70);
5846     break;
5847    case EVIL:
5848     GetRightArm()->SetStrength(70);
5849     GetLeftArm()->SetStrength(70);
5850   }
5851 }
5852 
PostConstruct()5853 void zombie::PostConstruct()
5854 {
5855   if(!RAND_N(3))
5856     GainIntrinsic(LEPROSY);
5857 }
5858 
MoveRandomly()5859 truth orc::MoveRandomly()
5860 {
5861   return GetConfig() == REPRESENTATIVE ? MoveRandomlyInRoom() : humanoid::MoveRandomly();
5862 }
5863 
PostConstruct()5864 void orc::PostConstruct()
5865 {
5866   if(!RAND_N(25))
5867     GainIntrinsic(LEPROSY);
5868 }
5869 
AllowEquipment(citem * Item,int EquipmentIndex) const5870 truth mistress::AllowEquipment(citem* Item, int EquipmentIndex) const
5871 {
5872   return ((EquipmentIndex != RIGHT_WIELDED_INDEX
5873            && EquipmentIndex != LEFT_WIELDED_INDEX)
5874           || Item->IsWhip());
5875 }
5876 
GetAttributeAverage() const5877 int humanoid::GetAttributeAverage() const
5878 {
5879   return GetSumOfAttributes() / 9;
5880 }
5881 
CreateCorpse(lsquare * Square)5882 void golem::CreateCorpse(lsquare* Square)
5883 {
5884   material* Material = GetTorso()->GetMainMaterial();
5885 
5886   if(Material->IsLiquid())
5887   {
5888     for(int d = 0; d < GetExtendedNeighbourSquares(); ++d)
5889     {
5890       lsquare* NeighbourSquare = Square->GetNeighbourLSquare(d);
5891 
5892       if(NeighbourSquare)
5893         NeighbourSquare->SpillFluid(0, static_cast<liquid*>(GetTorso()->GetMainMaterial()->SpawnMore(250 + RAND() % 250)));
5894     }
5895   }
5896   else if(Material->IsGaseous())
5897   {
5898     game::GetCurrentLevel()->GasExplosion(static_cast<gas*>(Material), Square, this);
5899   }
5900   else if(Material->IsSolid())
5901   {
5902     Square->AddItem(Material->CreateNaturalForm(ItemVolume));
5903   }
5904 
5905   SendToHell();
5906 }
5907 
golem()5908 golem::golem()
5909 {
5910   if(!game::IsLoading())
5911     ItemVolume = 50 + RAND_N(100);
5912 }
5913 
Save(outputfile & SaveFile) const5914 void golem::Save(outputfile& SaveFile) const
5915 {
5916   humanoid::Save(SaveFile);
5917   SaveFile << ItemVolume;
5918 }
5919 
Load(inputfile & SaveFile)5920 void golem::Load(inputfile& SaveFile)
5921 {
5922   humanoid::Load(SaveFile);
5923   SaveFile >> ItemVolume;
5924 }
5925 
CanVomit() const5926 truth humanoid::CanVomit() const
5927 {
5928   return HasHead() && character::CanVomit();
5929 }
5930 
CheckApply() const5931 truth humanoid::CheckApply() const
5932 {
5933   if(!character::CheckApply())
5934     return false;
5935 
5936   if(!HasAUsableArm())
5937   {
5938     ADD_MESSAGE("You need a usable arm to apply.");
5939     return false;
5940   }
5941 
5942   return true;
5943 }
5944 
GetSpellAPCost() const5945 int darkmage::GetSpellAPCost() const
5946 {
5947   switch(GetConfig())
5948   {
5949    case APPRENTICE: return 4000;
5950    case BATTLE_MAGE: return 2000;
5951    case ELDER: return 1000;
5952    case ARCH_MAGE: return 500;
5953   }
5954 
5955   return 4000;
5956 }
5957 
GetSpellAPCost() const5958 int aslonawizard::GetSpellAPCost() const
5959 {
5960   /*switch(GetConfig())
5961   {
5962    case APPRENTICE: return 4000;
5963    case BATTLE_MAGE: return 2000;
5964    case ELDER: return 1000;
5965    case ARCH_MAGE: return 500;
5966  }*/
5967 
5968   return 1000;
5969 }
5970 
GetSpellAPCost() const5971 int necromancer::GetSpellAPCost() const
5972 {
5973   switch(GetConfig())
5974   {
5975    case APPRENTICE_NECROMANCER: return 2000;
5976    case MASTER_NECROMANCER: return 1000;
5977   }
5978 
5979   return 4000;
5980 }
5981 
5982 /* Horrible repeating. Sorry */
5983 
BeTalkedTo()5984 void tailor::BeTalkedTo()
5985 {
5986   if(!GetPos().IsAdjacent(PLAYER->GetPos()))
5987     return;
5988 
5989   if(GetRelation(PLAYER) == HOSTILE)
5990   {
5991     ADD_MESSAGE("\"You talkin' to me? You talkin' to me? You talkin' to me? Then who "
5992                 "the hell else are you talkin' to? You talkin' to me? Well I'm the "
5993                 "only one here. Who do you think you're talking to? Oh yeah? Huh? Ok.\"");
5994     return;
5995   }
5996 
5997   if(PLAYER->PossessesItem(&item::IsFixableByTailor))
5998   {
5999     item* Item = PLAYER->SelectFromPossessions(CONST_S("\"What do you want me to fix?\""), &item::IsFixableByTailor);
6000 
6001     if(!Item)
6002       return;
6003 
6004     if(!(Item->GetMainMaterial()->GetCategoryFlags() & CAN_BE_TAILORED))
6005     {
6006       ADD_MESSAGE("\"I can't work on %s.\"", Item->GetMainMaterial()->GetName(false, false).CStr());
6007       return;
6008     }
6009 
6010     if(Item->GetMainMaterial()->IsBurning())
6011     {
6012       ADD_MESSAGE("\"Hey I'm no fire fighter! Put those flames out and then I might be able to do something for you.\"");
6013       return;
6014     }
6015 
6016     /** update messages */
6017 
6018     long FixPrice = Item->GetFixPrice();
6019 
6020     if(PLAYER->GetMoney() < FixPrice)
6021     {
6022       ADD_MESSAGE("\"Getting that fixed costs you %ld gold pieces. Get the money and we'll talk.\"", FixPrice);
6023       return;
6024     }
6025 
6026     ADD_MESSAGE("\"I can fix your %s, but it'll cost you %ld gold pieces.\"", Item->CHAR_NAME(UNARTICLED), FixPrice);
6027 
6028     if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]")))
6029     {
6030       Item->RemoveBurns();
6031       Item->ResetThermalEnergies(); // OK because the item shouldn't be burning in the first place
6032       Item->ResetBurning();
6033       Item->Fix();
6034       PLAYER->EditMoney(-FixPrice);
6035       ADD_MESSAGE("%s fixes %s in no time.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(DEFINITE));
6036     }
6037   }
6038   else
6039     ADD_MESSAGE("\"Come back when you have some weapons or armor I can fix.\"");
6040 }
6041 
PostConstruct()6042 void veterankamikazedwarf::PostConstruct()
6043 {
6044   kamikazedwarf::PostConstruct();
6045 
6046   ivantime Time;
6047   game::GetTime(Time);
6048   int Modifier = Time.Day - KAMIKAZE_INVISIBILITY_DAY_MIN;
6049 
6050   if(Time.Day >= KAMIKAZE_INVISIBILITY_DAY_MAX
6051      || (Modifier > 0
6052          && RAND_N(KAMIKAZE_INVISIBILITY_DAY_MAX - KAMIKAZE_INVISIBILITY_DAY_MIN) < Modifier))
6053     GainIntrinsic(INVISIBLE);
6054 }
6055 
PostConstruct()6056 void imp::PostConstruct()
6057 {
6058   humanoid::PostConstruct();
6059 
6060   ivantime Time;
6061   game::GetTime(Time);
6062   int Modifier = Time.Day - KAMIKAZE_INVISIBILITY_DAY_MIN;
6063 
6064   if(Time.Day >= KAMIKAZE_INVISIBILITY_DAY_MAX
6065      || (Modifier > 0
6066          && RAND_N(KAMIKAZE_INVISIBILITY_DAY_MAX - KAMIKAZE_INVISIBILITY_DAY_MIN) < Modifier))
6067     GainIntrinsic(INVISIBLE);
6068 }
6069 
PostConstruct()6070 void siren::PostConstruct()
6071 {
6072   humanoid::PostConstruct();
6073 
6074   if(GetConfig() != AMBASSADOR_SIREN)
6075   {
6076     ivantime Time;
6077     game::GetTime(Time);
6078     int Modifier = Time.Day - KAMIKAZE_INVISIBILITY_DAY_MIN;
6079 
6080     if(Time.Day >= KAMIKAZE_INVISIBILITY_DAY_MAX
6081        || (Modifier > 0
6082            && RAND_N(KAMIKAZE_INVISIBILITY_DAY_MAX - KAMIKAZE_INVISIBILITY_DAY_MIN) < Modifier))
6083       GainIntrinsic(INVISIBLE);
6084   }
6085 }
6086 
IsTransparent() const6087 truth humanoid::IsTransparent() const
6088 {
6089   return character::IsTransparent() || !(GetRightLeg() || GetLeftLeg());
6090 }
6091 
ModifySituationDanger(double & Danger) const6092 void humanoid::ModifySituationDanger(double& Danger) const
6093 {
6094   character::ModifySituationDanger(Danger);
6095 
6096   switch(GetUsableArms())
6097   {
6098    case 0: Danger *= 10;
6099    case 1: Danger *= 2;
6100   }
6101 
6102   switch(GetUsableLegs())
6103   {
6104    case 0: Danger *= 10;
6105    case 1: Danger *= 2;
6106   }
6107 }
6108 
GetAICommand()6109 void oree::GetAICommand()
6110 {
6111   if(!RAND_N(50))
6112     CallForMonsters();
6113 
6114   humanoid::GetAICommand();
6115 }
6116 
CallForMonsters()6117 void oree::CallForMonsters()
6118 {
6119   if(GetDungeon()->GetIndex() != ELPURI_CAVE || GetLevel()->GetIndex() != OREE_LAIR)
6120     return;
6121 
6122   character* ToBeCalled = 0;
6123 
6124   switch(RAND_N(6))
6125   {
6126    case 0:
6127     ToBeCalled = darkknight::Spawn(ELITE);
6128     break;
6129    case 1:
6130     ToBeCalled = frog::Spawn(RAND_2 ? GREATER_DARK : GIANT_DARK);
6131     break;
6132    case 2:
6133     ToBeCalled = frog::Spawn(DARK);
6134     break;
6135    case 3:
6136     ToBeCalled = darkmage::Spawn(RAND_2 ? APPRENTICE : BATTLE_MAGE);
6137     break;
6138    case 4:
6139     ToBeCalled = darkmage::Spawn(RAND_2 ? APPRENTICE : ELDER);
6140     break;
6141    case 5:
6142     ToBeCalled = necromancer::Spawn(RAND_2 ? APPRENTICE_NECROMANCER : MASTER_NECROMANCER);
6143     break;
6144   }
6145 
6146   v2 TryToCreate;
6147 
6148   for(int c = 0; c < 100; ++c)
6149   {
6150     TryToCreate = game::GetMonsterPortal()->GetPos() + game::GetMoveVector(RAND() % DIRECTION_COMMAND_KEYS);
6151 
6152     if(GetArea()->IsValidPos(TryToCreate)
6153        && ToBeCalled->CanMoveOn(GetNearLSquare(TryToCreate))
6154        && ToBeCalled->IsFreeForMe(GetNearLSquare(TryToCreate)))
6155     {
6156       ToBeCalled->SetTeam(game::GetTeam(MONSTER_TEAM));
6157       ToBeCalled->PutTo(TryToCreate);
6158       return;
6159     }
6160   }
6161 
6162   delete ToBeCalled;
6163 }
6164 
GetAICommand()6165 void priest::GetAICommand()
6166 {
6167   if(GetConfig() == INFUSCOR)
6168   {
6169     if(!RAND_N(50))
6170       CallForMonsters();
6171   }
6172 
6173   if(CheckAIZapOpportunity())
6174     return;
6175 
6176   StandIdleAI();
6177 }
6178 
CallForMonsters()6179 void priest::CallForMonsters()
6180 {
6181   if(GetDungeon()->GetIndex() != XINROCH_TOMB || GetLevel()->GetIndex() != NECRO_CHAMBER_LEVEL)
6182     return;
6183 
6184   character* ToBeCalled = 0;
6185 
6186   switch(RAND_N(6))
6187   {
6188    case 0:
6189     ToBeCalled = skeleton::Spawn(RAND_2 ? 0 : WARRIOR);
6190     break;
6191    case 1:
6192     ToBeCalled = zombie::Spawn();
6193     break;
6194    case 2:
6195     ToBeCalled = frog::Spawn(DARK);
6196     break;
6197    case 3:
6198     ToBeCalled = skeleton::Spawn(RAND_2 ? 0 : WARRIOR);
6199     break;
6200    case 4:
6201     ToBeCalled = zombie::Spawn();
6202     break;
6203    case 5:
6204     ToBeCalled = necromancer::Spawn(RAND_2 ? APPRENTICE_NECROMANCER : MASTER_NECROMANCER);
6205     break;
6206   }
6207 
6208   v2 TryToCreate;
6209 
6210   for(int c = 0; c < 100; ++c)
6211   {
6212     TryToCreate = game::GetMonsterPortal()->GetPos() + game::GetMoveVector(RAND() % DIRECTION_COMMAND_KEYS);
6213 
6214     if(GetArea()->IsValidPos(TryToCreate)
6215        && ToBeCalled->CanMoveOn(GetNearLSquare(TryToCreate))
6216        && ToBeCalled->IsFreeForMe(GetNearLSquare(TryToCreate)))
6217     {
6218       ToBeCalled->SetTeam(game::GetTeam(MONSTER_TEAM));
6219       ToBeCalled->PutTo(TryToCreate);
6220       return;
6221     }
6222   }
6223 
6224   delete ToBeCalled;
6225 }
6226 
RandomizeTryToUnStickBodyPart(ulong PossibleBodyParts) const6227 int humanoid::RandomizeTryToUnStickBodyPart(ulong PossibleBodyParts) const
6228 {
6229   int Possible = 0, PossibleArray[3];
6230 
6231   if(RightArmIsUsable() && 1 << RIGHT_ARM_INDEX & PossibleBodyParts)
6232     PossibleArray[Possible++] = RIGHT_ARM_INDEX;
6233 
6234   if(LeftArmIsUsable() && 1 << LEFT_ARM_INDEX & PossibleBodyParts)
6235     PossibleArray[Possible++] = LEFT_ARM_INDEX;
6236 
6237   if(Possible)
6238     return PossibleArray[RAND_N(Possible)];
6239 
6240   if(RightLegIsUsable() && 1 << RIGHT_LEG_INDEX & PossibleBodyParts)
6241     PossibleArray[Possible++] = RIGHT_LEG_INDEX;
6242 
6243   if(LeftLegIsUsable() && 1 << LEFT_LEG_INDEX & PossibleBodyParts)
6244     PossibleArray[Possible++] = LEFT_LEG_INDEX;
6245 
6246   if(Possible)
6247     return PossibleArray[RAND_N(Possible)];
6248 
6249   if(GetHead() && 1 << HEAD_INDEX & PossibleBodyParts)
6250     return HEAD_INDEX;
6251 
6252   if(GetGroin() && 1 << GROIN_INDEX & PossibleBodyParts)
6253     PossibleArray[Possible++] = GROIN_INDEX;
6254 
6255   if(1 << TORSO_INDEX & PossibleBodyParts)
6256     PossibleArray[Possible++] = TORSO_INDEX;
6257 
6258   return Possible ? PossibleArray[RAND_N(Possible)] : NONE_INDEX;
6259 }
6260 
HasAUsableArm() const6261 truth humanoid::HasAUsableArm() const
6262 {
6263   arm* R = GetRightArm(), * L = GetLeftArm();
6264   return (R && R->IsUsable()) || (L && L->IsUsable());
6265 }
6266 
HasAUsableLeg() const6267 truth humanoid::HasAUsableLeg() const
6268 {
6269   leg* R = GetRightLeg(), * L = GetLeftLeg();
6270   return (R && R->IsUsable()) || (L && L->IsUsable());
6271 }
6272 
HasTwoUsableLegs() const6273 truth humanoid::HasTwoUsableLegs() const
6274 {
6275   leg* R = GetRightLeg(), * L = GetLeftLeg();
6276   return R && R->IsUsable() && L && L->IsUsable();
6277 }
6278 
CanAttackWithAnArm() const6279 truth humanoid::CanAttackWithAnArm() const
6280 {
6281   arm* R = GetRightArm();
6282 
6283   if(R && R->GetDamage())
6284     return true;
6285 
6286   arm* L = GetLeftArm();
6287   return L && L->GetDamage();
6288 }
6289 
RightArmIsUsable() const6290 truth humanoid::RightArmIsUsable() const
6291 {
6292   arm* A = GetRightArm();
6293   return A && A->IsUsable();
6294 }
6295 
LeftArmIsUsable() const6296 truth humanoid::LeftArmIsUsable() const
6297 {
6298   arm* A = GetLeftArm();
6299   return A && A->IsUsable();
6300 }
6301 
RightLegIsUsable() const6302 truth humanoid::RightLegIsUsable() const
6303 {
6304   leg* L = GetRightLeg();
6305   return L && L->IsUsable();
6306 }
6307 
LeftLegIsUsable() const6308 truth humanoid::LeftLegIsUsable() const
6309 {
6310   leg* L = GetLeftLeg();
6311   return L && L->IsUsable();
6312 }
6313 
AllowUnconsciousness() const6314 truth humanoid::AllowUnconsciousness() const
6315 {
6316   return (DataBase->AllowUnconsciousness && TorsoIsAlive()
6317           && BodyPartIsVital(HEAD_INDEX));
6318 }
6319 
CanChokeOnWeb(web * Web) const6320 truth humanoid::CanChokeOnWeb(web* Web) const
6321 {
6322   return CanChoke() && Web->IsStuckToBodyPart(HEAD_INDEX);
6323 }
6324 
BrainsHurt() const6325 truth humanoid::BrainsHurt() const
6326 {
6327   head* Head = GetHead();
6328   return !Head || Head->IsBadlyHurt();
6329 }
6330 
IsHeadless() const6331 truth humanoid::IsHeadless() const
6332 {
6333   head* Head = GetHead();
6334   return !Head;
6335 }
6336 
PostConstruct()6337 void playerkind::PostConstruct()
6338 {
6339   int R = 0, G = 0, B = 0;
6340 
6341   switch(RAND_N(4))
6342   {
6343    case 0: R = 195; G = 165; B = 40; break;
6344    case 1: R = 130; G = 30; B = 0; break;
6345    case 2: R = 30; G = 30; B = 15; break;
6346    case 3: R = 50; G = 30; B = 5; break;
6347   }
6348 
6349   HairColor = MakeRGB16(R + RAND_N(41), G + RAND_N(41), B + RAND_N(41));
6350 
6351   switch(RAND_N(4))
6352   {
6353    case 0: R = 25; G = 0; B = 70; break;
6354    case 1: R = 5; G = 0; B = 50; break;
6355    case 2: R = 10; G = 10; B = 10; break;
6356    case 3: R = 60; G = 20; B = 0; break;
6357   }
6358 
6359   EyeColor = MakeRGB16(R + RAND_N(41), G + RAND_N(41), B + RAND_N(41));
6360   Talent = RAND_N(TALENTS);
6361   Weakness = RAND_N(TALENTS);
6362 }
6363 
GetHeadBitmapPos() const6364 v2 playerkind::GetHeadBitmapPos() const
6365 {
6366   int Sum = GetAttribute(INTELLIGENCE, false) + GetAttribute(WISDOM, false);
6367 
6368   if(Sum >= 60)
6369     return v2(96, 480);
6370   else if(Sum >= 40)
6371     return v2(96, 464);
6372   else
6373     return v2(96, 416);
6374 }
6375 
GetRightArmBitmapPos() const6376 v2 playerkind::GetRightArmBitmapPos() const
6377 {
6378   if(GetRightArm()->GetAttribute(ARM_STRENGTH, false) >= 20)
6379     return v2(64, 448);
6380   else
6381     return v2(64, 416);
6382 }
6383 
GetLeftArmBitmapPos() const6384 v2 playerkind::GetLeftArmBitmapPos() const
6385 {
6386   if(GetLeftArm()->GetAttribute(ARM_STRENGTH, false) >= 20)
6387     return v2(64, 448);
6388   else
6389     return v2(64, 416);
6390 }
6391 
GetNaturalSparkleFlags() const6392 int playerkind::GetNaturalSparkleFlags() const
6393 {
6394   return GetAttribute(CHARISMA) >= 30 ? SKIN_COLOR : 0;
6395 }
6396 
PostConstruct()6397 void slave::PostConstruct()
6398 {
6399   Talent = TALENT_STRONG;
6400   Weakness = TALENT_CLEVER;
6401 }
6402 
6403 cint TalentOfAttribute[ATTRIBUTES] =
6404 {
6405   TALENT_HEALTHY,
6406   TALENT_FAST_N_ACCURATE,
6407   TALENT_CLEVER,
6408   TALENT_CLEVER,
6409   TALENT_CLEVER,
6410   TALENT_CLEVER,
6411   TALENT_CLEVER,
6412   TALENT_STRONG,
6413   TALENT_STRONG,
6414   TALENT_FAST_N_ACCURATE,
6415   TALENT_FAST_N_ACCURATE
6416 };
6417 const double TalentBonusOfAttribute[ATTRIBUTES] = { 1.1, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25 };
6418 
GetNaturalExperience(int Identifier) const6419 double playerkind::GetNaturalExperience(int Identifier) const
6420 {
6421   double NE = DataBase->NaturalExperience[Identifier];
6422 
6423   if(Talent == TalentOfAttribute[Identifier])
6424     NE *= TalentBonusOfAttribute[Identifier];
6425 
6426   if(Weakness == TalentOfAttribute[Identifier])
6427     NE /= TalentBonusOfAttribute[Identifier];
6428 
6429   return NE;
6430 }
6431 
GetHeadBitmapPos() const6432 v2 bonesghost::GetHeadBitmapPos() const
6433 {
6434   int Sum = GetAttribute(INTELLIGENCE, false) + GetAttribute(WISDOM, false);
6435   // Bonesghosts have their attributes lowered upon generation, hence these lowered thresholds. Should be mathematically correct.
6436   if(Sum >= 52)
6437     return v2(96, 480);
6438   else if(Sum >= 32)
6439     return v2(96, 464);
6440   else
6441     return v2(96, 416);
6442 }
6443 
GetRightArmBitmapPos() const6444 v2 bonesghost::GetRightArmBitmapPos() const
6445 {
6446   if(GetRightArm()->GetAttribute(ARM_STRENGTH, false) >= 16)
6447     return v2(64, 448);
6448   else
6449     return v2(64, 416);
6450 }
6451 
GetLeftArmBitmapPos() const6452 v2 bonesghost::GetLeftArmBitmapPos() const
6453 {
6454   if(GetLeftArm()->GetAttribute(ARM_STRENGTH, false) >= 16)
6455     return v2(64, 448);
6456   else
6457     return v2(64, 416);
6458 }
6459 
PostConstruct()6460 void bonesghost::PostConstruct()
6461 {
6462   // This seems strange, but it works because the player is still alive when a bonesghost is created.
6463   HairColor = PLAYER->GetHairColor();
6464   EyeColor = PLAYER->GetEyeColor();
6465 }
6466 
GetRunDescriptionLine(int I) const6467 cchar* humanoid::GetRunDescriptionLine(int I) const
6468 {
6469   if(!GetRunDescriptionLineOne().IsEmpty())
6470     return !I ? GetRunDescriptionLineOne().CStr() : GetRunDescriptionLineTwo().CStr();
6471 
6472   if(IsFlying())
6473     return !I ? "Flying" : " very fast";
6474 
6475   if(IsSwimming())
6476   {
6477     if(IsPlayer() && game::IsInWilderness() && game::PlayerHasBoat())
6478       return !I ? "Sailing" : " very fast";
6479     else if(!GetRightArm() && !GetLeftArm() && !GetRightLeg() && !GetLeftLeg())
6480       return !I ? "Floating" : " ahead fast";
6481     else
6482       return !I ? "Swimming" : " very fast";
6483   }
6484 
6485   if(!GetRightLeg() && !GetLeftLeg())
6486     return !I ? "Rolling" : " very fast";
6487 
6488   if(!GetRightLeg() || !GetLeftLeg())
6489     return !I ? "Hopping" : " very fast";
6490 
6491   return !I ? "Running" : "";
6492 }
6493 
GetNormalDeathMessage() const6494 cchar* humanoid::GetNormalDeathMessage() const
6495 {
6496   if(BodyPartIsVital(HEAD_INDEX) && (!GetHead() || GetHead()->GetHP() <= 0))
6497     return !RAND_2 ? "beheaded @k" : "decapitated @k";
6498   else if(BodyPartIsVital(GROIN_INDEX) && (!GetGroin() || GetGroin()->GetHP() <= 0))
6499     return "killed @bkp dirty attack below the belt";
6500   else
6501     return character::GetNormalDeathMessage();
6502 }
6503 
SingRandomSong()6504 void kamikazedwarf::SingRandomSong()
6505 {
6506   festring Song;
6507   festring God = GetMasterGod()->GetName();
6508   festring Bodypart;
6509 
6510   switch(RAND_N(9))
6511   {
6512    case 0:
6513 
6514     switch(RAND_N(3))
6515     {
6516      case 0:
6517       Bodypart = "palm";
6518       break;
6519 
6520      case 1:
6521       Bodypart = "forehead";
6522       break;
6523 
6524      default:
6525       Bodypart = "tongue";
6526       break;
6527     }
6528     Song = festring("On the ") + Bodypart + festring(" of ") + God + " everybody fears everything";
6529     break;
6530    case 1:
6531     {
6532       cchar* Title = GetMasterGod()->GetSex() == MALE ? "King" : "Queen";
6533       Song = festring("Joy to the world, ") + God
6534              + " is come! Let all above Valpurus receive her " + Title;
6535       break;
6536     }
6537    case 2:
6538     Song = festring("Hark the herald angels sing. Glory to ") + God + "!";
6539     break;
6540    case 3:
6541     Song = festring("O ") + God
6542            + ", You are so big, So absolutely huge, Gosh, "
6543            "we're all really impressed down here, I can tell You.";
6544     break;
6545    case 4:
6546     Song = festring("Forgive us, O ") + God
6547            + " for this, our dreadful toadying and barefaced flattery";
6548     break;
6549    case 5:
6550     Song = festring("But you, ") + God
6551            + ", are so strong and, well, just so super fantastic. Amen.";
6552     break;
6553    case 6:
6554     Song = festring("O ") + God + ", please don't burn us";
6555     break;
6556    case 7:
6557     Song = festring("O ") + God + ", please don't grill or toast your flock";
6558     break;
6559    case 8:
6560     Song = festring("O ") + God + ", please don't simmer us in stock";
6561     break;
6562   }
6563 
6564   EditAP(-1000);
6565 
6566   if(CanBeSeenByPlayer())
6567     ADD_MESSAGE("%s sings: \"%s\"",
6568                 CHAR_DESCRIPTION(DEFINITE), Song.CStr());
6569   else
6570     ADD_MESSAGE("You hear someone sing: \"%s\"", Song.CStr());
6571 }
6572 
DisplayStethoscopeInfo(character *) const6573 void imperialist::DisplayStethoscopeInfo(character*) const
6574 {
6575   ADD_MESSAGE("You hear coins clinking inside.");
6576 }
6577 
ApplySpecialAttributeBonuses()6578 void humanoid::ApplySpecialAttributeBonuses()
6579 {
6580   if(GetHead())
6581   {
6582     AttributeBonus[CHARISMA] -= GetHead()->
6583                                 CalculateScarAttributePenalty(GetAttribute(CHARISMA, false));
6584   }
6585   else
6586     AttributeBonus[CHARISMA] -= GetAttribute(CHARISMA, false) - 1;
6587 }
6588 
GetAICommand()6589 void siren::GetAICommand()
6590 {
6591   if(TryToSing())
6592     return;
6593 
6594   humanoid::GetAICommand();
6595 }
6596 
MoveRandomly()6597 truth siren::MoveRandomly()
6598 {
6599   if(GetConfig() == AMBASSADOR_SIREN)
6600   {
6601     return MoveRandomlyInRoom();
6602   }
6603   else
6604   {
6605     return humanoid::MoveRandomly();
6606   }
6607 }
6608 
TryToSing()6609 truth siren::TryToSing()
6610 {
6611   truth Success = false;
6612   for(int d = 0; d < GetNeighbourSquares(); ++d)
6613   {
6614     lsquare* Square = GetNeighbourLSquare(d);
6615 
6616     if(Square && Square->GetCharacter())
6617     {
6618       Success = Square->GetCharacter()->ReceiveSirenSong(this);
6619 
6620       if(Success)
6621         break;
6622     }
6623   }
6624   if(Success)
6625     EditAP(-2000);
6626 
6627   return Success;
6628 }
6629 
MindWormCanPenetrateSkull(mindworm *) const6630 truth humanoid::MindWormCanPenetrateSkull(mindworm*) const
6631 {
6632   if(GetHelmet())
6633   {
6634     if(RAND() % 100 < GetHelmet()->GetCoverPercentile())
6635       return false;
6636   }
6637 
6638   return true;
6639 }
6640 
HasSadistWeapon() const6641 truth humanoid::HasSadistWeapon() const
6642 {
6643   arm* Right = GetRightArm(), * Left = GetLeftArm();
6644   return (Right && Right->HasSadistWeapon()) || (Left && Left->HasSadistWeapon());
6645 }
6646 
HasSadistAttackMode() const6647 truth humanoid::HasSadistAttackMode() const
6648 {
6649   return HasSadistWeapon() || IsUsingLegs();
6650 }
6651 
Save(outputfile & SaveFile) const6652 void petrusswife::Save(outputfile& SaveFile) const
6653 {
6654   humanoid::Save(SaveFile);
6655   SaveFile << GiftTotal;
6656 }
6657 
Load(inputfile & SaveFile)6658 void petrusswife::Load(inputfile& SaveFile)
6659 {
6660   humanoid::Load(SaveFile);
6661   SaveFile >> GiftTotal;
6662 }
6663 
BeTalkedTo()6664 void petrusswife::BeTalkedTo()
6665 {
6666   if(!GetPos().IsAdjacent(PLAYER->GetPos()))
6667     return;
6668 
6669   itemvector Item;
6670 
6671   // NOTE: Remember that Petrus' wife number 5 is mute.
6672   if(!PLAYER->SelectFromPossessions(Item,
6673       (GetConfig() == 5) ? CONST_S("Do you want to offer her a gift?") : CONST_S("\"Do you have something to give me?\""),
6674       0, &item::IsLuxuryItem)
6675      || Item.empty())
6676     humanoid::BeTalkedTo();
6677 
6678   int Accepted = 0;
6679   truth RefusedSomething = false;
6680 
6681   for(size_t c = 0; c < Item.size(); ++c)
6682     if(!MakesBurdened(GetCarriedWeight() + Item[c]->GetWeight()))
6683     {
6684       ++Accepted;
6685       GiftTotal += Item[c]->GetTruePrice();
6686       Item[c]->RemoveFromSlot();
6687       GetStack()->AddItem(Item[c]);
6688     }
6689     else
6690     {
6691       RefusedSomething = true;
6692       break;
6693     }
6694 
6695   if((GetConfig() == 5) && (Accepted || RefusedSomething))
6696   {
6697     ADD_MESSAGE("%s smiles at you.", CHAR_NAME(DEFINITE));
6698     return;
6699   }
6700 
6701   if(Accepted)
6702     ADD_MESSAGE("\"I thank you for your little gift%s.\"", Accepted == 1 ? "" : "s");
6703 
6704   if(RefusedSomething)
6705     ADD_MESSAGE("\"Unfortunately I cannot carry any more of your gifts. I'm a delicate woman, you see.\"");
6706 }
6707 
BeTalkedTo()6708 void guard::BeTalkedTo()
6709 {
6710   if(!(GetConfig() == EMISSARY) || GetRelation(PLAYER) == HOSTILE)
6711   {
6712     if(GetPos().IsAdjacent(PLAYER->GetPos()) && !(GetRelation(PLAYER) == HOSTILE))
6713     {
6714       itemvector Item;
6715 
6716       if(!PLAYER->SelectFromPossessions(Item, CONST_S("\"Do you have something to give me?\""), 0, &item::IsBeverage)
6717          || Item.empty())
6718 
6719 
6720       for(size_t c = 0; c < Item.size(); ++c)
6721       {
6722         Item[c]->RemoveFromSlot();
6723         GetStack()->AddItem(Item[c]);
6724       }
6725     }
6726 
6727     humanoid::BeTalkedTo();
6728     return;
6729   }
6730 
6731   /* We're talking to the emissary of Aslona from now on. */
6732   if(game::GetStoryState() == 1)
6733   {
6734     ADD_MESSAGE("%s eyes you, calculating: \"I might have work for you and I can make it worth your while.\"", CHAR_NAME(DEFINITE));
6735 
6736     if(game::TruthQuestion(CONST_S("Do you accept the quest? [y/N]"), REQUIRES_ANSWER))
6737     {
6738       game::TextScreen(CONST_S("\"I shouldn't be saying this so openly, but my kingdom is in dire straits and needs any\n"
6739                                "help it can get. High priest Petrus will not hear my pleas and I don't believe that\n"
6740                                "my colleagues in other lands will be more successful. Lord Regent is doing his best,\n"
6741                                "but his army just barely holds the rebels back.\"\n\n"
6742                                "\"I know you are just one man, but maybe you could help where an army couldn't. Please,\n"
6743                                "go to the Castle of Aslona and seek out Lord Efra Peredivall. He will know what must\n"
6744                                "be done to mercilessly crush the rebel scum!\""));
6745 
6746       game::GivePlayerBoat();
6747       game::LoadWorldMap();
6748       v2 AslonaPos = game::GetWorldMap()->GetEntryPos(0, ASLONA_CASTLE);
6749       game::GetWorldMap()->GetWSquare(AslonaPos)->ChangeOWTerrain(aslonacastle::Spawn());
6750       game::GetWorldMap()->RevealEnvironment(AslonaPos, 1);
6751       v2 RebelCampPos = game::GetWorldMap()->GetEntryPos(0, REBEL_CAMP);
6752       game::GetWorldMap()->GetWSquare(RebelCampPos)->ChangeOWTerrain(rebelcamp::Spawn());
6753       game::GetWorldMap()->RevealEnvironment(RebelCampPos, 0);
6754       game::SaveWorldMap();
6755       GetArea()->SendNewDrawRequest();
6756       ADD_MESSAGE("\"If you need to cross the sea, you can use my ship. It should be waiting at the shore.\"");
6757       game::SetAslonaStoryState(1);
6758       game::SetStoryState(2);
6759       return;
6760     }
6761     else
6762     {
6763       ADD_MESSAGE("%s narrows his eyes: \"I would think twice about brushing me aside, if I were you. Think about it.\"", CHAR_NAME(DEFINITE));
6764       return;
6765     }
6766   }
6767 
6768   if(game::GetAslonaStoryState() && !RAND_N(4)) // Isn't he charming?
6769     ADD_MESSAGE("\"You should know that I'm counting on you. My whole country is counting on you. Don't screw it up!\"");
6770   else
6771     humanoid::BeTalkedTo();
6772 }
6773 
GetAICommand()6774 void xinrochghost::GetAICommand()
6775 {
6776   if(GetHomeRoom())
6777     StandIdleAI();
6778   else
6779     humanoid::GetAICommand();
6780 }
6781 
CreateCorpse(lsquare * Square)6782 void xinrochghost::CreateCorpse(lsquare* Square)
6783 {
6784   SendToHell();
6785 /*This needs to be a function someday*/
6786   if(!game::GetCurrentLevel()->IsOnGround())
6787   {
6788     ADD_MESSAGE("Suddenly a horrible earthquake shakes the level.");
6789     ADD_MESSAGE("You hear an eerie scream: \"Ahh! Free at last! FREE TO BE REBORN!\"");
6790     int c, Tunnels = 2 + RAND() % 3;
6791     if(!game::GetCurrentLevel()->EarthquakesAffectTunnels())
6792       Tunnels = 0;
6793 
6794     for(c = 0; c < Tunnels; ++c)
6795       game::GetCurrentLevel()->AttachPos(game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE|ATTACHABLE));
6796 
6797     int ToEmpty = 10 + RAND() % 11;
6798 
6799     for(c = 0; c < ToEmpty; ++c)
6800       for(int i = 0; i < 50; ++i)
6801       {
6802         v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, NOT_WALKABLE);
6803         truth Correct = false;
6804 
6805         for(int d = 0; d < 8; ++d)
6806         {
6807           lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d);
6808 
6809           if(Square && Square->IsFlyable())
6810           {
6811             Correct = true;
6812             break;
6813           }
6814         }
6815 
6816         if(Correct)
6817         {
6818           game::GetCurrentLevel()->GetLSquare(Pos)->ChangeOLTerrainAndUpdateLights(0);
6819           break;
6820         }
6821       }
6822 
6823     int ToGround = 20 + RAND() % 21;
6824 
6825     for(c = 0; c < ToGround; ++c)
6826       for(int i = 0; i < 50; ++i)
6827       {
6828         v2 Pos = game::GetCurrentLevel()->GetRandomSquare(0, RAND() & 1 ? 0 : HAS_CHARACTER);
6829 
6830         if(Pos == ERROR_V2)
6831           continue;
6832 
6833         lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos);
6834         character* Char = Square->GetCharacter();
6835 
6836         if(Square->GetOLTerrain() || (Char && (Char->IsPlayer() || PLAYER->GetRelation(Char) != HOSTILE)))
6837           continue;
6838 
6839         int Walkables = 0;
6840 
6841         for(int d = 0; d < 8; ++d)
6842         {
6843           lsquare* NearSquare = game::GetCurrentLevel()->GetLSquare(Pos)->GetNeighbourLSquare(d);
6844 
6845           if(NearSquare && NearSquare->IsFlyable())
6846             ++Walkables;
6847         }
6848 
6849         if(Walkables > 6)
6850         {
6851           Square->ChangeOLTerrainAndUpdateLights(earth::Spawn());
6852 
6853           if(Char)
6854           {
6855             if(Char->CanBeSeenByPlayer())
6856               ADD_MESSAGE("%s is hit by a brick of earth falling from the roof!", Char->CHAR_NAME(DEFINITE));
6857 
6858             Char->ReceiveDamage(0, 20 + RAND() % 21, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true);
6859             Char->CheckDeath(CONST_S("killed by an earthquake"), 0);
6860           }
6861 
6862           Square->KickAnyoneStandingHereAway();
6863           Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 41, PHYSICAL_DAMAGE);
6864           break;
6865         }
6866       }
6867 
6868     // Generate a few boulders in the level
6869 
6870     int BoulderNumber = 10 + RAND() % 10;
6871 
6872     for(c = 0; c < BoulderNumber; ++c)
6873     {
6874       v2 Pos = game::GetCurrentLevel()->GetRandomSquare();
6875       lsquare* Square = game::GetCurrentLevel()->GetLSquare(Pos);
6876       character* MonsterHere = Square->GetCharacter();
6877 
6878       if(!Square->GetOLTerrain() && (!MonsterHere || MonsterHere->GetRelation(PLAYER) == HOSTILE))
6879       {
6880         Square->ChangeOLTerrainAndUpdateLights(boulder::Spawn(1 + (RAND() & 1)));
6881 
6882         if(MonsterHere)
6883           MonsterHere->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE, HEAD|TORSO, 8, true);
6884 
6885         Square->GetStack()->ReceiveDamage(0, 10 + RAND() % 10, PHYSICAL_DAMAGE);
6886       }
6887     }
6888 
6889     // Damage to items in the level
6890 
6891     for(int x = 0; x < game::GetCurrentLevel()->GetXSize(); ++x)
6892       for(int y = 0; y < game::GetCurrentLevel()->GetYSize(); ++y)
6893         game::GetCurrentLevel()->GetLSquare(x, y)->ReceiveEarthQuakeDamage();
6894   }
6895 }
6896 
SpecialEnemySightedReaction(character *)6897 truth darkknight::SpecialEnemySightedReaction(character*)
6898 {
6899   if((GetConfig() == MASTER))
6900   {
6901     const database* WarLordDataBase;
6902     databasecreator<character>::FindDataBase(WarLordDataBase, &skeleton::ProtoType, WAR_LORD);
6903     skeleton* Skeleton;
6904 
6905     if(!(WarLordDataBase->Flags & HAS_BEEN_GENERATED) && !(RAND() % 5))
6906     {
6907       if(CanBeSeenByPlayer())
6908         ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE));
6909 
6910       Skeleton = skeleton::Spawn(WAR_LORD);
6911       Skeleton->SetTeam(GetTeam());
6912       Skeleton->PutNear(GetPos());
6913       Skeleton->SignalGeneration();
6914 
6915       if(Skeleton->CanBeSeenByPlayer())
6916         ADD_MESSAGE("The whole area trembles terribly as %s emerges from the ground.", Skeleton->CHAR_NAME(DEFINITE));
6917       else if(CanBeSeenByPlayer())
6918         ADD_MESSAGE("%s casts a powerful spell which makes the whole area tremble.", CHAR_NAME(DEFINITE));
6919       else
6920         ADD_MESSAGE("You feel the presence of an ancient evil being awakened from its long slumber. You shiver.");
6921 
6922       Skeleton->SetGenerationDanger(GetGenerationDanger());
6923       return true;
6924     }
6925     else
6926       return false;
6927   }
6928   else
6929     return false;
6930 }
6931 
CheckForUsefulItemsOnGround(truth CheckFood)6932 truth darkknight::CheckForUsefulItemsOnGround(truth CheckFood)
6933 {
6934   if(GetConfig() == MASTER)
6935     return false;
6936   else
6937     return character::CheckForUsefulItemsOnGround(CheckFood);
6938 }
6939 
GetAICommand()6940 void goblin::GetAICommand()
6941 {
6942   if(CheckAIZapOpportunity())
6943     return;
6944 
6945   humanoid::GetAICommand();
6946 }
6947 
GetAICommand()6948 void cossack::GetAICommand()
6949 {
6950   if(CheckAIZapOpportunity())
6951     return;
6952 
6953   humanoid::GetAICommand();
6954 }
6955 
GetAICommand()6956 void werewolfwolf::GetAICommand()
6957 {
6958   if(GetConfig() == DRUID && !RAND_N(4))
6959     if(CheckAIZapOpportunity())
6960       return;
6961 
6962   humanoid::GetAICommand();
6963 }
6964 
CheckAIZapOpportunity()6965 truth humanoid::CheckAIZapOpportunity()
6966 {
6967   if(!HasAUsableArm() || StateIsActivated(CONFUSED))
6968     return false;
6969   else
6970     return character::CheckAIZapOpportunity();
6971 }
6972 
SpecialEnemySightedReaction(character * Char)6973 truth imp::SpecialEnemySightedReaction(character* Char)
6974 {
6975   if(GetPos().IsAdjacent(Char->GetPos()))
6976   {
6977     if((StateIsActivated(PANIC) || IsInBadCondition()) && !RAND_4)
6978     {
6979       if(CanBeSeenByPlayer())
6980         ADD_MESSAGE("%s shrieks!", CHAR_NAME(DEFINITE));
6981 
6982       TeleportRandomly(true);
6983       return true;
6984     }
6985     else
6986       return false;
6987   }
6988 
6989   if(GetHP() > (GetMaxHP() / 2) && !RAND_N(10))
6990   {
6991     if(CanBeSeenByPlayer())
6992       ADD_MESSAGE("%s chortles.", CHAR_NAME(DEFINITE));
6993 
6994     TeleportNear(Char);
6995     return true;
6996   }
6997 
6998   return false;
6999 }
7000 
SpecialBiteEffect(character * Victim,v2 HitPos,int BodyPartIndex,int Direction,truth BlockedByArmour,truth Critical,int DoneDamage)7001 truth crimsonimp::SpecialBiteEffect(character* Victim, v2 HitPos, int BodyPartIndex, int Direction,
7002                                     truth BlockedByArmour, truth Critical, int DoneDamage)
7003 {
7004   bodypart* BodyPart = Victim->GetBodyPart(BodyPartIndex);
7005 
7006   if(BodyPart && BodyPart->IsDestroyable(Victim)
7007      && BodyPart->GetMainMaterial() && BodyPart->CanBeBurned()
7008      && (BodyPart->GetMainMaterial()->GetInteractionFlags() & CAN_BURN)
7009      && !BodyPart->IsBurning())
7010       {
7011         if(BodyPart->TestActivationEnergy(150))
7012         {
7013           return true;
7014         }
7015       }
7016 
7017   return false;
7018 }
7019 
CreateCorpse(lsquare * Square)7020 void crimsonimp::CreateCorpse(lsquare* Square)
7021 {
7022   game::GetCurrentLevel()->Explosion(this, "consumed by the hellfire of "  + GetName(INDEFINITE),
7023                                      Square->GetPos(), 20 + RAND() % 5 - RAND() % 5);
7024   SendToHell();
7025 }
7026 
DrinkMagic(const beamdata & Beam)7027 truth mirrorimp::DrinkMagic(const beamdata& Beam)
7028 {
7029   if(!Beam.Wand)
7030     return false;
7031   if(!Beam.Wand->IsExplosive())
7032     return false;
7033 
7034   if(Beam.Owner && RAND_N(GetAttribute(MANA)) <= RAND_N(Beam.Owner->GetAttribute(WILL_POWER)))
7035   {
7036     Beam.Owner->EditExperience(WILL_POWER, 100, 1 << 12);
7037     return false;
7038   }
7039 
7040   festring DeathMsg = CONST_S("killed by an explosion of ");
7041   Beam.Wand->AddName(DeathMsg, INDEFINITE);
7042   DeathMsg << " caused @bk";
7043 
7044   if(IsPlayer())
7045     ADD_MESSAGE("You grin as %s %s.", Beam.Wand->GetExtendedDescription().CStr(), Beam.Wand->GetBreakMsg().CStr());
7046   else if(CanBeSeenByPlayer())
7047     ADD_MESSAGE("%s cackles with glee as %s %s.", CHAR_NAME(DEFINITE), Beam.Wand->GetExtendedDescription().CStr(),
7048                                                   Beam.Wand->GetBreakMsg().CStr());
7049 
7050   Beam.Wand->BreakEffect(this, DeathMsg);
7051   return true;
7052 }
7053 
CreateCorpse(lsquare * Square)7054 void mirrorimp::CreateCorpse(lsquare* Square)
7055 {
7056   decoration* Shard = decoration::Spawn(SHARD);
7057   Shard->InitMaterials(MAKE_MATERIAL(GLASS));
7058   Square->ChangeOLTerrainAndUpdateLights(Shard);
7059   SendToHell();
7060 }
7061 
BeTalkedTo()7062 void elder::BeTalkedTo()
7063 {
7064   if(game::TweraifIsFree() && !game::GetFreedomStoryState() && !HasBeenSpokenTo
7065      && !(GetRelation(PLAYER) == HOSTILE) && GetPos().IsAdjacent(PLAYER->GetPos()))
7066   {
7067     game::TextScreen(CONST_S("\"My boy, my wonderful boy! From the very day I found you,\n"
7068                              "I knew there was something special in you, something even\n"
7069                              "the accursed hippos couldn't spoil. And now you have saved us\n"
7070                              "from valpurian clutches and given us a chance at freedom!\n"
7071                              "Thank you so very, very much.\"\n\n"
7072                              "\"Alas, I'm afraid Tweraif is not yet out of the proverbial woods.\n"
7073                              "We are few and the Attnamese army is massive. Their battleships\n"
7074                              "will be ready once the winter ends and the ice thaws, and they will\n"
7075                              "not hesitate to bring their tyranny back. I still don't get why they\n"
7076                              "love those bananas so much.\"\n\n"
7077                              "\"We have no hope to defeat them in a fight, so fight them we shan't.\""));
7078 
7079     game::TextScreen(CONST_S("\"Let me tell you a story, or a myth if you will.\"\n\n"
7080                              "\"Once upon a time, there was a town. No one could find the town\n"
7081                              "unless they already knew where it was, and on one could enter\n"
7082                              "uninvited. The town was called Mondedr and it was concealed\n"
7083                              "from the world by the power of Cleptia. It was never conquered.\"\n\n"
7084                              "\"The thing is, I know for a fact that Mondedr exists, and that\n"
7085                              "their cloaking spell can be replicated. Attnam tried to take our\n"
7086                              "goddess away, but she is still strong in our hearts. I have faith\n"
7087                              "she will protect this island from valpurians, just as Cleptia did\n"
7088                              "for Mondedr.\""));
7089 
7090     game::TextScreen(CONST_S("\"The prayers are simple, but no god can affect the world uninvited,\n"
7091                              "and a miracle of such strength requires more power than any priest\n"
7092                              "could channel. We need a conduit, something close to Silva herself.\"\n\n"
7093                              "\"We need a scion of the Holy Mango World-tree.\"\n\n"
7094                              "\"You have done so much for your village, yet I must ask for another\n"
7095                              "favour. You know that the late viceroy destroyed the altar of Silva\n"
7096                              "in our shrine, but you might not know that there is another shrine of Silva\n"
7097                              "on this island, or rather below it. I would implore you to go down into\n"
7098                              "the underwater tunnel and find a strange formation of rock where our people\n"
7099                              "buried the stairs to the crystal cave of Silva under a cave-in,\n"
7100                              "once it was obvious that we will be conquered. We couldn't let the Attnamese\n"
7101                              "desecrate that most holy place. There, in an ancient temple of Silva,\n"
7102                              "grows a tree of wondrous power, a tiny sapling of the World-tree.\""));
7103 
7104     game::TextScreen(CONST_S("\"Please, bring back a seedling of this tree. Once we plant it here,\n"
7105                              "in the village, I can cast the spell and no army will find us.\n"
7106                              "The first valpurian attack surprised us, caught us unaware, unprepared\n"
7107                              "and unable to defend our land. So let's not repeat history and\n"
7108                              "get ready for them this time.\""));
7109 
7110     game::SetFreedomStoryState(1);
7111     GetArea()->SendNewDrawRequest();
7112     ADD_MESSAGE("\"Oh, and give my regards to Terra, if she's still alive.\"");
7113 
7114     HasBeenSpokenTo = true;
7115   }
7116   else if((game::GetFreedomStoryState() == 2) && !(GetRelation(PLAYER) == HOSTILE))
7117   {
7118     ADD_MESSAGE("\"You have the seedling! Wonderful. Please, plant it by the banana delivery spot, and we shan't fear the imperialists anymore.\"");
7119   }
7120   else
7121     humanoid::BeTalkedTo();
7122 }
7123 
BeTalkedTo()7124 void terra::BeTalkedTo()
7125 {
7126   if((game::GetFreedomStoryState() == 1) && !HasBeenSpokenTo && !(GetRelation(PLAYER) == HOSTILE)
7127      && GetPos().IsAdjacent(PLAYER->GetPos()))
7128   {
7129     game::TextScreen(CONST_S("\"Tweraif has been freed?! What wonderful news you bring me!\"\n\n"
7130                              "\"I have volunteered all those years ago to be buried here in this cave\n"
7131                              "along with the shrine, to tend it and to protect the rites and traditions\n"
7132                              "that the Attnamese would rather see burnt and forgotten. Yet I have hoped\n"
7133                              "every day that a word would come about an end to the tyranny, that\n"
7134                              "I would be free to return home. I guess my hope dwindled over the years,\n"
7135                              "but you are here now and my wishes came true. Thank you.\"\n\n"
7136                              "\"Nevertheless, I know what you came for. A seedling of this holy tree,\n"
7137                              "to channel the power of Silva and shroud Tweraif against further attacks.\n"
7138                              "I wish it was that simple, but I have no seeds to give you.\""));
7139 
7140     game::TextScreen(CONST_S("\"You see, this shrine is built in a remote, lost cave for a reason.\n"
7141                              "It is a guarding post, a bulwark, and a seal on a prison.\"\n\n"
7142                              "\"One thousand years ago, Nefas, the goddess of forbidden pleasures,\n"
7143                              "came to Scabies, the goddess of diseases, mutations and deformity,\n"
7144                              "in the form of a handsome hero, and seduced her. Whether it was\n"
7145                              "for Nefas' own amusement, or the humiliation Scabies suffered\n"
7146                              "when she discovered who she laid with, no one knows, but Scabies got\n"
7147                              "pregnant and eventually delivered a divine baby - a monstrous spider\n"
7148                              "the likes of which this world had never seen before.\"\n\n"
7149                              "\"The spider was a behemoth of her kind, massive and terrifying\n"
7150                              "and truly detestable. Spurned and abandoned by both her mothers,\n"
7151                              "the spider rampaged through the world until she was defeated and\n"
7152                              "bound by a circle of druids and priests. Her name is Lobh-se and\n"
7153                              "she is imprisoned below this cave, trapped by the power of Silva\n"
7154                              "channeled through the Holy Mango Tree.\""));
7155 
7156     game::TextScreen(CONST_S("\"Lobh-se is a terrible creature, an avatar of famine and consumption.\n"
7157                              "She breeds thousands of lesser spiders and immediately devours them\n"
7158                              "in her endless hunger. She strains against her bonds and even comes here,\n"
7159                              "feasting on the animals attracted to the magicks of Silva, and on the few\n"
7160                              "plants that scrape a living this deep underground. I can somewhat keep her\n"
7161                              "at bay, protecting myself and the tree, but the magic of the holy seedlings\n"
7162                              "is sweet to Lobh-se, and not strong enough to ward her off. She devoured\n"
7163                              "the last seedling just a few days ago.\"\n\n"
7164                              "\"You are a hero already for liberating our village,\n"
7165                              "but if you really wish to ensure the safety of Tweraif, you have to venture\n"
7166                              "deeper, to the very lair of Lobh-se. She may be a godling, but her body\n"
7167                              "is still mortal. Cut the seedling from her gullet, and I will keep her spirit\n"
7168                              "bound so that it cannot create a new body to harass this world.\""));
7169 
7170     game::TextScreen(CONST_S("\"May Silva bless you in your doings.\""));
7171 
7172     GetArea()->SendNewDrawRequest();
7173     ADD_MESSAGE("\"Oh, and give my love to Kaethos, if he's still alive.\"");
7174 
7175     HasBeenSpokenTo = true;
7176   }
7177   else if((game::GetFreedomStoryState() == 2) && !(GetRelation(PLAYER) == HOSTILE))
7178   {
7179     priest::BeTalkedTo(); // in case player also needs a cure, before the tip (below) to grant it wont be ignored
7180     ADD_MESSAGE("\"You bested her, I see! Now hurry back to the village, and Attnam shall threaten us no more.\"");
7181   }
7182   else
7183     priest::BeTalkedTo();
7184 }
7185 
GetAICommand()7186 void aslonawizard::GetAICommand()
7187 {
7188   SeekLeader(GetLeader());
7189 
7190   if(FollowLeader(GetLeader()))
7191     return;
7192 
7193   /*
7194    * Teleports when in danger, otherwise either blinks his allies close to
7195    * an enemy, or summons a gas golem.
7196    */
7197 
7198   character* NearestEnemy = 0;
7199   long NearestEnemyDistance = 0x7FFFFFFF;
7200   character* RandomFriend = 0;
7201   charactervector Friend;
7202   v2 Pos = GetPos();
7203 
7204   for(int c = 0; c < game::GetTeams(); ++c)
7205   {
7206     if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
7207     {
7208       for(character* p : game::GetTeam(c)->GetMember())
7209         if(p->IsEnabled())
7210         {
7211           long ThisDistance = Max<long>(abs(p->GetPos().X - Pos.X), abs(p->GetPos().Y - Pos.Y));
7212 
7213           if((ThisDistance < NearestEnemyDistance
7214               || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && p->CanBeSeenBy(this))
7215           {
7216             NearestEnemy = p;
7217             NearestEnemyDistance = ThisDistance;
7218           }
7219         }
7220     }
7221     else if(GetTeam()->GetRelation(game::GetTeam(c)) == FRIEND)
7222     {
7223       for(character* p : game::GetTeam(c)->GetMember())
7224         if(p->IsEnabled() && p->CanBeSeenBy(this))
7225           Friend.push_back(p);
7226     }
7227   }
7228 
7229   if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos) &&
7230      (!(RAND() & 4) || StateIsActivated(PANIC)))
7231   {
7232     if(CanBeSeenByPlayer())
7233       ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE));
7234 
7235     TeleportRandomly(true);
7236     EditAP(-GetSpellAPCost());
7237     return;
7238   }
7239 
7240   if(!RAND_2 && CheckAIZapOpportunity())
7241     return;
7242 
7243   if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3)
7244   {
7245     SetGoingTo((Pos << 1) - NearestEnemy->GetPos());
7246 
7247     if(MoveTowardsTarget(true))
7248       return;
7249   }
7250 
7251   if(Friend.size() && !(RAND() & 3))
7252   {
7253     RandomFriend = Friend[RAND() % Friend.size()];
7254     NearestEnemy = 0;
7255   }
7256 
7257   if(GetRelation(PLAYER) == HOSTILE && PLAYER->CanBeSeenBy(this))
7258     NearestEnemy = PLAYER;
7259 
7260   beamdata Beam
7261     (
7262       this,
7263       CONST_S("killed by the spells of ") + GetName(INDEFINITE),
7264       YOURSELF,
7265       0
7266     );
7267 
7268   if(NearestEnemy)
7269   {
7270     if(CanBeSeenByPlayer())
7271       ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE));
7272 
7273     if(RandomFriend && !RAND_N(4))
7274     {
7275       EditAP(-GetSpellAPCost());
7276 
7277       RandomFriend->GetLSquareUnder()->DrawParticles(RED);
7278       RandomFriend->TeleportNear(NearestEnemy);
7279       return;
7280     }
7281     else
7282     {
7283       lsquare* Square = NearestEnemy->GetLSquareUnder();
7284       character* ToBeCalled = 0;
7285 
7286       EditAP(-GetSpellAPCost());
7287 
7288       int GasMaterial[] = { MUSTARD_GAS, MAGIC_VAPOUR, SLEEPING_GAS, TELEPORT_GAS,
7289                             EVIL_WONDER_STAFF_VAPOUR, EVIL_WONDER_STAFF_VAPOUR };
7290       ToBeCalled = golem::Spawn(GasMaterial[RAND() % 6]);
7291       v2 Where = GetLevel()->GetNearestFreeSquare(ToBeCalled, Square->GetPos());
7292 
7293       if(Where == ERROR_V2)
7294       {
7295         if(CanBeSeenByPlayer())
7296           ADD_MESSAGE("Nothing happens.");
7297 
7298         delete ToBeCalled;
7299       }
7300       else
7301       {
7302         ToBeCalled->SetGenerationDanger(GetGenerationDanger());
7303         ToBeCalled->SetTeam(GetTeam());
7304         ToBeCalled->PutTo(Where);
7305 
7306         if(ToBeCalled->CanBeSeenByPlayer())
7307           ADD_MESSAGE("Suddenly %s materializes!", ToBeCalled->CHAR_NAME(INDEFINITE));
7308 
7309         ToBeCalled->GetLSquareUnder()->DrawParticles(RED);
7310       }
7311 
7312       if(CanBeSeenByPlayer())
7313         NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE)
7314                                                 + CONST_S(" interrupts you."));
7315       else
7316         NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you."));
7317 
7318       return;
7319     }
7320   }
7321 
7322   StandIdleAI();
7323 }
7324 
TakeHit(character * Enemy,item * Weapon,bodypart * EnemyBodyPart,v2 HitPos,double Damage,double ToHitValue,int Success,int Type,int Direction,truth Critical,truth ForceHit)7325 int gasghoul::TakeHit(character* Enemy, item* Weapon, bodypart* EnemyBodyPart, v2 HitPos, double Damage,
7326                       double ToHitValue, int Success, int Type, int Direction, truth Critical, truth ForceHit)
7327 {
7328   int Return = humanoid::TakeHit(Enemy, Weapon, EnemyBodyPart, HitPos, Damage,
7329                                  ToHitValue, Success, Type, Direction, Critical, ForceHit);
7330 
7331   if(Return != HAS_DODGED && Return != HAS_BLOCKED && GetLSquareUnder()->IsFlyable())
7332   {
7333     if(Enemy->IsPlayer())
7334       ADD_MESSAGE("%s releases a cloud of fumes as you strike %s.", CHAR_DESCRIPTION(DEFINITE), GetObjectPronoun().CStr());
7335     else if(IsPlayer())
7336       ADD_MESSAGE("You release a cloud of fumes as %s strikes you.", Enemy->CHAR_DESCRIPTION(DEFINITE));
7337     else if(CanBeSeenByPlayer() && Enemy->CanBeSeenByPlayer())
7338       ADD_MESSAGE("%s releases a cloud of fumes as %s strikes %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE),
7339                                                                     GetObjectPronoun().CStr());
7340     else if(CanBeSeenByPlayer())
7341       ADD_MESSAGE("%s releases a cloud of fumes as something strikes %s.", CHAR_DESCRIPTION(DEFINITE), GetObjectPronoun().CStr());
7342 
7343     int GasMaterial[] = { MUSTARD_GAS, SKUNK_SMELL, ACID_GAS, FIRE_GAS };
7344 
7345     if(Critical)
7346       GetLevel()->GasExplosion(gas::Spawn(GasMaterial[RAND() % 4], 100), GetLSquareUnder(), this);
7347     else
7348       GetLSquareUnder()->AddSmoke(gas::Spawn(GasMaterial[RAND() % 4], 100));
7349   }
7350 
7351   return Return;
7352 }
7353 
Save(outputfile & SaveFile) const7354 void elder::Save(outputfile& SaveFile) const
7355 {
7356   humanoid::Save(SaveFile);
7357   SaveFile << HasBeenSpokenTo;
7358 }
7359 
Load(inputfile & SaveFile)7360 void elder::Load(inputfile& SaveFile)
7361 {
7362   humanoid::Load(SaveFile);
7363   SaveFile >> HasBeenSpokenTo;
7364 }
7365 
Save(outputfile & SaveFile) const7366 void terra::Save(outputfile& SaveFile) const
7367 {
7368   humanoid::Save(SaveFile);
7369   SaveFile << HasBeenSpokenTo;
7370 }
7371 
Load(inputfile & SaveFile)7372 void terra::Load(inputfile& SaveFile)
7373 {
7374   humanoid::Load(SaveFile);
7375   SaveFile >> HasBeenSpokenTo;
7376 }
7377 
Save(outputfile & SaveFile) const7378 void aslonawizard::Save(outputfile& SaveFile) const
7379 {
7380   humanoid::Save(SaveFile);
7381   SaveFile << HasBeenSpokenTo;
7382 }
7383 
Load(inputfile & SaveFile)7384 void aslonawizard::Load(inputfile& SaveFile)
7385 {
7386   humanoid::Load(SaveFile);
7387   SaveFile >> HasBeenSpokenTo;
7388 }
7389 
Save(outputfile & SaveFile) const7390 void aslonacaptain::Save(outputfile& SaveFile) const
7391 {
7392   humanoid::Save(SaveFile);
7393   SaveFile << HasBeenSpokenTo;
7394 }
7395 
Load(inputfile & SaveFile)7396 void aslonacaptain::Load(inputfile& SaveFile)
7397 {
7398   humanoid::Load(SaveFile);
7399   SaveFile >> HasBeenSpokenTo;
7400 }
7401 
Save(outputfile & SaveFile) const7402 void aslonapriest::Save(outputfile& SaveFile) const
7403 {
7404   humanoid::Save(SaveFile);
7405   SaveFile << HasBeenSpokenTo;
7406 }
7407 
Load(inputfile & SaveFile)7408 void aslonapriest::Load(inputfile& SaveFile)
7409 {
7410   humanoid::Load(SaveFile);
7411   SaveFile >> HasBeenSpokenTo;
7412 }
7413 
BeTalkedTo()7414 void aslonawizard::BeTalkedTo()
7415 {
7416   if(GetPos().IsAdjacent(PLAYER->GetPos()) && !(GetRelation(PLAYER) == HOSTILE))
7417   {
7418     if((game::GetAslonaStoryState() > 1) && !HasBeenSpokenTo)
7419     {
7420       game::TextScreen(CONST_S("\"You have been sent by Lord Regent? Excellent! As the only protector of Aslona from\n"
7421                                "supernatural incursions, I cannot leave the castle, so I shall welcome any help you\n"
7422                                "can provide.\"\n\n"
7423                                "\"My request for you might cause some to brand me a coward. But no matter. We must win\n"
7424                                "the war quickly, or Aslona looses even if we eventually achieve victory. Swords and\n"
7425                                "spells may have won earlier battles, but now we need to end the rebels in one fell swoop.\n"
7426                                "Lord Mittrars is already working hard on locating the command centre of Harvan's forces,\n"
7427                                "so my task is to arrange for the weapon.\"\n\n"
7428                                "\"My research shows that such a weapon capable of complete obliteration appears in many\n"
7429                                "tales from the long lost empire of Otoul'iv Ik-Omit. I have uncovered the site of\n"
7430                                "a pyramid dating back to that era, and all sources indicate that deep within, an untouched\n"
7431                                "arms depot should be located.\"\n\n"
7432                                "\"Please fetch me this mighty weapon post haste. I'm sure you will recognize it once you see it.\""));
7433 
7434       game::LoadWorldMap();
7435       v2 PyramidPos = game::GetWorldMap()->GetEntryPos(0, PYRAMID);
7436       game::GetWorldMap()->GetWSquare(PyramidPos)->ChangeOWTerrain(pyramid::Spawn());
7437       game::GetWorldMap()->RevealEnvironment(PyramidPos, 1);
7438       game::SaveWorldMap();
7439 
7440       GetArea()->SendNewDrawRequest();
7441       ADD_MESSAGE("%s says with a concern: \"Be careful, the pyramid is said to be a dangerous place. Better be prepared when you go there.\"", CHAR_NAME(DEFINITE));
7442 
7443       HasBeenSpokenTo = true;
7444       return;
7445     }
7446     else if(PLAYER->HasNuke())
7447     {
7448       if(game::TruthQuestion(CONST_S("Turn in the thaumic bomb? [y/N]"), REQUIRES_ANSWER))
7449       {
7450         PLAYER->RemoveNuke(this);
7451         ADD_MESSAGE("%s beams: \"Yes! Thank you, thank you! With this, we can blow the rebels to tiny bits, we can end the war!\"", CHAR_NAME(DEFINITE));
7452         game::SetAslonaStoryState(game::GetAslonaStoryState() + 1);
7453         return;
7454       }
7455     }
7456   }
7457 
7458   humanoid::BeTalkedTo();
7459 }
7460 
CreateCorpse(lsquare * Square)7461 void aslonawizard::CreateCorpse(lsquare* Square)
7462 {
7463   game::GetCurrentLevel()->GasExplosion(gas::Spawn(MAGIC_VAPOUR, 100), Square, this);
7464   SendToHell();
7465 }
7466 
BeTalkedTo()7467 void aslonacaptain::BeTalkedTo()
7468 {
7469   if(GetPos().IsAdjacent(PLAYER->GetPos()) && !(GetRelation(PLAYER) == HOSTILE))
7470   {
7471     if((game::GetAslonaStoryState() > 1) && !HasBeenSpokenTo)
7472     {
7473       game::TextScreen(CONST_S("\"Finally someone not tangled up in their responsibilities! I was almost ready to drop\n"
7474                                "everything and go myself, but thankfully you are here.\"\n\n"
7475                                "\"Let me explain. When king Othyr died, Seges rest his soul, and Harvan fled justice to\n"
7476                                "start the rebellion, the crown prince, His Highness Artorius, was away on a visit to Castle\n"
7477                                "Noth. I sent for him to immediately return back to the Castle of Aslona, both for safety\n"
7478                                "and to pay respects to his father. But his retinue never arrived. My scouts found marks\n"
7479                                "of an ambush and tracked the responsible raiding party of goblins back to their lair\n"
7480                                "in a nearby ruined fort, where we also believe they imprisoned the young prince.\"\n\n"
7481                                "\"I would have loved to throw the whole army of Aslona at the goblins, but with\n"
7482                                "the ceaseless attacks of rebel squads, that would mean sacrificing the kingdom.\n"
7483                                "I was forced to wait for a momentary respite from the fighting, or for some trustworthy\n"
7484                                "outsider willing to go on a rescue mission.\"\n\n"
7485                                "\"It tears at my heart that poor prince languishes in some jail cell, as if his father's\n"
7486                                "death wasn't hard on him already. Save prince Artorius, I beg of you, and bring him to me\n"
7487                                "so that I need to worry no more.\""));
7488 
7489       game::LoadWorldMap();
7490       v2 GoblinPos = game::GetWorldMap()->GetEntryPos(0, GOBLIN_FORT);
7491       game::GetWorldMap()->GetWSquare(GoblinPos)->ChangeOWTerrain(goblinfort::Spawn());
7492       game::GetWorldMap()->RevealEnvironment(GoblinPos, 1);
7493       game::SaveWorldMap();
7494 
7495       GetArea()->SendNewDrawRequest();
7496       ADD_MESSAGE("%s says: \"Hurry, please.\"", CHAR_NAME(DEFINITE));
7497 
7498       HasBeenSpokenTo = true;
7499       return;
7500     }
7501     else if(HasBeenSpokenTo)
7502     {
7503       // Does the player have prince Artorius in his team?
7504       character* CrownPrince = 0;
7505       for(character* p : game::GetTeam(PLAYER_TEAM)->GetMember())
7506         if(p->IsEnabled() && !p->IsPlayer() && p->IsKing())
7507           CrownPrince = p;
7508 
7509       if(CrownPrince)
7510       {
7511         if(game::TruthQuestion(CONST_S("Entrust young prince to Lord Mittrars' care? [y/N]"), REQUIRES_ANSWER))
7512         {
7513           team* Team = game::GetTeam(ASLONA_TEAM);
7514           CrownPrince->ChangeTeam(Team);
7515 
7516           ADD_MESSAGE("\"Uncle Mittrars!\"");
7517           ADD_MESSAGE("\"My prince! Thanks Seges and all the gods of Law, I was already loosing any hope that I will see you again.\"");
7518           game::SetAslonaStoryState(game::GetAslonaStoryState() + 1);
7519           return;
7520         }
7521       }
7522     }
7523   }
7524 
7525   humanoid::BeTalkedTo();
7526 }
7527 
BeTalkedTo()7528 void aslonapriest::BeTalkedTo()
7529 {
7530   if(GetPos().IsAdjacent(PLAYER->GetPos()) && !(GetRelation(PLAYER) == HOSTILE))
7531   {
7532     if((game::GetAslonaStoryState() > 1) && !HasBeenSpokenTo)
7533     {
7534       game::TextScreen(CONST_S("\"Yes, I can most definitely put your skills to good use. You see, this senseless\n"
7535                                "war has already claimed many lives by the blades of the soldiers, but a more insidious\n"
7536                                "enemy is rearing her ugly head. I'm talking about Scabies. With many injured and access\n"
7537                                "to supplies limited, diseases are starting to spread, and even my skills and magic\n"
7538                                "are not enough without proper medical care and hygiene. Hygiene is essential to health,\n"
7539                                "but essential to hygiene is access to clean water, which is one of the things this castle\n"
7540                                "lacks right now.\"\n\n"
7541                                "\"I have found some adventurer's journal describing an artifact that might help solve\n"
7542                                "our troubles. A single tear of Silva, petrified into the form of an obsidian shard, yet still\n"
7543                                "weeping with rains of freshwater. I would ask of you to retrieve this shard for me. It should\n"
7544                                "be located in a nearby coal cave, though you should know the journal mentioned strange fungal\n"
7545                                "growths appearing in the cave, nourished by the life-giving water.\""));
7546 
7547       game::LoadWorldMap();
7548       v2 CavePos = game::GetWorldMap()->GetEntryPos(0, FUNGAL_CAVE);
7549       game::GetWorldMap()->GetWSquare(CavePos)->ChangeOWTerrain(fungalcave::Spawn());
7550       game::GetWorldMap()->RevealEnvironment(CavePos, 1);
7551       game::SaveWorldMap();
7552 
7553       GetArea()->SendNewDrawRequest();
7554       ADD_MESSAGE("%s says: \"Thank you very much for your kind help.\"", CHAR_NAME(DEFINITE));
7555 
7556       HasBeenSpokenTo = true;
7557       return;
7558     }
7559     else if(PLAYER->HasWeepObsidian())
7560     {
7561       if(game::TruthQuestion(CONST_S("Turn in the weeping obsidian? [y/N]"), REQUIRES_ANSWER))
7562       {
7563         PLAYER->RemoveWeepObsidian(this);
7564         ADD_MESSAGE("%s says: \"Wonderful! Let me get to work right away.\"", CHAR_NAME(DEFINITE));
7565         game::SetAslonaStoryState(game::GetAslonaStoryState() + 1);
7566         return;
7567       }
7568     }
7569   }
7570 
7571   priest::BeTalkedTo();
7572 }
7573 
BeTalkedTo()7574 void harvan::BeTalkedTo()
7575 {
7576   if(GetPos().IsAdjacent(PLAYER->GetPos()) && !(GetRelation(PLAYER) == HOSTILE))
7577   {
7578     if(!game::GetRebelStoryState())
7579     {
7580       game::TextScreen(CONST_S("\"Well met, adventurer! It's always nice to talk to someone unaffected by\n"
7581                                "the unfortunate quarrels of our kingdom.\"\n\n"
7582                                "\"I don't know how much have you heard, but our old king Othyr was murdered\n"
7583                                "and then even the crown prince Artorius disappeared in an alleged goblin raid.\n"
7584                                "Immediately, Lord Efra Peredivall named himself the Lord Regent of Aslona and\n"
7585                                "made it known that he intends to *use* his newfound power. I have known\n"
7586                                "Lord Peredivall for most of my life, I used to call him my friend, but I cannot\n"
7587                                "stand for this high treason, and the people of Aslona support me. But now we must\n"
7588                                "hide in the woods like brigands, hunted by those corrupt or mislead by\n"
7589                                "Lord Regent's lies. I fear we will need some edge if we want to win this war\n"
7590                                "without drowning in blood.\""));
7591 
7592       game::TextScreen(CONST_S("\"Ah-hah! And here I'm talking without realizing you could be of a great help!\n"
7593                                "You are an obvious foreigner, hardly suspected of any connections to us. You could\n"
7594                                "infiltrate Lord Regent's forces and try to glean his plans. Maybe even offer\n"
7595                                "your services and whatever he asks of you to acquire, bring to us instead!\n"
7596                                "Yes, go to the Castle of Aslona, help our cause and once we achieve victory,\n"
7597                                "I will reward you handsomely.\""));
7598 
7599       GetArea()->SendNewDrawRequest();
7600       ADD_MESSAGE("%s pats you on the back. \"Good luck and return soon, my friend.\"", CHAR_NAME(DEFINITE));
7601       game::SetRebelStoryState(2); // To have same StoryState values as Aslona.
7602       return;
7603     }
7604     else if(PLAYER->HasMasamune())
7605     {
7606       if(game::TruthQuestion(CONST_S("Turn in the noble katana named E-numa sa-am? [y/N]"), REQUIRES_ANSWER))
7607       {
7608         game::PlayVictoryMusic();
7609         game::TextScreen(CONST_S("You hand over the ancient katana and thus the regalia necessary to crown a new\n"
7610                                  "king of Aslona are together again.\n\n"
7611                                  "\"Thank you, my friend,\" Harvan says and then turns to his army. \"Comrades,\n"
7612                                  "this is our hour of victory. You have fought well. You are heroes, all of you.\n"
7613                                  "Our country and the very soul of our nation was saved only thanks to you, thanks\n"
7614                                  "to your courage, loyalty, selflessness and resolve. But now, the war is over.\n"
7615                                  "The traitors shall face justice and peace will return to our homes. Gods bless Aslona!\"\n\n"
7616                                  "A deafening cheer echoes his words.\n\nYou are victorious!"));
7617 
7618         game::GetCurrentArea()->SendNewDrawRequest();
7619         game::DrawEverything();
7620         PLAYER->ShowAdventureInfo();
7621 
7622         // Did the player do all quests just for rebels?
7623         festring Msg;
7624         if(game::GetRebelStoryState() == 5)
7625         {
7626           Msg = CONST_S("helped the rebels to an overwhelming victory");
7627           AddScoreEntry(Msg, 4, false);
7628         }
7629         else
7630         {
7631           Msg = CONST_S("helped the rebels to an uneasy victory");
7632           AddScoreEntry(Msg, 2, false);
7633         }
7634 
7635         game::End(Msg);
7636         return;
7637       }
7638     }
7639     else if(PLAYER->HasNuke())
7640     {
7641       if(game::TruthQuestion(CONST_S("Turn in the thaumic bomb? [y/N]"), REQUIRES_ANSWER))
7642       {
7643         PLAYER->RemoveNuke(this);
7644         ADD_MESSAGE("\"So this is the fate Lord Regent had planned for me and my people. Thank you for saving all of our lives, %s.\"",
7645                     PLAYER->GetAssignedName().CStr());
7646         game::SetRebelStoryState(game::GetRebelStoryState() + 1);
7647         return;
7648       }
7649     }
7650     else if(PLAYER->HasWeepObsidian())
7651     {
7652       if(game::TruthQuestion(CONST_S("Turn in the weeping obsidian? [y/N]"), REQUIRES_ANSWER))
7653       {
7654         PLAYER->RemoveWeepObsidian(this);
7655         ADD_MESSAGE("\"The royalists are starting to feel their isolation in the castle, it would seem. Sooner or later, they will loose their strength to oppose us.\"");
7656         game::SetRebelStoryState(game::GetRebelStoryState() + 1);
7657         return;
7658       }
7659     }
7660     else if(game::GetRebelStoryState() == 5 && game::GetStoryState() < 3)
7661     {
7662       game::TextScreen(CONST_S("\"You are a hero to me, you should know that. You have already done so much,\n"
7663                                "yet I must ask for one last favor.\"\n\n"
7664                                "\"This war is costly in both innocent lives and money we cannot spare,\n"
7665                                "it needs to end right now. Thanks to your help, we have the thaumic bomb and\n"
7666                                "could level the whole castle and wipe Lord Regent and his royalists from the surface\n"
7667                                "of the world, but I'm unwilling to sacrifice everyone in the castle and the castle\n"
7668                                "with itself. It would be a hollow victory to kill the traitor, but loose the kingdom\n"
7669                                "when the symbol of our proud history was in ruins.\"\n\n"
7670                                "\"Yet there is a third option. It is my sincere belief that many of Lord Peredivall's\n"
7671                                "troops would change sides if prince Artorius took on the title of his father as\n"
7672                                "the rightful king of Aslona. But for His Highness to be crowned the king without\n"
7673                                "any doubts or dispute, we need the regalia of Aslona, two ancient, masterwork katanas,\n"
7674                                "Asamarum and E-numa sa-am.\""));
7675 
7676       game::TextScreen(CONST_S("\"I have managed to take Asamarum with me during Lord Regent's coup, but he still\n"
7677                                "holds E-numa sa-am. One last time, I would ask you to steal into the Castle of Aslona\n"
7678                                "and retrieve the sword from Lord Regent, so that we might claim victory without\n"
7679                                "bloodshed.\""));
7680 
7681       GetArea()->SendNewDrawRequest();
7682       PLAYER->GetTeam()->Hostility(game::GetTeam(ASLONA_TEAM)); // Too easy otherwise.
7683       ADD_MESSAGE("%s hugs you tightly. \"Godspeed, my friend.\"", CHAR_NAME(DEFINITE));
7684       game::SetStoryState(3);
7685       return;
7686     }
7687     else
7688     {
7689       // Does the player have prince Artorius in his team?
7690       character* CrownPrince = 0;
7691       for(character* p : game::GetTeam(PLAYER_TEAM)->GetMember())
7692         if(p->IsEnabled() && !p->IsPlayer() && p->IsKing())
7693           CrownPrince = p;
7694 
7695       if(CrownPrince)
7696       {
7697         if(game::TruthQuestion(CONST_S("Entrust young prince to Harvan's care? [y/N]"), REQUIRES_ANSWER))
7698         {
7699           team* Team = game::GetTeam(REBEL_TEAM);
7700           CrownPrince->ChangeTeam(Team);
7701 
7702           ADD_MESSAGE("\"Hi, uncle Harvan! Where are we? When are we gonna go home?\"");
7703           ADD_MESSAGE("\"Your Highness, I'm so very glad to see you. Don't worry, I will take you home soon.\"");
7704           game::SetRebelStoryState(game::GetRebelStoryState() + 1);
7705           return;
7706         }
7707       }
7708     }
7709   }
7710 
7711   humanoid::BeTalkedTo();
7712 }
7713 
SpecialEnemySightedReaction(character * Char)7714 truth harvan::SpecialEnemySightedReaction(character* Char)
7715 {
7716   if(!Char->IsPlayer() || GetPos().IsAdjacent(Char->GetPos()))
7717     return false;
7718 
7719   if(GetHP() > (GetMaxHP() / 2) && !RAND_N(10))
7720   {
7721     if(CanBeSeenByPlayer())
7722     {
7723       ADD_MESSAGE("%s screams at you: \"Get over here!\"", CHAR_NAME(DEFINITE));
7724     }
7725     else
7726       ADD_MESSAGE("\"Get over here!\"");
7727 
7728     Char->TeleportNear(this);
7729   }
7730 
7731   return false;
7732 }
7733 
BeTalkedTo()7734 void lordregent::BeTalkedTo()
7735 {
7736   if(GetPos().IsAdjacent(PLAYER->GetPos()) && !(GetRelation(PLAYER) == HOSTILE))
7737   {
7738     if(game::GetAslonaStoryState() == 1)
7739     {
7740       game::TextScreen(CONST_S("\"Sir Lancelyn sent you? He thought you can be of any use to me? I doubt that,\n"
7741                                "but then again maybe there is something in you, we shall see.\"\n\n"
7742                                "\"Several of my advisors have been complaining lately that we don't have enough\n"
7743                                "expendable personnel to send on special missions. Go see Lord Mittrars,\n"
7744                                "Myrddin the wizard and Senex of Seges, and report back once they are happy.\"\n\n"
7745                                "\"Now away with you, I have other things to worry about.\""));
7746 
7747       GetArea()->SendNewDrawRequest();
7748       ADD_MESSAGE("%s sighs: \"I don't have time for this.\"", CHAR_NAME(DEFINITE));
7749       game::SetAslonaStoryState(2);
7750       return;
7751     }
7752     else if(PLAYER->HasMuramasa())
7753     {
7754       if(game::TruthQuestion(CONST_S("Turn in the wicked katana named Asa'marum? [y/N]"), REQUIRES_ANSWER))
7755       {
7756         game::PlayVictoryMusic();
7757         game::TextScreen(CONST_S("You hand over the ancient katana and thus the regalia necessary to crown a new\n"
7758                                  "king of Aslona are together again.\n\n"
7759                                  "\"I am in your debt,\" Lord Peredivall says and then turns to his army. \"Citizens,\n"
7760                                  "this is our hour of victory. You have fought well. You are heroes, all of you.\n"
7761                                  "Our country and the very soul of our nation was saved only thanks to you, thanks\n"
7762                                  "to your bravery, honor, devotion and determination. But now, the war is over.\n"
7763                                  "The traitors shall face justice and peace will return to our homes. Gods bless Aslona!\"\n\n"
7764                                  "A deafening cheer echoes his words.\n\nYou are victorious!"));
7765 
7766         game::GetCurrentArea()->SendNewDrawRequest();
7767         game::DrawEverything();
7768         PLAYER->ShowAdventureInfo();
7769 
7770         // Did the player do all quests just for the royalists?
7771         festring Msg;
7772         if(game::GetAslonaStoryState() == 5)
7773         {
7774           Msg = CONST_S("helped the royalists to an overwhelming victory");
7775           AddScoreEntry(Msg, 4, false);
7776         }
7777         else
7778         {
7779           Msg = CONST_S("helped the royalists to an uneasy victory");
7780           AddScoreEntry(Msg, 2, false);
7781         }
7782 
7783         game::End(Msg);
7784         return;
7785       }
7786     }
7787     else if(game::GetAslonaStoryState() == 5 && game::GetStoryState() < 3)
7788     {
7789       game::TextScreen(CONST_S("\"Ah, you are back? I guess I owe you an apology. I expected you to try and leverage\n"
7790                                "some money from us, and then scram. But you really showed what you are made of!\n"
7791                                "I hope you could lend your services to Aslona one last time.\"\n\n"
7792                                "\"This civil war needs to end right now, before our kingdom tears itself apart, but\n"
7793                                "I don't like the options I see. The scouts of Lord Mittrars finally discovered\n"
7794                                "the location of Harvan's headquarters, but attacking them head-on in a difficult\n"
7795                                "terrain would be foolish and costly. Myrddin informed me he has a magical item\n"
7796                                "that could destroy the rebels once and for all, but I'm hesitant to condemn\n"
7797                                "every single one of those misguided souls to death.\""));
7798 
7799       game::TextScreen(CONST_S("\"Yet there is a third option. It is my sincere belief that many of Harvan's troops\n"
7800                                "would change sides if prince Artorius took the throne of his father as the rightful\n"
7801                                "king of Aslona. But for His Highness to be crowned the king without any doubts or\n"
7802                                "dispute, we need the regalia of Aslona, two ancient katanas of immaculate craft,\n"
7803                                "Asamarum and E-numa sa-am.\"\n\n"
7804                                "\"E-numa sa-am I already have, but Harvan absconded with Asamarum when he fled after\n"
7805                                "the old king's death. And where a direct assault would be hard-pressed for victory,\n"
7806                                "I think you as an outlander could slip through the sentries of the rebels' camp\n"
7807                                "unaccosted and steal Asamarum from Harvan.\"\n\n"
7808                                "\"A stealth mission, if you will.\""));
7809 
7810       GetArea()->SendNewDrawRequest();
7811       PLAYER->GetTeam()->Hostility(game::GetTeam(REBEL_TEAM)); // So much for a stealth mission. ;)
7812       ADD_MESSAGE("%s smiles at you: \"Together, we'll bring Harvan to justice.\"", CHAR_NAME(DEFINITE));
7813       game::SetStoryState(3);
7814       return;
7815     }
7816     else
7817     {
7818       // Does the player have prince Artorius in his team?
7819       character* CrownPrince = 0;
7820       for(character* p : game::GetTeam(PLAYER_TEAM)->GetMember())
7821         if(p->IsEnabled() && !p->IsPlayer() && p->IsKing())
7822           CrownPrince = p;
7823 
7824       if(CrownPrince)
7825       {
7826         ADD_MESSAGE("%s bows his head slightly. \"Your Highness, it is excellent to see you safe and sound. Please, hurry to tell Lord Mittrars about your return. He was truly sick with worry.\"", CHAR_NAME(DEFINITE));
7827         return;
7828       }
7829       else if(PLAYER->HasNuke() || PLAYER->HasWeepObsidian())
7830       {
7831         ADD_MESSAGE("\"Ah, seems like you were not idle. I'm sure my advisors will be thrilled.\"");
7832         return;
7833       }
7834     }
7835   }
7836 
7837   humanoid::BeTalkedTo();
7838 }
7839 
7840 struct distancepair
7841 {
distancepairdistancepair7842   distancepair(long Distance, character* Char) : Distance(Distance), Char(Char) { }
operator <distancepair7843   bool operator<(const distancepair& D) const { return Distance > D.Distance; }
7844   long Distance;
7845   character* Char;
7846 };
7847 
SpecialBodyPartSeverReaction()7848 void lordregent::SpecialBodyPartSeverReaction()
7849 {
7850   if(HasHead())
7851   {
7852     if(CanBeSeenByPlayer())
7853     {
7854       ADD_MESSAGE("%s prays to Seges. You feel the sudden presence of enemies.", CHAR_NAME(DEFINITE));
7855     }
7856 
7857     // Summons allies and then teleports away.
7858     std::vector<distancepair> ToSort;
7859     v2 Pos = GetPos();
7860 
7861     for(character* p : GetTeam()->GetMember())
7862       if(p->IsEnabled() && p != this)
7863         ToSort.push_back(distancepair((Pos - p->GetPos()).GetLengthSquare(), p));
7864 
7865     if(ToSort.size() > 5)
7866       std::sort(ToSort.begin(), ToSort.end());
7867 
7868     for(uint c = 0; c < 5 && c < ToSort.size(); ++c)
7869       ToSort[c].Char->TeleportNear(this);
7870 
7871     /* Teleport away, but rather than doing it the simple way, we're going to use
7872      * a teleport beam to also remove any items on the square. In effect, if the
7873      * player cuts off Lord Efra's sword arm, he will teleport and so will
7874      * Masamune, rather than for it to stay lying on the ground.
7875      */
7876     beamdata Beam
7877       (
7878         this,
7879         CONST_S("killed by the fickle favor of Seges"),
7880         YOURSELF,
7881         0
7882       );
7883     GetLSquareUnder()->Teleport(Beam);
7884     //TeleportRandomly(true);
7885   }
7886 }
7887 
BeTalkedTo()7888 void child::BeTalkedTo()
7889 {
7890   if(GetConfig() == KING &&
7891      GetRelation(PLAYER) != HOSTILE &&
7892      GetTeam() != PLAYER->GetTeam() &&
7893      GetDungeon()->GetIndex() == GOBLIN_FORT &&
7894      GetLevel()->GetIndex() == KING_LEVEL &&
7895      GetPos().IsAdjacent(PLAYER->GetPos())
7896    ) // Prince Artorius will follow you back to Aslona.
7897   {
7898     ADD_MESSAGE("%s looks at you with hope. \"I want to go home. Will you take me home, %s?\"",
7899                 CHAR_DESCRIPTION(DEFINITE), PLAYER->GetAssignedName().CStr());
7900 
7901     ChangeTeam(PLAYER->GetTeam());
7902     return;
7903   }
7904 
7905   character::BeTalkedTo();
7906 }
7907 
MoveRandomly()7908 truth child::MoveRandomly()
7909 {
7910   return GetConfig() == KING ? MoveRandomlyInRoom() : humanoid::MoveRandomly();
7911 }
7912