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