cosmetics
[k8-i-v-a-n.git] / src / game / human.cpp
blob48900617bdcc0ec9f8372b1eb459990982846516
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
13 /* Compiled through charsset.cpp */
15 cint humanoid::DrawOrder[] = { TORSO_INDEX, GROIN_INDEX, RIGHT_LEG_INDEX, LEFT_LEG_INDEX, RIGHT_ARM_INDEX, LEFT_ARM_INDEX, HEAD_INDEX };
17 truth humanoid::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX || I == GROIN_INDEX; }
18 truth humanoid::BodyPartCanBeSevered(int I) const { return I != TORSO_INDEX && I != GROIN_INDEX; }
19 int humanoid::OpenMultiplier() const { return HasAUsableArm() ? 1 : 3; }
20 int humanoid::CloseMultiplier() const { return HasAUsableArm() ? 1 : 2; }
21 int humanoid::GetCarryingStrength() const { return Max(GetAttribute(LEG_STRENGTH), 1) + CarryingBonus; }
22 void humanoid::CalculateBodyParts() { BodyParts = HUMANOID_BODYPARTS; }
23 void humanoid::CalculateAllowedWeaponSkillCategories() { AllowedWeaponSkillCategories = WEAPON_SKILL_CATEGORIES; }
25 v2 farmer::GetHeadBitmapPos() const { return v2(96, (4 + (RAND() & 1)) << 4); }
26 v2 farmer::GetRightArmBitmapPos() const { return v2(64, (RAND() & 1) << 4); }
28 void guard::SetWayPoints(const fearray<packv2>& What) { ArrayToVector(What, WayPoints); }
30 cchar* oree::FirstPersonBiteVerb() const { return "vomit acidous blood at"; }
31 cchar* oree::FirstPersonCriticalBiteVerb() const { return "vomit very acidous blood at"; }
32 cchar* oree::ThirdPersonBiteVerb() const { return "vomits acidous blood at"; }
33 cchar* oree::ThirdPersonCriticalBiteVerb() const { return "vomits very acidous blood at"; }
34 cchar* oree::BiteNoun() const { return "liquid"; }
36 truth skeleton::BodyPartIsVital(int I) const { return I == GROIN_INDEX || I == TORSO_INDEX; }
38 truth communist::ShowClassDescription() const { return GetAssignedName() != "Ivan"; }
40 v2 housewife::GetHeadBitmapPos() const { return v2(112, (RAND() % 6) << 4); }
42 truth zombie::BodyPartIsVital(int I) const { return I == GROIN_INDEX || I == TORSO_INDEX; }
43 festring zombie::GetZombieDescription() const { return Description; }
45 truth angel::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX; }
47 truth genie::BodyPartIsVital(int I) const { return I == TORSO_INDEX || I == HEAD_INDEX; }
49 material* golem::CreateBodyPartMaterial(int, long Volume) const { return MAKE_MATERIAL(GetConfig(), Volume); }
51 truth sumowrestler::EquipmentIsAllowed(int I) const { return I == BELT_INDEX; }
53 truth ennerbeast::Hit(character* Enemy, v2, int, int)
55 if(CheckIfTooScaredToHit(Enemy))
56 return false;
58 if(RAND() & 1)
59 ADD_MESSAGE("%s yells: UGH UGHAaaa!", CHAR_DESCRIPTION(DEFINITE));
60 else
61 ADD_MESSAGE("%s yells: Uga Ugar Ugade Ugat!", CHAR_DESCRIPTION(DEFINITE));
63 rect Rect;
64 femath::CalculateEnvironmentRectangle(Rect, GetLevel()->GetBorder(), GetPos(), 30);
66 for(int x = Rect.X1; x <= Rect.X2; ++x)
67 for(int y = Rect.Y1; y <= Rect.Y2; ++y)
69 int ScreamStrength = int(70 / (hypot(GetPos().X - x, GetPos().Y - y) + 1));
71 if(ScreamStrength)
73 character* Char = GetNearSquare(x, y)->GetCharacter();
75 if(Char && Char != this)
77 msgsystem::EnterBigMessageMode();
79 if(Char->IsPlayer())
80 ADD_MESSAGE("You are hit by the horrible waves of high sound.");
81 else if(Char->CanBeSeenByPlayer())
82 ADD_MESSAGE("%s is hit by the horrible waves of high sound.", Char->CHAR_NAME(DEFINITE));
84 Char->ReceiveDamage(this, ScreamStrength, SOUND, ALL, YOURSELF, true);
85 Char->CheckDeath(CONST_S("killed @bkp scream"), this);
86 msgsystem::LeaveBigMessageMode();
89 GetNearLSquare(x, y)->GetStack()->ReceiveDamage(this, ScreamStrength, SOUND);
93 EditNP(-100);
94 EditAP(-1000000 / GetCWeaponSkill(BITE)->GetBonus());
95 EditStamina(-1000, false);
96 return true;
99 void skeleton::CreateCorpse(lsquare* Square)
101 if(GetHead())
103 item* Skull = SevereBodyPart(HEAD_INDEX, false, Square->GetStack());
104 Square->AddItem(Skull);
107 int Amount = 2 + (RAND() & 3);
109 for(int c = 0; c < Amount; ++c)
110 Square->AddItem(bone::Spawn());
112 SendToHell();
115 void humanoid::Save(outputfile& SaveFile) const
117 character::Save(SaveFile);
118 SaveFile << SWeaponSkill;
121 void humanoid::Load(inputfile& SaveFile)
123 character::Load(SaveFile);
124 SaveFile >> SWeaponSkill;
126 if(GetRightWielded())
127 for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
128 if((*i)->IsSkillOf(GetRightWielded()))
130 SetCurrentRightSWeaponSkill(*i);
131 break;
134 if(GetLeftWielded())
135 for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
136 if((*i)->IsSkillOf(GetLeftWielded()))
138 SetCurrentLeftSWeaponSkill(*i);
139 break;
143 truth golem::MoveRandomly()
145 if(!(RAND() % 500))
147 Engrave(CONST_S("Golem Needs Master"));
148 EditAP(-1000);
149 return true;
151 else
152 return character::MoveRandomly();
155 void ennerbeast::GetAICommand()
157 SeekLeader(GetLeader());
159 if(StateIsActivated(PANIC) || !(RAND() % 3))
160 Hit(0, ZERO_V2, YOURSELF);
162 if(CheckForEnemies(false, false, true))
163 return;
165 if(FollowLeader(GetLeader()))
166 return;
168 if(MoveRandomly())
169 return;
171 EditAP(-1000);
174 item* humanoid::GetMainWielded() const
176 if(GetMainArm())
177 if(GetMainArm()->GetWielded())
178 return GetMainArm()->GetWielded();
179 else
180 if(GetSecondaryArm())
181 return GetSecondaryArm()->GetWielded();
182 else
183 return 0;
184 else
185 if(GetSecondaryArm())
186 return GetSecondaryArm()->GetWielded();
187 else
188 return 0;
191 item* humanoid::GetSecondaryWielded() const
193 if(GetMainArm() && GetMainArm()->GetWielded() && GetSecondaryArm())
194 return GetSecondaryArm()->GetWielded();
195 else
196 return 0;
199 truth humanoid::Hit(character* Enemy, v2 HitPos, int Direction, int Flags)
201 if(CheckIfTooScaredToHit(Enemy))
202 return false;
204 if(IsPlayer())
206 if(!(Enemy->IsMasochist() && GetRelation(Enemy) == FRIEND) && GetRelation(Enemy) != HOSTILE && !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]")))
207 return false;
209 else if(GetAttribute(WISDOM) >= Enemy->GetAttackWisdomLimit())
210 return false;
212 if(GetBurdenState() == OVER_LOADED)
214 if(IsPlayer())
215 ADD_MESSAGE("You cannot fight while carrying so much.");
217 return false;
220 int c, AttackStyles;
222 for(c = 0, AttackStyles = 0; c < 8; ++c)
223 if(GetAttackStyle() & (1 << c))
224 ++AttackStyles;
226 int Chosen = RAND() % AttackStyles;
228 for(c = 0, AttackStyles = 0; c < 8; ++c)
229 if(GetAttackStyle() & (1 << c) && AttackStyles++ == Chosen)
231 Chosen = 1 << c;
232 break;
235 switch(Chosen)
237 case USE_ARMS:
238 if(CanAttackWithAnArm() && (!(Flags & SADIST_HIT) || HasSadistWeapon()))
240 msgsystem::EnterBigMessageMode();
241 Hostility(Enemy);
242 long FirstAPCost = 0, SecondAPCost = 0;
243 arm* FirstArm, * SecondArm;
245 if(RAND() & 1)
247 FirstArm = GetRightArm();
248 SecondArm = GetLeftArm();
250 else
252 FirstArm = GetLeftArm();
253 SecondArm = GetRightArm();
256 int Strength = Max(GetAttribute(ARM_STRENGTH), 1);
258 if(FirstArm && FirstArm->GetDamage() && (!(Flags & SADIST_HIT) || FirstArm->HasSadistWeapon()))
260 FirstAPCost = FirstArm->GetAPCost();
261 FirstArm->Hit(Enemy, HitPos, Direction, Flags);
263 if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE)))
264 DropBodyPart(FirstArm->GetBodyPartIndex());
267 if(!GetAction() && IsEnabled() && Enemy->IsEnabled() && SecondArm && SecondArm->GetDamage() && (!(Flags & SADIST_HIT) || SecondArm->HasSadistWeapon()))
269 SecondAPCost = SecondArm->GetAPCost();
270 SecondArm->Hit(Enemy, HitPos, Direction, Flags);
272 if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE)))
273 DropBodyPart(SecondArm->GetBodyPartIndex());
276 EditNP(-50);
277 EditAP(-Max(FirstAPCost, SecondAPCost));
278 EditStamina(-10000 / Strength, false);
279 msgsystem::LeaveBigMessageMode();
280 return true;
282 case USE_LEGS:
283 if(HasTwoUsableLegs())
285 msgsystem::EnterBigMessageMode();
286 Hostility(Enemy);
287 Kick(GetNearLSquare(HitPos), Direction, Flags & SADIST_HIT);
289 if(StateIsActivated(LEPROSY) && !RAND_N(25 * GetAttribute(ENDURANCE)))
290 DropBodyPart(RAND_2 ? RIGHT_LEG_INDEX : LEFT_LEG_INDEX);
292 msgsystem::LeaveBigMessageMode();
293 return true;
295 case USE_HEAD:
296 if(GetHead())
298 msgsystem::EnterBigMessageMode();
299 Hostility(Enemy);
300 Bite(Enemy, HitPos, Direction, Flags & SADIST_HIT);
301 msgsystem::LeaveBigMessageMode();
302 return true;
304 default:
305 if(IsPlayer())
306 ADD_MESSAGE("You are currently quite unable to damage anything.");
308 return false;
312 truth humanoid::AddSpecialSkillInfo(felist& List) const
314 truth Something = false;
316 if(CurrentRightSWeaponSkill && CurrentRightSWeaponSkill->GetHits() / 100)
318 List.AddEntry(CONST_S(""), LIGHT_GRAY);
319 festring Buffer = CONST_S("right accustomization");
320 Buffer.Resize(30);
321 Buffer << CurrentRightSWeaponSkill->GetLevel();
322 Buffer.Resize(40);
323 Buffer << CurrentRightSWeaponSkill->GetHits() / 100;
324 Buffer.Resize(50);
326 if(CurrentRightSWeaponSkill->GetLevel() != 20)
327 Buffer << (CurrentRightSWeaponSkill->GetLevelMap(CurrentRightSWeaponSkill->GetLevel() + 1) - CurrentRightSWeaponSkill->GetHits()) / 100;
328 else
329 Buffer << '-';
331 Buffer.Resize(60);
332 int Bonus = CurrentRightSWeaponSkill->GetBonus();
333 Buffer << '+' << (Bonus - 1000) / 10;
335 if(Bonus %= 10)
336 Buffer << '.' << Bonus;
338 Buffer << '%';
339 List.AddEntry(Buffer, WHITE);
340 Something = true;
343 if(CurrentLeftSWeaponSkill && CurrentLeftSWeaponSkill->GetHits() / 100)
345 if(!Something)
346 List.AddEntry(CONST_S(""), LIGHT_GRAY);
348 festring Buffer = CONST_S("left accustomization");
349 Buffer.Resize(30);
350 Buffer << CurrentLeftSWeaponSkill->GetLevel();
351 Buffer.Resize(40);
352 Buffer << CurrentLeftSWeaponSkill->GetHits() / 100;
353 Buffer.Resize(50);
355 if(CurrentLeftSWeaponSkill->GetLevel() != 20)
356 Buffer << (CurrentLeftSWeaponSkill->GetLevelMap(CurrentLeftSWeaponSkill->GetLevel() + 1) - CurrentLeftSWeaponSkill->GetHits()) / 100;
357 else
358 Buffer << '-';
360 Buffer.Resize(60);
361 int Bonus = CurrentLeftSWeaponSkill->GetBonus();
362 Buffer << '+' << (Bonus - 1000) / 10;
364 if(Bonus %= 10)
365 Buffer << '.' << Bonus;
367 Buffer << '%';
368 List.AddEntry(Buffer, WHITE);
369 Something = true;
372 return Something;
375 void priest::BeTalkedTo () {
376 if (GetRelation(PLAYER) == HOSTILE) {
377 ADD_MESSAGE("\"Sinner! My hands shall pour Dinive Wrath upon thee!\"");
378 return;
380 for (int c = 0; c < PLAYER->GetBodyParts(); ++c) {
381 if (!PLAYER->GetBodyPart(c) && PLAYER->CanCreateBodyPart(c)) {
382 truth HasOld = false;
383 for (std::list<ulong>::const_iterator i = PLAYER->GetOriginalBodyPartID(c).begin(); i != PLAYER->GetOriginalBodyPartID(c).end(); ++i) {
384 bodypart *OldBodyPart = static_cast<bodypart *>(PLAYER->SearchForItem(*i));
385 if (OldBodyPart) {
386 HasOld = true;
387 long Price = GetConfig() == VALPURUS ? 50 : 10;
388 if (PLAYER->GetMoney() >= Price) {
389 if (!OldBodyPart->CanRegenerate())
390 ADD_MESSAGE("\"Sorry, I cannot put back bodyparts made of %s, not even your severed %s.\"", OldBodyPart->GetMainMaterial()->GetName(false, false).CStr(), PLAYER->GetBodyPartName(c).CStr());
391 else {
392 ADD_MESSAGE("\"I could put your old %s back in exchange for %ld gold.\"", PLAYER->GetBodyPartName(c).CStr(), Price);
393 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
394 OldBodyPart->SetHP(1);
395 PLAYER->SetMoney(PLAYER->GetMoney()-Price);
396 SetMoney(GetMoney()+Price);
397 OldBodyPart->RemoveFromSlot();
398 PLAYER->AttachBodyPart(OldBodyPart);
399 return;
402 } else {
403 ADD_MESSAGE("\"Your %s is severed. Help yourself and get %ldgp and I'll help you too.\"", PLAYER->GetBodyPartName(c).CStr(), Price);
407 long Price = GetConfig() == VALPURUS ? 100 : 20;
408 if (PLAYER->GetMoney() >= Price) {
409 if (HasOld)
410 ADD_MESSAGE("\"I could still summon up a new one for %ld gold.\"", Price);
411 else
412 ADD_MESSAGE("\"Since you don't seem to have your original %s with you, I could summon up a new one for %ld gold.\"", PLAYER->GetBodyPartName(c).CStr(), Price);
413 if (game::TruthQuestion(CONST_S("Agreed? [y/N]"))) {
414 PLAYER->SetMoney(PLAYER->GetMoney()-Price);
415 SetMoney(GetMoney()+Price);
416 PLAYER->CreateBodyPart(c);
417 PLAYER->GetBodyPart(c)->SetHP(1);
418 return;
420 } else if (!HasOld) {
421 ADD_MESSAGE("\"You don't have your original %s with you. I could create you a new one, but my Divine Employer is not a communist and you need %ldgp first.\"", PLAYER->GetBodyPartName(c).CStr(), Price);
425 if (PLAYER->TemporaryStateIsActivated(POISONED)) {
426 long Price = GetConfig() == VALPURUS ? 25 : 5;
427 if (PLAYER->GetMoney() >= Price) {
428 ADD_MESSAGE("\"You seem to be rather ill. I could give you a small dose of antidote for %ld gold pieces.\"", Price);
429 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
430 ADD_MESSAGE("You feel better.");
431 PLAYER->DeActivateTemporaryState(POISONED);
432 PLAYER->SetMoney(PLAYER->GetMoney()-Price);
433 SetMoney(GetMoney()+Price);
434 return;
436 } else {
437 ADD_MESSAGE("\"You seem to be rather ill. Get %ld gold pieces and I'll fix that.\"", Price);
440 if (PLAYER->TemporaryStateIsActivated(LEPROSY)) {
441 long Price = GetConfig() == VALPURUS ? 100 : 20;
442 if (PLAYER->GetMoney() >= Price) {
443 ADD_MESSAGE("\"You seem to have contracted the vile disease of leprosy. I could give you a small dose of medicince for %ld gold pieces.\"", Price);
444 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
445 ADD_MESSAGE("You feel better.");
446 PLAYER->DeActivateTemporaryState(LEPROSY);
447 PLAYER->SetMoney(PLAYER->GetMoney()-Price);
448 SetMoney(GetMoney()+Price);
449 return;
451 } else {
452 ADD_MESSAGE("\"You seem to be falling apart. Get %ld gold pieces and I'll fix that.\"", Price);
455 if (PLAYER->TemporaryStateIsActivated(LYCANTHROPY)) {
456 long Price = GetConfig() == VALPURUS ? 100 : 20;
457 if (PLAYER->GetMoney() >= Price) {
458 ADD_MESSAGE("\"You seem to be turning into a werewolf quite frequently. Well, everyone has right to little secret habits, but if you wish to donate %ldgp to %s, maybe I could pray %s to remove the canine blood from your veins, just so you don't scare our blessed youth.\"", Price, GetMasterGod()->GetName(), GetMasterGod()->GetObjectPronoun());
459 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
460 ADD_MESSAGE("You feel better.");
461 PLAYER->DeActivateTemporaryState(LYCANTHROPY);
462 PLAYER->SetMoney(PLAYER->GetMoney()-Price);
463 SetMoney(GetMoney()+Price);
464 return;
466 } else {
467 ADD_MESSAGE("\"You seem to be lycanthropic. I might be able to do something for that but I need %ldgp for the ritual materials first.\"", Price);
470 humanoid::BeTalkedTo();
474 void skeleton::BeTalkedTo()
476 if(GetHead())
477 humanoid::BeTalkedTo();
478 else
479 ADD_MESSAGE("The headless %s remains silent.", PLAYER->CHAR_DESCRIPTION(UNARTICLED));
482 void communist::BeTalkedTo()
484 if(GetRelation(PLAYER) != HOSTILE
485 && GetTeam() != PLAYER->GetTeam()
486 && PLAYER->GetRelativeDanger(this, true) > 0.1)
488 ADD_MESSAGE("%s seems to be very friendly. \"%s make good communist. %s go with %s!\"", CHAR_DESCRIPTION(DEFINITE), PLAYER->GetAssignedName().CStr(), CHAR_NAME(UNARTICLED), PLAYER->GetAssignedName().CStr());
490 for(std::list<character*>::const_iterator i = GetTeam()->GetMember().begin(); i != GetTeam()->GetMember().end();)
491 if(*i != this)
493 character* Char = *i++;
494 Char->ChangeTeam(PLAYER->GetTeam());
496 else
497 ++i;
499 ChangeTeam(PLAYER->GetTeam());
501 else if(GetTeam() != PLAYER->GetTeam() && !(RAND() % 5))
502 ADD_MESSAGE("\"You weak. Learn killing and come back.\"");
503 else
504 character::BeTalkedTo();
507 void hunter::BeTalkedTo()
509 if(GetRelation(PLAYER) != HOSTILE && GetMainWielded() && !(RAND() % 10))
510 ADD_MESSAGE("\"This is my %s. There are many like it but this one is mine. My %s is my best friend.\"", GetMainWielded()->CHAR_NAME(UNARTICLED), GetMainWielded()->CHAR_NAME(UNARTICLED));
511 else
512 character::BeTalkedTo();
515 void slave::BeTalkedTo()
517 if(GetRelation(PLAYER) == HOSTILE)
519 ADD_MESSAGE("\"Yikes!\"");
520 return;
523 room* Room = GetHomeRoom();
525 if(Room && Room->MasterIsActive())
527 character* Master = Room->GetMaster();
529 if(PLAYER->GetMoney() >= 50)
531 ADD_MESSAGE("%s talks: \"Do you want to buy me? 50 gold pieces. I work very hard.\"", CHAR_DESCRIPTION(DEFINITE));
533 if(game::TruthQuestion(CONST_S("Do you want to buy him? [y/N]")))
535 PLAYER->SetMoney(PLAYER->GetMoney() - 50);
536 Master->SetMoney(Master->GetMoney() + 50);
537 ChangeTeam(PLAYER->GetTeam());
538 RemoveHomeData();
541 else
542 ADD_MESSAGE("\"Don't touch me! Master doesn't want people to touch sale items. I'm worth 50 gold pieces, you know!\"");
544 return;
547 if(GetTeam() == PLAYER->GetTeam())
549 if((PLAYER->GetMainWielded() && PLAYER->GetMainWielded()->IsWhip()) || (PLAYER->GetSecondaryWielded() && PLAYER->GetSecondaryWielded()->IsWhip()))
550 ADD_MESSAGE("\"Don't hit me! I work! I obey! I don't think!\"");
551 else
552 character::BeTalkedTo();
554 else
555 ADD_MESSAGE("\"I'm free! Rejoice!\"");
558 void slave::GetAICommand()
560 SeekLeader(GetLeader());
562 if(CheckForEnemies(true, true, true))
563 return;
565 if(CheckForUsefulItemsOnGround())
566 return;
568 if(FollowLeader(GetLeader()))
569 return;
571 if(CheckForDoors())
572 return;
574 if(!GetHomeRoom() || !GetHomeRoom()->MasterIsActive())
576 RemoveHomeData();
578 if(MoveRandomly())
579 return;
581 else if(MoveTowardsHomePos())
582 return;
584 EditAP(-1000);
587 void librarian::BeTalkedTo()
589 if(GetRelation(PLAYER) == HOSTILE)
591 ADD_MESSAGE("\"The pen is mightier than the sword! Fall, unlearned one!\"");
592 return;
595 static long Said;
597 switch(RandomizeReply(Said, 12))
599 case 0:
600 if(game::GetPetrus() && !game::GetStoryState())
601 ADD_MESSAGE("\"Thou shouldst visit Petrus if thou art in need of further adventures.\"");
602 else
603 ADD_MESSAGE("\"They say a wand of polymorph hath dozens of uses.\"");
605 break;
606 case 1:
607 if(game::GetPetrus() && game::GetStoryState() == 1)
608 ADD_MESSAGE("\"Thou art going to fight Elpuri? Beware! It is a powerful enemy. Other monsters are very vulnerable if surrounded by thy party, but not that beast, for it may slay a horde of thy friends at once with its horrendous tail attack.\"");
609 else
610 ADD_MESSAGE("\"Thou shalt remember: Scientia est potentia.\"");
612 break;
613 case 2:
614 if(game::GetPetrus() && game::GetStoryState() == 1)
615 ADD_MESSAGE("\"Elpuri the Dark Frog abhors light and resides in a level of eternal darkness.\"");
616 else
617 ADD_MESSAGE("\"Shh! Thou shalt be silent in the library.\"");
619 break;
620 case 3:
621 if(game::GetPetrus() && game::GetStoryState() == 1)
622 ADD_MESSAGE("\"Elpuri's attacks are so strong that they may shatter many of thy precious items.\"");
623 else
624 ADD_MESSAGE("\"Dost thou not smell all the knowledge floating around here?\"");
626 break;
627 case 4:
628 ADD_MESSAGE("\"It is said that Loricatus, the god of smithing, can upgrade thy weapons' materials.\"");
629 break;
630 case 5:
631 if(game::GetPetrus() && game::GetStoryState() == 1)
632 ADD_MESSAGE("\"The Shirt of the Golden Eagle is a legendary artifact. Thou canst not find a better armor.\"");
633 else
634 ADD_MESSAGE("\"In this book they talk about Mortifer, the great chaos god. He hates us mortals more than anything and will respond only to Champions of Evil.\"");
636 break;
637 case 6:
638 ADD_MESSAGE("\"Attnam is traditionally ruled by the high priest of the Great Frog. He holds the Shirt of the Golden Eagle and has always killed his predecessor.\"");
639 break;
640 case 7:
641 ADD_MESSAGE("\"They say thou shouldst keep all the artifacts thou findst. They shall make thee famous after thy retirement.\"");
642 break;
643 case 8:
644 ADD_MESSAGE("\"If thou wilt ever encounter an enner beast, know this: It is a horrible foe. It may shatter thy items and armor with its scream that penetrates iron and stone. Thou shouldst not engage it in melee but rather kill it from afar.\"");
645 break;
646 case 9:
647 if(game::GetPetrus() && game::GetStoryState() == 1)
648 ADD_MESSAGE("\"Thou art not alone in thy attempt to defeat Elpuri. A brave adventurer called Ivan also diveth into its cave not long ago.\"");
649 else
650 ADD_MESSAGE("\"It is said that chaotic gods offer great power to their followers. But thou must remember: unlike lawfuls, they shall not help thee when things go bad.\"");
652 break;
653 case 10:
654 ADD_MESSAGE("\"If a man cannot choose, he ceases to be a man.\"");
655 break;
656 case 11:
657 ADD_MESSAGE("%s sighs: \"The censorship laws in this town are really too strict...\"", CHAR_DESCRIPTION(DEFINITE));
658 break;
662 truth communist::MoveRandomly()
664 switch(RAND() % 1000)
666 case 0:
667 if(CanBeSeenByPlayer())
668 ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED));
670 Engrave(CONST_S("The bourgeois is a bourgeois -- for the benefit of the working class."));
671 return true;
672 case 1:
673 if(CanBeSeenByPlayer())
674 ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED));
676 Engrave(CONST_S("Proletarians of all countries, unite!"));
677 return true;
678 case 2:
679 if(CanBeSeenByPlayer())
680 ADD_MESSAGE("%s engraves something to the ground.", CHAR_NAME(UNARTICLED));
682 Engrave(CONST_S("Capital is therefore not only personal; it is a social power."));
683 return true;
684 default:
685 return character::MoveRandomly();
689 void zombie::BeTalkedTo()
691 if(GetRelation(PLAYER) == HOSTILE && PLAYER->GetAttribute(INTELLIGENCE) > 5)
693 if(RAND() % 5)
695 if(GetHead())
696 ADD_MESSAGE("\"Need brain!!\"");
697 else
698 ADD_MESSAGE("\"Need head with brain!!\"");
700 else
701 ADD_MESSAGE("\"Redrum! Redrum! Redrum!\"");
703 else
704 ADD_MESSAGE("\"Need brain but you too stoopid!\"");
707 void angel::Save(outputfile& SaveFile) const
709 humanoid::Save(SaveFile);
710 SaveFile << LastHealed;
713 void angel::Load(inputfile& SaveFile)
715 humanoid::Load(SaveFile);
716 SaveFile >> LastHealed;
719 void angel::CreateInitialEquipment(int SpecialFlags)
721 humanoid::CreateInitialEquipment(SpecialFlags);
722 GetStack()->AddItem(holybook::Spawn(GetConfig(), SpecialFlags));
723 armor* Equipment;
724 meleeweapon* Weapon;
726 switch(GetMasterGod()->GetBasicAlignment())
728 case GOOD:
729 Equipment = bodyarmor::Spawn(PLATE_MAIL, SpecialFlags|NO_MATERIALS);
730 Equipment->InitMaterials(MAKE_MATERIAL(ANGEL_HAIR), !(SpecialFlags & NO_PIC_UPDATE));
731 Equipment->SetEnchantment(1);
732 SetBodyArmor(Equipment);
733 Weapon = meleeweapon::Spawn(LONG_SWORD, SpecialFlags|NO_MATERIALS);
734 Weapon->InitMaterials(MAKE_MATERIAL(MITHRIL), MAKE_MATERIAL(MITHRIL), !(SpecialFlags & NO_PIC_UPDATE));
735 Weapon->SetEnchantment(2);
736 SetRightWielded(Weapon);
737 Equipment = shield::Spawn(0, SpecialFlags|NO_MATERIALS);
738 Equipment->InitMaterials(MAKE_MATERIAL(MITHRIL), !(SpecialFlags & NO_PIC_UPDATE));
739 Equipment->SetEnchantment(2);
740 SetLeftWielded(Equipment);
741 GetCWeaponSkill(LARGE_SWORDS)->AddHit(20000);
742 GetCWeaponSkill(SHIELDS)->AddHit(50000);
743 GetCurrentRightSWeaponSkill()->AddHit(20000);
744 GetCurrentLeftSWeaponSkill()->AddHit(20000);
745 GetRightArm()->SetDexterity(40);
746 GetLeftArm()->SetDexterity(40);
747 break;
748 case NEUTRAL:
749 Equipment = cloak::Spawn(0, SpecialFlags|NO_MATERIALS);
750 Equipment->InitMaterials(MAKE_MATERIAL(ANGEL_HAIR), !(SpecialFlags & NO_PIC_UPDATE));
751 Equipment->SetEnchantment(1);
752 SetCloak(Equipment);
753 Weapon = meleeweapon::Spawn(WAR_HAMMER, SpecialFlags|NO_MATERIALS);
754 Weapon->InitMaterials(MAKE_MATERIAL(MITHRIL), MAKE_MATERIAL(TEAK_WOOD), !(SpecialFlags & NO_PIC_UPDATE));
755 Weapon->SetEnchantment(2);
756 SetRightWielded(Weapon);
757 Weapon = meleeweapon::Spawn(WAR_HAMMER, SpecialFlags|NO_MATERIALS);
758 Weapon->InitMaterials(MAKE_MATERIAL(MITHRIL), MAKE_MATERIAL(TEAK_WOOD), !(SpecialFlags & NO_PIC_UPDATE));
759 Weapon->SetEnchantment(2);
760 SetLeftWielded(Weapon);
761 GetCWeaponSkill(BLUNT_WEAPONS)->AddHit(50000);
762 GetCurrentRightSWeaponSkill()->AddHit(20000);
763 GetCurrentLeftSWeaponSkill()->AddHit(20000);
764 SetEndurance(40);
765 break;
766 case EVIL:
767 Weapon = meleeweapon::Spawn(HALBERD, SpecialFlags|NO_MATERIALS);
768 Weapon->InitMaterials(MAKE_MATERIAL(MITHRIL), MAKE_MATERIAL(EBONY_WOOD), !(SpecialFlags & NO_PIC_UPDATE));
769 Weapon->SetEnchantment(2);
770 SetRightWielded(Weapon);
771 Equipment = gauntlet::Spawn(0, SpecialFlags|NO_MATERIALS);
772 Equipment->InitMaterials(MAKE_MATERIAL(ANGEL_HAIR), !(SpecialFlags & NO_PIC_UPDATE));
773 Equipment->SetEnchantment(1);
774 SetRightGauntlet(Equipment);
775 Equipment = gauntlet::Spawn(0, SpecialFlags|NO_MATERIALS);
776 Equipment->InitMaterials(MAKE_MATERIAL(ANGEL_HAIR), !(SpecialFlags & NO_PIC_UPDATE));
777 Equipment->SetEnchantment(1);
778 SetLeftGauntlet(Equipment);
779 GetCWeaponSkill(POLE_ARMS)->AddHit(100000);
780 GetCurrentRightSWeaponSkill()->AddHit(100000);
781 GetRightArm()->SetStrength(40);
782 GetLeftArm()->SetStrength(40);
786 void kamikazedwarf::CreateInitialEquipment(int SpecialFlags)
788 humanoid::CreateInitialEquipment(SpecialFlags);
789 SetRightWielded(holybook::Spawn(GetConfig(), SpecialFlags));
790 GetCWeaponSkill(UNCATEGORIZED)->AddHit(GetWSkillHits());
791 GetCurrentRightSWeaponSkill()->AddHit(GetWSkillHits());
794 truth kamikazedwarf::Hit(character* Enemy, v2 HitPos, int Direction, int Flags)
796 if(!IsPlayer())
798 itemvector KamikazeWeapon;
799 sortdata SortData(KamikazeWeapon, this, false, &item::IsKamikazeWeapon);
800 SortAllItems(SortData);
802 if(!KamikazeWeapon.empty())
804 if(IsElite() && RAND() & 1)
805 ADD_MESSAGE("%s shouts: \"This time I won't fail, O Great %s!\"", CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName());
806 else if(RAND() & 1)
807 ADD_MESSAGE("%s shouts: \"For %s!\"", CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName());
808 else
809 ADD_MESSAGE("%s screams: \"%s, here I come!\"", CHAR_DESCRIPTION(DEFINITE), GetMasterGod()->GetName());
811 if(KamikazeWeapon[RAND_N(KamikazeWeapon.size())]->Apply(this))
812 return true;
816 return humanoid::Hit(Enemy, HitPos, Direction, Flags);
819 void kamikazedwarf::GetAICommand()
821 if(GetHomeRoom())
822 StandIdleAI();
823 else
825 if(!RAND_N(50))
827 SingRandomSong();
828 return;
831 character::GetAICommand();
835 int humanoid::GetSize() const
837 int Size = 0;
839 if(GetHead())
840 Size += GetHead()->GetSize();
842 if(GetTorso())
843 Size += GetTorso()->GetSize();
845 leg* RightLeg = GetRightLeg();
846 leg* LeftLeg = GetLeftLeg();
848 if(LeftLeg && RightLeg)
849 Size += Max(LeftLeg->GetSize(), RightLeg->GetSize());
850 else if(LeftLeg)
851 Size += LeftLeg->GetSize();
852 else if(RightLeg)
853 Size += RightLeg->GetSize();
855 return Size;
858 long humanoid::GetBodyPartSize(int I, int TotalSize) const
860 switch(I)
862 case HEAD_INDEX: return 20;
863 case TORSO_INDEX: return ((TotalSize - 20) << 1) / 5;
864 case RIGHT_ARM_INDEX:
865 case LEFT_ARM_INDEX: return (TotalSize - 20) * 3 / 5;
866 case GROIN_INDEX: return (TotalSize - 20) / 3;
867 case RIGHT_LEG_INDEX:
868 case LEFT_LEG_INDEX: return (TotalSize - 20) * 3 / 5;
871 ABORT("Illegal humanoid bodypart size request!");
872 return 0;
875 long humanoid::GetBodyPartVolume(int I) const
877 switch(I)
879 case HEAD_INDEX: return 4000;
880 case TORSO_INDEX: return (GetTotalVolume() - 4000) * 13 / 30;
881 case RIGHT_ARM_INDEX:
882 case LEFT_ARM_INDEX: return (GetTotalVolume() - 4000) / 10;
883 case GROIN_INDEX: return (GetTotalVolume() - 4000) / 10;
884 case RIGHT_LEG_INDEX:
885 case LEFT_LEG_INDEX: return ((GetTotalVolume() - 4000) << 1) / 15;
888 ABORT("Illegal humanoid bodypart volume request!");
889 return 0;
892 bodypart* humanoid::MakeBodyPart(int I) const
894 switch(I)
896 case TORSO_INDEX: return humanoidtorso::Spawn(0, NO_MATERIALS);
897 case HEAD_INDEX: return head::Spawn(0, NO_MATERIALS);
898 case RIGHT_ARM_INDEX: return rightarm::Spawn(0, NO_MATERIALS);
899 case LEFT_ARM_INDEX: return leftarm::Spawn(0, NO_MATERIALS);
900 case GROIN_INDEX: return groin::Spawn(0, NO_MATERIALS);
901 case RIGHT_LEG_INDEX: return rightleg::Spawn(0, NO_MATERIALS);
902 case LEFT_LEG_INDEX: return leftleg::Spawn(0, NO_MATERIALS);
905 ABORT("Weird bodypart to make for a humanoid. It must be your fault!");
906 return 0;
909 truth humanoid::ReceiveDamage(character* Damager, int Damage, int Type, int TargetFlags, int Direction, truth Divide, truth PenetrateArmor, truth Critical, truth ShowMsg)
911 int ChooseFrom[MAX_BODYPARTS], BodyParts = 0;
913 if(TargetFlags & RIGHT_ARM && GetRightArm())
914 ChooseFrom[BodyParts++] = 2;
916 if(TargetFlags & LEFT_ARM && GetLeftArm())
917 ChooseFrom[BodyParts++] = 3;
919 if(TargetFlags & RIGHT_LEG && GetRightLeg())
920 ChooseFrom[BodyParts++] = 5;
922 if(TargetFlags & LEFT_LEG && GetLeftLeg())
923 ChooseFrom[BodyParts++] = 6;
925 if(TargetFlags & HEAD && GetHead())
926 ChooseFrom[BodyParts++] = 1;
928 if(TargetFlags & TORSO && GetTorso())
929 ChooseFrom[BodyParts++] = 0;
931 if(TargetFlags & GROIN && GetGroin())
932 ChooseFrom[BodyParts++] = 4;
934 if(!BodyParts)
935 return false;
937 truth Affected = false;
939 if(Divide)
941 int c;
942 long TotalVolume = 0;
944 for(c = 0; c < BodyParts; ++c)
945 TotalVolume += GetBodyPart(ChooseFrom[c])->GetBodyPartVolume();
947 for(c = 0; c < BodyParts; ++c)
948 if(ReceiveBodyPartDamage(Damager, long(Damage) * GetBodyPart(ChooseFrom[c])->GetBodyPartVolume() / TotalVolume, Type, ChooseFrom[c], Direction, PenetrateArmor, Critical, false))
949 Affected = true;
951 else
953 long Possibility[MAX_BODYPARTS], PossibilitySum = 0;
954 int Index = 0;
956 for(int c = 0; c < BodyParts; ++c)
957 PossibilitySum += Possibility[Index++] = GetBodyPart(ChooseFrom[c])->GetBodyPartVolume();
959 Index = femath::WeightedRand(Possibility, PossibilitySum);
960 Affected = ReceiveBodyPartDamage(Damager, Damage, Type, ChooseFrom[Index], Direction, PenetrateArmor, Critical, false);
963 if(!Affected && ShowMsg)
965 if(IsPlayer())
966 ADD_MESSAGE("You are not hurt.");
967 else if(CanBeSeenByPlayer())
968 ADD_MESSAGE("%s is not hurt.", GetPersonalPronoun().CStr());
971 if(DamageTypeAffectsInventory(Type))
973 for(int c = 0; c < GetEquipments(); ++c)
975 item* Equipment = GetEquipment(c);
977 if(Equipment)
978 Equipment->ReceiveDamage(Damager, Damage, Type);
981 GetStack()->ReceiveDamage(Damager, Damage, Type);
984 return Affected;
987 arm* humanoid::GetMainArm() const
989 return GetRightArm() ? GetRightArm() : GetLeftArm();
992 arm* humanoid::GetSecondaryArm() const
994 return GetRightArm() ? GetLeftArm() : 0;
997 cchar* humanoid::GetEquipmentName(int I) const // convert to array
999 switch(I)
1001 case HELMET_INDEX: return "helmet";
1002 case AMULET_INDEX: return "amulet";
1003 case CLOAK_INDEX: return "cloak";
1004 case BODY_ARMOR_INDEX: return "body armor";
1005 case BELT_INDEX: return "belt";
1006 case RIGHT_WIELDED_INDEX: return "right hand wielded";
1007 case LEFT_WIELDED_INDEX: return "left hand wielded";
1008 case RIGHT_RING_INDEX: return "right ring";
1009 case LEFT_RING_INDEX: return "left ring";
1010 case RIGHT_GAUNTLET_INDEX: return "right gauntlet";
1011 case LEFT_GAUNTLET_INDEX: return "left gauntlet";
1012 case RIGHT_BOOT_INDEX: return "right boot";
1013 case LEFT_BOOT_INDEX: return "left boot";
1016 return "forbidden piece of cloth";
1019 sorter humanoid::EquipmentSorter(int I) const
1021 switch(I)
1023 case HELMET_INDEX: return &item::IsHelmet;
1024 case AMULET_INDEX: return &item::IsAmulet;
1025 case CLOAK_INDEX: return &item::IsCloak;
1026 case BODY_ARMOR_INDEX: return &item::IsBodyArmor;
1027 case BELT_INDEX: return &item::IsBelt;
1028 case RIGHT_WIELDED_INDEX:
1029 case LEFT_WIELDED_INDEX: return 0;
1030 case RIGHT_RING_INDEX:
1031 case LEFT_RING_INDEX: return &item::IsRing;
1032 case RIGHT_GAUNTLET_INDEX:
1033 case LEFT_GAUNTLET_INDEX: return &item::IsGauntlet;
1034 case RIGHT_BOOT_INDEX:
1035 case LEFT_BOOT_INDEX: return &item::IsBoot;
1038 return 0;
1041 bodypart* humanoid::GetBodyPartOfEquipment(int I) const
1043 switch(I)
1045 case HELMET_INDEX:
1046 case AMULET_INDEX:
1047 return GetHead();
1048 case CLOAK_INDEX:
1049 case BODY_ARMOR_INDEX:
1050 case BELT_INDEX:
1051 return GetTorso();
1052 case RIGHT_WIELDED_INDEX:
1053 case RIGHT_RING_INDEX:
1054 case RIGHT_GAUNTLET_INDEX:
1055 return GetRightArm();
1056 case LEFT_WIELDED_INDEX:
1057 case LEFT_RING_INDEX:
1058 case LEFT_GAUNTLET_INDEX:
1059 return GetLeftArm();
1060 case RIGHT_BOOT_INDEX:
1061 return GetRightLeg();
1062 case LEFT_BOOT_INDEX:
1063 return GetLeftLeg();
1066 return 0;
1069 item* humanoid::GetEquipment(int I) const
1071 switch(I)
1073 case HELMET_INDEX: return GetHelmet();
1074 case AMULET_INDEX: return GetAmulet();
1075 case CLOAK_INDEX: return GetCloak();
1076 case BODY_ARMOR_INDEX: return GetBodyArmor();
1077 case BELT_INDEX: return GetBelt();
1078 case RIGHT_WIELDED_INDEX: return GetRightWielded();
1079 case LEFT_WIELDED_INDEX: return GetLeftWielded();
1080 case RIGHT_RING_INDEX: return GetRightRing();
1081 case LEFT_RING_INDEX: return GetLeftRing();
1082 case RIGHT_GAUNTLET_INDEX: return GetRightGauntlet();
1083 case LEFT_GAUNTLET_INDEX: return GetLeftGauntlet();
1084 case RIGHT_BOOT_INDEX: return GetRightBoot();
1085 case LEFT_BOOT_INDEX: return GetLeftBoot();
1088 return 0;
1091 void humanoid::SetEquipment(int I, item* What)
1093 switch(I)
1095 case HELMET_INDEX: SetHelmet(What); break;
1096 case AMULET_INDEX: SetAmulet(What); break;
1097 case CLOAK_INDEX: SetCloak(What); break;
1098 case BODY_ARMOR_INDEX: SetBodyArmor(What); break;
1099 case BELT_INDEX: SetBelt(What); break;
1100 case RIGHT_WIELDED_INDEX: SetRightWielded(What); break;
1101 case LEFT_WIELDED_INDEX: SetLeftWielded(What); break;
1102 case RIGHT_RING_INDEX: SetRightRing(What); break;
1103 case LEFT_RING_INDEX: SetLeftRing(What); break;
1104 case RIGHT_GAUNTLET_INDEX: SetRightGauntlet(What); break;
1105 case LEFT_GAUNTLET_INDEX: SetLeftGauntlet(What); break;
1106 case RIGHT_BOOT_INDEX: SetRightBoot(What); break;
1107 case LEFT_BOOT_INDEX: SetLeftBoot(What); break;
1111 void humanoid::SwitchToDig(item* DigItem, v2 Square)
1113 dig* Dig = dig::Spawn(this);
1115 if(GetRightArm())
1117 item* Item = GetRightArm()->GetWielded();
1119 if(Item && Item != DigItem)
1121 Dig->SetRightBackupID(GetRightArm()->GetWielded()->GetID());
1122 GetRightArm()->GetWielded()->MoveTo(GetStack());
1126 if(GetLeftArm())
1128 item* Item = GetLeftArm()->GetWielded();
1130 if(Item && Item != DigItem)
1132 Dig->SetLeftBackupID(GetLeftArm()->GetWielded()->GetID());
1133 GetLeftArm()->GetWielded()->MoveTo(GetStack());
1137 if(GetMainWielded() != DigItem)
1139 Dig->SetMoveDigger(true);
1140 DigItem->RemoveFromSlot();
1142 if(GetMainArm() && GetMainArm()->IsUsable())
1143 GetMainArm()->SetWielded(DigItem);
1144 else
1145 GetSecondaryArm()->SetWielded(DigItem);
1147 else
1148 Dig->SetMoveDigger(false);
1150 Dig->SetSquareDug(Square);
1151 SetAction(Dig);
1154 truth humanoid::CheckKick() const
1156 if(!CanKick())
1158 if(IsPlayer())
1159 ADD_MESSAGE("This race can't kick.");
1161 return false;
1164 if(GetUsableLegs() < 2)
1166 if(IsPlayer())
1167 ADD_MESSAGE("How are you going to do that with %s?", GetUsableLegs() ? "only one usable leg" : "no usable legs");
1169 return false;
1171 else
1172 return true;
1175 int humanoid::GetUsableLegs() const
1177 int Legs = 0;
1179 if(RightLegIsUsable())
1180 ++Legs;
1182 if(LeftLegIsUsable())
1183 ++Legs;
1185 return Legs;
1188 int humanoid::GetUsableArms() const
1190 int Arms = 0;
1192 if(RightArmIsUsable())
1193 ++Arms;
1195 if(LeftArmIsUsable())
1196 ++Arms;
1198 return Arms;
1201 truth humanoid::CheckThrow() const
1203 if(!character::CheckThrow())
1204 return false;
1206 if(HasAUsableArm())
1207 return true;
1208 else
1210 ADD_MESSAGE("You don't have a usable arm to do that!");
1211 return false;
1215 truth humanoid::CheckOffer() const
1217 if(HasAUsableArm())
1218 return true;
1219 else
1221 ADD_MESSAGE("You need a usable arm to offer.");
1222 return false;
1226 v2 humanoid::GetEquipmentPanelPos(int I) const // convert to array
1228 switch(I)
1230 case HELMET_INDEX: return v2(34, -22);
1231 case AMULET_INDEX: return v2(14, -22);
1232 case CLOAK_INDEX: return v2(-6, -22);
1233 case BODY_ARMOR_INDEX: return v2(54, -22);
1234 case BELT_INDEX: return v2(24, 70);
1235 case RIGHT_WIELDED_INDEX: return v2(-14, 4);
1236 case LEFT_WIELDED_INDEX: return v2(62, 4);
1237 case RIGHT_RING_INDEX: return v2(-14, 44);
1238 case LEFT_RING_INDEX: return v2(62, 44);
1239 case RIGHT_GAUNTLET_INDEX: return v2(-14, 24);
1240 case LEFT_GAUNTLET_INDEX: return v2(62, 24);
1241 case RIGHT_BOOT_INDEX: return v2(4, 70);
1242 case LEFT_BOOT_INDEX: return v2(44, 70);
1245 return v2(24, 12);
1248 void humanoid::DrawSilhouette(truth AnimationDraw) const
1250 int c;
1251 blitdata B1 = { DOUBLE_BUFFER,
1252 { 0, 0 },
1253 { 0, 0 },
1254 { TILE_SIZE, TILE_SIZE },
1255 { ivanconfig::GetContrastLuminance() },
1256 TRANSPARENT_COLOR,
1257 ALLOW_ANIMATE };
1259 v2 Where(RES.X - SILHOUETTE_SIZE.X - 39, 53);
1260 cint Equipments = GetEquipments();
1262 if(CanUseEquipment())
1263 for(c = 0; c < Equipments; ++c)
1264 if(GetBodyPartOfEquipment(c) && EquipmentIsAllowed(c))
1266 v2 Pos = Where + GetEquipmentPanelPos(c);
1268 if(!AnimationDraw)
1269 DOUBLE_BUFFER->DrawRectangle(Pos + v2(-1, -1), Pos + TILE_V2, DARK_GRAY);
1271 item* Equipment = GetEquipment(c);
1273 if(Equipment && (!AnimationDraw || Equipment->IsAnimated()))
1275 igraph::BlitBackGround(Pos, TILE_V2);
1276 B1.Dest = Pos;
1278 if(Equipment->AllowAlphaEverywhere())
1279 B1.CustomData |= ALLOW_ALPHA;
1281 Equipment->Draw(B1);
1282 B1.CustomData &= ~ALLOW_ALPHA;
1286 if(!AnimationDraw)
1288 blitdata B2 = { DOUBLE_BUFFER,
1289 { 0, 0 },
1290 { Where.X + 8, Where.Y },
1291 { SILHOUETTE_SIZE.X, SILHOUETTE_SIZE.Y },
1292 { 0 },
1294 0 };
1296 for(int c = 0; c < BodyParts; ++c)
1298 bodypart* BodyPart = GetBodyPart(c);
1300 if(BodyPart)
1302 int Type = BodyPart->IsUsable() ? SILHOUETTE_NORMAL : SILHOUETTE_INTER_LACED;
1303 bitmap* Cache = igraph::GetSilhouetteCache(c, BodyPart->GetConditionColorIndex(), Type);
1304 Cache->NormalMaskedBlit(B2);
1305 BodyPart->DrawScars(B2);
1311 int humanoid::GetGlobalResistance(int Type) const
1313 int Resistance = GetResistance(Type);
1315 if(GetCloak())
1316 Resistance += GetCloak()->GetResistance(Type);
1318 if(!(Type & PHYSICAL_DAMAGE))
1320 if(GetAmulet())
1321 Resistance += GetAmulet()->GetResistance(Type);
1323 if(GetRightRing())
1324 Resistance += GetRightRing()->GetResistance(Type);
1326 if(GetLeftRing())
1327 Resistance += GetLeftRing()->GetResistance(Type);
1330 return Resistance;
1333 truth humanoid::TryToRiseFromTheDead()
1335 int c;
1337 for(c = 0; c < BodyParts; ++c)
1338 if(!GetBodyPart(c))
1340 bodypart* BodyPart = SearchForOriginalBodyPart(c);
1342 if(BodyPart)
1344 BodyPart->RemoveFromSlot();
1345 AttachBodyPart(BodyPart);
1346 BodyPart->SetHP(1);
1350 for(c = 0; c < BodyParts; ++c)
1352 bodypart* BodyPart = GetBodyPart(c);
1354 if(BodyPartIsVital(c) && !BodyPart)
1355 if(!HandleNoBodyPart(c))
1356 return false;
1358 if(BodyPart)
1360 BodyPart->ResetSpoiling();
1362 if(BodyPart->CanRegenerate() || BodyPart->GetHP() < 1)
1363 BodyPart->SetHP(1);
1367 ResetStates();
1368 return true;
1371 truth humanoid::HandleNoBodyPart(int I)
1373 switch(I)
1375 case HEAD_INDEX:
1376 if(CanBeSeenByPlayer())
1377 ADD_MESSAGE("The headless body of %s vibrates violently.", CHAR_NAME(DEFINITE));
1379 return false;
1380 case GROIN_INDEX:
1381 if(CanBeSeenByPlayer())
1382 ADD_MESSAGE("The groinless body of %s vibrates violently.", CHAR_NAME(DEFINITE));
1384 return false;
1385 case TORSO_INDEX:
1386 ABORT("The corpse does not have a torso.");
1387 default:
1388 return true;
1392 v2 humanoid::GetBodyPartBitmapPos(int I, truth) const
1394 switch(I)
1396 case TORSO_INDEX: return GetTorsoBitmapPos();
1397 case HEAD_INDEX: return GetHeadBitmapPos();
1398 case RIGHT_ARM_INDEX: return GetRightArmBitmapPos();
1399 case LEFT_ARM_INDEX: return GetLeftArmBitmapPos();
1400 case GROIN_INDEX: return GetGroinBitmapPos();
1401 case RIGHT_LEG_INDEX: return GetRightLegBitmapPos();
1402 case LEFT_LEG_INDEX: return GetLeftLegBitmapPos();
1405 ABORT("Weird bodypart BitmapPos request for a humanoid!");
1406 return v2();
1409 col16 humanoid::GetBodyPartColorB(int I, truth) const
1411 switch(I)
1413 case TORSO_INDEX: return GetTorsoMainColor();
1414 case HEAD_INDEX: return GetCapColor();
1415 case RIGHT_ARM_INDEX:
1416 case LEFT_ARM_INDEX: return GetArmMainColor();
1417 case GROIN_INDEX:
1418 case RIGHT_LEG_INDEX:
1419 case LEFT_LEG_INDEX: return GetLegMainColor();
1422 ABORT("Weird bodypart col B request for a humanoid!");
1423 return 0;
1426 col16 humanoid::GetBodyPartColorC(int I, truth) const
1428 switch(I)
1430 case TORSO_INDEX: return GetBeltColor();
1431 case HEAD_INDEX: return GetHairColor();
1432 case RIGHT_ARM_INDEX:
1433 case LEFT_ARM_INDEX: return GetGauntletColor();
1434 case GROIN_INDEX:
1435 case RIGHT_LEG_INDEX:
1436 case LEFT_LEG_INDEX: return GetBootColor();
1439 ABORT("Weird bodypart col C request for a humanoid!");
1440 return 0;
1443 col16 humanoid::GetBodyPartColorD(int I, truth) const
1445 switch(I)
1447 case TORSO_INDEX: return GetTorsoSpecialColor();
1448 case HEAD_INDEX: return GetEyeColor();
1449 case RIGHT_ARM_INDEX:
1450 case LEFT_ARM_INDEX: return GetArmSpecialColor();
1451 case GROIN_INDEX:
1452 case RIGHT_LEG_INDEX:
1453 case LEFT_LEG_INDEX: return GetLegSpecialColor();
1456 ABORT("Weird bodypart col D request for a humanoid!");
1457 return 0;
1460 int humanoid::GetBodyPartSparkleFlags(int I) const
1462 truth Sparkling = false;
1463 int SparkleFlags = GetNaturalSparkleFlags() & SKIN_COLOR ? SPARKLING_A : 0;
1465 switch(I)
1467 case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & TORSO_MAIN_COLOR; break;
1468 case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & CAP_COLOR; break;
1469 case RIGHT_ARM_INDEX:
1470 case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & ARM_MAIN_COLOR; break;
1471 case GROIN_INDEX:
1472 case RIGHT_LEG_INDEX:
1473 case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & LEG_MAIN_COLOR; break;
1476 SparkleFlags |= Sparkling ? SPARKLING_B : 0;
1477 Sparkling = false;
1479 switch(I)
1481 case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & BELT_COLOR; break;
1482 case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & HAIR_COLOR; break;
1483 case RIGHT_ARM_INDEX:
1484 case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & GAUNTLET_COLOR; break;
1485 case GROIN_INDEX:
1486 case RIGHT_LEG_INDEX:
1487 case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & BOOT_COLOR; break;
1490 SparkleFlags |= Sparkling ? SPARKLING_C : 0;
1491 Sparkling = false;
1493 switch(I)
1495 case TORSO_INDEX: Sparkling = GetNaturalSparkleFlags() & TORSO_SPECIAL_COLOR; break;
1496 case HEAD_INDEX: Sparkling = GetNaturalSparkleFlags() & EYE_COLOR; break;
1497 case RIGHT_ARM_INDEX:
1498 case LEFT_ARM_INDEX: Sparkling = GetNaturalSparkleFlags() & ARM_SPECIAL_COLOR; break;
1499 case GROIN_INDEX:
1500 case RIGHT_LEG_INDEX:
1501 case LEFT_LEG_INDEX: Sparkling = GetNaturalSparkleFlags() & LEG_SPECIAL_COLOR; break;
1504 SparkleFlags |= Sparkling ? SPARKLING_D : 0;
1505 return SparkleFlags;
1508 playerkind::playerkind() : SoulID(0), IsBonePlayer(false), IsClone(false)
1512 shopkeeper::shopkeeper()
1514 if(!game::IsLoading())
1515 Money = GetMoney() + RAND() % 2001;
1518 void humanoid::Bite(character* Enemy, v2 HitPos, int Direction, truth ForceHit)
1520 /* This function ought not to be called without a head */
1522 EditNP(-50);
1523 EditAP(-GetHead()->GetBiteAPCost());
1524 EditExperience(AGILITY, 150, 1 << 9);
1525 EditStamina(-1000, false);
1526 Enemy->TakeHit(this, 0, GetHead(), HitPos, GetHead()->GetBiteDamage(), GetHead()->GetBiteToHitValue(), RAND() % 26 - RAND() % 26, BITE_ATTACK, Direction, !(RAND() % GetCriticalModifier()), ForceHit);
1529 void humanoid::Kick(lsquare* Square, int Direction, truth ForceHit)
1531 leg* KickLeg = RAND_2 ? GetRightLeg() : GetLeftLeg();
1532 EditNP(-50);
1533 EditAP(-KickLeg->GetKickAPCost());
1534 EditStamina(-10000 / GetAttribute(LEG_STRENGTH), false);
1536 if(Square->BeKicked(this, 0, KickLeg, KickLeg->GetKickDamage(), KickLeg->GetKickToHitValue(), RAND() % 26 - RAND() % 26, Direction, !(RAND() % GetCriticalModifier()), ForceHit))
1538 KickLeg->EditExperience(LEG_STRENGTH, 75, 1 << 9);
1539 KickLeg->EditExperience(AGILITY, 75, 1 << 9);
1543 /* Returns the average number of APs required to kill Enemy */
1545 double humanoid::GetTimeToKill(ccharacter* Enemy, truth UseMaxHP) const
1547 double Effectivity = 0;
1548 int AttackStyles = 0;
1550 if(IsUsingArms())
1552 arm* RightArm = GetRightArm();
1554 if(RightArm)
1556 double Damage = RightArm->GetDamage();
1558 if(Damage)
1559 Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Damage) + 1, RightArm->GetToHitValue(), AttackIsBlockable(GetRightWielded() ? WEAPON_ATTACK : UNARMED_ATTACK), UseMaxHP) * RightArm->GetAPCost());
1562 arm* LeftArm = GetLeftArm();
1564 if(LeftArm)
1566 double Damage = LeftArm->GetDamage();
1568 if(Damage)
1569 Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Damage) + 1, LeftArm->GetToHitValue(), AttackIsBlockable(GetLeftWielded() ? WEAPON_ATTACK : UNARMED_ATTACK), UseMaxHP) * LeftArm->GetAPCost());
1572 ++AttackStyles;
1575 if(IsUsingLegs())
1577 leg* RightLeg = GetRightLeg();
1578 leg* LeftLeg = GetLeftLeg();
1579 double TimeToDie = Enemy->GetTimeToDie(this, int(RightLeg->GetKickDamage()) + 1, RightLeg->GetKickToHitValue(), AttackIsBlockable(KICK_ATTACK), UseMaxHP) * RightLeg->GetKickAPCost()
1580 + Enemy->GetTimeToDie(this, int(LeftLeg->GetKickDamage()) + 1, LeftLeg->GetKickToHitValue(), AttackIsBlockable(KICK_ATTACK), UseMaxHP) * LeftLeg->GetKickAPCost();
1581 Effectivity += 2 / TimeToDie;
1582 ++AttackStyles;
1585 if(IsUsingHead())
1587 head* Head = GetHead();
1588 Effectivity += 1 / (Enemy->GetTimeToDie(this, int(Head->GetBiteDamage()) + 1, Head->GetBiteToHitValue(), AttackIsBlockable(BITE_ATTACK), UseMaxHP) * Head->GetBiteAPCost());
1589 ++AttackStyles;
1592 if(StateIsActivated(HASTE))
1593 Effectivity *= 2;
1595 if(StateIsActivated(SLOW))
1596 Effectivity /= 2;
1598 return AttackStyles ? AttackStyles / Effectivity : 10000000;
1601 int humanoid::GetAttribute(int Identifier, truth AllowBonus) const
1603 if(Identifier < BASE_ATTRIBUTES)
1604 return character::GetAttribute(Identifier, AllowBonus);
1605 else
1607 int Attrib = 0;
1609 if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY)
1611 arm* RightArm = GetRightArm();
1613 if(RightArm)
1614 Attrib += RightArm->GetAttribute(Identifier, AllowBonus);
1616 arm* LeftArm = GetLeftArm();
1618 if(LeftArm)
1619 Attrib += LeftArm->GetAttribute(Identifier, AllowBonus);
1621 else if(Identifier == LEG_STRENGTH || Identifier == AGILITY)
1623 leg* RightLeg = GetRightLeg();
1625 if(RightLeg)
1626 Attrib += RightLeg->GetAttribute(Identifier, AllowBonus);
1628 leg* LeftLeg = GetLeftLeg();
1630 if(LeftLeg)
1631 Attrib += LeftLeg->GetAttribute(Identifier, AllowBonus);
1633 else
1635 ABORT("Illegal humanoid attribute %d request!", Identifier);
1636 return 0xEBBA;
1639 return Attrib >> 1;
1643 truth humanoid::EditAttribute(int Identifier, int Value)
1645 if(Identifier < BASE_ATTRIBUTES)
1646 return character::EditAttribute(Identifier, Value);
1647 else if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY)
1649 truth Success = false;
1651 if(GetRightArm() && GetRightArm()->EditAttribute(Identifier, Value))
1652 Success = true;
1654 if(GetLeftArm() && GetLeftArm()->EditAttribute(Identifier, Value))
1655 Success = true;
1657 return Success;
1659 else if(Identifier == LEG_STRENGTH || Identifier == AGILITY)
1661 truth Success = false;
1663 if(GetRightLeg() && GetRightLeg()->EditAttribute(Identifier, Value))
1664 Success = true;
1666 if(GetLeftLeg() && GetLeftLeg()->EditAttribute(Identifier, Value))
1667 Success = true;
1669 return Success;
1671 else
1673 ABORT("Illegal humanoid attribute %d edit request!", Identifier);
1674 return false;
1678 void humanoid::EditExperience(int Identifier, double Value, double Speed)
1680 if(!AllowExperience())
1681 return;
1683 if(Identifier < BASE_ATTRIBUTES)
1684 character::EditExperience(Identifier, Value, Speed);
1685 else if(Identifier == ARM_STRENGTH || Identifier == DEXTERITY)
1687 if(GetRightArm())
1688 GetRightArm()->EditExperience(Identifier, Value, Speed);
1690 if(GetLeftArm())
1691 GetLeftArm()->EditExperience(Identifier, Value, Speed);
1693 else if(Identifier == LEG_STRENGTH || Identifier == AGILITY)
1695 if(GetRightLeg())
1696 GetRightLeg()->EditExperience(Identifier, Value, Speed);
1698 if(GetLeftLeg())
1699 GetLeftLeg()->EditExperience(Identifier, Value, Speed);
1701 else
1702 ABORT("Illegal humanoid attribute %d experience edit request!", Identifier);
1705 int humanoid::DrawStats(truth AnimationDraw) const
1707 DrawSilhouette(AnimationDraw);
1709 if(AnimationDraw)
1710 return 15;
1712 int PanelPosX = RES.X - 96, PanelPosY = 15;
1713 PrintAttribute("AStr", ARM_STRENGTH, PanelPosX, PanelPosY++);
1714 PrintAttribute("LStr", LEG_STRENGTH, PanelPosX, PanelPosY++);
1715 PrintAttribute("Dex", DEXTERITY, PanelPosX, PanelPosY++);
1716 PrintAttribute("Agi", AGILITY, PanelPosX, PanelPosY++);
1717 return PanelPosY;
1720 int humanoid::GetRandomStepperBodyPart() const
1722 int Possible = 0, PossibleArray[3];
1724 if(GetRightLeg())
1725 PossibleArray[Possible++] = RIGHT_LEG_INDEX;
1727 if(GetLeftLeg())
1728 PossibleArray[Possible++] = LEFT_LEG_INDEX;
1730 if(Possible)
1731 return PossibleArray[RAND_N(Possible)];
1733 if(GetRightArm())
1734 PossibleArray[Possible++] = RIGHT_ARM_INDEX;
1736 if(GetLeftArm())
1737 PossibleArray[Possible++] = LEFT_ARM_INDEX;
1739 if(Possible)
1740 return PossibleArray[RAND_N(Possible)];
1742 if(GetHead())
1743 PossibleArray[Possible++] = HEAD_INDEX;
1745 if(GetGroin())
1746 PossibleArray[Possible++] = GROIN_INDEX;
1748 PossibleArray[Possible++] = TORSO_INDEX;
1749 return PossibleArray[RAND_N(Possible)];
1752 int humanoid::CheckForBlock(character* Enemy, item* Weapon, double ToHitValue, int Damage, int Success, int Type)
1754 if(GetAction())
1755 return Damage;
1757 if(GetRightWielded())
1758 Damage = CheckForBlockWithArm(Enemy, Weapon, GetRightArm(), ToHitValue, Damage, Success, Type);
1760 if(Damage && GetLeftWielded() && (!Weapon || Weapon->Exists()))
1761 Damage = CheckForBlockWithArm(Enemy, Weapon, GetLeftArm(), ToHitValue, Damage, Success, Type);
1763 return Damage;
1766 truth humanoid::CanWield() const
1768 return CanUseEquipment(RIGHT_WIELDED_INDEX) || CanUseEquipment(LEFT_WIELDED_INDEX);
1771 /* return true if still in balance */
1773 truth humanoid::CheckBalance(double KickDamage)
1775 return !CanMove()
1776 || IsStuck()
1777 || !KickDamage
1778 || (GetUsableLegs() != 1
1779 && !IsFlying()
1780 && KickDamage * 5 < RAND() % GetSize());
1783 long humanoid::GetMoveAPRequirement(int Difficulty) const
1785 if(IsFlying())
1786 return (!StateIsActivated(PANIC) ? 10000000 : 8000000) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase());
1788 switch(GetUsableLegs())
1790 case 0:
1791 return (!StateIsActivated(PANIC) ? 20000000 : 16000000) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase());
1792 case 1:
1793 return (!StateIsActivated(PANIC) ? 13333333 : 10666667) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase());
1794 case 2:
1795 return (!StateIsActivated(PANIC) ? 10000000 : 8000000) * Difficulty / (APBonus(GetAttribute(AGILITY)) * GetMoveEase());
1798 ABORT("A %d legged humanoid invaded the dungeon!", GetUsableLegs());
1799 return 0;
1802 void hunter::CreateBodyParts(int SpecialFlags)
1804 for(int c = 0; c < BodyParts; ++c)
1805 if(c != LEFT_ARM_INDEX)
1806 CreateBodyPart(c, SpecialFlags);
1807 else
1808 SetBodyPart(LEFT_ARM_INDEX, 0);
1811 truth humanoid::EquipmentEasilyRecognized(int I) const
1813 if(GetRelation(PLAYER) != HOSTILE)
1814 return true;
1816 switch(I)
1818 case AMULET_INDEX:
1819 case RIGHT_RING_INDEX:
1820 case LEFT_RING_INDEX:
1821 case BELT_INDEX:
1822 return false;
1825 return true;
1828 void humanoid::SignalEquipmentAdd(int EquipmentIndex)
1830 character::SignalEquipmentAdd(EquipmentIndex);
1832 if(EquipmentIndex == RIGHT_WIELDED_INDEX)
1833 EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, GetRightWielded());
1834 else if(EquipmentIndex == LEFT_WIELDED_INDEX)
1835 EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, GetLeftWielded());
1837 if(!IsInitializing())
1838 CalculateBattleInfo();
1841 void humanoid::SignalEquipmentRemoval(int EquipmentIndex, citem* Item)
1843 character::SignalEquipmentRemoval(EquipmentIndex, Item);
1845 if(EquipmentIndex == RIGHT_WIELDED_INDEX)
1846 EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0);
1847 else if(EquipmentIndex == LEFT_WIELDED_INDEX)
1848 EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0);
1850 if(!IsInitializing())
1851 CalculateBattleInfo();
1854 void humanoid::SWeaponSkillTick()
1856 for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end();)
1858 if((*i)->Tick() && IsPlayer())
1860 item* Item = SearchForItem(*i);
1862 if(Item)
1863 (*i)->AddLevelDownMessage(Item->CHAR_NAME(UNARTICLED));
1866 if(!(*i)->GetHits() && *i != GetCurrentRightSWeaponSkill() && *i != GetCurrentLeftSWeaponSkill())
1868 std::list<sweaponskill*>::iterator Dirt = i++;
1869 SWeaponSkill.erase(Dirt);
1871 else
1872 ++i;
1876 void angel::GetAICommand()
1878 if((LastHealed || game::GetTick() - LastHealed > 10000) && AttachBodyPartsOfFriendsNear())
1879 return;
1881 humanoid::GetAICommand();
1884 /* Returns true if the angel finds somebody near to heal else false */
1886 truth angel::AttachBodyPartsOfFriendsNear()
1888 character* HurtOne = 0;
1889 bodypart* SeveredOne = 0;
1891 for(int d = 0; d < GetNeighbourSquares(); ++d)
1893 square* Square = GetNeighbourSquare(d);
1895 if(Square)
1897 character* Char = Square->GetCharacter();
1899 if(Char && (!HurtOne || Char->IsPlayer()) && GetRelation(Char) == FRIEND && !Char->HasAllBodyParts())
1901 bodypart* BodyPart = Char->FindRandomOwnBodyPart(false);
1903 if(BodyPart)
1905 HurtOne = Char;
1906 SeveredOne = BodyPart;
1912 if(HurtOne)
1914 if(HurtOne->IsPlayer())
1915 ADD_MESSAGE("%s puts your %s back to its place.", CHAR_DESCRIPTION(DEFINITE), SeveredOne->GetBodyPartName().CStr());
1916 else if(CanBeSeenByPlayer())
1917 ADD_MESSAGE("%s helps %s by putting %s %s in its old place.", CHAR_DESCRIPTION(DEFINITE), HurtOne->CHAR_DESCRIPTION(DEFINITE), HurtOne->GetPossessivePronoun().CStr(), SeveredOne->GetBodyPartName().CStr());
1919 SeveredOne->SetHP(1);
1920 SeveredOne->RemoveFromSlot();
1921 HurtOne->AttachBodyPart(SeveredOne);
1922 LastHealed = game::GetTick();
1923 DexterityAction(10);
1924 return true;
1926 else
1927 return false;
1930 void humanoid::DrawBodyParts(blitdata& BlitData) const
1932 bitmap* TileBuffer = igraph::GetTileBuffer();
1933 bitmap* RealBitmap = BlitData.Bitmap;
1934 blitdata B = { TileBuffer,
1935 { BlitData.Dest.X, BlitData.Dest.Y },
1936 { 0, 0 },
1937 { TILE_SIZE, TILE_SIZE },
1938 { 0 },
1939 TRANSPARENT_COLOR,
1940 BlitData.CustomData };
1942 RealBitmap->NormalBlit(B);
1943 TileBuffer->FillPriority(0);
1944 B.Src.X = B.Src.Y = 0;
1945 B.Luminance = BlitData.Luminance;
1947 for(int c = 0; c < BodyParts; ++c)
1949 bodypart* BodyPart = GetBodyPart(DrawOrder[c]);
1951 if(BodyPart)
1953 B.Dest = GetDrawDisplacement(c);
1954 BodyPart->Draw(B);
1958 B.Dest.X = B.Dest.Y = 0;
1959 arm* LeftArm = GetLeftArm();
1961 if(LeftArm)
1962 LeftArm->DrawWielded(B);
1964 arm* RightArm = GetRightArm();
1966 if(RightArm)
1967 RightArm->DrawWielded(B);
1969 TileBuffer->FastBlit(RealBitmap, BlitData.Dest);
1972 v2 kamikazedwarf::GetDrawDisplacement(int I) const
1974 static v2 DrawDisplacement[] = { v2(0, 0), v2(0, 1), v2(0, -1), v2(0, -1), v2(0, -1), v2(0, 0), v2(0, 0) };
1975 return DrawDisplacement[I];
1978 col16 angel::GetTorsoMainColor() const
1980 return GetMasterGod()->GetColor();
1983 col16 angel::GetArmMainColor() const
1985 return GetMasterGod()->GetColor();
1988 col16 kamikazedwarf::GetTorsoMainColor() const
1990 return GetMasterGod()->GetColor();
1993 col16 kamikazedwarf::GetGauntletColor() const
1995 return GetMasterGod()->GetColor();
1998 col16 kamikazedwarf::GetLegMainColor() const
2000 return GetMasterGod()->GetColor();
2003 col16 housewife::GetHairColor() const
2005 static col16 HouseWifeHairColor[] = { MakeRGB16(48, 40, 8), MakeRGB16(60, 48, 24), MakeRGB16(200, 0, 0) };
2006 return HouseWifeHairColor[RAND() % 3];
2009 int angel::GetAttribute(int Identifier, truth AllowBonus) const // temporary until wings are bodyparts
2011 if(Identifier == LEG_STRENGTH)
2012 return GetDefaultLegStrength();
2013 else if(Identifier == AGILITY)
2014 return GetDefaultAgility();
2015 else
2016 return humanoid::GetAttribute(Identifier, AllowBonus);
2019 int genie::GetAttribute(int Identifier, truth AllowBonus) const // temporary until someone invents a better way of doing this
2021 if(Identifier == LEG_STRENGTH)
2022 return GetDefaultLegStrength();
2023 else if(Identifier == AGILITY)
2024 return GetDefaultAgility();
2025 else
2026 return humanoid::GetAttribute(Identifier, AllowBonus);
2029 truth humanoid::CanUseStethoscope(truth PrintReason) const
2031 if(!GetUsableArms())
2033 if(PrintReason)
2034 ADD_MESSAGE("You need a usable arm to use a stethoscope.");
2036 return false;
2039 if(!GetHead())
2041 if(PrintReason)
2042 ADD_MESSAGE("You need a head to use stethoscope.");
2044 return false;
2047 return true;
2050 truth humanoid::IsUsingArms() const
2052 return GetAttackStyle() & USE_ARMS && CanAttackWithAnArm();
2055 truth humanoid::IsUsingLegs() const
2057 return (GetAttackStyle() & USE_LEGS
2058 || (GetAttackStyle() & USE_ARMS && !CanAttackWithAnArm()))
2059 && HasTwoUsableLegs();
2062 truth humanoid::IsUsingHead() const
2064 return (GetAttackStyle() & USE_HEAD
2065 || ((GetAttackStyle() & USE_LEGS
2066 || (GetAttackStyle() & USE_ARMS && !CanAttackWithAnArm()))
2067 && !HasTwoUsableLegs()))
2068 && GetHead();
2071 void humanoid::CalculateBattleInfo()
2073 CalculateDodgeValue();
2074 doforbodyparts()(this, &bodypart::CalculateAttackInfo);
2077 item* skeleton::SevereBodyPart(int BodyPartIndex, truth ForceDisappearance, stack* EquipmentDropStack)
2079 if(BodyPartIndex == RIGHT_ARM_INDEX)
2080 EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0);
2081 else if(BodyPartIndex == LEFT_ARM_INDEX)
2082 EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0);
2084 item* BodyPart = GetBodyPart(BodyPartIndex);
2085 item* Bone = 0;
2087 if(!ForceDisappearance)
2089 if(BodyPartIndex == HEAD_INDEX)
2090 Bone = skull::Spawn(0, NO_MATERIALS);
2091 else
2092 Bone = bone::Spawn(0, NO_MATERIALS);
2094 Bone->InitMaterials(BodyPart->GetMainMaterial());
2095 BodyPart->DropEquipment(EquipmentDropStack);
2096 BodyPart->RemoveFromSlot();
2097 BodyPart->SetMainMaterial(0, NO_PIC_UPDATE|NO_SIGNALS);
2099 else
2101 BodyPart->DropEquipment(EquipmentDropStack);
2102 BodyPart->RemoveFromSlot();
2105 BodyPart->SendToHell();
2106 CalculateAttributeBonuses();
2107 CalculateBattleInfo();
2108 SignalPossibleTransparencyChange();
2109 RemoveTraps(BodyPartIndex);
2110 return Bone;
2113 void zombie::CreateBodyParts(int SpecialFlags)
2115 bool Anyway = false;
2116 if(GetConfig() == ZOMBIE_OF_KHAZ_ZADM)
2118 Anyway = true;
2119 } // Khaz-Zadm needs his hands...
2121 for(int c = 0; c < BodyParts; ++c)
2122 if(Anyway || BodyPartIsVital(c) || RAND_N(3) || (c == HEAD_INDEX && !RAND_N(3)))
2124 bodypart* BodyPart = CreateBodyPart(c, SpecialFlags|NO_PIC_UPDATE);
2125 BodyPart->GetMainMaterial()->SetSpoilCounter(2000 + RAND_N(1000));
2129 void humanoid::AddSpecialEquipmentInfo(festring& String, int I) const
2131 if((I == RIGHT_WIELDED_INDEX && GetRightArm()->TwoHandWieldIsActive()) || (I == LEFT_WIELDED_INDEX && GetLeftArm()->TwoHandWieldIsActive()))
2132 String << " (in both hands)";
2135 /* Yes, this is evil. */
2137 #define INSTANTIATE(name) if(DataBase->name.IsValid() && (Item = DataBase->name.Instantiate(SpecialFlags))) Set##name(Item);
2139 void humanoid::CreateInitialEquipment(int SpecialFlags)
2141 character::CreateInitialEquipment(SpecialFlags);
2142 item* Item;
2144 INSTANTIATE(Helmet);
2145 INSTANTIATE(Amulet);
2146 INSTANTIATE(Cloak);
2147 INSTANTIATE(BodyArmor);
2148 INSTANTIATE(Belt);
2149 INSTANTIATE(RightWielded);
2150 INSTANTIATE(LeftWielded);
2151 INSTANTIATE(RightRing);
2152 INSTANTIATE(LeftRing);
2153 INSTANTIATE(RightGauntlet);
2154 INSTANTIATE(LeftGauntlet);
2155 INSTANTIATE(RightBoot);
2156 INSTANTIATE(LeftBoot);
2158 if(CurrentRightSWeaponSkill)
2159 CurrentRightSWeaponSkill->AddHit(GetRightSWeaponSkillHits() * 100);
2161 if(CurrentLeftSWeaponSkill)
2162 CurrentLeftSWeaponSkill->AddHit(GetLeftSWeaponSkillHits() * 100);
2165 festring humanoid::GetBodyPartName(int I, truth Articled) const
2167 festring Article;
2169 if(Articled)
2170 Article << 'a';
2172 switch(I)
2174 case HEAD_INDEX: return Article + "head";
2175 case TORSO_INDEX: return Article + "torso";
2176 case RIGHT_ARM_INDEX: return Article + "right arm";
2177 case LEFT_ARM_INDEX: return Article + "left arm";
2178 case GROIN_INDEX: return Article + "groin";
2179 case RIGHT_LEG_INDEX: return Article + "right leg";
2180 case LEFT_LEG_INDEX: return Article + "left leg";
2183 ABORT("Illegal humanoid bodypart name request!");
2184 return "";
2187 void humanoid::CreateBlockPossibilityVector(blockvector& Vector, double ToHitValue) const
2189 double RightBlockChance = 0;
2190 int RightBlockCapability = 0;
2191 double LeftBlockChance = 0;
2192 int LeftBlockCapability = 0;
2193 arm* RightArm = GetRightArm();
2194 arm* LeftArm = GetLeftArm();
2196 if(RightArm)
2198 RightBlockChance = RightArm->GetBlockChance(ToHitValue);
2199 RightBlockCapability = RightArm->GetBlockCapability();
2202 if(LeftArm)
2204 LeftBlockChance = LeftArm->GetBlockChance(ToHitValue);
2205 LeftBlockCapability = LeftArm->GetBlockCapability();
2208 /* Double block */
2210 if(RightBlockCapability + LeftBlockCapability)
2211 Vector.push_back(std::make_pair(RightBlockChance * LeftBlockChance, RightBlockCapability + LeftBlockCapability));
2213 /* Right block */
2215 if(RightBlockCapability)
2216 Vector.push_back(std::make_pair(RightBlockChance * (1 - LeftBlockChance), RightBlockCapability));
2218 /* Left block */
2220 if(LeftBlockCapability)
2221 Vector.push_back(std::make_pair(LeftBlockChance * (1 - RightBlockChance), LeftBlockCapability));
2224 item* humanoid::SevereBodyPart(int BodyPartIndex, truth ForceDisappearance, stack* EquipmentDropStack)
2226 if(BodyPartIndex == RIGHT_ARM_INDEX)
2227 EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, 0);
2228 else if(BodyPartIndex == LEFT_ARM_INDEX)
2229 EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, 0);
2231 return character::SevereBodyPart(BodyPartIndex, ForceDisappearance, EquipmentDropStack);
2234 humanoid::humanoid(const humanoid& Humanoid) : mybase(Humanoid), CurrentRightSWeaponSkill(0), CurrentLeftSWeaponSkill(0)
2236 SWeaponSkill.resize(Humanoid.SWeaponSkill.size());
2237 std::list<sweaponskill*>::iterator i1 = SWeaponSkill.begin();
2238 std::list<sweaponskill*>::const_iterator i2 = Humanoid.SWeaponSkill.begin();
2240 for(; i1 != SWeaponSkill.end(); ++i1, ++i2)
2241 *i1 = new sweaponskill(**i2);
2244 cfestring& humanoid::GetDeathMessage() const
2246 static festring HeadlessDeathMsg = CONST_S("@Dd dies without a sound.");
2247 return GetHead() || character::GetDeathMessage() != "@Dd dies screaming." ? character::GetDeathMessage() : HeadlessDeathMsg;
2250 int humanoid::GetSWeaponSkillLevel(citem* Item) const
2252 std::list<sweaponskill*>::const_iterator i;
2254 for(i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
2255 if((*i)->IsSkillOf(Item))
2256 return (*i)->GetLevel();
2258 for(idholder* I = Item->GetCloneMotherID(); I; I = I->Next)
2259 for(i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
2260 if((*i)->IsSkillOfCloneMother(Item, I->ID))
2261 return (*i)->GetLevel();
2263 return 0;
2266 truth humanoid::UseMaterialAttributes() const
2268 return combinebodypartpredicates()(this, &bodypart::UseMaterialAttributes, 0);
2271 col24 angel::GetBaseEmitation() const
2273 switch(GetMasterGod()->GetBasicAlignment())
2275 case GOOD: return MakeRGB24(150, 150, 150);
2276 case NEUTRAL: return MakeRGB24(120, 120, 150);
2277 case EVIL: return MakeRGB24(150, 110, 110);
2280 return 0;
2283 void bananagrower::BeTalkedTo()
2285 static long Said;
2287 if(GetRelation(PLAYER) == HOSTILE)
2288 ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]);
2289 else if(!game::TweraifIsFree())
2291 if(GetRelation(PLAYER) != HOSTILE
2292 && Profession.Find("president", 0) != festring::NPos && !(RAND() % 7))
2293 ADD_MESSAGE("\"I'm glad Petrus spared my life even though I was the president.\"");
2295 ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 6)]);
2297 else
2298 ProcessAndAddMessage(GetFriendlyReplies()[6 + RandomizeReply(Said, 3)]);
2301 void bananagrower::RandomizeProfession()
2303 switch(RAND_N(12))
2305 case 0:
2306 Profession = CONST_S("the president of Tweraif");
2307 break;
2308 case 1:
2309 Profession = CONST_S("a diplomat");
2310 break;
2311 case 2:
2312 Profession = CONST_S("a teacher");
2313 break;
2314 case 3:
2315 Profession = CONST_S("a philosopher");
2316 break;
2317 case 4:
2318 Profession = CONST_S("a journalist");
2319 break;
2320 case 5:
2321 Profession = CONST_S("an alchemist");
2322 break;
2323 case 6:
2324 Profession = CONST_S("a renown mathematician");
2325 break;
2326 case 7:
2327 Profession = CONST_S("a priest of Silva");
2328 break;
2329 case 8:
2330 case 9:
2331 case 10:
2332 case 11:
2333 Profession = CONST_S("a professor of ");
2334 AddRandomScienceName(Profession);
2335 break;
2339 void bananagrower::PostConstruct()
2341 Stamina = MaxStamina / 5;
2342 RandomizeProfession();
2343 HasDroppedBananas = FeedingSumo = false;
2346 void bananagrower::Save(outputfile& SaveFile) const
2348 humanoid::Save(SaveFile);
2349 SaveFile << Profession << HasDroppedBananas << FeedingSumo;
2352 void bananagrower::Load(inputfile& SaveFile)
2354 humanoid::Load(SaveFile);
2355 SaveFile >> Profession >> HasDroppedBananas >> FeedingSumo;
2358 void smith::BeTalkedTo()
2360 if(GetRelation(PLAYER) == HOSTILE)
2362 ADD_MESSAGE("\"You talkin' to me? You talkin' to me? You talkin' to me? Then who the hell else are you talkin' to? You talkin' to me? Well I'm the only one here. Who do you think you're talking to? Oh yeah? Huh? Ok.\"");
2363 return;
2366 if(!GetMainWielded() || !GetMainWielded()->CanBeUsedBySmith())
2368 ADD_MESSAGE("\"Sorry, I need an intact hammer to practise the art of smithing.\"");
2369 return;
2372 if(PLAYER->PossessesItem(&item::IsFixableBySmith))
2374 item* Item = PLAYER->SelectFromPossessions(CONST_S("\"What do you want me to fix?\""), &item::IsFixableBySmith);
2376 if(!Item)
2377 return;
2379 if(!(Item->GetMainMaterial()->GetCategoryFlags() & IS_METAL))
2381 ADD_MESSAGE("\"I only fix items made of metal.\"");
2382 return;
2385 /** update messages */
2387 long FixPrice = Item->GetFixPrice();
2389 if(PLAYER->GetMoney() < FixPrice)
2391 ADD_MESSAGE("\"Getting that fixed costs you %ld gold pieces. Get the money and we'll talk.\"", FixPrice);
2392 return;
2395 ADD_MESSAGE("\"I can fix your %s, but it'll cost you %ld gold pieces.\"", Item->CHAR_NAME(UNARTICLED), FixPrice);
2397 if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]")))
2399 Item->RemoveRust();
2400 Item->Fix();
2401 PLAYER->EditMoney(-FixPrice);
2402 ADD_MESSAGE("%s fixes %s in no time.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(DEFINITE));
2405 else
2406 ADD_MESSAGE("\"Come back when you have some weapons or armor I can fix.\"");
2409 void humanoid::CalculateDodgeValue()
2411 DodgeValue = 0.05 * GetMoveEase() * GetAttribute(AGILITY) / sqrt(GetSize());
2413 if(IsFlying())
2414 DodgeValue *= 2;
2415 else
2417 if(!HasAUsableLeg())
2418 DodgeValue *= 0.50;
2419 if(!HasTwoUsableLegs())
2420 DodgeValue *= 0.75;
2423 if(DodgeValue < 1)
2424 DodgeValue = 1;
2427 truth humanoid::CheckZap()
2429 if(!GetUsableArms())
2431 ADD_MESSAGE("You need at least one usable arm to zap.");
2432 return false;
2434 else
2435 return character::CheckZap();
2438 void bananagrower::GetAICommand()
2440 if(game::TweraifIsFree())
2442 humanoid::GetAICommand();
2443 return;
2446 if(CheckForEnemies(false, false, true, true))
2447 return;
2449 if(!IsEnabled())
2450 return;
2452 cv2 BananaTarget = FeedingSumo ? SUMO_ROOM_POS + v2(1, 2) : v2(45, 45);
2454 if(GetPos() == BananaTarget)
2456 itemvector ItemVector;
2457 GetStack()->FillItemVector(ItemVector);
2458 int BananasDropped = 0;
2459 uint c;
2461 for(c = 0; c < ItemVector.size(); ++c)
2462 if(ItemVector[c]->IsBanana())
2464 ItemVector[c]->MoveTo(GetStackUnder());
2465 ++BananasDropped;
2468 if(BananasDropped)
2470 if(CanBeSeenByPlayer())
2471 ADD_MESSAGE("%s drops %s.", CHAR_NAME(DEFINITE), BananasDropped == 1 ? "a banana" : "some bananas");
2473 return;
2476 ItemVector.clear();
2477 GetStackUnder()->FillItemVector(ItemVector);
2478 int PeelsPickedUp = 0;
2480 for(c = 0; c < ItemVector.size(); ++c)
2481 if(ItemVector[c]->IsBananaPeel())
2483 ItemVector[c]->MoveTo(GetStack());
2484 ++PeelsPickedUp;
2487 if(PeelsPickedUp)
2489 if(CanBeSeenByPlayer())
2490 ADD_MESSAGE("%s picks up %s.", CHAR_NAME(DEFINITE), PeelsPickedUp == 1 ? "a banana peel" : "some banana peels");
2492 return;
2495 HasDroppedBananas = true;
2498 if(!HasDroppedBananas)
2500 SetGoingTo(BananaTarget);
2502 if(MoveTowardsTarget(true))
2503 return;
2505 else if(GetPos().X == 54)
2507 if(CanBeSeenByPlayer())
2508 ADD_MESSAGE("%s leaves the town to gather more bananas.", CHAR_NAME(DEFINITE));
2510 GetStack()->Clean();
2511 character* Sumo = game::GetSumo();
2512 FeedingSumo = Sumo && Sumo->GetNP() < (SATIATED_LEVEL + BLOATED_LEVEL) >> 1 && !(RAND() % 15);
2513 int Bananas = FeedingSumo ? 3 : 10;
2515 for(int c = 0; c < Bananas; ++c)
2516 GetStack()->AddItem(banana::Spawn());
2518 v2 Where = GetLevel()->GetNearestFreeSquare(this, v2(0, 45));
2520 if(Where == ERROR_V2)
2521 Where = GetLevel()->GetRandomSquare(this, NOT_IN_ROOM); // this is odd but at least it doesn't crash
2523 Move(Where, true);
2524 RandomizeProfession();
2525 RestoreBodyParts();
2526 RestoreHP();
2527 Stamina = MaxStamina / 5;
2528 ResetStates();
2529 TemporaryState = 0;
2531 if(CanBeSeenByPlayer())
2532 ADD_MESSAGE("%s enters the town.", CHAR_NAME(INDEFINITE));
2534 HasDroppedBananas = false;
2536 else
2538 SetGoingTo(v2(54, 45));
2540 if(MoveTowardsTarget(true))
2541 return;
2544 EditAP(-1000);
2547 truth humanoid::CheckTalk()
2549 if(!character::CheckTalk())
2550 return false;
2552 if(!GetHead())
2554 ADD_MESSAGE("You need a head to talk.");
2555 return false;
2558 return true;
2561 truth angel::CanCreateBodyPart(int I) const
2563 return I == TORSO_INDEX || I == HEAD_INDEX || I == RIGHT_ARM_INDEX || I == LEFT_ARM_INDEX;
2566 truth genie::CanCreateBodyPart(int I) const
2568 return I == TORSO_INDEX || I == HEAD_INDEX || I == RIGHT_ARM_INDEX || I == LEFT_ARM_INDEX;
2571 truth bananagrower::HandleCharacterBlockingTheWay(character* Char, v2 Pos, int Dir)
2573 return Char->GetPos() == v2(45, 45) && (Displace(Char, true) || Hit(Char, Pos, Dir));
2576 festring& bananagrower::ProcessMessage(festring& Msg) const
2578 character::ProcessMessage(Msg);
2579 SEARCH_N_REPLACE(Msg, "@pd", GetProfession());
2580 SEARCH_N_REPLACE(Msg, "@Pd", GetProfession().CapitalizeCopy());
2581 return Msg;
2584 void elder::CreateBodyParts(int SpecialFlags)
2586 for(int c = 0; c < BodyParts; ++c)
2587 if(c != LEFT_LEG_INDEX)
2588 CreateBodyPart(c, SpecialFlags);
2589 else
2590 SetBodyPart(LEFT_LEG_INDEX, 0);
2593 /*void encourager::GetAICommand()
2595 if(CheckForEnemies(true, true, true))
2596 return;
2598 if(CheckForUsefulItemsOnGround())
2599 return;
2601 if(CheckForDoors())
2602 return;
2604 if(game::GetTick() - LastHit > 200)
2606 static int NotDiagonal[] = { 1, 3, 4, 6 };
2608 for(int d = 0; d < 4; ++d)
2610 square* Square = GetNeighbourSquare(NotDiagonal[d]);
2612 if(Square)
2614 character* Char = Square->GetCharacter();
2616 if(Char && Char->IsBananaGrower() && Hit(Char, Square->GetPos(), NotDiagonal[d], true))
2618 LastHit = game::GetTick();
2619 TerminateGoingTo();
2620 return;
2626 if(MoveTowardsHomePos())
2627 return;
2629 EditAP(-1000);
2632 /*void encourager::Save(outputfile& SaveFile) const
2634 humanoid::Save(SaveFile);
2635 SaveFile << LastHit;
2638 void encourager::Load(inputfile& SaveFile)
2640 humanoid::Load(SaveFile);
2641 SaveFile >> LastHit;
2644 long skeleton::GetBodyPartVolume(int I) const
2646 switch(I)
2648 case HEAD_INDEX: return 600;
2649 case TORSO_INDEX: return (GetTotalVolume() - 600) * 13 / 30;
2650 case RIGHT_ARM_INDEX:
2651 case LEFT_ARM_INDEX: return (GetTotalVolume() - 600) / 10;
2652 case GROIN_INDEX: return (GetTotalVolume() - 600) / 10;
2653 case RIGHT_LEG_INDEX:
2654 case LEFT_LEG_INDEX: return ((GetTotalVolume() - 600) << 1) / 15;
2657 ABORT("Illegal humanoid bodypart volume request!");
2658 return 0;
2661 truth humanoid::CheckIfEquipmentIsNotUsable(int I) const
2663 return (I == RIGHT_WIELDED_INDEX && GetRightArm()->CheckIfWeaponTooHeavy("this item"))
2664 || (I == LEFT_WIELDED_INDEX && GetLeftArm()->CheckIfWeaponTooHeavy("this item"))
2665 || (I == RIGHT_WIELDED_INDEX && GetLeftWielded() && GetLeftWielded()->IsTwoHanded() && GetLeftArm()->CheckIfWeaponTooHeavy(festring(GetPossessivePronoun() + " other wielded item").CStr()))
2666 || (I == LEFT_WIELDED_INDEX && GetRightWielded() && GetRightWielded()->IsTwoHanded() && GetRightArm()->CheckIfWeaponTooHeavy(festring(GetPossessivePronoun() + " other wielded item").CStr()));
2669 int mistress::TakeHit(character* Enemy, item* Weapon, bodypart* EnemyBodyPart, v2 HitPos, double Damage, double ToHitValue, int Success, int Type, int Direction, truth Critical, truth ForceHit)
2671 int Return = humanoid::TakeHit(Enemy, Weapon, EnemyBodyPart, HitPos, Damage, ToHitValue, Success, Type, Direction, Critical, ForceHit);
2673 if(Return == HAS_HIT && Critical)
2675 if(IsPlayer())
2676 ADD_MESSAGE("Aahhh. The pain feels unbelievably good.");
2677 else if(CanBeSeenByPlayer())
2678 ADD_MESSAGE("%s screams: \"Oh the delightful pain!\"", CHAR_NAME(DEFINITE));
2679 else
2680 ADD_MESSAGE("You hear someone screaming: \"Oh the delightful pain!\"");
2683 return Return;
2686 truth petrusswife::SpecialEnemySightedReaction(character* Char)
2688 item* Weapon = Char->GetMainWielded();
2690 if(Weapon && Weapon->IsWeapon(Char) && !(RAND() % 20))
2691 ADD_MESSAGE("%s screams: \"Oh my Frog, %s's got %s %s!\"", CHAR_DESCRIPTION(DEFINITE), Char->CHAR_PERSONAL_PRONOUN_THIRD_PERSON_VIEW, Weapon->GetArticle(), Weapon->GetNameSingular().CStr());
2693 return false;
2696 truth housewife::SpecialEnemySightedReaction(character* Char)
2698 item* Weapon = Char->GetMainWielded();
2700 if(Weapon && Weapon->IsWeapon(Char) && !(RAND() % 5))
2701 ADD_MESSAGE("%s screams: \"Oh my Frog, %s's got %s %s!\"", CHAR_DESCRIPTION(DEFINITE), Char->CHAR_PERSONAL_PRONOUN_THIRD_PERSON_VIEW, Weapon->GetArticle(), Weapon->GetNameSingular().CStr());
2703 return false;
2706 void guard::Save(outputfile& SaveFile) const
2708 humanoid::Save(SaveFile);
2709 SaveFile << WayPoints << NextWayPoint;
2712 void guard::Load(inputfile& SaveFile)
2714 humanoid::Load(SaveFile);
2715 SaveFile >> WayPoints >> NextWayPoint;
2718 void guard::GetAICommand()
2720 if(GetConfig() == MASTER && HP << 1 < MaxHP && (GetPos() - v2(30, 17)).GetLengthSquare() > 9)
2722 if(CanBeSeenByPlayer())
2723 ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE));
2725 GetLevel()->GetLSquare(30, 16)->KickAnyoneStandingHereAway();
2726 Move(v2(30, 16), true);
2727 EditAP(-1000);
2728 return;
2731 if(WayPoints.size() && !IsGoingSomeWhere())
2733 if(GetPos() == WayPoints[NextWayPoint]) {
2734 if(NextWayPoint < WayPoints.size() - 1) ++NextWayPoint; else NextWayPoint = 0;
2737 GoingTo = WayPoints[NextWayPoint];
2740 SeekLeader(GetLeader());
2742 if(CheckForEnemies(true, true, true))
2743 return;
2745 if(CheckForUsefulItemsOnGround())
2746 return;
2748 if(FollowLeader(GetLeader()))
2749 return;
2751 if(CheckForDoors())
2752 return;
2754 if(MoveTowardsHomePos())
2755 return;
2757 if(CheckSadism())
2758 return;
2760 if(CheckForBeverage())
2761 return;
2763 EditAP(-1000);
2766 truth mistress::ReceiveDamage(character* Damager, int Damage, int Type, int TargetFlags, int Direction, truth Divide, truth PenetrateArmor, truth Critical, truth ShowMsg)
2768 truth Success = humanoid::ReceiveDamage(Damager, Damage, Type, TargetFlags, Direction, Divide, PenetrateArmor, Critical, ShowMsg);
2770 if(Type & SOUND && Success && !(RAND() & 7))
2772 if(IsPlayer())
2773 ADD_MESSAGE("Aahhh. The pain feels unbelievably good.");
2774 else if(CanBeSeenByPlayer())
2775 ADD_MESSAGE("%s screams: \"Oh the delightful pain!\"", CHAR_NAME(DEFINITE));
2776 else
2777 ADD_MESSAGE("You hear someone screaming: \"Oh the delightful pain!\"");
2780 return Success;
2783 void humanoid::AddSpecialStethoscopeInfo(felist& Info) const
2785 Info.AddEntry(CONST_S("Arm strength: ") + GetAttribute(ARM_STRENGTH), LIGHT_GRAY);
2786 Info.AddEntry(CONST_S("Leg strength: ") + GetAttribute(LEG_STRENGTH), LIGHT_GRAY);
2787 Info.AddEntry(CONST_S("Dexterity: ") + GetAttribute(DEXTERITY), LIGHT_GRAY);
2788 Info.AddEntry(CONST_S("Agility: ") + GetAttribute(AGILITY), LIGHT_GRAY);
2791 item* humanoid::GetPairEquipment(int I) const
2793 switch(I)
2795 case RIGHT_WIELDED_INDEX: return GetLeftWielded();
2796 case LEFT_WIELDED_INDEX: return GetRightWielded();
2797 case RIGHT_GAUNTLET_INDEX: return GetLeftGauntlet();
2798 case LEFT_GAUNTLET_INDEX: return GetRightGauntlet();
2799 case RIGHT_BOOT_INDEX: return GetLeftBoot();
2800 case LEFT_BOOT_INDEX: return GetRightBoot();
2803 return 0;
2806 cfestring& humanoid::GetStandVerb() const
2808 if(ForceCustomStandVerb())
2809 return DataBase->StandVerb;
2811 static festring HasntFeet = CONST_S("crawling");
2812 static festring Hovering = CONST_S("hovering");
2813 static festring Swimming = CONST_S("swimming");
2815 if(StateIsActivated(LEVITATION))
2816 return Hovering;
2818 if(IsSwimming())
2819 return Swimming;
2821 return HasAUsableLeg() ? DataBase->StandVerb : HasntFeet;
2824 void darkmage::GetAICommand()
2826 SeekLeader(GetLeader());
2828 if(FollowLeader(GetLeader()))
2829 return;
2831 character* NearestEnemy = 0;
2832 long NearestEnemyDistance = 0x7FFFFFFF;
2833 character* RandomFriend = 0;
2834 charactervector Friend;
2835 v2 Pos = GetPos();
2837 for(int c = 0; c < game::GetTeams(); ++c)
2839 if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
2841 for(std::list<character*>::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i)
2842 if((*i)->IsEnabled())
2844 long ThisDistance = Max<long>(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y));
2846 if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this))
2848 NearestEnemy = *i;
2849 NearestEnemyDistance = ThisDistance;
2853 else if(GetTeam()->GetRelation(game::GetTeam(c)) == FRIEND)
2855 for(std::list<character*>::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i)
2856 if((*i)->IsEnabled() && (*i)->CanBeSeenBy(this))
2857 Friend.push_back(*i);
2861 if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos))
2863 if(NearestEnemy->IsSmall()
2864 && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit()
2865 && !(RAND() % 5)
2866 && Hit(NearestEnemy, NearestEnemy->GetPos(), game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos())))
2867 return;
2868 else if((GetConfig() == ARCH_MAGE && RAND() & 1)
2869 || (GetConfig() == ELDER && !(RAND() & 3)))
2871 if(CanBeSeenByPlayer())
2872 ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE));
2874 TeleportRandomly(true);
2875 EditAP(-GetSpellAPCost());
2876 return;
2880 if(NearestEnemy && ((GetConfig() != APPRENTICE && NearestEnemyDistance < 10) || StateIsActivated(PANIC)) && RAND() & 3)
2882 SetGoingTo((Pos << 1) - NearestEnemy->GetPos());
2884 if(MoveTowardsTarget(true))
2885 return;
2888 if(Friend.size() && !(RAND() & 3))
2890 RandomFriend = Friend[RAND() % Friend.size()];
2891 NearestEnemy = 0;
2894 beamdata Beam
2896 this,
2897 CONST_S("killed by the spells of ") + GetName(INDEFINITE),
2898 YOURSELF,
2902 if(NearestEnemy)
2904 lsquare* Square = NearestEnemy->GetLSquareUnder();
2905 EditAP(-GetSpellAPCost());
2907 if(CanBeSeenByPlayer())
2908 ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE));
2910 switch(GetConfig())
2912 case APPRENTICE:
2913 Square->DrawLightning(v2(8, 8), WHITE, YOURSELF);
2914 Square->Lightning(Beam);
2915 break;
2916 case BATTLE_MAGE:
2917 if(RAND() % 20)
2919 Square->DrawLightning(v2(8, 8), WHITE, YOURSELF);
2920 Square->Lightning(Beam);
2922 else
2924 Square->DrawParticles(RED);
2925 Square->LowerEnchantment(Beam);
2928 break;
2929 case ELDER:
2930 switch(RAND() % 20)
2932 case 0:
2933 case 1:
2934 case 2: Square->DrawParticles(RED); Square->Strike(Beam); break;
2935 case 3: Square->DrawParticles(RED); Square->FireBall(Beam); break;
2936 case 4:
2937 case 5:
2938 case 6: Square->DrawParticles(RED); Square->Slow(Beam); break;
2939 case 7: Square->DrawParticles(RED); Square->Teleport(Beam); break;
2940 case 8:
2941 case 9:
2942 case 10: Square->DrawParticles(RED); Square->LowerEnchantment(Beam); break;
2943 default: Square->DrawLightning(v2(8, 8), WHITE, YOURSELF); Square->Lightning(Beam); break;
2946 break;
2947 case ARCH_MAGE:
2948 switch(RAND() % 20)
2950 case 0:
2951 case 1:
2952 case 2: Square->DrawParticles(RED); Square->FireBall(Beam); break;
2953 case 3:
2955 character* Char = NearestEnemy->DuplicateToNearestSquare(this, CHANGE_TEAM|MIRROR|(1000 << LE_BASE_SHIFT)|(1000 << LE_RAND_SHIFT));
2957 if(Char)
2959 if(Char->CanBeSeenByPlayer())
2960 ADD_MESSAGE("%s materializes!", Char->CHAR_NAME(INDEFINITE));
2962 break;
2965 case 4:
2966 case 5: Square->DrawParticles(RED); Square->Slow(Beam); break;
2967 case 6: Square->DrawParticles(RED); Square->Teleport(Beam); break;
2968 case 7:
2969 case 8:
2970 case 9: Square->DrawParticles(RED); Square->LowerEnchantment(Beam); break;
2971 case 10:
2973 golem* Golem = golem::Spawn(RAND() % 3 ? ARCANITE : OCTIRON);
2974 v2 Where = GetLevel()->GetNearestFreeSquare(Golem, Square->GetPos());
2976 if(Where == ERROR_V2)
2978 if(CanBeSeenByPlayer())
2979 ADD_MESSAGE("Nothing happens.");
2981 delete Golem;
2983 else
2985 Golem->SetGenerationDanger(GetGenerationDanger());
2986 Golem->SetTeam(GetTeam());
2987 Golem->PutTo(Where);
2989 if(Golem->CanBeSeenByPlayer())
2990 ADD_MESSAGE("Suddenly %s materializes!", Golem->CHAR_NAME(INDEFINITE));
2992 Golem->GetLSquareUnder()->DrawParticles(RED);
2995 break;
2997 default: Square->DrawParticles(RED); Square->Strike(Beam); break;
3000 break;
3003 if(CanBeSeenByPlayer())
3004 NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE) + CONST_S(" interrupts you."));
3005 else
3006 NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you."));
3008 return;
3011 if(RandomFriend)
3013 lsquare* Square = RandomFriend->GetLSquareUnder();
3014 EditAP(-GetSpellAPCost());
3015 Square->DrawParticles(RED);
3017 switch(GetConfig())
3019 case APPRENTICE:
3020 case BATTLE_MAGE:
3021 Square->Haste(Beam);
3022 break;
3023 case ARCH_MAGE:
3024 if(!(RAND() & 31))
3026 RandomFriend->DuplicateToNearestSquare(this, CHANGE_TEAM);
3027 return;
3029 case ELDER:
3030 if(RAND() & 1)
3031 Square->Invisibility(Beam);
3032 else
3033 Square->Haste(Beam);
3035 break;
3038 return;
3041 if(CheckForDoors())
3042 return;
3044 if(CheckSadism())
3045 return;
3047 if(MoveRandomly())
3048 return;
3050 EditAP(-1000);
3053 void zombie::GetAICommand()
3055 if(!GetHead())
3057 for(stackiterator i = GetLSquareUnder()->GetStack()->GetBottom(); i.HasItem(); ++i)
3059 head* Head = i->Behead();
3061 if(Head)
3063 if(CanBeSeenByPlayer())
3064 ADD_MESSAGE("%s takes %s and attaches it to its torso.", CHAR_NAME(DEFINITE), Head->CHAR_NAME(INDEFINITE));
3066 Head->RemoveFromSlot();
3067 AttachBodyPart(Head);
3068 Head->SetHP(1);
3069 DexterityAction(10);
3070 return;
3075 humanoid::GetAICommand();
3078 head* humanoid::Behead()
3080 head* Head = GetHead();
3082 if(Head)
3083 SevereBodyPart(HEAD_INDEX);
3085 return Head;
3088 truth communist::BoundToUse(citem* Item, int I) const
3090 return Item && Item->IsGorovitsFamilyRelic() && Item->IsInCorrectSlot(I);
3093 festring werewolfwolf::GetKillName() const
3095 if(GetPolymorphBackup() && GetPolymorphBackup()->GetType() == werewolfhuman::ProtoType.GetIndex())
3096 return GetName(INDEFINITE);
3098 return humanoid::GetKillName();
3101 int humanoid::GetRandomApplyBodyPart() const
3103 if(RightArmIsUsable())
3105 if(LeftArmIsUsable())
3106 return RAND_2 ? RIGHT_ARM_INDEX : LEFT_ARM_INDEX;
3107 else
3108 return RIGHT_ARM_INDEX;
3110 else if(LeftArmIsUsable())
3111 return LEFT_ARM_INDEX;
3113 if(GetHead())
3114 return HEAD_INDEX;
3116 return TORSO_INDEX;
3119 void golem::BeTalkedTo()
3121 static long Said;
3123 if(GetRelation(PLAYER) == HOSTILE)
3124 Engrave(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]);
3125 else
3126 Engrave(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]);
3128 if(CanBeSeenByPlayer())
3129 ADD_MESSAGE("%s engraves something.", CHAR_NAME(DEFINITE));
3132 #ifdef WIZARD
3134 void humanoid::AddAttributeInfo(festring& Entry) const
3136 Entry.Resize(45);
3137 Entry << GetAttribute(ARM_STRENGTH);
3138 Entry.Resize(48);
3139 Entry << GetAttribute(LEG_STRENGTH);
3140 Entry.Resize(51);
3141 Entry << GetAttribute(DEXTERITY);
3142 Entry.Resize(54);
3143 Entry << GetAttribute(AGILITY);
3144 character::AddAttributeInfo(Entry);
3147 void humanoid::AddAttackInfo(felist& List) const
3149 if(GetAttackStyle() & USE_ARMS)
3151 if(GetRightArm())
3152 GetRightArm()->AddAttackInfo(List);
3154 if(GetLeftArm())
3155 GetLeftArm()->AddAttackInfo(List);
3158 festring Entry;
3160 if(IsUsingLegs())
3162 GetRightLeg()->AddAttackInfo(List);
3163 GetLeftLeg()->AddAttackInfo(List);
3166 if(IsUsingHead())
3168 Entry = CONST_S(" bite attack");
3169 Entry.Resize(50);
3170 Entry << GetHead()->GetBiteMinDamage() << '-' << GetHead()->GetBiteMaxDamage();
3171 Entry.Resize(60);
3172 Entry << int(GetHead()->GetBiteToHitValue());
3173 Entry.Resize(70);
3174 Entry << GetHead()->GetBiteAPCost();
3175 List.AddEntry(Entry, LIGHT_GRAY);
3179 void humanoid::AddDefenceInfo(felist& List) const
3181 character::AddDefenceInfo(List);
3183 if(GetRightArm())
3184 GetRightArm()->AddDefenceInfo(List);
3186 if(GetLeftArm())
3187 GetLeftArm()->AddDefenceInfo(List);
3190 void humanoid::DetachBodyPart()
3192 int ToBeDetached;
3194 switch(game::KeyQuestion(CONST_S("What limb? (l)eft arm, (r)ight arm, (L)eft leg, (R)ight leg, (h)ead?"), KEY_ESC, 5, 'l','r','L','R', 'h'))
3196 case 'l':
3197 ToBeDetached = LEFT_ARM_INDEX;
3198 break;
3199 case 'r':
3200 ToBeDetached = RIGHT_ARM_INDEX;
3201 break;
3202 case 'L':
3203 ToBeDetached = LEFT_LEG_INDEX;
3204 break;
3205 case 'R':
3206 ToBeDetached = RIGHT_LEG_INDEX;
3207 break;
3208 case 'h':
3209 ToBeDetached = HEAD_INDEX;
3210 break;
3211 default:
3212 return;
3215 if(GetBodyPart(ToBeDetached))
3217 item* ToDrop = SevereBodyPart(ToBeDetached);
3218 SendNewDrawRequest();
3220 if(ToDrop)
3222 GetStack()->AddItem(ToDrop);
3223 ToDrop->DropEquipment();
3226 ADD_MESSAGE("Bodypart detached!");
3228 else
3229 ADD_MESSAGE("That bodypart has already been detached.");
3231 CheckDeath(CONST_S("removed one of his vital bodyparts"), 0);
3234 #else
3236 void humanoid::AddAttributeInfo(festring&) const { }
3237 void humanoid::AddAttackInfo(felist&) const { }
3238 void humanoid::AddDefenceInfo(felist&) const { }
3239 void humanoid::DetachBodyPart() { }
3241 #endif
3243 truth ennerbeast::MustBeRemovedFromBone() const
3245 return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM || GetDungeon()->GetIndex() != ELPURI_CAVE || GetLevel()->GetIndex() != ENNER_BEAST_LEVEL;
3248 truth communist::MustBeRemovedFromBone() const
3250 return !IsEnabled() || GetTeam()->GetID() != IVAN_TEAM || GetDungeon()->GetIndex() != ELPURI_CAVE|| GetLevel()->GetIndex() != IVAN_LEVEL;
3253 truth humanoid::PreProcessForBone()
3255 for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
3256 (*i)->PreProcessForBone();
3258 return character::PreProcessForBone();
3261 void humanoid::FinalProcessForBone()
3263 character::FinalProcessForBone();
3265 for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end();)
3267 boneidmap::iterator BI = game::GetBoneItemIDMap().find(-(*i)->GetID());
3269 if(BI == game::GetBoneItemIDMap().end())
3271 std::list<sweaponskill*>::iterator Dirt = i++;
3272 SWeaponSkill.erase(Dirt);
3274 else
3276 (*i)->SetID(BI->second);
3277 ++i;
3282 void angel::FinalProcessForBone()
3284 humanoid::FinalProcessForBone();
3285 LastHealed = 0;
3288 /*void encourager::FinalProcessForBone()
3290 humanoid::FinalProcessForBone();
3291 LastHit = 0;
3294 void playerkind::Save(outputfile& SaveFile) const
3296 humanoid::Save(SaveFile);
3297 SaveFile << SoulID << HairColor << EyeColor << Talent << Weakness << IsBonePlayer << IsClone;
3300 void playerkind::Load(inputfile& SaveFile)
3302 humanoid::Load(SaveFile);
3303 SaveFile >> SoulID >> HairColor >> EyeColor >> Talent >> Weakness >> IsBonePlayer >> IsClone;
3306 void playerkind::SetSoulID(ulong What)
3308 SoulID = What;
3310 if(GetPolymorphBackup())
3311 GetPolymorphBackup()->SetSoulID(What);
3314 truth playerkind::SuckSoul(character* Soul)
3316 if(Soul->GetID() == SoulID)
3318 SoulID = 0;
3319 return true;
3322 return false;
3325 truth playerkind::TryToRiseFromTheDead()
3327 if(humanoid::TryToRiseFromTheDead())
3329 if(IsEnabled() && SoulID)
3331 ADD_MESSAGE("The soulless body of %s wobbles for a moment.", CHAR_NAME(DEFINITE));
3332 return false;
3335 return true;
3337 else
3338 return false;
3341 void playerkind::FinalProcessForBone()
3343 humanoid::FinalProcessForBone();
3344 IsBonePlayer = true;
3346 if(SoulID)
3348 boneidmap::iterator BI = game::GetBoneCharacterIDMap().find(SoulID);
3350 if(BI != game::GetBoneCharacterIDMap().end())
3351 SoulID = BI->second;
3352 else
3353 SoulID = 0;
3357 playerkind::playerkind(const playerkind& Char) : mybase(Char), SoulID(Char.SoulID), HairColor(Char.HairColor), EyeColor(Char.EyeColor), Talent(Char.Talent), Weakness(Char.Weakness), IsBonePlayer(Char.IsBonePlayer), IsClone(true)
3361 void playerkind::BeTalkedTo()
3363 if(IsClone && IsBonePlayer)
3365 if(GetRelation(PLAYER) == HOSTILE)
3367 ADD_MESSAGE("Oh no, you too! Why does everyone bully me!");
3368 return;
3371 static long Said;
3373 switch(RandomizeReply(Said, 4))
3375 case 0:
3376 ADD_MESSAGE("\"I'd like to write a memoir, but alas I doubt anyone would believe it.\"");
3377 break;
3378 case 1:
3379 ADD_MESSAGE("\"Then that damned clone appeared, took all my equipment and claimed I was his slave...\"");
3380 break;
3381 case 2:
3382 ADD_MESSAGE("\"The level was a catastrophe for the party, but luckily you saved the day.\"");
3383 break;
3384 case 3:
3385 ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\"");
3386 break;
3389 else if(IsClone)
3391 if(GetRelation(PLAYER) == HOSTILE)
3393 ADD_MESSAGE("%s seems extremely irritated. \"Vanish, you foul mirror image!\"", CHAR_DESCRIPTION(DEFINITE));
3394 return;
3397 static long Said;
3399 switch(RandomizeReply(Said, 4))
3401 case 0:
3402 ADD_MESSAGE("\"Hey, those clothes are mine! Give them back!\"");
3403 break;
3404 case 1:
3405 ADD_MESSAGE("\"What, you summoned me? What a coincidence, I remember summoning you, too.\"");
3406 break;
3407 case 2:
3408 ADD_MESSAGE("\"I'm leading this party, not you, Mr. copy guy!\"");
3409 break;
3410 case 3:
3411 ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\"");
3412 break;
3415 else
3417 if(GetRelation(PLAYER) == HOSTILE)
3419 ADD_MESSAGE("Let's finish what my ghost failed to do!");
3420 return;
3423 static long Said;
3425 switch(RandomizeReply(Said, 4))
3427 case 0:
3428 ADD_MESSAGE("\"What was it like? Death, you mean? Well, just like New Attnam. Very hot and whips everywhere.\"");
3429 break;
3430 case 1:
3431 ADD_MESSAGE("\"Stop it already! I *don't* want to know how my corpse smelled!\"");
3432 break;
3433 case 2:
3434 ADD_MESSAGE("\"I'm sorry about that ghost thing. That YASD was just a bit too much to handle, so I lost myself.\"");
3435 break;
3436 case 3:
3437 ADD_MESSAGE("\"Oh, how I hate bananas. I Hate Them! I HATE THEM SO MUCH!!!\"");
3438 break;
3443 void humanoid::EnsureCurrentSWeaponSkillIsCorrect(sweaponskill*& Skill, citem* Wielded)
3445 if(Wielded)
3447 if(!Skill || !Skill->IsSkillOf(Wielded))
3449 if(Skill)
3450 EnsureCurrentSWeaponSkillIsCorrect(Skill, 0);
3452 std::list<sweaponskill*>::iterator i;
3454 for(i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
3455 if((*i)->IsSkillOf(Wielded))
3457 Skill = *i;
3458 return;
3461 for(idholder* I = Wielded->GetCloneMotherID(); I; I = I->Next)
3462 for(i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
3463 if((*i)->IsSkillOfCloneMother(Wielded, I->ID))
3465 Skill = new sweaponskill(**i);
3466 Skill->SetID(Wielded->GetID());
3467 SWeaponSkill.push_back(Skill);
3468 return;
3471 Skill = new sweaponskill(Wielded);
3472 SWeaponSkill.push_back(Skill);
3475 else if(Skill)
3477 if(!Skill->GetHits() && (CurrentRightSWeaponSkill != Skill || CurrentLeftSWeaponSkill != Skill))
3478 for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
3479 if(*i == Skill)
3481 delete *i;
3482 SWeaponSkill.erase(i);
3483 break;
3486 Skill = 0;
3490 humanoid::~humanoid()
3492 for(std::list<sweaponskill*>::iterator i = SWeaponSkill.begin(); i != SWeaponSkill.end(); ++i)
3493 delete *i;
3496 truth guard::MoveTowardsHomePos()
3498 if(GetConfig() == MASTER && GetPos() != v2(30, 16))
3500 if(CanBeSeenByPlayer())
3501 ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE));
3503 GetLevel()->GetLSquare(30, 16)->KickAnyoneStandingHereAway();
3504 Move(v2(30, 16), true);
3506 if(CanBeSeenByPlayer())
3507 ADD_MESSAGE("%s appears.", CHAR_NAME(DEFINITE));
3509 EditAP(-1000);
3510 return true;
3512 else
3513 return humanoid::MoveTowardsHomePos();
3516 bodypart* ennerbeast::MakeBodyPart(int I) const
3518 if(I == HEAD_INDEX)
3519 return ennerhead::Spawn(0, NO_MATERIALS);
3520 else
3521 return humanoid::MakeBodyPart(I);
3524 int humanoid::GetSumOfAttributes() const
3526 return character::GetSumOfAttributes() + GetAttribute(LEG_STRENGTH) + GetAttribute(DEXTERITY) ;
3529 truth humanoid::CheckConsume(cfestring& Verb) const
3531 if(!HasHead())
3533 if(IsPlayer())
3534 ADD_MESSAGE("You need a head to %s.", Verb.CStr());
3536 return false;
3539 return character::CheckConsume(Verb);
3542 truth humanoid::CanConsume(material* Material) const
3544 return character::CanConsume(Material) && HasHead();
3547 void femaleslave::BeTalkedTo()
3549 static long Said;
3551 if(GetConfig() != NEW_ATTNAM || GetRelation(PLAYER) == HOSTILE)
3552 humanoid::BeTalkedTo();
3553 else if(!game::TweraifIsFree())
3554 ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 4)]);
3555 else
3556 ProcessAndAddMessage(GetFriendlyReplies()[4 + RandomizeReply(Said, 3)]);
3559 void necromancer::GetAICommand()
3561 SeekLeader(GetLeader());
3563 if(FollowLeader(GetLeader()))
3564 return;
3566 character* NearestEnemy = 0;
3567 long NearestEnemyDistance = 0x7FFFFFFF;
3568 v2 Pos = GetPos();
3570 for(int c = 0; c < game::GetTeams(); ++c)
3571 if(GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
3573 for(std::list<character*>::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i)
3574 if((*i)->IsEnabled())
3576 long ThisDistance = Max<long>(abs((*i)->GetPos().X - Pos.X), abs((*i)->GetPos().Y - Pos.Y));
3578 if((ThisDistance < NearestEnemyDistance || (ThisDistance == NearestEnemyDistance && !(RAND() % 3))) && (*i)->CanBeSeenBy(this))
3580 NearestEnemy = *i;
3581 NearestEnemyDistance = ThisDistance;
3586 if(NearestEnemy && NearestEnemy->GetPos().IsAdjacent(Pos))
3588 if(GetConfig() == MASTER_NECROMANCER && !(RAND() & 3))
3590 if(CanBeSeenByPlayer())
3591 ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE));
3593 TeleportRandomly(true);
3594 EditAP(-GetSpellAPCost());
3595 return;
3597 else if(NearestEnemy->IsSmall()
3598 && GetAttribute(WISDOM) < NearestEnemy->GetAttackWisdomLimit()
3599 && !(RAND() & 3)
3600 && Hit(NearestEnemy, NearestEnemy->GetPos(), game::GetDirectionForVector(NearestEnemy->GetPos() - GetPos())))
3601 return;
3604 if(!NearestEnemy)
3606 if(!RAND_N(3) && TryToRaiseZombie())
3607 return;
3609 else
3611 if(!RAND_N(6) && TryToRaiseZombie())
3612 return;
3615 if(NearestEnemy && !(RAND() % (GetConfig() == APPRENTICE_NECROMANCER ? 3 : 2)))
3617 lsquare* Square = NearestEnemy->GetLSquareUnder();
3618 EditAP(-GetSpellAPCost());
3620 if(CanBeSeenByPlayer())
3621 ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE));
3623 truth Interrupt = false;
3625 switch(GetConfig())
3627 case APPRENTICE_NECROMANCER:
3628 RaiseSkeleton();
3629 break;
3630 case MASTER_NECROMANCER:
3631 if(RAND() % 5)
3632 RaiseSkeleton();
3633 else
3635 Square->DrawLightning(v2(8, 8), WHITE, YOURSELF);
3637 beamdata Beam
3639 this,
3640 CONST_S("killed by the spells of ") + GetName(INDEFINITE),
3641 YOURSELF,
3645 Square->Lightning(Beam);
3646 Interrupt = true;
3649 break;
3652 if(Interrupt) {
3653 if(CanBeSeenByPlayer())
3654 NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE) + CONST_S(" interrupts you."));
3655 else
3656 NearestEnemy->DeActivateVoluntaryAction(CONST_S("The spell interrupts you."));
3658 return;
3661 if(NearestEnemy && (NearestEnemyDistance < 10 || StateIsActivated(PANIC)) && RAND() & 3)
3663 SetGoingTo((Pos << 1) - NearestEnemy->GetPos());
3665 if(MoveTowardsTarget(true))
3666 return;
3669 if(CheckForDoors())
3670 return;
3672 if(CheckSadism())
3673 return;
3675 if(MoveRandomly())
3676 return;
3678 EditAP(-1000);
3681 truth necromancer::TryToRaiseZombie()
3683 for(int c = 0; c < game::GetTeams(); ++c)
3684 for(std::list<character*>::const_iterator i = game::GetTeam(c)->GetMember().begin();
3685 i != game::GetTeam(c)->GetMember().end(); ++i)
3686 if(!(*i)->IsEnabled() && (*i)->GetMotherEntity()
3687 && (*i)->GetMotherEntity()->Exists()
3688 && (GetConfig() == MASTER_NECROMANCER
3689 || (*i)->GetMotherEntity()->GetSquareUnderEntity()->CanBeSeenBy(this)))
3691 character* Zombie = (*i)->GetMotherEntity()->TryNecromancy(this);
3693 if(Zombie)
3695 if(Zombie->CanBeSeenByPlayer())
3696 ADD_MESSAGE("%s calls %s back to cursed undead life.", CHAR_DESCRIPTION(DEFINITE), Zombie->CHAR_NAME(INDEFINITE));
3697 else if(CanBeSeenByPlayer())
3698 ADD_MESSAGE("%s casts a spell, but you notice no effect.", CHAR_NAME(DEFINITE));
3700 EditAP(-GetSpellAPCost());
3701 return true;
3705 return false;
3708 void necromancer::RaiseSkeleton()
3710 /* Gum solution */
3712 const database* WarLordDataBase;
3713 databasecreator<character>::FindDataBase(WarLordDataBase, &skeleton::ProtoType, WAR_LORD);
3714 skeleton* Skeleton;
3716 if(GetConfig() == MASTER_NECROMANCER && !(WarLordDataBase->Flags & HAS_BEEN_GENERATED) && !(RAND() % 250))
3718 Skeleton = skeleton::Spawn(WAR_LORD);
3719 Skeleton->SetTeam(GetTeam());
3720 Skeleton->PutNear(GetPos());
3721 Skeleton->SignalGeneration();
3723 if(Skeleton->CanBeSeenByPlayer())
3724 ADD_MESSAGE("The whole area trembles terribly as %s emerges from the ground.", Skeleton->CHAR_NAME(DEFINITE));
3725 else if(CanBeSeenByPlayer())
3726 ADD_MESSAGE("%s casts a powerful spell which makes the whole area tremble.", CHAR_NAME(DEFINITE));
3727 else
3728 ADD_MESSAGE("You feel the presence of an ancient evil being awakened from its long slumber. You shiver.");
3730 else
3732 Skeleton = skeleton::Spawn(GetConfig() == APPRENTICE_NECROMANCER ? 0 : WARRIOR, NO_EQUIPMENT);
3733 Skeleton->SetTeam(GetTeam());
3734 Skeleton->PutNear(GetPos());
3736 if(Skeleton->CanBeSeenByPlayer())
3737 ADD_MESSAGE("The ground shakes and %s emerges from it.", Skeleton->CHAR_NAME(INDEFINITE));
3738 else if(CanBeSeenByPlayer())
3739 ADD_MESSAGE("%s casts a spell, but you notice no effect.", CHAR_NAME(DEFINITE));
3742 Skeleton->SetGenerationDanger(GetGenerationDanger());
3743 EditAP(-GetSpellAPCost());
3746 void humanoid::StayOn(liquid* Liquid)
3748 if(IsFlying())
3749 return;
3751 truth Standing = false;
3753 if(GetRightLeg())
3755 GetRightLeg()->StayOn(Liquid);
3756 Standing = true;
3759 if(IsEnabled() && GetLeftLeg())
3761 GetLeftLeg()->StayOn(Liquid);
3762 Standing = true;
3765 if(!Standing)
3767 bodypart* BodyPart[MAX_BODYPARTS];
3768 int Index = 0;
3770 for(int c = 0; c < BodyParts; ++c)
3771 if(GetBodyPart(c))
3772 BodyPart[Index++] = GetBodyPart(c);
3774 BodyPart[RAND() % Index]->StayOn(Liquid);
3778 bodypart* playerkind::MakeBodyPart(int I) const
3780 switch(I)
3782 case TORSO_INDEX: return playerkindtorso::Spawn(0, NO_MATERIALS);
3783 case HEAD_INDEX: return playerkindhead::Spawn(0, NO_MATERIALS);
3784 case RIGHT_ARM_INDEX: return playerkindrightarm::Spawn(0, NO_MATERIALS);
3785 case LEFT_ARM_INDEX: return playerkindleftarm::Spawn(0, NO_MATERIALS);
3786 case GROIN_INDEX: return playerkindgroin::Spawn(0, NO_MATERIALS);
3787 case RIGHT_LEG_INDEX: return playerkindrightleg::Spawn(0, NO_MATERIALS);
3788 case LEFT_LEG_INDEX: return playerkindleftleg::Spawn(0, NO_MATERIALS);
3791 ABORT("Weird bodypart to make for a playerkind. It must be your fault!");
3792 return 0;
3795 truth golem::AddAdjective(festring& String, truth Articled) const
3797 int TotalRustLevel = sumbodypartproperties()(this, &bodypart::GetMainMaterialRustLevel);
3799 if(!TotalRustLevel)
3800 return humanoid::AddAdjective(String, Articled);
3801 else
3803 if(Articled)
3804 String << "a ";
3806 if(TotalRustLevel <= GetBodyParts())
3807 String << "slightly rusted ";
3808 else if(TotalRustLevel <= GetBodyParts() << 1)
3809 String << "rusted ";
3810 else
3811 String << "very rusted ";
3813 String << GetAdjective() << ' ';
3814 return true;
3818 void oree::Bite(character* Enemy, v2 HitPos, int, truth)
3820 if(IsPlayer())
3821 ADD_MESSAGE("You vomit acidous blood at %s.", Enemy->CHAR_DESCRIPTION(DEFINITE));
3822 else if(Enemy->IsPlayer() || CanBeSeenByPlayer() || Enemy->CanBeSeenByPlayer())
3823 ADD_MESSAGE("%s vomits acidous blood at %s.", CHAR_DESCRIPTION(DEFINITE), Enemy->CHAR_DESCRIPTION(DEFINITE));
3825 Vomit(HitPos, 500 + RAND() % 500, false);
3828 void sumowrestler::GetAICommand()
3830 EditNP(-25);
3832 SeekLeader(GetLeader());
3834 if(CheckForEnemies(true, true, true))
3835 return;
3837 if(CheckForUsefulItemsOnGround())
3838 return;
3840 if(CheckForFood(4))
3841 return;
3843 if(FollowLeader(GetLeader()))
3844 return;
3846 if(CheckForDoors())
3847 return;
3849 if(MoveTowardsHomePos())
3850 return;
3852 EditAP(-1000);
3855 void sumowrestler::BeTalkedTo()
3857 static long Said;
3859 if(GetRelation(PLAYER) == HOSTILE)
3860 ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]);
3861 else if(!game::TweraifIsFree())
3862 ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, 6)]);
3863 else
3864 ProcessAndAddMessage(GetFriendlyReplies()[6 + RandomizeReply(Said, 3)]);
3867 character* tourist::GetLeader() const
3869 character* Guide = game::GetTeam(TOURIST_GUIDE_TEAM)->GetLeader();
3870 return Guide && Guide->GetRelation(this) != HOSTILE ? Guide : GetTeam()->GetLeader();
3873 void elder::GetAICommand()
3875 /* Select a place to guide the tourists to */
3877 if(!(RAND() % 10))
3878 SetGoingTo(GetLevel()->GetRandomSquare());
3880 humanoid::GetAICommand();
3883 void tourist::GetAICommand()
3885 if(game::IsSumoWrestling() && !(RAND() % 10))
3887 if(GetConfig() == HUSBAND)
3889 if(RAND() & 1)
3890 ADD_MESSAGE("%s shouts: \"Show that skinny wimp what you've got, Huang!\"", CHAR_DESCRIPTION(DEFINITE));
3891 else
3892 ADD_MESSAGE("%s screams: \"Go for it, Huang!\"", CHAR_DESCRIPTION(DEFINITE));
3894 else if(GetConfig() == WIFE)
3896 if(RAND() & 1)
3897 ADD_MESSAGE("%s encourages you: \"Knock him out, %s!\"", CHAR_DESCRIPTION(DEFINITE), game::GetPlayerName().CStr());
3898 else
3899 ADD_MESSAGE("%s cheers you: \"A handsome guy like you can't lose to that banana ball!\"", CHAR_DESCRIPTION(DEFINITE));
3901 else if(GetConfig() == CHILD)
3903 if(RAND() & 1)
3904 ADD_MESSAGE("%s yells: \"More blood on the ring!!!\"", CHAR_DESCRIPTION(DEFINITE));
3905 else
3906 ADD_MESSAGE("%s cries: \"Kill him, Pong!!!\"", CHAR_DESCRIPTION(DEFINITE));
3910 humanoid::GetAICommand();
3914 ///////////////////////////////////////////////////////////////////////////////
3915 character* humanoid::CreateZombie() const
3917 if(!TorsoIsAlive())
3918 return 0;
3920 humanoid* Zombie = zombie::Spawn();
3921 int c;
3923 for(c = 0; c < BodyParts; ++c)
3925 bodypart* BodyPart = GetBodyPart(c);
3927 if(!BodyPart)
3929 BodyPart = SearchForOriginalBodyPart(c);
3931 if(BodyPart)
3933 BodyPart->RemoveFromSlot();
3934 BodyPart->SendToHell();
3938 if(BodyPart)
3940 bodypart* ZombieBodyPart = Zombie->GetBodyPart(c);
3942 if(!ZombieBodyPart)
3943 ZombieBodyPart = Zombie->CreateBodyPart(c);
3945 material* M = BodyPart->GetMainMaterial()->Duplicate();
3946 M->SetSpoilCounter(2000 + RAND() % 1000);
3947 M->SetSkinColor(Zombie->GetSkinColor());
3948 ZombieBodyPart->ChangeMainMaterial(M);
3949 ZombieBodyPart->CopyAttributes(BodyPart);
3951 else if(!Zombie->BodyPartIsVital(c))
3953 bodypart* ZombieBodyPart = Zombie->GetBodyPart(c);
3955 if(ZombieBodyPart)
3957 ZombieBodyPart->RemoveFromSlot();
3958 ZombieBodyPart->SendToHell();
3963 for(c = 0; c < Zombie->AllowedWeaponSkillCategories; ++c)
3964 Zombie->CWeaponSkill[c] = CWeaponSkill[c];
3966 Zombie->SWeaponSkill.resize(SWeaponSkill.size());
3967 std::list<sweaponskill*>::iterator i1 = Zombie->SWeaponSkill.begin();
3968 std::list<sweaponskill*>::const_iterator i2 = SWeaponSkill.begin();
3970 for(; i2 != SWeaponSkill.end(); ++i1, ++i2)
3971 *i1 = new sweaponskill(**i2);
3973 memcpy(Zombie->BaseExperience,
3974 BaseExperience,
3975 BASE_ATTRIBUTES * sizeof(*BaseExperience));
3976 Zombie->CalculateAll();
3977 Zombie->RestoreHP();
3978 Zombie->RestoreStamina();
3979 static_cast<zombie*>(Zombie)->SetDescription(GetZombieDescription());
3980 Zombie->GenerationDanger = GenerationDanger;
3981 return Zombie;
3984 void zombie::AddPostFix(festring& String, int Case) const
3986 if(!Description.IsEmpty())
3987 String << Description;
3988 else
3989 humanoid::AddPostFix(String, Case);
3992 void zombie::Save(outputfile& SaveFile) const
3994 humanoid::Save(SaveFile);
3995 SaveFile << Description;
3998 void zombie::Load(inputfile& SaveFile)
4000 humanoid::Load(SaveFile);
4001 SaveFile >> Description;
4004 int darkknight::ModifyBodyPartHitPreference(int I, int Modifier) const
4006 return IsLimbIndex(I) ? Modifier << 1 : Modifier;
4009 int darkknight::ModifyBodyPartToHitChance(int I, int Chance) const
4011 return IsLimbIndex(I) ? Chance << 1 : Chance;
4014 void darkknight::SpecialBodyPartSeverReaction()
4016 if(!IsPlayer())
4018 if(IsUsingHead())
4019 ADD_MESSAGE("%s screams: \"I'll do you for that! I'll bite your legs off!\"", CHAR_DESCRIPTION(DEFINITE));
4020 else if(!(RAND() % 5))
4021 switch(RAND() % 3)
4023 case 0:
4024 ADD_MESSAGE("%s states calmly: \"'Tis but a scratch.\"", CHAR_DESCRIPTION(DEFINITE)); break;
4025 case 1:
4026 ADD_MESSAGE("%s states calmly: \"Just a flesh wound.\"", CHAR_DESCRIPTION(DEFINITE)); break;
4027 case 2:
4028 ADD_MESSAGE("%s shouts: \"I'm invincible!\"", CHAR_DESCRIPTION(DEFINITE)); break;
4033 void humanoid::LeprosyHandler()
4035 if(IsImmuneToLeprosy())
4037 return;
4040 if(!RAND_N(1000 * GetAttribute(ENDURANCE)))
4041 DropRandomNonVitalBodypart();
4043 if(!game::IsInWilderness())
4045 for(int d = 0; d < GetNeighbourSquares(); ++d)
4047 lsquare* Square = GetNeighbourLSquare(d);
4049 if(Square && Square->GetCharacter())
4050 Square->GetCharacter()->TryToInfectWithLeprosy(this);
4054 character::LeprosyHandler();
4057 void humanoid::DropRandomNonVitalBodypart()
4059 int BodyPartIndexToDrop = GetRandomNonVitalBodyPart();
4061 if(BodyPartIndexToDrop != NONE_INDEX)
4062 DropBodyPart(BodyPartIndexToDrop);
4065 void humanoid::DropBodyPart(int Index)
4067 if(!GetBodyPart(Index)->IsAlive())
4068 return;
4070 festring NameOfDropped = GetBodyPart(Index)->GetBodyPartName();
4071 item* Dropped = SevereBodyPart(Index);
4073 if(Dropped)
4075 GetStack()->AddItem(Dropped);
4076 Dropped->DropEquipment();
4078 if(IsPlayer())
4080 ADD_MESSAGE("You feel very ill. Your %s snaps off.", NameOfDropped.CStr());
4081 game::AskForEscPress(CONST_S("Bodypart severed!"));
4082 DeActivateVoluntaryAction();
4084 else if(CanBeSeenByPlayer())
4085 ADD_MESSAGE("Suddenly %s's %s snaps off.", CHAR_NAME(DEFINITE), NameOfDropped.CStr());
4087 else
4089 if(IsPlayer())
4091 ADD_MESSAGE("You feel very ill. Your %s disappears.", NameOfDropped.CStr());
4092 game::AskForEscPress(CONST_S("Bodypart destroyed!"));
4093 DeActivateVoluntaryAction();
4095 else if(CanBeSeenByPlayer())
4096 ADD_MESSAGE("Suddenly %s's %s disappears.", CHAR_NAME(DEFINITE), NameOfDropped.CStr());
4100 void humanoid::DuplicateEquipment(character* Receiver, ulong Flags)
4102 character::DuplicateEquipment(Receiver, Flags);
4103 EnsureCurrentSWeaponSkillIsCorrect(CurrentRightSWeaponSkill, GetRightWielded());
4104 EnsureCurrentSWeaponSkillIsCorrect(CurrentLeftSWeaponSkill, GetLeftWielded());
4107 truth character::CanHear() const
4109 return DataBase->CanHear && HasHead();
4112 col16 veterankamikazedwarf::GetTorsoMainColor() const
4114 return GetMasterGod()->GetEliteColor();
4117 col16 veterankamikazedwarf::GetGauntletColor() const
4119 return GetMasterGod()->GetEliteColor();
4122 col16 veterankamikazedwarf::GetLegMainColor() const
4124 return GetMasterGod()->GetEliteColor();
4127 col16 archangel::GetTorsoMainColor() const
4129 return GetMasterGod()->GetEliteColor();
4132 col16 archangel::GetArmMainColor() const
4134 return GetMasterGod()->GetEliteColor();
4137 void archangel::CreateInitialEquipment(int SpecialFlags)
4139 humanoid::CreateInitialEquipment(SpecialFlags);
4140 GetStack()->AddItem(holybook::Spawn(GetConfig(), SpecialFlags));
4141 armor* Equipment;
4142 meleeweapon* Weapon;
4144 switch(GetMasterGod()->GetBasicAlignment())
4146 case GOOD:
4147 Weapon = flamingsword::Spawn(0, SpecialFlags|NO_MATERIALS);
4148 Weapon->InitMaterials(MAKE_MATERIAL(DIAMOND), MAKE_MATERIAL(ADAMANT), !(SpecialFlags & NO_PIC_UPDATE));
4149 Weapon->SetEnchantment(4);
4150 SetRightWielded(Weapon);
4151 Equipment = shield::Spawn(0, SpecialFlags|NO_MATERIALS);
4152 Equipment->InitMaterials(MAKE_MATERIAL(DIAMOND), !(SpecialFlags & NO_PIC_UPDATE));
4153 Equipment->SetEnchantment(4);
4154 SetLeftWielded(Equipment);
4155 GetCWeaponSkill(LARGE_SWORDS)->AddHit(200000);
4156 GetCWeaponSkill(SHIELDS)->AddHit(500000);
4157 GetCurrentRightSWeaponSkill()->AddHit(200000);
4158 GetCurrentLeftSWeaponSkill()->AddHit(200000);
4159 GetRightArm()->SetDexterity(70);
4160 GetLeftArm()->SetDexterity(70);
4161 break;
4162 case NEUTRAL:
4163 Weapon = meleeweapon::Spawn(WAR_HAMMER, SpecialFlags|NO_MATERIALS);
4164 Weapon->InitMaterials(MAKE_MATERIAL(SAPPHIRE), MAKE_MATERIAL(OCTIRON), !(SpecialFlags & NO_PIC_UPDATE));
4165 Weapon->SetEnchantment(4);
4166 SetRightWielded(Weapon);
4167 GetCWeaponSkill(BLUNT_WEAPONS)->AddHit(500000);
4168 GetCurrentRightSWeaponSkill()->AddHit(200000);
4169 SetEndurance(70);
4170 break;
4171 case EVIL:
4172 Weapon = meleeweapon::Spawn(HALBERD, SpecialFlags|NO_MATERIALS);
4173 Weapon->InitMaterials(MAKE_MATERIAL(RUBY), MAKE_MATERIAL(OCTIRON), !(SpecialFlags & NO_PIC_UPDATE));
4174 Weapon->SetEnchantment(4);
4175 SetLeftWielded(Weapon);
4176 GetCWeaponSkill(POLE_ARMS)->AddHit(500000);
4177 GetCurrentLeftSWeaponSkill()->AddHit(500000);
4178 GetRightArm()->SetStrength(70);
4179 GetLeftArm()->SetStrength(70);
4183 void zombie::PostConstruct()
4185 if(!RAND_N(3))
4186 GainIntrinsic(LEPROSY);
4189 void orc::PostConstruct()
4191 if(!RAND_N(25))
4192 GainIntrinsic(LEPROSY);
4195 truth mistress::AllowEquipment(citem* Item, int EquipmentIndex) const
4197 return ((EquipmentIndex != RIGHT_WIELDED_INDEX
4198 && EquipmentIndex != LEFT_WIELDED_INDEX)
4199 || Item->IsWhip());
4202 int humanoid::GetAttributeAverage() const
4204 return GetSumOfAttributes() / 9;
4207 void golem::CreateCorpse(lsquare* Square)
4209 material* Material = GetTorso()->GetMainMaterial();
4211 if(Material->IsSolid())
4212 Square->AddItem(Material->CreateNaturalForm(ItemVolume));
4214 SendToHell();
4217 golem::golem()
4219 if(!game::IsLoading())
4220 ItemVolume = 50 + RAND_N(100);
4223 void golem::Save(outputfile& SaveFile) const
4225 humanoid::Save(SaveFile);
4226 SaveFile << ItemVolume;
4229 void golem::Load(inputfile& SaveFile)
4231 humanoid::Load(SaveFile);
4232 SaveFile >> ItemVolume;
4235 truth humanoid::CanVomit() const
4237 return HasHead() && character::CanVomit();
4240 truth humanoid::CheckApply() const
4242 if(!character::CheckApply())
4243 return false;
4245 if(!HasAUsableArm())
4247 ADD_MESSAGE("You need a usable arm to apply.");
4248 return false;
4251 return true;
4254 int darkmage::GetSpellAPCost() const
4256 switch(GetConfig())
4258 case APPRENTICE: return 4000;
4259 case BATTLE_MAGE: return 2000;
4260 case ELDER: return 1000;
4261 case ARCH_MAGE: return 500;
4264 return 4000;
4267 int necromancer::GetSpellAPCost() const
4269 switch(GetConfig())
4271 case APPRENTICE_NECROMANCER: return 2000;
4272 case MASTER_NECROMANCER: return 1000;
4275 return 4000;
4278 /* Horrible repeating. Sorry */
4280 void tailor::BeTalkedTo()
4282 if(GetRelation(PLAYER) == HOSTILE)
4284 ADD_MESSAGE("\"You talkin' to me? You talkin' to me? You talkin' to me? Then who the hell else are you talkin' to? You talkin' to me? Well I'm the only one here. Who do you think you're talking to? Oh yeah? Huh? Ok.\"");
4285 return;
4288 if(PLAYER->PossessesItem(&item::IsFixableByTailor))
4290 item* Item = PLAYER->SelectFromPossessions(CONST_S("\"What do you want me to fix?\""), &item::IsFixableByTailor);
4292 if(!Item)
4293 return;
4295 if(!(Item->GetMainMaterial()->GetCategoryFlags() & CAN_BE_TAILORED))
4297 ADD_MESSAGE("\"I can't work on %s.\"", Item->GetMainMaterial()->GetName(false, false).CStr());
4298 return;
4301 /** update messages */
4303 long FixPrice = Item->GetFixPrice();
4305 if(PLAYER->GetMoney() < FixPrice)
4307 ADD_MESSAGE("\"Getting that fixed costs you %ld gold pieces. Get the money and we'll talk.\"", FixPrice);
4308 return;
4311 ADD_MESSAGE("\"I can fix your %s, but it'll cost you %ld gold pieces.\"", Item->CHAR_NAME(UNARTICLED), FixPrice);
4313 if(game::TruthQuestion(CONST_S("Do you accept this deal? [y/N]")))
4315 Item->Fix();
4316 PLAYER->EditMoney(-FixPrice);
4317 ADD_MESSAGE("%s fixes %s in no time.", CHAR_NAME(DEFINITE), Item->CHAR_NAME(DEFINITE));
4320 else
4321 ADD_MESSAGE("\"Come back when you have some weapons or armor I can fix.\"");
4324 void veterankamikazedwarf::PostConstruct()
4326 kamikazedwarf::PostConstruct();
4327 ivantime Time;
4328 game::GetTime(Time);
4329 int Modifier = Time.Day - KAMIKAZE_INVISIBILITY_DAY_MIN;
4331 if(Time.Day >= KAMIKAZE_INVISIBILITY_DAY_MAX
4332 || (Modifier > 0
4333 && RAND_N(KAMIKAZE_INVISIBILITY_DAY_MAX - KAMIKAZE_INVISIBILITY_DAY_MIN) < Modifier))
4334 GainIntrinsic(INVISIBLE);
4337 truth humanoid::IsTransparent() const
4339 return character::IsTransparent() || !(GetRightLeg() || GetLeftLeg());
4342 void humanoid::ModifySituationDanger(double& Danger) const
4344 character::ModifySituationDanger(Danger);
4346 switch(GetUsableArms())
4348 case 0: Danger *= 10;
4349 case 1: Danger *= 2;
4352 switch(GetUsableLegs())
4354 case 0: Danger *= 10;
4355 case 1: Danger *= 2;
4359 void oree::GetAICommand()
4361 if(!RAND_N(50))
4362 CallForMonsters();
4364 humanoid::GetAICommand();
4367 void oree::CallForMonsters()
4369 if(GetDungeon()->GetIndex() != ELPURI_CAVE || GetLevel()->GetIndex() != OREE_LAIR)
4370 return;
4372 character* ToBeCalled = 0;
4374 switch(RAND_N(6))
4376 case 0:
4377 ToBeCalled = darkknight::Spawn(ELITE);
4378 break;
4379 case 1:
4380 ToBeCalled = frog::Spawn(RAND_2 ? GREATER_DARK : GIANT_DARK);
4381 break;
4382 case 2:
4383 ToBeCalled = frog::Spawn(DARK);
4384 break;
4385 case 3:
4386 ToBeCalled = darkmage::Spawn(RAND_2 ? APPRENTICE : BATTLE_MAGE);
4387 break;
4388 case 4:
4389 ToBeCalled = darkmage::Spawn(RAND_2 ? APPRENTICE : ELDER);
4390 break;
4391 case 5:
4392 ToBeCalled = necromancer::Spawn(RAND_2 ? APPRENTICE_NECROMANCER : MASTER_NECROMANCER);
4393 break;
4396 v2 TryToCreate;
4398 for(int c = 0; c < 100; ++c)
4400 TryToCreate = game::GetMonsterPortal()->GetPos() + game::GetMoveVector(RAND() % DIRECTION_COMMAND_KEYS);
4402 if(GetArea()->IsValidPos(TryToCreate) && ToBeCalled->CanMoveOn(GetNearLSquare(TryToCreate)) && ToBeCalled->IsFreeForMe(GetNearLSquare(TryToCreate)))
4404 ToBeCalled->SetTeam(game::GetTeam(MONSTER_TEAM));
4405 ToBeCalled->PutTo(TryToCreate);
4406 return;
4410 delete ToBeCalled;
4413 int humanoid::RandomizeTryToUnStickBodyPart(ulong PossibleBodyParts) const
4415 int Possible = 0, PossibleArray[3];
4417 if(RightArmIsUsable() && 1 << RIGHT_ARM_INDEX & PossibleBodyParts)
4418 PossibleArray[Possible++] = RIGHT_ARM_INDEX;
4420 if(LeftArmIsUsable() && 1 << LEFT_ARM_INDEX & PossibleBodyParts)
4421 PossibleArray[Possible++] = LEFT_ARM_INDEX;
4423 if(Possible)
4424 return PossibleArray[RAND_N(Possible)];
4426 if(RightLegIsUsable() && 1 << RIGHT_LEG_INDEX & PossibleBodyParts)
4427 PossibleArray[Possible++] = RIGHT_LEG_INDEX;
4429 if(LeftLegIsUsable() && 1 << LEFT_LEG_INDEX & PossibleBodyParts)
4430 PossibleArray[Possible++] = LEFT_LEG_INDEX;
4432 if(Possible)
4433 return PossibleArray[RAND_N(Possible)];
4435 if(GetHead() && 1 << HEAD_INDEX & PossibleBodyParts)
4436 return HEAD_INDEX;
4438 if(GetGroin() && 1 << GROIN_INDEX & PossibleBodyParts)
4439 PossibleArray[Possible++] = GROIN_INDEX;
4441 if(1 << TORSO_INDEX & PossibleBodyParts)
4442 PossibleArray[Possible++] = TORSO_INDEX;
4444 return Possible ? PossibleArray[RAND_N(Possible)] : NONE_INDEX;
4447 truth humanoid::HasAUsableArm() const
4449 arm* R = GetRightArm(), * L = GetLeftArm();
4450 return (R && R->IsUsable()) || (L && L->IsUsable());
4453 truth humanoid::HasAUsableLeg() const
4455 leg* R = GetRightLeg(), * L = GetLeftLeg();
4456 return (R && R->IsUsable()) || (L && L->IsUsable());
4459 truth humanoid::HasTwoUsableLegs() const
4461 leg* R = GetRightLeg(), * L = GetLeftLeg();
4462 return R && R->IsUsable() && L && L->IsUsable();
4465 truth humanoid::CanAttackWithAnArm() const
4467 arm* R = GetRightArm();
4469 if(R && R->GetDamage())
4470 return true;
4472 arm* L = GetLeftArm();
4473 return L && L->GetDamage();
4476 truth humanoid::RightArmIsUsable() const
4478 arm* A = GetRightArm();
4479 return A && A->IsUsable();
4482 truth humanoid::LeftArmIsUsable() const
4484 arm* A = GetLeftArm();
4485 return A && A->IsUsable();
4488 truth humanoid::RightLegIsUsable() const
4490 leg* L = GetRightLeg();
4491 return L && L->IsUsable();
4494 truth humanoid::LeftLegIsUsable() const
4496 leg* L = GetLeftLeg();
4497 return L && L->IsUsable();
4500 truth humanoid::AllowUnconsciousness() const
4502 return (DataBase->AllowUnconsciousness && TorsoIsAlive()
4503 && BodyPartIsVital(HEAD_INDEX));
4506 truth humanoid::CanChokeOnWeb(web* Web) const
4508 return CanChoke() && Web->IsStuckToBodyPart(HEAD_INDEX);
4511 truth humanoid::BrainsHurt() const
4513 head* Head = GetHead();
4514 return !Head || Head->IsBadlyHurt();
4517 void playerkind::PostConstruct()
4519 int R = 0, G = 0, B = 0;
4521 switch(RAND_N(4))
4523 case 0: R = 195; G = 165; B = 40; break;
4524 case 1: R = 130; G = 30; B = 0; break;
4525 case 2: R = 30; G = 30; B = 15; break;
4526 case 3: R = 50; G = 30; B = 5; break;
4529 HairColor = MakeRGB16(R + RAND_N(41), G + RAND_N(41), B + RAND_N(41));
4531 switch(RAND_N(4))
4533 case 0: R = 25; G = 0; B = 70; break;
4534 case 1: R = 5; G = 0; B = 50; break;
4535 case 2: R = 10; G = 10; B = 10; break;
4536 case 3: R = 60; G = 20; B = 0; break;
4539 EyeColor = MakeRGB16(R + RAND_N(41), G + RAND_N(41), B + RAND_N(41));
4540 Talent = RAND_N(TALENTS);
4541 Weakness = RAND_N(TALENTS);
4544 v2 playerkind::GetHeadBitmapPos() const
4546 int Sum = GetAttribute(INTELLIGENCE, false) + GetAttribute(WISDOM, false);
4548 if(Sum >= 60)
4549 return v2(96, 480);
4550 else if(Sum >= 40)
4551 return v2(96, 464);
4552 else
4553 return v2(96, 416);
4556 v2 playerkind::GetRightArmBitmapPos() const
4558 if(GetRightArm()->GetAttribute(ARM_STRENGTH, false) >= 20)
4559 return v2(64, 448);
4560 else
4561 return v2(64, 416);
4564 v2 playerkind::GetLeftArmBitmapPos() const
4566 if(GetLeftArm()->GetAttribute(ARM_STRENGTH, false) >= 20)
4567 return v2(64, 448);
4568 else
4569 return v2(64, 416);
4572 int playerkind::GetNaturalSparkleFlags() const
4574 return GetAttribute(CHARISMA) >= 30 ? SKIN_COLOR : 0;
4577 void slave::PostConstruct()
4579 Talent = TALENT_STRONG;
4580 Weakness = TALENT_CLEVER;
4583 cint TalentOfAttribute[ATTRIBUTES] = { TALENT_HEALTHY, TALENT_FAST_N_ACCURATE, TALENT_CLEVER, TALENT_CLEVER, TALENT_CLEVER, TALENT_CLEVER, TALENT_CLEVER, TALENT_STRONG, TALENT_STRONG, TALENT_FAST_N_ACCURATE, TALENT_FAST_N_ACCURATE };
4584 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 };
4586 double playerkind::GetNaturalExperience(int Identifier) const
4588 double NE = DataBase->NaturalExperience[Identifier];
4590 if(Talent == TalentOfAttribute[Identifier])
4591 NE *= TalentBonusOfAttribute[Identifier];
4593 if(Weakness == TalentOfAttribute[Identifier])
4594 NE /= TalentBonusOfAttribute[Identifier];
4596 return NE;
4599 cchar* humanoid::GetRunDescriptionLine(int I) const
4601 if(!GetRunDescriptionLineOne().IsEmpty())
4602 return !I ? GetRunDescriptionLineOne().CStr() : GetRunDescriptionLineTwo().CStr();
4604 if(IsFlying())
4605 return !I ? "Flying" : "very fast";
4607 if(IsSwimming())
4609 if(!GetRightArm() && !GetLeftArm() && !GetRightLeg() && !GetLeftLeg())
4610 return !I ? "Floating" : "ahead fast";
4611 else
4612 return !I ? "Swimming" : "very fast";
4615 if(!GetRightLeg() && !GetLeftLeg())
4616 return !I ? "Rolling" : "very fast";
4618 if(!GetRightLeg() || !GetLeftLeg())
4619 return !I ? "Hopping" : "very fast";
4621 return !I ? "Running" : "";
4624 cchar* humanoid::GetNormalDeathMessage() const
4626 if(BodyPartIsVital(HEAD_INDEX) && (!GetHead() || GetHead()->GetHP() <= 0))
4627 return "beheaded @k";
4628 else if(BodyPartIsVital(GROIN_INDEX) && (!GetGroin() || GetGroin()->GetHP() <= 0))
4629 return "killed @bkp dirty attack below the belt";
4630 else
4631 return "killed @k";
4634 void kamikazedwarf::SingRandomSong()
4636 festring Song;
4637 festring God = GetMasterGod()->GetName();
4638 festring Bodypart;
4640 switch(RAND_N(9))
4642 case 0:
4644 switch(RAND_N(3))
4646 case 0:
4647 Bodypart = "palm";
4648 break;
4650 case 1:
4651 Bodypart = "forehead";
4652 break;
4654 default:
4655 Bodypart = "tongue";
4656 break;
4658 Song = festring("On the ") + Bodypart + festring(" of ") + God + " everybody fears everything";
4659 break;
4660 case 1:
4662 festring Title = GetMasterGod()->GetSex() == MALE ? "King" : "Queen";
4663 Song = festring("Joy to the world, ") + God
4664 + " is come! Let all above Valpurus receive her " + Title;
4665 break;
4667 case 2:
4668 Song = festring("Hark the herald angels sing. Glory to ") + God + "!";
4669 break;
4670 case 3:
4671 Song = festring("O ") + God
4672 + ", You are so big, So absolutely huge, Gosh, "
4673 "we're all really impressed down here, I can tell You.";
4674 break;
4675 case 4:
4676 Song = festring("Forgive us, O ") + God
4677 + " for this, our dreadful toadying and barefaced flattery";
4678 break;
4679 case 5:
4680 Song = festring("But you, ") + God
4681 + ", are so strong and, well, just so super fantastic. Amen.";
4682 break;
4683 case 6:
4684 Song = festring("O ") + God + ", please don't burn us";
4685 break;
4686 case 7:
4687 Song = festring("O ") + God + ", please don't grill or toast your flock";
4688 break;
4689 case 8:
4690 Song = festring("O ") + God + ", please don't simmer us in stock";
4691 break;
4694 EditAP(-1000);
4696 if(CanBeSeenByPlayer())
4697 ADD_MESSAGE("%s sings: \"%s\"",
4698 CHAR_DESCRIPTION(DEFINITE), Song.CStr());
4699 else
4700 ADD_MESSAGE("You hear someone sing: \"%s\"", Song.CStr());
4703 void humanoid::ApplySpecialAttributeBonuses()
4705 if(GetHead())
4707 AttributeBonus[CHARISMA] -= GetHead()->
4708 CalculateScarAttributePenalty(GetAttribute(CHARISMA, false));
4710 else
4711 AttributeBonus[CHARISMA] -= GetAttribute(CHARISMA, false) - 1;
4714 void siren::GetAICommand()
4716 if(TryToSing())
4717 return;
4719 humanoid::GetAICommand();
4722 truth siren::TryToSing()
4724 truth Success=false;
4725 for(int d = 0; d < GetNeighbourSquares(); ++d)
4727 lsquare* Square = GetNeighbourLSquare(d);
4729 if(Square && Square->GetCharacter())
4731 Success = true;
4732 Square->GetCharacter()->ReceiveSirenSong(this);
4735 if(Success)
4736 EditAP(-2000);
4738 return Success;
4741 truth humanoid::MindWormCanPenetrateSkull(mindworm* Worm) const
4743 if(GetHelmet())
4745 if(RAND_N(102) > GetHelmet()->GetCoverPercentile())
4747 return RAND_2;
4751 return RAND_2;
4754 truth humanoid::HasSadistWeapon() const
4756 arm* Right = GetRightArm(), * Left = GetLeftArm();
4757 return (Right && Right->HasSadistWeapon()) || (Left && Left->HasSadistWeapon());
4760 truth humanoid::HasSadistAttackMode() const
4762 return HasSadistWeapon() || IsUsingLegs();
4765 void petrusswife::Save(outputfile& SaveFile) const
4767 humanoid::Save(SaveFile);
4768 SaveFile << GiftTotal;
4771 void petrusswife::Load(inputfile& SaveFile)
4773 humanoid::Load(SaveFile);
4774 SaveFile >> GiftTotal;
4777 void petrusswife::BeTalkedTo()
4779 itemvector Item;
4781 if(!PLAYER->SelectFromPossessions(Item, CONST_S("Do you have something to give me?"), 0, &item::IsLuxuryItem)
4782 || Item.empty())
4783 humanoid::BeTalkedTo();
4785 int Accepted = 0;
4786 truth RefusedSomething = false;
4788 for(unsigned int c = 0; c < Item.size(); ++c)
4789 if(!MakesBurdened(GetCarriedWeight() + Item[c]->GetWeight()))
4791 ++Accepted;
4792 GiftTotal += Item[c]->GetTruePrice();
4793 Item[c]->RemoveFromSlot();
4794 GetStack()->AddItem(Item[c]);
4796 else
4798 RefusedSomething = true;
4799 break;
4802 if(Accepted)
4803 ADD_MESSAGE("\"I thank you for your little gift%s.\"", Accepted == 1 ? "" : "s");
4805 if(RefusedSomething)
4806 ADD_MESSAGE("\"Unfortunately I cannot carry any more of your gifts. I'm a delicate woman, you see.\"");
4809 void guard::BeTalkedTo()
4811 itemvector Item;
4813 if(!PLAYER->SelectFromPossessions(Item, CONST_S("Do you have something to give me?"), 0, &item::IsBeverage)
4814 || Item.empty())
4815 humanoid::BeTalkedTo();
4817 for(unsigned int c = 0; c < Item.size(); ++c)
4819 Item[c]->RemoveFromSlot();
4820 GetStack()->AddItem(Item[c]);
4825 void denim::GetAICommand () {
4826 int Enemies = 0;
4827 for (int c = 0; c < game::GetTeams(); ++c) {
4828 if (GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) Enemies += game::GetTeam(c)->GetEnabledMembers();
4830 StandIdleAI();
4834 v2 wisefarmer::GetHeadBitmapPos () const { return v2(96, (4 + (RAND() & 1)) << 4); }
4835 v2 wisefarmer::GetRightArmBitmapPos () const { return v2(64, (RAND() & 1) << 4); }
4837 void wisefarmer::GetAICommand () {
4838 int Enemies = 0;
4839 for (int c = 0; c < game::GetTeams(); ++c) {
4840 if (GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) Enemies += game::GetTeam(c)->GetEnabledMembers();
4842 StandIdleAI();
4846 void raven::GetAICommand () {
4847 int Enemies = 0;
4848 for (int c = 0; c < game::GetTeams(); ++c) {
4849 if (GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) Enemies += game::GetTeam(c)->GetEnabledMembers();
4851 StandIdleAI();
4855 void vulcan::GetAICommand () {
4856 int Enemies = 0;
4857 for (int c = 0; c < game::GetTeams(); ++c) {
4858 if (GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) Enemies += game::GetTeam(c)->GetEnabledMembers();
4860 StandIdleAI();
4864 truth rogue::IsRetreating () const {
4865 if (humanoid::IsRetreating()) return true;
4866 for (stackiterator i = GetStack()->GetBottom(); i.HasItem(); ++i) if ((*i)->GetSparkleFlags()) return true;
4867 return false;
4871 void rogue::GetAICommand () {
4872 if (!IsRetreating()) {
4873 character *Char = GetRandomNeighbour();
4874 if (Char) {
4875 itemvector Sparkling;
4876 for (stackiterator i = Char->GetStack()->GetBottom(); i.HasItem(); ++i) {
4877 if ((*i)->GetSparkleFlags() && !MakesBurdened((*i)->GetWeight())) Sparkling.push_back(*i);
4879 if (!Sparkling.empty()) {
4880 item *ToSteal = Sparkling[RAND() % Sparkling.size()];
4881 ToSteal->RemoveFromSlot();
4882 GetStack()->AddItem(ToSteal);
4883 if (Char->IsPlayer()) ADD_MESSAGE("%s steals your %s.", CHAR_NAME(DEFINITE), ToSteal->CHAR_NAME(UNARTICLED));
4884 EditAP(-500);
4885 return;
4889 humanoid::GetAICommand();
4893 void assassin::BeTalkedTo () {
4894 if (GetRelation(PLAYER) == HOSTILE) {
4895 if (PLAYER->GetMoney() >= 1500) {
4896 ADD_MESSAGE("%s talks: \"If you shell out 1500 gold pieces I'll join your side\"", CHAR_DESCRIPTION(DEFINITE));
4897 if (game::TruthQuestion(CONST_S("Do you want to bribe him? [y/N]"))) {
4898 PLAYER->SetMoney(PLAYER->GetMoney()-1500);
4899 ChangeTeam(PLAYER->GetTeam());
4900 RemoveHomeData();
4902 } else {
4903 ADD_MESSAGE("\"Trying to reason me with diplomancy won't work on me.\"");
4909 ///////////////////////////////////////////////////////////////////////////////
4910 petrus::petrus () : LastHealed(0) {
4911 game::SetPetrus(this);
4915 petrus::~petrus () {
4916 game::SetPetrus(0);
4920 void petrus::FinalProcessForBone () {
4921 humanoid::FinalProcessForBone();
4922 LastHealed = 0;
4926 truth petrus::HealFully (character *ToBeHealed) {
4927 truth DidSomething = false;
4928 for (int c = 0; c < ToBeHealed->GetBodyParts(); ++c) {
4929 if (!ToBeHealed->GetBodyPart(c)) {
4930 bodypart* BodyPart = 0;
4931 for (std::list<ulong>::const_iterator i = ToBeHealed->GetOriginalBodyPartID(c).begin(); i != ToBeHealed->GetOriginalBodyPartID(c).end(); ++i) {
4932 BodyPart = static_cast<bodypart *>(ToBeHealed->SearchForItem(*i));
4933 if (BodyPart) break;
4935 if (!BodyPart || !BodyPart->CanRegenerate()) continue;
4936 BodyPart->RemoveFromSlot();
4937 ToBeHealed->AttachBodyPart(BodyPart);
4938 BodyPart->RestoreHP();
4939 DidSomething = true;
4940 if (ToBeHealed->IsPlayer())
4941 ADD_MESSAGE("%s attaches your old %s back and heals it.", CHAR_NAME(DEFINITE), BodyPart->GetBodyPartName().CStr());
4942 else if (CanBeSeenByPlayer())
4943 ADD_MESSAGE("%s attaches the old %s of %s back and heals it.", CHAR_NAME(DEFINITE), BodyPart->GetBodyPartName().CStr(), ToBeHealed->CHAR_DESCRIPTION(DEFINITE));
4946 if (ToBeHealed->IsInBadCondition()) {
4947 ToBeHealed->RestoreLivingHP();
4948 DidSomething = true;
4949 if (ToBeHealed->IsPlayer())
4950 ADD_MESSAGE("%s heals you fully.", CHAR_DESCRIPTION(DEFINITE));
4951 else if (CanBeSeenByPlayer())
4952 ADD_MESSAGE("%s heals %s fully.", CHAR_DESCRIPTION(DEFINITE), ToBeHealed->CHAR_DESCRIPTION(DEFINITE));
4954 if (ToBeHealed->TemporaryStateIsActivated(POISONED)) {
4955 ToBeHealed->DeActivateTemporaryState(POISONED);
4956 DidSomething = true;
4957 if (ToBeHealed->IsPlayer())
4958 ADD_MESSAGE("%s removes the foul poison in your body.", CHAR_DESCRIPTION(DEFINITE));
4959 else if (CanBeSeenByPlayer())
4960 ADD_MESSAGE("%s removes the foul poison in %s's body.", CHAR_DESCRIPTION(DEFINITE), ToBeHealed->CHAR_DESCRIPTION(DEFINITE));
4962 if (DidSomething) {
4963 LastHealed = game::GetTick();
4964 DexterityAction(10);
4965 return true;
4967 return false;
4971 void petrus::Save (outputfile &SaveFile) const {
4972 humanoid::Save(SaveFile);
4973 SaveFile << LastHealed;
4977 void petrus::Load (inputfile &SaveFile) {
4978 humanoid::Load(SaveFile);
4979 SaveFile >> LastHealed;
4980 game::SetPetrus(this);
4984 void petrus::GetAICommand () {
4985 int Enemies = 0;
4986 for (int c = 0; c < game::GetTeams(); ++c) {
4987 if (GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE) Enemies += game::GetTeam(c)->GetEnabledMembers();
4990 if (Enemies && !RAND_N(250 / Min(Enemies, 50))) {
4991 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s shouts a magnificent prayer to Valpurus.", CHAR_NAME(DEFINITE));
4992 angel *Angel = angel::Spawn(VALPURUS);
4993 Angel->SetLifeExpectancy(10000, 0);
4994 v2 Where = GetLevel()->GetNearestFreeSquare(Angel, GetPos());
4995 if (Where == ERROR_V2) Where = GetLevel()->GetRandomSquare(Angel);
4996 Angel->SetTeam(GetTeam());
4997 Angel->PutTo(Where);
4998 if (Angel->CanBeSeenByPlayer()) ADD_MESSAGE("%s materializes.", Angel->CHAR_NAME(INDEFINITE));
4999 EditAP(-1000);
5000 return;
5003 if (HP << 1 < MaxHP && (GetPos() - v2(28, 20)).GetLengthSquare() > 400 && !RAND_N(10)) {
5004 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE));
5005 GetLevel()->GetLSquare(28, 20)->KickAnyoneStandingHereAway();
5006 Move(v2(28, 20), true);
5007 EditAP(-1000);
5008 return;
5011 if (!LastHealed || game::GetTick()-LastHealed > 16384) {
5012 for (int d = 0; d < GetNeighbourSquares(); ++d) {
5013 square *Square = GetNeighbourSquare(d);
5014 if (Square) {
5015 character *Char = Square->GetCharacter();
5016 if (Char && GetRelation(Char) == FRIEND && HealFully(Char)) return;
5021 StandIdleAI();
5025 void petrus::CreateCorpse (lsquare *Square) {
5026 if (game::GetStoryState() == 2) game::GetTeam(ATTNAM_TEAM)->SetRelation(game::GetTeam(PLAYER_TEAM), FRIEND);
5027 Square->AddItem(leftnutofpetrus::Spawn());
5028 SendToHell();
5032 void petrus::BeTalkedTo () {
5033 if (GetRelation(PLAYER) == HOSTILE) {
5034 ADD_MESSAGE("Heretic! Dev/null is a place not worthy to receive thee!");
5035 return;
5038 if (PLAYER->HasGoldenEagleShirt()) {
5039 ADD_MESSAGE("Petrus smiles. \"Thou hast defeated Oree! Mayst thou be blessed by Valpurus for the rest of thy life! And thou possess the Shirt of the Golden Eagle, the symbol of Our status! Return it now, please.\"");
5040 if (game::TruthQuestion(CONST_S("Will you give the Shirt of the Golden Eagle to Petrus? [y/n]"), REQUIRES_ANSWER)) {
5041 game::TextScreen(CONST_S(
5042 "The Holy Shirt is returned to its old owner and you kneel down to receive your reward.\n"
5043 "Petrus taps your shoulder with the Justifier and raises you to nobility."));
5044 if (true || game::TruthQuestion(CONST_S("Do you want to become a duke? [y/n]"), REQUIRES_ANSWER)) {
5045 game::TextScreen(CONST_S(
5046 "Later you receive a small dukedom in the middle of tundra\n"
5047 "where you rule with justice till the end of your content life.\n\n"
5048 "You are victorious!"));
5049 game::GetCurrentArea()->SendNewDrawRequest();
5050 game::DrawEverything();
5051 PLAYER->ShowAdventureInfo();
5052 festring Msg = CONST_S("retrieved the Shirt of the Golden Eagle and was raised to nobility");
5053 AddScoreEntry(Msg, 4, false);
5054 game::End(Msg);
5055 } else {
5056 GetArea()->SendNewDrawRequest();
5058 return;
5060 ADD_MESSAGE("Petrus's face turns red. \"I see. Thy greed hath overcome thy wisdom. Then, we shall fight for the shiny shirt. May Valpurus bless him who is better.\"");
5061 /* And now we actually make his face change color ;-) */
5062 GetHead()->GetMainMaterial()->SetSkinColor(MakeRGB16(255, 75, 50));
5063 GetHead()->UpdatePictures();
5064 SendNewDrawRequest();
5065 game::AskForEscPress(CONST_S("You are attacked!"));
5066 PLAYER->GetTeam()->Hostility(GetTeam());
5067 game::SetStoryState(2);
5068 return;
5071 if (PLAYER->HasHeadOfElpuri()) {
5072 game::TextScreen(CONST_S(
5073 "You have slain Elpuri, and Petrus grants you the freedom you desire.\n"
5074 "You spend the next months in Attnam as an honored hero."));
5075 if (true || game::TruthQuestion(CONST_S("Do you want to sail beyond the sunset? [y/n]"), REQUIRES_ANSWER)) {
5076 game::TextScreen(CONST_S(
5077 "When the sea finally melts, you board the first ship,\n"
5078 "leaving your past forever behind.\n\n"
5079 "You are victorious!"));
5080 game::GetCurrentArea()->SendNewDrawRequest();
5081 game::DrawEverything();
5082 PLAYER->ShowAdventureInfo();
5083 festring Msg = CONST_S("defeated Elpuri and continued to further adventures");
5084 AddScoreEntry(Msg, 2, false);
5085 game::End(Msg);
5086 } else {
5087 GetArea()->SendNewDrawRequest();
5089 return;
5092 if (!game::GetStoryState()) {
5093 if (PLAYER->RemoveEncryptedScroll()) {
5094 game::TextScreen(CONST_S(
5095 "You kneel down and bow before the high priest and hand him the encrypted scroll.\n"
5096 "Petrus raises his arm, the scroll glows yellow, and lo! The letters are clear and\n"
5097 "readable. Petrus asks you to voice them aloud. The first two thousand words praise\n"
5098 "Valpurus the Creator and all His manifestations and are followed by a canticle of\n"
5099 "Saint Petrus the Lion-Hearted lasting roughly three thousand words. Finally there\n"
5100 "are some sentences actually concerning your mission:\n\n"
5101 "\"Alas, I fear dirty tongues have spread lies to my Lord's ears. I assure all tales\n"
5102 "of treasures here in New Attnam are but mythic legends. There is nothing of value here.\n"
5103 "The taxes are already an unbearable burden and I can't possibly pay more. However I do\n"
5104 "not question the wisdom of the government's decisions. I will contribute what I can:\n"
5105 "the ostriches will deliver an extra 10000 bananas to the capital and additionally the\n"
5106 "slave that brought the message will henceforth be at Your disposal. I am certain this\n"
5107 "satisfies the crown's needs.\"\n\n"
5108 "\"Yours sincerely,\n"
5109 "Richel Decos, the viceroy of New Attnam\""));
5110 game::TextScreen(CONST_S(
5111 "You almost expected the last bit. Petrus seems to be deep in his thoughts and you\n"
5112 "wonder what shape your destiny is taking in his mind. Suddenly he seems to return\n"
5113 "to this reality and talks to you.\n\n"
5114 "\"Oh, thou art still here. We were just discussing telepathically with Sir Galladon.\n"
5115 "We started doubting Decos's alleged poverty a while back when he bought a couple of\n"
5116 "medium-sized castles nearby. Thy brethren from New Attnam have also told Us about\n"
5117 "vast riches seized from them. Our law says all such stolen valuables belong to\n"
5118 "the Cathedral's treasury, so this is a severe claim. However, proof is needed,\n"
5119 "and even if such was provided, We couldn't send soldiers over the snow fields\n"
5120 "ere spring.\""));
5121 if (game::GetLiberator()) {
5122 //TODO: do something?
5123 game::TextScreen(CONST_S(
5124 "\"Thy brethren from New Attnam have also told Us that Decos dies while sleeping.\n"
5125 "While We are sad to hear this, We hope that We shall not be forced to send our\n"
5126 "soldiers to New Attnam. We are sure that Our people there will work even better now.\""));
5128 game::TextScreen(CONST_S(
5129 "\"However, since thou now servest Us, We ought to find thee something to do. Sir\n"
5130 "Galladon hath told Us his agents witnessed thou leaving the dreaded underwater tunnel.\n"
5131 "This means thou most likely hast defeated genetrix vesana and art a talented warrior.\n"
5132 "We happen to have a task perfect for such a person. An evil dark frog named Elpuri who\n"
5133 "hates Valpurus and Attnam more than anything hath taken control over an abandoned mine\n"
5134 "nearby. It is pestering our fine city in many ways and reconnaissance has reported an\n"
5135 "army of monsters gathering in the cave. Our guards are not trained to fight underground\n"
5136 "and We dare not send them. To make things worse, someone hath recently stolen Us the\n"
5137 "greatest armor in existence - the Shirt of the Golden Eagle. Elpuri cannot wear\n"
5138 "it but he who can is now nearly immortal.\"\n\n"
5139 "\"We have marked the location of the gloomy cave on thy world map. We want you to dive\n"
5140 "into it and slay the vile frog. Bring Us its head and We reward thee with freedom.\n"
5141 "Shouldst thou also find the Shirt, We'll knight thee. Good luck, and return when\n"
5142 "thou hast succeeded.\""));
5143 game::LoadWorldMap();
5144 v2 ElpuriCavePos = game::GetWorldMap()->GetEntryPos(0, ELPURI_CAVE);
5145 game::GetWorldMap()->GetWSquare(ElpuriCavePos)->ChangeOWTerrain(elpuricave::Spawn());
5146 game::GetWorldMap()->RevealEnvironment(ElpuriCavePos, 1);
5147 game::SaveWorldMap();
5148 GetArea()->SendNewDrawRequest();
5149 ADD_MESSAGE("\"And by the way, visit the librarian. He might have advice for thee.\"");
5150 game::SetStoryState(1);
5151 } else {
5152 ADD_MESSAGE("\"Yes, citizen? We are quite busy now, thou shalt not disturb Us without proper cause.\"");
5154 return;
5156 /* StoryState == 1 */
5157 ADD_MESSAGE("Petrus says: \"Bring me the head of Elpuri and we'll talk.\"");
5161 truth petrus::MoveTowardsHomePos () {
5162 if (GetPos() != v2(28, 20)) {
5163 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE));
5164 GetLevel()->GetLSquare(28, 20)->KickAnyoneStandingHereAway();
5165 Move(v2(28, 20), true);
5166 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s appears.", CHAR_NAME(DEFINITE));
5167 EditAP(-1000);
5168 return true;
5170 return false;
5174 ///////////////////////////////////////////////////////////////////////////////
5175 void raven::BeTalkedTo () {
5176 if (GetRelation(PLAYER) == HOSTILE) {
5177 ADD_MESSAGE("I bet you are with those filthy Attnamanese bastards!");
5178 return;
5180 if (!game::GetRingOfThieves()) {
5181 if (PLAYER->RemoveRingOfThieves()) {
5182 game::TextScreen(CONST_S(
5183 "You hand over the Ring of Thieves, Raven's eyes glow in amazement.\n"
5184 "\"May Cleptia bless this mighty warrior, whom vanquished Vulcan-Loki!\n"
5185 "Retrieving the ring is no easy task, Vulcan is a powerful adversary.\n"
5186 "The importance of the Ring of Thieves is great. It is used to channel\n"
5187 "Cleptia's power to help Mondedr avoid it's enemies, including Petrus.\n"
5188 "Without it we would have been raided by every nation that hates us.\n"
5189 "Not a mere mortal like Vulcan-Loki could channel the ring's power for\n"
5190 "himself so he decided to contract a deal with Oree to assist him.\n"
5191 "To reward your efforts I will give you the artifact whip, Gleipnir.\"\n\n"
5192 "\"I pray to Cleptia that you will use it well..\"\n\n"""));
5193 game::GetGod(CLEPTIA)->AdjustRelation(500);
5194 game::GetGod(NEFAS)->AdjustRelation(50);
5195 game::GetGod(SCABIES)->AdjustRelation(50);
5196 game::GetGod(INFUSCOR)->AdjustRelation(50);
5197 game::GetGod(CRUENTUS)->AdjustRelation(50);
5198 game::GetGod(MORTIFER)->AdjustRelation(50);
5199 meleeweapon *Weapon = whipofthievery::Spawn();
5200 Weapon->InitMaterials(MAKE_MATERIAL(SPIDER_SILK), MAKE_MATERIAL(EBONY_WOOD), true);
5201 PLAYER->GetGiftStack()->AddItem(Weapon);
5202 GetArea()->SendNewDrawRequest();
5203 ADD_MESSAGE("A whip materializes near your feet.");
5204 game::SetRingOfThieves(1);
5205 } else {
5206 ADD_MESSAGE("\"Mondedr's most important artifact, the Ring of Thieves, has been stolen by Vulcan-Loki; residing at the deepest floor of the underground temple, he awaits Oree to receive it and reward him with incredible power.\"");
5208 } else {
5209 /* StoryState == 100 */
5210 ADD_MESSAGE("\"I must thank you again, I hope you make good use of that whip.\"");
5215 ///////////////////////////////////////////////////////////////////////////////
5216 void mysteryman::BeTalkedTo () {
5217 if (GetRelation(PLAYER) == HOSTILE) {
5218 ADD_MESSAGE("This is why the government doesn't mess with me, fool!");
5219 return;
5221 if (!game::GetMondedrPass()) {
5222 if (PLAYER->RemoveMondedrPass()) {
5223 game::TextScreen(CONST_S(
5224 "You hand over the mondedr pass,\n"
5225 "the man quickly snatches it and starts reading it right away.\n"
5226 "The man scans the text very quickly, his eyes run around a hundred laps per second.\n"
5227 "His eyes come to a halt.\n"
5228 "\"I have revealed the location of Mondedr.\" He hands you a map.\n"
5229 "\"It is a good thing I was born in this world, no? You owe me nothing, the \n"
5230 "the amount of information in the sheet of paper is what repays it.\"\n\n"""));
5231 game::LoadWorldMap();
5232 v2 MondedrPos = game::GetWorldMap()->GetEntryPos(0, MONDEDR);
5233 game::GetWorldMap()->GetWSquare(MondedrPos)->ChangeOWTerrain(mondedr::Spawn());
5234 game::GetWorldMap()->RevealEnvironment(MondedrPos, 1);
5235 game::SaveWorldMap();
5236 GetArea()->SendNewDrawRequest();
5237 ADD_MESSAGE("\"And by the way, the government always watches you.\"");
5238 game::SetMondedrPass(1);
5239 } else {
5240 ADD_MESSAGE("\"Shoo, come back if you have a sheet of paper they call 'Mondedr Pass'\"");
5242 } else {
5243 /* StoryState == 100 */
5244 ADD_MESSAGE("The man says: \"I don't have anymore business with you, shoo.\"");
5249 ///////////////////////////////////////////////////////////////////////////////
5250 void imperialist::BeTalkedTo () {
5251 decosadshirt *Shirt = static_cast<decosadshirt *>(PLAYER->SearchForItem(this, &item::IsDecosAdShirt));
5252 if (Shirt) {
5253 ulong Reward = Shirt->GetEquippedTicks()/500;
5254 if (Reward) {
5255 ADD_MESSAGE("%s smiles. \"I see you have advertised our company diligently. Here's %ldgp as a token of my gratitude.\"", CHAR_NAME(DEFINITE), Reward);
5256 PLAYER->EditMoney(Reward);
5257 Shirt->SetEquippedTicks(0);
5258 } else if (!(RAND()%5)) {
5259 ADD_MESSAGE("\"Come back when you've worn the shirt for some time and I'll reward you generously!\"");
5261 return;
5263 static long Said;
5264 if (GetRelation(PLAYER) == HOSTILE)
5265 ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said, GetHostileReplies().Size)]);
5266 else if (!game::PlayerIsSumoChampion())
5267 ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size)]);
5268 else
5269 ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said, GetFriendlyReplies().Size-1)]);
5273 void imperialist::DisplayStethoscopeInfo (character *) const {
5274 ADD_MESSAGE("You hear coins clinking inside.");
5278 void imperialist::CreateCorpse (lsquare *Square) {
5279 if (!game::GetLiberator()) {
5280 game::SetLiberator(1);
5281 ADD_MESSAGE("You liberate citizens of New Attnam!");
5283 imperialistsysbase::CreateCorpse(Square);
5287 ///////////////////////////////////////////////////////////////////////////////
5288 exiledpriest::exiledpriest () :
5289 exiledpriestsysbase(),
5290 mAtHome(false)
5295 exiledpriest::~exiledpriest () {
5299 void exiledpriest::Save (outputfile &saveFile) const {
5300 priest::Save(saveFile);
5301 saveFile << mAtHome;
5305 void exiledpriest::Load (inputfile &saveFile) {
5306 priest::Load(saveFile);
5307 saveFile >> mAtHome;
5311 void exiledpriest::healBodyParts () {
5312 for (int c = 0; c < PLAYER->GetBodyParts(); ++c) {
5313 if (!PLAYER->GetBodyPart(c) && PLAYER->CanCreateBodyPart(c)) {
5314 truth HasOld = false;
5315 for (std::list<ulong>::const_iterator i = PLAYER->GetOriginalBodyPartID(c).begin(); i != PLAYER->GetOriginalBodyPartID(c).end(); ++i) {
5316 bodypart *OldBodyPart = static_cast<bodypart *>(PLAYER->SearchForItem(*i));
5317 if (OldBodyPart) {
5318 HasOld = true;
5319 if (!OldBodyPart->CanRegenerate())
5320 ADD_MESSAGE("\"Sorry, I cannot put back bodyparts made of %s, not even your severed %s.\"", OldBodyPart->GetMainMaterial()->GetName(false, false).CStr(), PLAYER->GetBodyPartName(c).CStr());
5321 else {
5322 ADD_MESSAGE("\"I will put your old %s back.\"", PLAYER->GetBodyPartName(c).CStr());
5323 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
5324 OldBodyPart->SetHP(1);
5325 OldBodyPart->RemoveFromSlot();
5326 PLAYER->AttachBodyPart(OldBodyPart);
5327 healBodyParts(); //FIXME
5328 return;
5333 if (HasOld)
5334 ADD_MESSAGE("\"I could summon up a new %s.\"", PLAYER->GetBodyPartName(c).CStr());
5335 else
5336 ADD_MESSAGE("\"Since you don't seem to have your original %s with you, I could summon up a new one.\"", PLAYER->GetBodyPartName(c).CStr());
5337 if (game::TruthQuestion(CONST_S("Agreed? [y/N]"))) {
5338 PLAYER->CreateBodyPart(c);
5339 PLAYER->GetBodyPart(c)->SetHP(1);
5340 healBodyParts(); //FIXME
5341 return;
5348 void exiledpriest::healDeseases () {
5349 if (PLAYER->TemporaryStateIsActivated(POISONED)) {
5350 ADD_MESSAGE("\"You seem to be rather ill. I could give you a small dose of antidote.\"");
5351 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
5352 ADD_MESSAGE("You feel better.");
5353 PLAYER->DeActivateTemporaryState(POISONED);
5356 if (PLAYER->TemporaryStateIsActivated(PARASITIZED)) {
5357 ADD_MESSAGE("\"You seem to have something inside you. I could give you a small dose of antidote.\"");
5358 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
5359 ADD_MESSAGE("You feel better.");
5360 PLAYER->DeActivateTemporaryState(PARASITIZED);
5363 if (PLAYER->TemporaryStateIsActivated(LEPROSY)) {
5364 ADD_MESSAGE("\"You seem to have contracted the vile disease of leprosy. I could give you a small dose of medicince.\"");
5365 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
5366 ADD_MESSAGE("You feel better.");
5367 PLAYER->DeActivateTemporaryState(LEPROSY);
5370 if (PLAYER->TemporaryStateIsActivated(LYCANTHROPY)) {
5371 ADD_MESSAGE("\"You seem to be turning into a werewolf quite frequently. Well, everyone has right to little secret habits, but I could pray %s to remove the canine blood from your veins, just so you don't scare our blessed youth.\"", GetMasterGod()->GetName());
5372 if (game::TruthQuestion(CONST_S("Do you agree? [y/N]"))) {
5373 ADD_MESSAGE("You feel better.");
5374 PLAYER->DeActivateTemporaryState(LYCANTHROPY);
5380 void exiledpriest::healAll () {
5381 healBodyParts();
5382 healDeseases();
5386 //FIXME: GENDER!
5387 void exiledpriest::BeTalkedTo () {
5388 if (GetRelation(PLAYER) != HOSTILE && game::GetLiberator()) {
5389 if (!mAtHome) {
5390 ADD_MESSAGE(
5391 "Priestess says: "
5392 "\"Let %s bless you! I can return to my temple now! "
5393 "Get this things and remember how kind %s is!\"",
5394 GetMasterGod()->GetName(), GetMasterGod()->GetName());
5395 //GetMasterGod()->GetObjectPronoun());
5396 mAtHome = true;
5397 //GetGiftStack()
5399 potion *bottle = potion::Spawn(0, NO_MATERIALS);
5400 bottle->InitMaterials(MAKE_MATERIAL(GLASS), MAKE_MATERIAL(HEALING_LIQUID));
5401 PLAYER->GetStack()->AddItem(bottle);
5402 ADD_MESSAGE("Priestess gives %s to you.", bottle->CHAR_DESCRIPTION(DEFINITE));
5405 potion *bottle = potion::Spawn(0, NO_MATERIALS);
5406 bottle->InitMaterials(MAKE_MATERIAL(GLASS), MAKE_MATERIAL(ANTIDOTE_LIQUID));
5407 PLAYER->GetStack()->AddItem(bottle);
5408 ADD_MESSAGE("Priestess gives %s to you.", bottle->CHAR_DESCRIPTION(DEFINITE));
5410 if (RAND_N(100) >= 90) {
5411 potion *bottle = potion::Spawn(0, NO_MATERIALS);
5412 bottle->InitMaterials(MAKE_MATERIAL(GLASS), MAKE_MATERIAL(VODKA));
5413 PLAYER->GetStack()->AddItem(bottle);
5414 ADD_MESSAGE("Priestess gives %s to you.", bottle->CHAR_DESCRIPTION(DEFINITE));
5417 ADD_MESSAGE("I will heal you for free.");
5418 healAll();
5419 return;
5421 priest::BeTalkedTo();
5425 truth exiledpriest::MoveTowardsHomePos () {
5426 if (!mAtHome) return false;
5427 v2 homePos(12, 10);
5428 if (GetPos() != homePos) {
5429 SetGoingTo(homePos);
5430 return MoveTowardsTarget(false) || (!GetPos().IsAdjacent(homePos) && MoveRandomly());
5432 return false;
5436 void exiledpriest::GetAICommand () {
5437 priest::GetAICommand();