3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
8 * See LICENSING which should be included
9 * along with this file for more details
13 /* Compiled through charsset.cpp */
15 int nonhumanoid::GetUnarmedMinDamage() const { return int(UnarmedDamage
* 0.75); }
16 int nonhumanoid::GetUnarmedMaxDamage() const { return int(UnarmedDamage
* 1.25 + 1); }
17 int nonhumanoid::GetKickMinDamage() const { return int(KickDamage
* 0.75); }
18 int nonhumanoid::GetKickMaxDamage() const { return int(KickDamage
* 1.25 + 1); }
19 int nonhumanoid::GetBiteMinDamage() const { return int(BiteDamage
* 0.75); }
20 int nonhumanoid::GetBiteMaxDamage() const { return int(BiteDamage
* 1.25 + 1); }
21 int nonhumanoid::GetCarryingStrength() const { return (Max(GetAttribute(LEG_STRENGTH
), 1) << 1) + CarryingBonus
; }
22 truth
nonhumanoid::UseMaterialAttributes() const { return GetTorso()->UseMaterialAttributes(); }
24 truth
elpuri::SpecialEnemySightedReaction(character
*) { return !(Active
= true); }
26 cchar
* billswill::FirstPersonBiteVerb() const { return "emit psi waves at"; }
27 cchar
* billswill::FirstPersonCriticalBiteVerb() const { return "emit powerful psi waves at"; }
28 cchar
* billswill::ThirdPersonBiteVerb() const { return "emits psi waves at"; }
29 cchar
* billswill::ThirdPersonCriticalBiteVerb() const { return "emits powerful psi waves at"; }
30 int billswill::GetBodyPartWobbleData(int) const { return WOBBLE_HORIZONTALLY
|(2 << WOBBLE_FREQ_SHIFT
); }
32 int mommo::GetBodyPartWobbleData(int) const { return (GetConfig() == CONICAL
? WOBBLE_HORIZONTALLY
: WOBBLE_VERTICALLY
)|(2 << WOBBLE_FREQ_SHIFT
); }
34 bodypart
* dog::MakeBodyPart(int) const { return dogtorso::Spawn(0, NO_MATERIALS
); }
36 bodypart
* spider::MakeBodyPart(int) const { return spidertorso::Spawn(0, NO_MATERIALS
); }
38 int dolphin::GetSpecialBodyPartFlags(int) const { return RAND() & (MIRROR
|ROTATE
); }
40 bodypart
* bat::MakeBodyPart(int) const { return battorso::Spawn(0, NO_MATERIALS
); }
42 col16
chameleon::GetSkinColor() const { return MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190); }
44 void floatingeye::SetWayPoints(const fearray
<packv2
>& What
) { ArrayToVector(What
, WayPoints
); }
46 bodypart
* eddy::MakeBodyPart(int) const { return eddytorso::Spawn(0, NO_MATERIALS
); }
47 int eddy::GetBodyPartWobbleData(int) const { return WOBBLE_VERTICALLY
|(2 << WOBBLE_FREQ_SHIFT
); }
49 bodypart
* magicmushroom::MakeBodyPart(int) const { return magicmushroomtorso::Spawn(0, NO_MATERIALS
); }
51 cchar
* ghost::FirstPersonBiteVerb() const { return "touch"; }
52 cchar
* ghost::FirstPersonCriticalBiteVerb() const { return "awfully touch"; }
53 cchar
* ghost::ThirdPersonBiteVerb() const { return "touches"; }
54 cchar
* ghost::ThirdPersonCriticalBiteVerb() const { return "awfully touches"; }
55 truth
ghost::SpecialEnemySightedReaction(character
*) { return !(Active
= true); }
56 int ghost::GetBodyPartWobbleData(int) const { return WOBBLE_HORIZONTALLY
|(2 << WOBBLE_FREQ_SHIFT
); }
58 cchar
* magpie::FirstPersonBiteVerb() const { return "peck"; }
59 cchar
* magpie::FirstPersonCriticalBiteVerb() const { return "critically peck"; }
60 cchar
* magpie::ThirdPersonBiteVerb() const { return "pecks"; }
61 cchar
* magpie::ThirdPersonCriticalBiteVerb() const { return "critically pecks"; }
63 bodypart
* largecreature::MakeBodyPart(int) const { return largetorso::Spawn(0, NO_MATERIALS
); }
64 lsquare
* largecreature::GetNeighbourLSquare(int I
) const { return static_cast<lsquare
*>(GetNeighbourSquare(I
)); }
65 wsquare
* largecreature::GetNeighbourWSquare(int I
) const { return static_cast<wsquare
*>(GetNeighbourSquare(I
)); }
67 int hattifattener::GetSpecialBodyPartFlags(int) const { return ST_LIGHTNING
; }
68 int hattifattener::GetBodyPartWobbleData(int) const { return WOBBLE_HORIZONTALLY
|(1 << WOBBLE_SPEED_SHIFT
)|(1 << WOBBLE_FREQ_SHIFT
); }
70 col16
vladimir::GetSkinColor() const { return MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190); }
72 bodypart
* blinkdog::MakeBodyPart(int) const { return blinkdogtorso::Spawn(0, NO_MATERIALS
); }
74 int mysticfrog::GetBodyPartWobbleData(int) const { return WOBBLE_HORIZONTALLY
|(1 << WOBBLE_SPEED_SHIFT
)|(3 << WOBBLE_FREQ_SHIFT
); }
75 bodypart
* mysticfrog::MakeBodyPart(int) const { return mysticfrogtorso::Spawn(0, NO_MATERIALS
); }
77 bodypart
* lobhse::MakeBodyPart(int) const { return lobhsetorso::Spawn(0, NO_MATERIALS
); }
79 truth
elpuri::Hit(character
* Enemy
, v2
, int, int Flags
)
81 if(CheckIfTooScaredToHit(Enemy
))
84 character
* EnemyHit
[MAX_NEIGHBOUR_SQUARES
];
87 for(int d
= 0; d
< GetExtendedNeighbourSquares(); ++d
)
90 lsquare
* Square
= GetNeighbourLSquare(d
);
94 character
* ByStander
= Square
->GetCharacter();
96 if(ByStander
&& (ByStander
== Enemy
|| GetRelation(ByStander
) == HOSTILE
))
100 for(int c
= 0; c
< EnemiesHit
; ++c
)
101 if(EnemyHit
[c
] == ByStander
)
106 nonhumanoid::Hit(ByStander
, Square
->GetPos(), YOURSELF
, Flags
);
107 ByStander
->DamageAllItems(this, RAND() % 36 + RAND() % 36, PHYSICAL_DAMAGE
);
108 EnemyHit
[EnemiesHit
++] = ByStander
;
112 Square
->GetStack()->ReceiveDamage(this, RAND() % 36 + RAND() % 36, PHYSICAL_DAMAGE
, game::GetLargeMoveDirection(d
));
120 truth
dog::Catches (item
*Thingy
) {
121 if (Thingy
->DogWillCatchAndConsume(this)) {
122 if (ConsumeItem(Thingy
, CONST_S("eating"))) {
124 ADD_MESSAGE("You catch %s in mid-air and consume it.", Thingy
->CHAR_NAME(DEFINITE
));
126 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s catches %s and eats it.", CHAR_NAME(DEFINITE
), Thingy
->CHAR_NAME(DEFINITE
));
127 ChangeTeam(PLAYER
->GetTeam());
129 } else if (IsPlayer()) {
130 ADD_MESSAGE("You catch %s in mid-air.", Thingy
->CHAR_NAME(DEFINITE
));
131 } else if (CanBeSeenByPlayer()) {
132 ADD_MESSAGE("%s catches %s.", CHAR_NAME(DEFINITE
), Thingy
->CHAR_NAME(DEFINITE
));
139 truth
unicorn::SpecialEnemySightedReaction(character
*)
143 MonsterTeleport("neighs happily");
147 if(StateIsActivated(PANIC
) || (RAND() & 1 && IsInBadCondition()))
149 MonsterTeleport("neighs");
153 if(!(RAND() % 3) && MoveRandomly())
159 void nonhumanoid::Save(outputfile
& SaveFile
) const
161 character::Save(SaveFile
);
162 SaveFile
<< StrengthExperience
<< AgilityExperience
;
165 void nonhumanoid::Load(inputfile
& SaveFile
)
167 character::Load(SaveFile
);
168 SaveFile
>> StrengthExperience
>> AgilityExperience
;
171 void nonhumanoid::CalculateUnarmedDamage()
173 UnarmedDamage
= sqrt(5e-12 * GetAttribute(ARM_STRENGTH
)) * GetBaseUnarmedStrength() * GetCWeaponSkill(UNARMED
)->GetBonus();
176 void nonhumanoid::CalculateUnarmedToHitValue()
178 UnarmedToHitValue
= GetAttribute(DEXTERITY
) * sqrt(2.5 * GetAttribute(PERCEPTION
)) * GetCWeaponSkill(UNARMED
)->GetBonus() * GetMoveEase() / 500000;
181 void nonhumanoid::CalculateUnarmedAPCost()
183 UnarmedAPCost
= Max(sLong(10000000000. / (APBonus(GetAttribute(DEXTERITY
)) * GetMoveEase() * GetCWeaponSkill(UNARMED
)->GetBonus())), 100);
186 void nonhumanoid::CalculateKickDamage()
188 KickDamage
= sqrt(5e-12 * GetAttribute(LEG_STRENGTH
)) * GetBaseKickStrength() * GetCWeaponSkill(KICK
)->GetBonus();
191 void nonhumanoid::CalculateKickToHitValue()
193 KickToHitValue
= GetAttribute(AGILITY
) * sqrt(2.5 * GetAttribute(PERCEPTION
)) * GetCWeaponSkill(KICK
)->GetBonus() * GetMoveEase() / 1000000;
196 void nonhumanoid::CalculateKickAPCost()
198 KickAPCost
= Max(sLong(20000000000. / (APBonus(GetAttribute(AGILITY
)) * GetMoveEase() * GetCWeaponSkill(KICK
)->GetBonus())), 1000);
201 void nonhumanoid::CalculateBiteDamage()
203 BiteDamage
= sqrt(5e-12 * GetAttribute(ARM_STRENGTH
)) * GetBaseBiteStrength() * GetCWeaponSkill(BITE
)->GetBonus();
206 void nonhumanoid::CalculateBiteToHitValue()
208 BiteToHitValue
= GetAttribute(AGILITY
) * sqrt(2.5 * GetAttribute(PERCEPTION
)) * GetCWeaponSkill(BITE
)->GetBonus() * GetMoveEase() / 1000000;
211 void nonhumanoid::CalculateBiteAPCost()
213 BiteAPCost
= Max(sLong(10000000000. / (APBonus(GetAttribute(DEXTERITY
)) * GetMoveEase() * GetCWeaponSkill(BITE
)->GetBonus())), 100);
216 void nonhumanoid::InitSpecialAttributes()
218 StrengthExperience
= GetNaturalExperience(ARM_STRENGTH
);
219 AgilityExperience
= GetNaturalExperience(AGILITY
);
220 LimitRef(StrengthExperience
, MIN_EXP
, MAX_EXP
);
221 LimitRef(AgilityExperience
, MIN_EXP
, MAX_EXP
);
224 void nonhumanoid::Bite(character
* Enemy
, v2 HitPos
, int Direction
, truth ForceHit
)
227 EditAP(-GetBiteAPCost());
228 EditExperience(ARM_STRENGTH
, 75, 1 << 8);
229 EditExperience(AGILITY
, 150, 1 << 8);
230 EditStamina(-10000 / GetAttribute(ARM_STRENGTH
), false);
231 Enemy
->TakeHit(this, 0, GetTorso(), HitPos
, GetBiteDamage(), GetBiteToHitValue(), RAND() % 26 - RAND() % 26, BITE_ATTACK
, Direction
, !(RAND() % GetCriticalModifier()), ForceHit
);
234 void nonhumanoid::Kick(lsquare
* Square
, int Direction
, truth ForceHit
)
237 EditAP(-GetKickAPCost());
238 EditStamina(-10000 / GetAttribute(ARM_STRENGTH
), false);
240 if(Square
->BeKicked(this, 0, GetTorso(), GetKickDamage(), GetKickToHitValue(), RAND() % 26 - RAND() % 26, Direction
, !(RAND() % GetCriticalModifier()), ForceHit
))
242 EditExperience(LEG_STRENGTH
, 150, 1 << 8);
243 EditExperience(AGILITY
, 75, 1 << 8);
247 truth
nonhumanoid::Hit(character
* Enemy
, v2 HitPos
, int Direction
, int Flags
)
249 if(CheckIfTooScaredToHit(Enemy
))
254 if(!(Enemy
->IsMasochist() && GetRelation(Enemy
) == FRIEND
) && GetRelation(Enemy
) != HOSTILE
&& !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]")))
257 else if(GetAttribute(WISDOM
) >= Enemy
->GetAttackWisdomLimit())
260 if(GetBurdenState() == OVER_LOADED
)
263 ADD_MESSAGE("You cannot fight while carrying so much.");
268 /* Behold this Terrible Father of Gum Solutions! */
270 int AttackStyle
= GetAttackStyle();
272 if(AttackStyle
& USE_LEGS
)
274 room
* Room
= GetNearLSquare(HitPos
)->GetRoom();
276 if(Room
&& !Room
->AllowKick(this, GetNearLSquare(HitPos
)))
277 AttackStyle
&= ~USE_LEGS
;
282 for(c
= 0, AttackStyles
= 0; c
< 8; ++c
)
283 if(AttackStyle
& (1 << c
))
286 int Chosen
= RAND() % AttackStyles
;
288 for(c
= 0, AttackStyles
= 0; c
< 8; ++c
)
289 if(AttackStyle
& (1 << c
) && AttackStyles
++ == Chosen
)
298 msgsystem::EnterBigMessageMode();
300 UnarmedHit(Enemy
, HitPos
, Direction
, Flags
& SADIST_HIT
);
301 msgsystem::LeaveBigMessageMode();
304 msgsystem::EnterBigMessageMode();
306 Kick(GetNearLSquare(HitPos
), Direction
, Flags
& SADIST_HIT
);
307 msgsystem::LeaveBigMessageMode();
310 msgsystem::EnterBigMessageMode();
312 Bite(Enemy
, HitPos
, Direction
, Flags
& SADIST_HIT
);
313 msgsystem::LeaveBigMessageMode();
316 ABORT("Strange alien attack style requested!");
321 void nonhumanoid::UnarmedHit(character
* Enemy
, v2 HitPos
, int Direction
, truth ForceHit
)
324 EditAP(-GetUnarmedAPCost());
325 EditStamina(-10000 / GetAttribute(ARM_STRENGTH
), false);
327 switch(Enemy
->TakeHit(this, 0, GetTorso(), HitPos
, GetUnarmedDamage(), GetUnarmedToHitValue(), RAND() % 26 - RAND() % 26, UNARMED_ATTACK
, Direction
, !(RAND() % GetCriticalModifier()), ForceHit
))
333 EditExperience(ARM_STRENGTH
, 150, 1 << 8);
335 EditExperience(DEXTERITY
, 75, 1 << 8);
339 /* Returns the average number of APs required to kill Enemy */
341 double nonhumanoid::GetTimeToKill(ccharacter
* Enemy
, truth UseMaxHP
) const
343 double Effectivity
= 0;
344 int AttackStyles
= 0;
348 Effectivity
+= 1 / (Enemy
->GetTimeToDie(this, int(GetUnarmedDamage()) + 1, GetUnarmedToHitValue(), AttackIsBlockable(UNARMED_ATTACK
), UseMaxHP
) * GetUnarmedAPCost());
354 Effectivity
+= 1 / (Enemy
->GetTimeToDie(this, int(GetKickDamage()) + 1, GetKickToHitValue(), AttackIsBlockable(KICK_ATTACK
), UseMaxHP
) * GetKickAPCost());
360 Effectivity
+= 1 / (Enemy
->GetTimeToDie(this, int(GetBiteDamage()) + 1, GetBiteToHitValue(), AttackIsBlockable(BITE_ATTACK
), UseMaxHP
) * GetBiteAPCost());
364 if(StateIsActivated(HASTE
))
367 if(StateIsActivated(SLOW
))
370 return AttackStyles
/ Effectivity
;
373 int nonhumanoid::GetAttribute(int Identifier
, truth AllowBonus
) const
375 if(Identifier
< BASE_ATTRIBUTES
)
376 return character::GetAttribute(Identifier
, AllowBonus
);
377 else if(Identifier
== ARM_STRENGTH
|| Identifier
== LEG_STRENGTH
)
379 if(!UseMaterialAttributes())
380 return int(StrengthExperience
* EXP_DIVISOR
);
382 return GetTorso()->GetMainMaterial()->GetStrengthValue();
384 else if(Identifier
== DEXTERITY
|| Identifier
== AGILITY
)
386 if(!UseMaterialAttributes())
387 return int(AgilityExperience
* EXP_DIVISOR
);
389 return (GetTorso()->GetMainMaterial()->GetFlexibility() << 2);
393 ABORT("Illegal nonhumanoid attribute %d request!", Identifier
);
398 truth
nonhumanoid::EditAttribute(int Identifier
, int Value
)
400 if(Identifier
< BASE_ATTRIBUTES
)
401 return character::EditAttribute(Identifier
, Value
);
402 else if(Identifier
== ARM_STRENGTH
|| Identifier
== LEG_STRENGTH
)
403 return !UseMaterialAttributes() && RawEditAttribute(StrengthExperience
, Value
);
404 else if(Identifier
== DEXTERITY
|| Identifier
== AGILITY
)
405 return !UseMaterialAttributes() && RawEditAttribute(AgilityExperience
, Value
);
408 ABORT("Illegal nonhumanoid attribute %d edit request!", Identifier
);
413 void nonhumanoid::EditExperience(int Identifier
, double Value
, double Speed
)
415 if(!AllowExperience())
418 if(Identifier
< BASE_ATTRIBUTES
)
419 character::EditExperience(Identifier
, Value
, Speed
);
420 else if(Identifier
== ARM_STRENGTH
|| Identifier
== LEG_STRENGTH
)
422 if(!UseMaterialAttributes())
424 int Change
= RawEditExperience(StrengthExperience
,
425 GetNaturalExperience(ARM_STRENGTH
),
430 cchar
* Adj
= Change
> 0 ? "stronger" : "weaker";
433 ADD_MESSAGE("Your feel %s!", Adj
);
434 else if(IsPet() && CanBeSeenByPlayer())
435 ADD_MESSAGE("Suddenly %s looks %s.", CHAR_NAME(DEFINITE
), Adj
);
437 CalculateBurdenState();
438 CalculateBattleInfo();
442 else if(Identifier
== DEXTERITY
|| Identifier
== AGILITY
)
444 if(!UseMaterialAttributes())
446 int Change
= RawEditExperience(AgilityExperience
,
447 GetNaturalExperience(AGILITY
),
452 cchar
* Adj
= Change
> 0 ? "very agile" : "sluggish";
455 ADD_MESSAGE("Your feel %s!", Adj
);
456 else if(IsPet() && CanBeSeenByPlayer())
457 ADD_MESSAGE("Suddenly %s looks %s.", CHAR_NAME(DEFINITE
), Adj
);
459 CalculateBattleInfo();
464 ABORT("Illegal nonhumanoid attribute %d experience edit request!", Identifier
);
467 int nonhumanoid::DrawStats(truth AnimationDraw
) const
472 int PanelPosX
= RES
.X
- 96, PanelPosY
= 3;
473 PrintAttribute("Str", ARM_STRENGTH
, PanelPosX
, PanelPosY
++);
474 PrintAttribute("Agi", AGILITY
, PanelPosX
, PanelPosY
++);
478 void nonhumanoid::CalculateBattleInfo()
480 CalculateDodgeValue();
481 CalculateUnarmedAttackInfo();
482 CalculateKickAttackInfo();
483 CalculateBiteAttackInfo();
486 void nonhumanoid::CalculateUnarmedAttackInfo()
488 CalculateUnarmedDamage();
489 CalculateUnarmedToHitValue();
490 CalculateUnarmedAPCost();
493 void nonhumanoid::CalculateKickAttackInfo()
495 CalculateKickDamage();
496 CalculateKickToHitValue();
497 CalculateKickAPCost();
500 void nonhumanoid::CalculateBiteAttackInfo()
502 CalculateBiteDamage();
503 CalculateBiteToHitValue();
504 CalculateBiteAPCost();
507 void dog::BeTalkedTo () {
509 if (GetRelation(PLAYER
) != HOSTILE
) {
512 if (GetHP()<< 1 > GetMaxHP()) Reply
= Last
? "barks happily" : "wags its tail happily";
513 else Reply
= Last
? "yelps" : "howls";
514 ADD_MESSAGE("%s %s.", CHAR_NAME(DEFINITE
), Reply
);
517 character::BeTalkedTo();
519 } else if (RAND_N(5)) {
520 ADD_MESSAGE("\"Can't you understand I can't speak?\"");
522 ADD_MESSAGE("\"Meow.\"");
526 col16
wolf::GetSkinColor() const
528 int Element
= 40 + RAND() % 50;
529 return MakeRGB16(Element
, Element
, Element
);
532 void genetrixvesana::GetAICommand()
536 SeekLeader(GetLeader());
538 if(FollowLeader(GetLeader()))
543 int NumberOfPlants
= RAND() % 3 + RAND() % 3 + RAND() % 3 + RAND() % 3;
545 for(int c1
= 0; c1
< 50 && NumberOfPlants
; ++c1
)
547 for(int c2
= 0; c2
< game::GetTeams() && NumberOfPlants
; ++c2
)
548 if(GetTeam()->GetRelation(game::GetTeam(c2
)) == HOSTILE
)
549 for(std::list
<character
*>::const_iterator i
= game::GetTeam(c2
)->GetMember().begin(); i
!= game::GetTeam(c2
)->GetMember().end() && NumberOfPlants
; ++i
)
550 if((*i
)->IsEnabled())
552 lsquare
* LSquare
= (*i
)->GetNeighbourLSquare(RAND() % GetNeighbourSquares());
554 if(LSquare
&& (LSquare
->GetWalkability() & WALK
) && !LSquare
->GetCharacter())
557 sLong RandomValue
= RAND() % TurnsExisted
;
559 if(RandomValue
< 250)
560 NewPlant
= carnivorousplant::Spawn();
561 else if(RandomValue
< 1500)
562 NewPlant
= carnivorousplant::Spawn(GREATER
);
564 NewPlant
= carnivorousplant::Spawn(GIANTIC
);
566 for(int c
= 3; c
< TurnsExisted
/ 500; ++c
)
567 NewPlant
->EditAllAttributes(1);
569 NewPlant
->SetGenerationDanger(GetGenerationDanger());
570 NewPlant
->SetTeam(GetTeam());
571 NewPlant
->PutTo(LSquare
->GetPos());
574 if(NewPlant
->CanBeSeenByPlayer())
577 ADD_MESSAGE("%s sprouts from the ground near you.", NewPlant
->CHAR_NAME(INDEFINITE
));
578 else if((*i
)->CanBeSeenByPlayer())
579 ADD_MESSAGE("%s sprouts from the ground near %s.", NewPlant
->CHAR_NAME(INDEFINITE
), (*i
)->CHAR_NAME(DEFINITE
));
581 ADD_MESSAGE("%s sprouts from the ground.", NewPlant
->CHAR_NAME(INDEFINITE
));
591 if(AttackAdjacentEnemyAI())
600 col16
carnivorousplant::GetTorsoSpecialColor() const // the flower
603 return MakeRGB16(RAND() % 100, 125 + RAND() % 125, RAND() % 100);
604 else if(GetConfig() == GREATER
)
605 return MakeRGB16(RAND() % 100, RAND() % 100, 125 + RAND() % 125);
607 return MakeRGB16(125 + RAND() % 125, 125 + RAND() % 125, RAND() % 100);
610 void ostrich::GetAICommand()
612 if(game::TweraifIsFree())
614 nonhumanoid::GetAICommand();
618 if(CheckForEnemies(false, false, true, true))
624 if(GetPos() == v2(45, 45))
625 HasDroppedBananas
= true;
627 itemvector ItemVector
;
628 GetStackUnder()->FillItemVector(ItemVector
);
629 int BananasPicked
= 0;
631 for(uInt c
= 0; c
< ItemVector
.size(); ++c
)
632 if(ItemVector
[c
]->IsBanana() && ItemVector
[c
]->CanBeSeenBy(this)
633 && ItemVector
[c
]->IsPickable(this)
634 && !MakesBurdened(GetCarriedWeight() + ItemVector
[c
]->GetWeight()))
636 ItemVector
[c
]->MoveTo(GetStack());
642 if(CanBeSeenByPlayer())
643 ADD_MESSAGE("%s picks up %s.", CHAR_NAME(DEFINITE
), BananasPicked
== 1 ? "the banana" : "some bananas");
648 if(!HasDroppedBananas
)
650 SetGoingTo(v2(45, 45));
652 if(MoveTowardsTarget(true))
655 else if(GetPos().Y
== 54)
657 if(CanBeSeenByPlayer())
658 ADD_MESSAGE("%s leaves the town.", CHAR_NAME(DEFINITE
));
660 itemvector ItemVector
;
661 GetStack()->FillItemVector(ItemVector
);
663 for(uInt c
= 0; c
< ItemVector
.size(); ++c
)
665 ItemVector
[c
]->RemoveFromSlot();
666 ItemVector
[c
]->SendToHell();
669 v2 Where
= GetLevel()->GetNearestFreeSquare(this, v2(45, 0));
671 if(Where
== ERROR_V2
)
672 Where
= GetLevel()->GetRandomSquare(this, NOT_IN_ROOM
); // this is odd but at least it doesn't crash
679 GainIntrinsic(LEVITATION
);
681 if(CanBeSeenByPlayer())
682 ADD_MESSAGE("%s enters the town.", CHAR_NAME(INDEFINITE
));
684 HasDroppedBananas
= false;
688 SetGoingTo(v2(45, 54));
690 if(MoveTowardsTarget(true))
697 void ostrich::Save(outputfile
& SaveFile
) const
699 nonhumanoid::Save(SaveFile
);
700 SaveFile
<< HasDroppedBananas
;
703 void ostrich::Load(inputfile
& SaveFile
)
705 nonhumanoid::Load(SaveFile
);
706 SaveFile
>> HasDroppedBananas
;
709 truth
ostrich::HandleCharacterBlockingTheWay(character
* Char
, v2 Pos
, int Dir
)
711 return Char
->GetPos() == v2(45, 45) && (Displace(Char
, true) || Hit(Char
, Pos
, Dir
));
714 void elpuri::Save(outputfile
& SaveFile
) const
716 largecreature::Save(SaveFile
);
720 void elpuri::Load(inputfile
& SaveFile
)
722 largecreature::Load(SaveFile
);
726 void elpuri::GetAICommand()
729 character::GetAICommand();
732 if(CheckForEnemies(false, false, false))
739 int elpuri::ReceiveBodyPartDamage(character
* Damager
, int Damage
, int Type
, int BodyPartIndex
, int Direction
, truth PenetrateResistance
, truth Critical
, truth ShowNoDamageMsg
, truth CaptureBodyPart
)
742 return character::ReceiveBodyPartDamage(Damager
, Damage
, Type
, BodyPartIndex
, Direction
, PenetrateResistance
, Critical
, ShowNoDamageMsg
, CaptureBodyPart
);
745 void mommo::CreateCorpse(lsquare
* Square
)
747 for(int d
= 0; d
< GetExtendedNeighbourSquares(); ++d
)
749 lsquare
* NeighbourSquare
= Square
->GetNeighbourLSquare(d
);
752 NeighbourSquare
->SpillFluid(0, static_cast<liquid
*>(GetTorso()->GetMainMaterial()->SpawnMore(250 + RAND() % 250)));
758 void carnivorousplant::CreateCorpse(lsquare
* Square
)
760 int Amount
= !GetConfig() ? (RAND() % 7 ? 0 : 1) : GetConfig() == GREATER
? (RAND() & 1 ? 0 : (RAND() % 5 ? 1 : (RAND() % 5 ? 2 : 3))) : (!(RAND() % 3) ? 0 : (RAND() % 3 ? 1 : (RAND() % 3 ? 2 : 3)));
762 for(int c
= 0; c
< Amount
; ++c
)
763 Square
->AddItem(kiwi::Spawn());
765 nonhumanoid::CreateCorpse(Square
);
768 void genetrixvesana::CreateCorpse(lsquare
* Square
)
770 for(int c
= 0; c
< 3; ++c
)
771 Square
->AddItem(pineapple::Spawn());
773 largecreature::CreateCorpse(Square
);
776 void nonhumanoid::AddSpecialStethoscopeInfo(felist
& Info
) const
778 Info
.AddEntry(CONST_S("Strength: ") + GetAttribute(ARM_STRENGTH
), LIGHT_GRAY
);
779 Info
.AddEntry(CONST_S("Agility: ") + GetAttribute(AGILITY
), LIGHT_GRAY
);
782 void floatingeye::Save(outputfile
& SaveFile
) const
784 nonhumanoid::Save(SaveFile
);
785 SaveFile
<< WayPoints
<< NextWayPoint
;
788 void floatingeye::Load(inputfile
& SaveFile
)
790 nonhumanoid::Load(SaveFile
);
791 SaveFile
>> WayPoints
>> NextWayPoint
;
794 void floatingeye::GetAICommand()
796 if(WayPoints
.size() && !IsGoingSomeWhere())
798 if(GetPos() == WayPoints
[NextWayPoint
]) {
799 if(NextWayPoint
< WayPoints
.size() - 1) ++NextWayPoint
; else NextWayPoint
= 0;
802 GoingTo
= WayPoints
[NextWayPoint
];
805 SeekLeader(GetLeader());
807 if(CheckForEnemies(false, false, true))
810 if(FollowLeader(GetLeader()))
819 truth
floatingeye::Hit(character
* Enemy
, v2
, int, int)
822 ADD_MESSAGE("You stare at %s.", Enemy
->CHAR_DESCRIPTION(DEFINITE
));
823 else if(Enemy
->IsPlayer() && CanBeSeenByPlayer())
824 ADD_MESSAGE("%s stares at you.", CHAR_NAME(DEFINITE
));
830 int floatingeye::TakeHit(character
* Enemy
, item
* Weapon
, bodypart
* EnemyBodyPart
, v2 HitPos
, double Damage
, double ToHitValue
, int Success
, int Type
, int Direction
, truth Critical
, truth ForceHit
)
832 if(CanBeSeenBy(Enemy
) && Enemy
->HasEyes() && RAND() % 3 && Enemy
->LoseConsciousness(150 + RAND_N(150))) /* Changes for fainting 2 out of 3 */
834 if(!Enemy
->IsPlayer())
835 Enemy
->EditExperience(WISDOM
, 75, 1 << 13);
840 return nonhumanoid::TakeHit(Enemy
, Weapon
, EnemyBodyPart
, HitPos
, Damage
, ToHitValue
, Success
, Type
, Direction
, Critical
, ForceHit
);
843 void elpuri::CreateCorpse(lsquare
* Square
)
845 largecreature::CreateCorpse(Square
);
846 Square
->AddItem(headofelpuri::Spawn());
849 truth
snake::SpecialBiteEffect(character
* Char
, v2
, int, int, truth BlockedByArmour
)
853 Char
->BeginTemporaryState(POISONED
, 400 + RAND_N(200));
860 truth
spider::SpecialBiteEffect(character
* Char
, v2
, int, int, truth BlockedByArmour
)
864 Char
->BeginTemporaryState(POISONED
, GetConfig() == LARGE
? 80 + RAND_N(40) : 400 + RAND_N(200));
871 truth
chameleon::SpecialEnemySightedReaction(character
*)
873 if(HP
!= MaxHP
|| !(RAND() % 3))
875 character
* NewForm
= PolymorphRandomly(100, 1000, 500 + RAND() % 500);
876 NewForm
->GainIntrinsic(POLYMORPH
);
883 int chameleon::TakeHit(character
* Enemy
, item
* Weapon
, bodypart
* EnemyBodyPart
, v2 HitPos
, double Damage
, double ToHitValue
, int Success
, int Type
, int Direction
, truth Critical
, truth ForceHit
)
885 int Return
= nonhumanoid::TakeHit(Enemy
, Weapon
, EnemyBodyPart
, HitPos
, Damage
, ToHitValue
, Success
, Type
, Direction
, Critical
, ForceHit
);
887 if(Return
!= HAS_DIED
)
889 character
* NewForm
= PolymorphRandomly(100, 1000, 500 + RAND() % 500);
890 NewForm
->GainIntrinsic(POLYMORPH
);
896 truth
eddy::Hit(character
* Enemy
, v2
, int, int)
900 if(!(Enemy
->IsMasochist() && GetRelation(Enemy
) == FRIEND
) && GetRelation(Enemy
) != HOSTILE
&& !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]")))
909 ADD_MESSAGE("You engulf %s.", Enemy
->CHAR_DESCRIPTION(DEFINITE
));
910 else if(Enemy
->IsPlayer() || CanBeSeenByPlayer() || Enemy
->CanBeSeenByPlayer())
911 ADD_MESSAGE("%s engulfs %s.", CHAR_DESCRIPTION(DEFINITE
), Enemy
->CHAR_DESCRIPTION(DEFINITE
));
913 Enemy
->TeleportRandomly();
916 ADD_MESSAGE("You miss %s.", Enemy
->CHAR_DESCRIPTION(DEFINITE
));
922 void mushroom::Save(outputfile
& SaveFile
) const
924 nonhumanoid::Save(SaveFile
);
928 void mushroom::Load(inputfile
& SaveFile
)
930 nonhumanoid::Load(SaveFile
);
934 void mushroom::GetAICommand()
936 SeekLeader(GetLeader());
938 if(FollowLeader(GetLeader()))
941 lsquare
* CradleSquare
= GetNeighbourLSquare(RAND() % 8);
943 if(CradleSquare
&& !CradleSquare
->GetCharacter()
944 && (CradleSquare
->GetWalkability() & WALK
))
946 int SpoiledItems
= 0;
947 int MushroomsNear
= 0;
949 for(int d
= 0; d
< 8; ++d
)
951 lsquare
* Square
= CradleSquare
->GetNeighbourLSquare(d
);
955 character
* Char
= Square
->GetCharacter();
957 if(Char
&& Char
->IsMushroom())
960 SpoiledItems
+= Square
->GetSpoiledItems();
964 if((SpoiledItems
&& MushroomsNear
< 5 && !RAND_N(50)) || (MushroomsNear
< 3 && !RAND_N((1 + MushroomsNear
) * 100)))
966 mushroom
* Child
= static_cast<mushroom
*>(GetProtoType()->Spawn(GetConfig()));
967 Child
->SetSpecies(Species
);
968 Child
->SetTeam(GetTeam());
969 Child
->SetGenerationDanger(GetGenerationDanger());
970 Child
->PutTo(CradleSquare
->GetPos());
972 for(int c
= 0; c
< BASE_ATTRIBUTES
; ++c
)
973 Child
->BaseExperience
[c
] = RandomizeBabyExperience(BaseExperience
[c
] * 4);
975 if(Child
->CanBeSeenByPlayer())
976 ADD_MESSAGE("%s pops out from the ground.", Child
->CHAR_NAME(INDEFINITE
));
980 if(AttackAdjacentEnemyAI())
989 void mushroom::PostConstruct()
993 case 0: SetSpecies(MakeRGB16(125 + RAND() % 125, RAND() % 100, RAND() % 100)); break;
994 case 1: SetSpecies(MakeRGB16(RAND() % 100, 125 + RAND() % 125, RAND() % 100)); break;
995 case 2: SetSpecies(MakeRGB16(RAND() % 100, RAND() % 100, 125 + RAND() % 125)); break;
999 void magicmushroom::GetAICommand()
1003 if(CanBeSeenByPlayer())
1004 ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE
));
1006 TeleportRandomly(true);
1009 else if(!(RAND() % 50))
1011 lsquare
* Square
= GetNeighbourLSquare(RAND() % 8);
1013 if(Square
&& Square
->IsFlyable())
1015 if(CanBeSeenByPlayer())
1016 ADD_MESSAGE("%s releases odd-looking gas.", CHAR_NAME(DEFINITE
));
1018 Square
->AddSmoke(gas::Spawn(MAGIC_VAPOUR
, 1000));
1023 mushroom::GetAICommand();
1026 void mushroom::SetSpecies(int What
)
1032 truth
twoheadedmoose::Hit(character
* Enemy
, v2 HitPos
, int Direction
, int Flags
)
1034 if(CheckIfTooScaredToHit(Enemy
))
1039 if(!(Enemy
->IsMasochist() && GetRelation(Enemy
) == FRIEND
) && GetRelation(Enemy
) != HOSTILE
&& !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]")))
1042 else if(GetAttribute(WISDOM
) >= Enemy
->GetAttackWisdomLimit())
1045 if(GetBurdenState() == OVER_LOADED
)
1048 ADD_MESSAGE("You cannot fight while carrying so much.");
1054 msgsystem::EnterBigMessageMode();
1055 Bite(Enemy
, HitPos
, Direction
, Flags
& SADIST_HIT
);
1056 v2 Pos
[MAX_NEIGHBOUR_SQUARES
];
1057 character
* Char
[MAX_NEIGHBOUR_SQUARES
];
1060 for(int d
= 0; d
< GetNeighbourSquares(); ++d
)
1062 lsquare
* LSquare
= GetNeighbourLSquare(d
);
1066 character
* Enemy
= LSquare
->GetCharacter();
1068 if(Enemy
&& GetRelation(Enemy
) == HOSTILE
&& GetAttribute(WISDOM
) < Enemy
->GetAttackWisdomLimit())
1070 Pos
[Index
] = LSquare
->GetPos();
1071 Char
[Index
++] = Enemy
;
1078 int ChosenIndex
= RAND() % Index
;
1079 Bite(Char
[ChosenIndex
], Pos
[ChosenIndex
], game::GetDirectionForVector(Pos
[ChosenIndex
] - GetPos()), Flags
& SADIST_HIT
);
1082 msgsystem::LeaveBigMessageMode();
1086 truth
magpie::IsRetreating() const
1088 if(nonhumanoid::IsRetreating())
1091 for(stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
)
1092 if((*i
)->GetSparkleFlags())
1098 void magpie::GetAICommand()
1102 character
* Char
= GetRandomNeighbour();
1106 itemvector Sparkling
;
1108 for(stackiterator i
= Char
->GetStack()->GetBottom(); i
.HasItem(); ++i
)
1110 if((*i
)->GetSparkleFlags() && !MakesBurdened((*i
)->GetWeight()))
1111 Sparkling
.push_back(*i
);
1114 if(!Sparkling
.empty())
1116 item
* ToSteal
= Sparkling
[RAND() % Sparkling
.size()];
1117 ToSteal
->RemoveFromSlot();
1118 GetStack()->AddItem(ToSteal
);
1120 if(Char
->IsPlayer())
1121 ADD_MESSAGE("%s steals your %s.", CHAR_NAME(DEFINITE
), ToSteal
->CHAR_NAME(UNARTICLED
));
1129 nonhumanoid::GetAICommand();
1132 void eddy::GetAICommand()
1134 if(!GetLSquareUnder()->GetOLTerrain() && !(RAND() % 500))
1136 decoration
* Couch
= decoration::Spawn(RAND_N(5) ? COUCH
: DOUBLE_BED
);
1138 if(CanBeSeenByPlayer())
1139 ADD_MESSAGE("%s spits out %s.", CHAR_NAME(DEFINITE
), Couch
->CHAR_NAME(INDEFINITE
));
1141 GetLSquareUnder()->ChangeOLTerrainAndUpdateLights(Couch
);
1146 if(GetStackUnder()->GetItems() && !(RAND() % 10))
1148 if(CanBeSeenByPlayer())
1149 ADD_MESSAGE("%s engulfs something under it.", CHAR_NAME(DEFINITE
));
1151 GetStackUnder()->TeleportRandomly(3);
1158 if(CanBeSeenByPlayer())
1159 ADD_MESSAGE("%s engulfs itself.", CHAR_NAME(DEFINITE
));
1161 TeleportRandomly(true);
1166 nonhumanoid::GetAICommand();
1169 void skunk::GetAICommand()
1175 character
* Char
= GetRandomNeighbour(HOSTILE
);
1179 int Amount
= 500 / Char
->GetSquaresUnder();
1180 truth Success
= false;
1182 for(int c
= 0; c
< Char
->GetSquaresUnder(); ++c
)
1183 if(Char
->GetLSquareUnder(c
)->IsFlyable())
1186 Char
->GetLSquareUnder(c
)->AddSmoke(gas::Spawn(SKUNK_SMELL
, Amount
));
1191 if(CanBeSeenByPlayer())
1192 ADD_MESSAGE("%s stinks.", CHAR_NAME(DEFINITE
));
1202 if(CanBeSeenByPlayer())
1203 ADD_MESSAGE("%s stinks.", CHAR_NAME(DEFINITE
));
1205 GetLSquareUnder()->AddSmoke(gas::Spawn(SKUNK_SMELL
, 500));
1208 nonhumanoid::GetAICommand();
1211 truth
elpuri::TryToRiseFromTheDead()
1213 character::TryToRiseFromTheDead();
1215 for(int c
= 0; c
< GetSquaresUnder(); ++c
)
1216 for(stackiterator i
= GetLSquareUnder(c
)->GetStack()->GetBottom(); i
.HasItem(); ++i
)
1217 if(i
->IsHeadOfElpuri())
1220 i
->RemoveFromSlot();
1224 if(CanBeSeenByPlayer())
1226 ADD_MESSAGE("The headless body of %s vibrates violently.", CHAR_NAME(DEFINITE
));
1227 ADD_MESSAGE("%s dies.", CHAR_NAME(DEFINITE
));
1233 truth
nonhumanoid::EditAllAttributes(int Amount
)
1238 LimitRef(StrengthExperience
+= Amount
* EXP_MULTIPLIER
, MIN_EXP
, MAX_EXP
);
1239 LimitRef(AgilityExperience
+= Amount
* EXP_MULTIPLIER
, MIN_EXP
, MAX_EXP
);
1240 return character::EditAllAttributes(Amount
)
1242 && (StrengthExperience
!= MIN_EXP
|| AgilityExperience
!= MIN_EXP
))
1244 && (StrengthExperience
!= MAX_EXP
|| AgilityExperience
!= MAX_EXP
));
1249 void nonhumanoid::AddAttributeInfo(festring
& Entry
) const
1252 Entry
<< GetAttribute(ARM_STRENGTH
);
1254 Entry
<< "- - " << GetAttribute(AGILITY
);
1255 character::AddAttributeInfo(Entry
);
1258 void nonhumanoid::AddAttackInfo(felist
& List
) const
1264 Entry
= CONST_S(" unarmed attack");
1266 Entry
<< GetUnarmedMinDamage() << '-' << GetUnarmedMaxDamage();
1268 Entry
<< int(GetUnarmedToHitValue());
1270 Entry
<< GetUnarmedAPCost();
1271 List
.AddEntry(Entry
, LIGHT_GRAY
);
1276 Entry
= CONST_S(" kick attack");
1278 Entry
<< GetKickMinDamage() << '-' << GetKickMaxDamage();
1280 Entry
<< int(GetKickToHitValue());
1282 Entry
<< GetKickAPCost();
1283 List
.AddEntry(Entry
, LIGHT_GRAY
);
1288 Entry
= CONST_S(" bite attack");
1290 Entry
<< GetBiteMinDamage() << '-' << GetBiteMaxDamage();
1292 Entry
<< int(GetBiteToHitValue());
1294 Entry
<< GetBiteAPCost();
1295 List
.AddEntry(Entry
, LIGHT_GRAY
);
1301 void nonhumanoid::AddAttributeInfo(festring
&) const { }
1302 void nonhumanoid::AddAttackInfo(felist
&) const { }
1306 truth
elpuri::MustBeRemovedFromBone() const
1308 return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM
|| GetDungeon()->GetIndex() != ELPURI_CAVE
|| GetLevel()->GetIndex() != DARK_LEVEL
;
1311 truth
genetrixvesana::MustBeRemovedFromBone() const
1313 return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM
|| GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL
|| GetLevel()->GetIndex() != VESANA_LEVEL
;
1316 void ghost::AddName(festring
& String
, int Case
) const
1318 if(OwnerSoul
.IsEmpty() || Case
& PLURAL
)
1319 character::AddName(String
, Case
);
1322 character::AddName(String
, (Case
|ARTICLE_BIT
)&~INDEFINE_BIT
);
1323 String
<< " of " << OwnerSoul
;
1327 void ghost::Save(outputfile
& SaveFile
) const
1329 nonhumanoid::Save(SaveFile
);
1330 SaveFile
<< OwnerSoul
<< Active
;
1333 void ghost::Load(inputfile
& SaveFile
)
1335 nonhumanoid::Load(SaveFile
);
1336 SaveFile
>> OwnerSoul
>> Active
;
1339 truth
ghost::RaiseTheDead(character
* Summoner
)
1341 itemvector ItemVector
;
1342 GetStackUnder()->FillItemVector(ItemVector
);
1344 for(uInt c
= 0; c
< ItemVector
.size(); ++c
)
1345 if(ItemVector
[c
]->SuckSoul(this, Summoner
))
1349 ADD_MESSAGE("You shudder.");
1350 else if(CanBeSeenByPlayer())
1351 ADD_MESSAGE("%s shudders.", CHAR_NAME(DEFINITE
));
1356 int ghost::ReceiveBodyPartDamage(character
* Damager
, int Damage
, int Type
, int BodyPartIndex
, int Direction
, truth PenetrateResistance
, truth Critical
, truth ShowNoDamageMsg
, truth CaptureBodyPart
)
1361 return character::ReceiveBodyPartDamage(Damager
, Damage
, Type
, BodyPartIndex
, Direction
, PenetrateResistance
, Critical
, ShowNoDamageMsg
, CaptureBodyPart
);
1367 void ghost::GetAICommand()
1370 character::GetAICommand();
1373 if(CheckForEnemies(false, false, false))
1380 int largecreature::GetSquareIndex(v2 Pos
) const
1382 v2 RelativePos
= Pos
- GetPos();
1383 return RelativePos
.X
+ (RelativePos
.Y
<< 1);
1386 square
* largecreature::GetNeighbourSquare(int I
) const
1388 square
* SquareUnder
= GetSquareUnder();
1389 area
* Area
= SquareUnder
->GetArea();
1390 v2 Pos
= SquareUnder
->GetPos() + game::GetLargeMoveVector(I
);
1391 return Area
->IsValidPos(Pos
) ? SquareUnder
->GetArea()->GetSquare(Pos
) : 0;
1394 int largecreature::CalculateNewSquaresUnder(lsquare
** NewSquare
, v2 Pos
) const
1396 level
* Level
= GetLevel();
1398 for(int c
= 0; c
< 4; ++c
)
1400 v2 SquarePos
= Pos
+ game::GetLargeMoveVector(12 + c
);
1402 if(Level
->IsValidPos(SquarePos
))
1403 NewSquare
[c
] = Level
->GetLSquare(SquarePos
);
1411 truth
largecreature::IsFreeForMe(square
* Square
) const
1413 v2 Pos
= Square
->GetPos();
1414 area
* Area
= Square
->GetArea();
1416 for(int c
= 0; c
< 4; ++c
)
1418 v2 SquarePos
= Pos
+ game::GetLargeMoveVector(12 + c
);
1420 if(!Area
->IsValidPos(SquarePos
) || (Area
->GetSquare(SquarePos
)->GetCharacter() && Area
->GetSquare(SquarePos
)->GetCharacter() != static_cast<ccharacter
*>(this)))
1427 truth
largecreature::CanTheoreticallyMoveOn(const lsquare
* LSquare
) const
1429 v2 Pos
= LSquare
->GetPos();
1430 level
* Level
= LSquare
->GetLevel();
1432 for(int c
= 0; c
< 4; ++c
)
1434 v2 SquarePos
= Pos
+ game::GetLargeMoveVector(12 + c
);
1436 if(!Level
->IsValidPos(SquarePos
) || !(GetMoveType() & Level
->GetLSquare(SquarePos
)->GetTheoreticalWalkability()))
1443 truth
largecreature::CanMoveOn(const lsquare
* LSquare
) const
1445 v2 Pos
= LSquare
->GetPos();
1446 level
* Level
= LSquare
->GetLevel();
1448 for(int c
= 0; c
< 4; ++c
)
1450 v2 SquarePos
= Pos
+ game::GetLargeMoveVector(12 + c
);
1452 if(!Level
->IsValidPos(SquarePos
) || !PartCanMoveOn(Level
->GetLSquare(SquarePos
)))
1459 truth
largecreature::CanMoveOn(const square
* Square
) const
1461 v2 Pos
= Square
->GetPos();
1462 area
* Area
= Square
->GetArea();
1464 for(int c
= 0; c
< 4; ++c
)
1466 v2 SquarePos
= Pos
+ game::GetLargeMoveVector(12 + c
);
1467 if(!Area
->IsValidPos(SquarePos
) || !(GetMoveType() & Area
->GetSquare(SquarePos
)->GetSquareWalkability()))
1474 void largecreature::PutTo(v2 Pos
)
1476 for(int c
= 0; c
< 4; ++c
)
1478 SquareUnder
[c
] = game::GetCurrentArea()->GetSquare(Pos
+ game::GetLargeMoveVector(12 + c
));
1479 SquareUnder
[c
]->AddCharacter(this);
1483 void largecreature::Remove()
1485 for(int c
= 0; c
< 4; ++c
)
1487 SquareUnder
[c
]->RemoveCharacter();
1492 void largecreature::CreateCorpse(lsquare
* Square
)
1494 if(!BodyPartsDisappearWhenSevered() && !game::AllBodyPartsVanish())
1496 corpse
* Corpse
= largecorpse::Spawn(0, NO_MATERIALS
);
1497 Corpse
->SetDeceased(this);
1498 Square
->AddItem(Corpse
);
1505 void largecreature::LoadSquaresUnder()
1507 for(int c
= 0; c
< 4; ++c
)
1508 SquareUnder
[c
] = game::GetSquareInLoad()->GetArea()->GetSquare(game::GetSquareInLoad()->GetPos() + game::GetLargeMoveVector(12 + c
));
1511 truth
vladimir::MustBeRemovedFromBone() const
1513 return !IsEnabled() || GetTeam()->GetID() != IVAN_TEAM
|| GetDungeon()->GetIndex() != ELPURI_CAVE
|| GetLevel()->GetIndex() != IVAN_LEVEL
;
1516 void hattifattener::GetAICommand()
1520 if(CanBeSeenByPlayer())
1521 ADD_MESSAGE("%s emits a lightning bolt!", CHAR_DESCRIPTION(DEFINITE
));
1526 "killed by a hattifattener's lightning",
1535 GetLevel()->LightningBeam(Beam
);
1540 SeekLeader(GetLeader());
1542 if(FollowLeader(GetLeader()))
1551 void hattifattener::CreateCorpse(lsquare
* Square
)
1553 level
* Level
= Square
->GetLevel();
1554 feuLong StackSize
= Level
->AddRadiusToSquareStack(Square
->GetPos(), 9);
1555 lsquare
** SquareStack
= Level
->GetSquareStack();
1558 for(c
= 0; c
< StackSize
; ++c
)
1559 SquareStack
[c
]->RemoveFlags(IN_SQUARE_STACK
);
1561 fearray
<lsquare
*> Stack(SquareStack
, StackSize
);
1562 Level
->LightningVisualizer(Stack
, WHITE
);
1564 for(c
= 0; c
< Stack
.Size
; ++c
)
1569 CONST_S("killed by electricity released by a dying hattifattener"),
1574 Stack
[c
]->Lightning(Beam
);
1580 void hedgehog::SpecialBodyDefenceEffect(character
* Enemy
, bodypart
* BodyPart
, int Type
)
1582 if(Type
!= WEAPON_ATTACK
&& RAND() & 1)
1584 if(Enemy
->IsPlayer())
1585 ADD_MESSAGE("%s spines jab your %s!", CHAR_POSSESSIVE_PRONOUN
, BodyPart
->GetBodyPartName().CStr());
1586 else if(CanBeSeenByPlayer() || Enemy
->CanBeSeenByPlayer())
1587 ADD_MESSAGE("%s spines jab %s!", CHAR_POSSESSIVE_PRONOUN
, Enemy
->CHAR_NAME(DEFINITE
));
1589 Enemy
->ReceiveBodyPartDamage(this, 1 + (RAND() & 1), PHYSICAL_DAMAGE
, BodyPart
->GetBodyPartIndex(), YOURSELF
, false, false, true, false);
1590 Enemy
->CheckDeath(CONST_S("killed by the pointy spines of ") + GetName(INDEFINITE
), this);
1594 void genetrixvesana::Save(outputfile
& SaveFile
) const
1596 nonhumanoid::Save(SaveFile
);
1597 SaveFile
<< TurnsExisted
;
1600 void genetrixvesana::Load(inputfile
& SaveFile
)
1602 nonhumanoid::Load(SaveFile
);
1603 SaveFile
>> TurnsExisted
;
1606 truth
largecreature::CreateRoute()
1610 if(GetAttribute(INTELLIGENCE
) >= 10 && !StateIsActivated(CONFUSED
))
1612 node
* Node
= GetLevel()->FindRoute(GetPos(), GoingTo
, Illegal
, 0, this);
1617 Route
.push_back(Node
->Pos
);
1623 IntelligenceAction(5);
1630 void bunny::GetAICommand()
1632 if(GetConfig() < 4 && GetNP() > (SATIATED_LEVEL
+ BLOATED_LEVEL
) >> 1)
1634 if(CanBeSeenByPlayer())
1635 ADD_MESSAGE("%s looks more mature.", CHAR_NAME(DEFINITE
));
1637 GetTorso()->SetSize(GetTorso()->GetSize() << 1);
1638 LimitRef(StrengthExperience
*= 2, MIN_EXP
, MAX_EXP
);
1639 LimitRef(AgilityExperience
*= 2, MIN_EXP
, MAX_EXP
);
1641 for(int c
= 0; c
< BASE_ATTRIBUTES
; ++c
)
1642 BaseExperience
[c
] = Limit(BaseExperience
[c
] * 2, MIN_EXP
, MAX_EXP
);
1644 GetTorso()->GetMainMaterial()->SetVolume(GetTorso()->GetMainMaterial()->GetVolume() << 1);
1645 SetConfig(GetConfig() + 2);
1650 SeekLeader(GetLeader());
1652 if(FollowLeader(GetLeader()))
1655 if(CheckForEnemies(true, true, true))
1658 if(CheckForUsefulItemsOnGround())
1667 if(CheckForMatePartner())
1676 void bunny::SignalNaturalGeneration()
1678 character
* Partner
= bunny::Spawn(GetConfig()^1);
1679 Partner
->SetTeam(GetTeam());
1680 Partner
->SetGenerationDanger(GetGenerationDanger());
1681 Partner
->PutNear(GetPos());
1684 truth
bunny::CheckForMatePartner()
1686 if(GetConfig() == ADULT_MALE
)
1688 character
* BestPartner
= 0;
1689 double BestPartnerDanger
= 0;
1691 for(int c
= 0; c
< game::GetTeams(); ++c
)
1692 if(GetTeam()->GetRelation(game::GetTeam(c
)) != HOSTILE
)
1693 for(std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
)
1694 if((*i
)->IsEnabled() && (*i
)->IsBunny() && (*i
)->GetConfig() == ADULT_FEMALE
&& (*i
)->GetNP() > SATIATED_LEVEL
)
1696 double Danger
= (*i
)->GetRelativeDanger(this, true);
1698 if(Danger
> BestPartnerDanger
)
1701 BestPartnerDanger
= Danger
;
1705 if(BestPartner
&& !GetPos().IsAdjacent(BestPartner
->GetPos()))
1707 SetGoingTo(BestPartner
->GetPos());
1708 MoveTowardsTarget(true);
1713 if(GetConfig() == ADULT_FEMALE
&& GetNP() > NOT_HUNGER_LEVEL
+ 10000)
1715 for(int d
= 0; d
< GetNeighbourSquares(); ++d
)
1717 lsquare
* Square
= GetNeighbourLSquare(d
);
1721 character
* Father
= Square
->GetCharacter();
1723 if(Father
&& Father
->IsBunny() && Father
->GetConfig() == ADULT_MALE
&& GetRelation(Father
) != HOSTILE
)
1725 if(CanBeSeenByPlayer())
1727 if(Father
->IsPlayer())
1728 ADD_MESSAGE("You have much fun with %s.", CHAR_NAME(DEFINITE
));
1729 else if(Father
->CanBeSeenByPlayer())
1730 ADD_MESSAGE("%s and %s seem to have much fun together.", Father
->CHAR_NAME(DEFINITE
), CHAR_NAME(DEFINITE
));
1732 ADD_MESSAGE("%s seems to have much fun.", CHAR_NAME(DEFINITE
));
1736 if(Father
->IsPlayer())
1737 ADD_MESSAGE("You have much fun with something.");
1738 else if(Father
->CanBeSeenByPlayer())
1739 ADD_MESSAGE("%s seems to have much fun.", Father
->CHAR_NAME(DEFINITE
));
1742 bunny
* Baby
= bunny::Spawn(BABY_MALE
+ (RAND() & 1));
1743 Baby
->StrengthExperience
= RandomizeBabyExperience(StrengthExperience
+ static_cast<bunny
*>(Father
)->StrengthExperience
);
1744 Baby
->AgilityExperience
= RandomizeBabyExperience(AgilityExperience
+ static_cast<bunny
*>(Father
)->AgilityExperience
);
1746 if(Baby
->GetConfig() == BABY_MALE
)
1748 Baby
->StrengthExperience
*= 4;
1749 Baby
->AgilityExperience
*= 4;
1753 Baby
->StrengthExperience
*= 2;
1754 Baby
->AgilityExperience
*= 6;
1757 Baby
->StrengthExperience
/= 3;
1758 Baby
->AgilityExperience
/= 5;
1760 for(int c
= 0; c
< BASE_ATTRIBUTES
; ++c
)
1761 Baby
->BaseExperience
[c
] = RandomizeBabyExperience(BaseExperience
[c
] + static_cast<bunny
*>(Father
)->BaseExperience
[c
]);
1763 Baby
->CalculateAll();
1765 Baby
->RestoreStamina();
1766 Baby
->SetTeam(GetTeam());
1767 Baby
->SetGenerationDanger(GetGenerationDanger());
1768 Baby
->PutNear(GetPos());
1770 if(Baby
->CanBeSeenByPlayer())
1771 ADD_MESSAGE("%s is born.", Baby
->CHAR_NAME(INDEFINITE
));
1774 Father
->EditAP(-3000);
1776 EditStamina(-GetMaxStamina() >> 1, true);
1777 Father
->EditStamina(-(Father
->GetMaxStamina() << 2) / 5, true);
1787 truth
bunny::Catches(item
* Thingy
)
1789 if(Thingy
->BunnyWillCatchAndConsume(this))
1791 if(ConsumeItem(Thingy
, CONST_S("eating")))
1794 ADD_MESSAGE("You catch %s in mid-air and consume it.", Thingy
->CHAR_NAME(DEFINITE
));
1797 if(CanBeSeenByPlayer())
1798 ADD_MESSAGE("%s catches %s and eats it.", CHAR_NAME(DEFINITE
), Thingy
->CHAR_NAME(DEFINITE
));
1800 ChangeTeam(PLAYER
->GetTeam());
1804 ADD_MESSAGE("You catch %s in mid-air.", Thingy
->CHAR_NAME(DEFINITE
));
1805 else if(CanBeSeenByPlayer())
1806 ADD_MESSAGE("%s catches %s.", CHAR_NAME(DEFINITE
), Thingy
->CHAR_NAME(DEFINITE
));
1814 truth
largecreature::PlaceIsIllegal(v2 Pos
, v2 Illegal
) const
1816 for(int c
= 0; c
< 4; ++c
)
1817 if(Pos
+ game::GetLargeMoveVector(12 + c
) == Illegal
)
1823 truth
mommo::Hit(character
* Enemy
, v2 Pos
, int, int)
1825 if(CheckIfTooScaredToHit(Enemy
))
1830 if(!(Enemy
->IsMasochist() && GetRelation(Enemy
) == FRIEND
) && GetRelation(Enemy
) != HOSTILE
&& !game::TruthQuestion(CONST_S("This might cause a hostile reaction. Are you sure? [y/N]")))
1833 else if(GetAttribute(WISDOM
) >= Enemy
->GetAttackWisdomLimit())
1839 ADD_MESSAGE("You spill acidous slime at %s.", Enemy
->CHAR_DESCRIPTION(DEFINITE
));
1840 else if(Enemy
->IsPlayer() || CanBeSeenByPlayer() || Enemy
->CanBeSeenByPlayer())
1841 ADD_MESSAGE("%s spills acidous slime at %s.", CHAR_DESCRIPTION(DEFINITE
), Enemy
->CHAR_DESCRIPTION(DEFINITE
));
1843 Vomit(Pos
, 250 + RAND() % 250, false);
1848 void mommo::GetAICommand()
1850 SeekLeader(GetLeader());
1852 if(CheckForEnemies(false, false, true))
1857 VomitAtRandomDirection(350 + RAND() % 350);
1862 if(FollowLeader(GetLeader()))
1871 void dog::GetAICommand () {
1872 if (!game::IsInWilderness() && !(RAND()&7))
1873 GetLSquareUnder()->SpillFluid(this, liquid::Spawn(DOG_DROOL
, 25+RAND()%50), false, false);
1874 character::GetAICommand();
1877 truth
blinkdog::SpecialEnemySightedReaction(character
*)
1879 if(!(RAND() & 15) && SummonFriend())
1884 MonsterTeleport("playful bark");
1888 if((!(RAND() & 3) && StateIsActivated(PANIC
))
1889 || (!(RAND() & 7) && IsInBadCondition()))
1891 MonsterTeleport("frightened howl");
1898 void blinkdog::MonsterTeleport(cchar
* BarkMsg
)
1900 if(CanBeSeenByPlayer())
1901 ADD_MESSAGE("You hear a %s inside your head as %s vanishes!", BarkMsg
, CHAR_NAME(DEFINITE
));
1903 ADD_MESSAGE("You hear a %s inside your head.", BarkMsg
);
1906 rect
Border(Pos
+ v2(-5, -5), Pos
+ v2(5, 5));
1907 Pos
= GetLevel()->GetRandomSquare(this, 0, &Border
);
1910 Pos
= GetLevel()->GetRandomSquare(this);
1914 if(CanBeSeenByPlayer())
1915 ADD_MESSAGE("%s materializes from nowhere!", CHAR_NAME(INDEFINITE
));
1920 int unicorn::TakeHit(character
* Enemy
, item
* Weapon
, bodypart
* EnemyBodyPart
, v2 HitPos
, double Damage
, double ToHitValue
, int Success
, int Type
, int Direction
, truth Critical
, truth ForceHit
)
1922 int Return
= nonhumanoid::TakeHit(Enemy
, Weapon
, EnemyBodyPart
, HitPos
, Damage
, ToHitValue
, Success
, Type
, Direction
, Critical
, ForceHit
);
1924 if(Return
!= HAS_DIED
1925 && (StateIsActivated(PANIC
)
1926 || (RAND() & 1 && IsInBadCondition())
1928 MonsterTeleport("neighs in terror");
1933 int blinkdog::TakeHit(character
* Enemy
, item
* Weapon
, bodypart
* EnemyBodyPart
, v2 HitPos
, double Damage
, double ToHitValue
, int Success
, int Type
, int Direction
, truth Critical
, truth ForceHit
)
1935 int Return
= nonhumanoid::TakeHit(Enemy
, Weapon
, EnemyBodyPart
, HitPos
, Damage
, ToHitValue
, Success
, Type
, Direction
, Critical
, ForceHit
);
1937 if(Return
!= HAS_DIED
)
1939 if(!(RAND() & 15) && SummonFriend())
1942 if((RAND() & 1 && StateIsActivated(PANIC
))
1943 || (!(RAND() & 3) && IsInBadCondition())
1945 MonsterTeleport("terrified yelp");
1951 void unicorn::MonsterTeleport(cchar
* NeighMsg
)
1953 if(CanBeSeenByPlayer())
1954 ADD_MESSAGE("%s %s and disappears!", CHAR_NAME(DEFINITE
), NeighMsg
);
1956 Move(GetLevel()->GetRandomSquare(this), true);
1958 if(CanBeSeenByPlayer())
1959 ADD_MESSAGE("Suddenly %s appears from nothing!", CHAR_NAME(INDEFINITE
));
1964 truth
blinkdog::SummonFriend()
1970 blinkdog
* Buddy
= blinkdog::Spawn();
1971 Buddy
->SummonModifier
= SummonModifier
;
1972 Buddy
->SetTeam(GetTeam());
1973 Buddy
->SetGenerationDanger(GetGenerationDanger());
1974 Buddy
->PutNear(GetPos());
1976 if(CanBeSeenByPlayer())
1978 ADD_MESSAGE("%s wags its tail in a mysterious pattern.", CHAR_NAME(DEFINITE
));
1980 if(Buddy
->CanBeSeenByPlayer())
1981 ADD_MESSAGE("Another of its kin appears!");
1983 else if(Buddy
->CanBeSeenByPlayer())
1984 ADD_MESSAGE("%s appears!", Buddy
->CHAR_NAME(INDEFINITE
));
1990 blinkdog::blinkdog()
1992 if(!game::IsLoading())
1993 SummonModifier
= RAND_2
+ RAND_2
+ RAND_2
+ RAND_2
+ RAND_2
+ RAND_2
+ RAND_2
;
1996 void blinkdog::Save(outputfile
& SaveFile
) const
1998 dog::Save(SaveFile
);
1999 SaveFile
<< SummonModifier
;
2002 void blinkdog::Load(inputfile
& SaveFile
)
2004 dog::Load(SaveFile
);
2005 SaveFile
>> SummonModifier
;
2008 void genetrixvesana::FinalProcessForBone()
2010 largecreature::FinalProcessForBone();
2014 void carnivorousplant::GetAICommand()
2016 SeekLeader(GetLeader());
2018 if(FollowLeader(GetLeader()))
2021 if(AttackAdjacentEnemyAI())
2024 if(CheckForUsefulItemsOnGround())
2033 void mysticfrog::GetAICommand()
2035 SeekLeader(GetLeader());
2037 if(FollowLeader(GetLeader()))
2040 character
* NearestEnemy
= 0;
2041 sLong NearestEnemyDistance
= 0x7FFFFFFF;
2042 character
* RandomFriend
= 0;
2043 charactervector Friend
;
2045 truth Enemies
= false;
2047 for(int c
= 0; c
< game::GetTeams(); ++c
)
2049 if(GetTeam()->GetRelation(game::GetTeam(c
)) == HOSTILE
)
2051 for(std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
)
2052 if((*i
)->IsEnabled())
2055 sLong ThisDistance
= Max
<sLong
>(abs((*i
)->GetPos().X
- Pos
.X
), abs((*i
)->GetPos().Y
- Pos
.Y
));
2057 if((ThisDistance
< NearestEnemyDistance
|| (ThisDistance
== NearestEnemyDistance
&& !(RAND() % 3))) && (*i
)->CanBeSeenBy(this))
2060 NearestEnemyDistance
= ThisDistance
;
2064 else if(GetTeam()->GetRelation(game::GetTeam(c
)) == FRIEND
)
2066 for(std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
)
2067 if((*i
)->IsEnabled() && (*i
)->CanBeSeenBy(this))
2068 Friend
.push_back(*i
);
2072 if(NearestEnemy
&& NearestEnemy
->GetPos().IsAdjacent(Pos
))
2074 if(NearestEnemy
->IsSmall()
2075 && GetAttribute(WISDOM
) < NearestEnemy
->GetAttackWisdomLimit()
2077 && Hit(NearestEnemy
, NearestEnemy
->GetPos(), game::GetDirectionForVector(NearestEnemy
->GetPos() - GetPos())))
2079 else if(!(RAND() & 3))
2081 if(CanBeSeenByPlayer())
2082 ADD_MESSAGE("%s invokes a spell and disappears.", CHAR_NAME(DEFINITE
));
2084 TeleportRandomly(true);
2085 EditAP(-GetSpellAPCost());
2090 if(NearestEnemy
&& (NearestEnemyDistance
< 10 || StateIsActivated(PANIC
)) && RAND() & 3)
2092 SetGoingTo((Pos
<< 1) - NearestEnemy
->GetPos());
2094 if(MoveTowardsTarget(true))
2098 if(Friend
.size() && !(RAND() & 3))
2100 RandomFriend
= Friend
[RAND() % Friend
.size()];
2104 if(GetRelation(PLAYER
) == HOSTILE
&& PLAYER
->CanBeSeenBy(this) && !RAND_4
)
2105 NearestEnemy
= PLAYER
;
2110 CONST_S("killed by the spells of ") + GetName(INDEFINITE
),
2117 lsquare
* Square
= NearestEnemy
->GetLSquareUnder();
2118 EditAP(-GetSpellAPCost());
2120 if(CanBeSeenByPlayer())
2121 ADD_MESSAGE("%s invokes a spell!", CHAR_NAME(DEFINITE
));
2130 case 5: Square
->DrawParticles(RED
); if(NearestEnemy
->TeleportRandomItem(GetConfig() == DARK
)) break;
2135 case 10: Square
->DrawParticles(RED
); Square
->Teleport(Beam
); break;
2139 case 14: Square
->DrawParticles(RED
); Square
->Slow(Beam
); break;
2140 case 15: Square
->DrawParticles(RED
); Square
->LowerEnchantment(Beam
); break;
2141 default: Square
->DrawLightning(v2(8, 8), WHITE
, YOURSELF
); Square
->Lightning(Beam
); break;
2144 if(CanBeSeenByPlayer())
2145 NearestEnemy
->DeActivateVoluntaryAction(CONST_S("The spell of ") + GetName(DEFINITE
) + CONST_S(" interrupts you."));
2147 NearestEnemy
->DeActivateVoluntaryAction(CONST_S("The spell interrupts you."));
2152 if(RandomFriend
&& Enemies
)
2154 lsquare
* Square
= RandomFriend
->GetLSquareUnder();
2155 EditAP(-GetSpellAPCost());
2156 Square
->DrawParticles(RED
);
2159 Square
->Invisibility(Beam
);
2161 Square
->Haste(Beam
);
2169 truth
largecreature::PartCanMoveOn(const lsquare
* LSquare
) const
2171 int Walkability
= LSquare
->GetWalkability();
2173 if(GetMoveType() & Walkability
)
2176 if(DestroysWalls() && Walkability
& ETHEREAL
)
2178 olterrain
* Terrain
= LSquare
->GetOLTerrain();
2180 if(Terrain
&& Terrain
->WillBeDestroyedBy(this))
2182 room
* Room
= LSquare
->GetRoom();
2184 if(!Room
|| Room
->IsOKToDestroyWalls(this))
2192 void spider::GetAICommand()
2194 SeekLeader(GetLeader());
2196 if(FollowLeader(GetLeader()))
2199 character
* NearestChar
= 0;
2200 sLong NearestDistance
= 0x7FFFFFFF;
2204 for(int c
= 0; c
< game::GetTeams(); ++c
)
2205 if(GetTeam()->GetRelation(game::GetTeam(c
)) == HOSTILE
)
2206 for(std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin();
2207 i
!= game::GetTeam(c
)->GetMember().end(); ++i
)
2208 if((*i
)->IsEnabled() && GetAttribute(WISDOM
) < (*i
)->GetAttackWisdomLimit())
2210 sLong ThisDistance
= Max
<sLong
>(abs((*i
)->GetPos().X
- Pos
.X
), abs((*i
)->GetPos().Y
- Pos
.Y
));
2213 if((ThisDistance
< NearestDistance
2214 || (ThisDistance
== NearestDistance
&& !(RAND() % 3)))
2215 && (*i
)->CanBeSeenBy(this, false, IsGoingSomeWhere())
2216 && (!IsGoingSomeWhere() || HasClearRouteTo((*i
)->GetPos())))
2219 NearestDistance
= ThisDistance
;
2223 if(Hostiles
&& !RAND_N(Max(80 / Hostiles
, 8)))
2225 web
* Web
= web::Spawn();
2226 Web
->SetStrength(GetConfig() == LARGE
? 10 : 25);
2228 if(GetLSquareUnder()->AddTrap(Web
))
2230 if(CanBeSeenByPlayer())
2231 ADD_MESSAGE("%s spins a web.", CHAR_NAME(DEFINITE
));
2240 if(NearestChar
->IsStuck())
2241 SetGoingTo(NearestChar
->GetPos());
2243 SetGoingTo((Pos
<< 1) - NearestChar
->GetPos());
2245 if(MoveTowardsTarget(true))
2255 void largecat::Save(outputfile
& SaveFile
) const
2257 nonhumanoid::Save(SaveFile
);
2261 void largecat::Load(inputfile
& SaveFile
)
2263 nonhumanoid::Load(SaveFile
);
2267 truth
largecat::SpecialSaveLife()
2269 if(--Lives
<= 0 || game::IsInWilderness())
2273 ADD_MESSAGE("But wait! You seem to have miraculously avoided certain death!");
2274 else if(CanBeSeenByPlayer())
2275 ADD_MESSAGE("But wait, %s seems to have miraculously avoided certain death!", GetPersonalPronoun().CStr());
2278 rect
Border(Pos
+ v2(-20, -20), Pos
+ v2(20, 20));
2279 Pos
= GetLevel()->GetRandomSquare(this, 0, &Border
);
2282 Pos
= GetLevel()->GetRandomSquare(this);
2286 if(!IsPlayer() && CanBeSeenByPlayer())
2287 ADD_MESSAGE("%s appears!", CHAR_NAME(INDEFINITE
));
2290 game::AskForEscPress(CONST_S("Life saved!"));
2298 if(GetNP() < SATIATED_LEVEL
)
2299 SetNP(SATIATED_LEVEL
);
2301 SendNewDrawRequest();
2304 GetAction()->Terminate(false);
2309 truth
lobhse::MustBeRemovedFromBone() const
2311 return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM
|| GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL
|| GetLevel()->GetIndex() != SPIDER_LEVEL
;
2314 truth
lobhse::SpecialBiteEffect(character
* Char
, v2
, int, int, truth BlockedByArmour
)
2316 if(!BlockedByArmour
)
2318 Char
->BeginTemporaryState(POISONED
, 80 + RAND() % 40);
2325 void lobhse::GetAICommand()
2333 void lobhse::CreateCorpse(lsquare
* Square
)
2335 largecreature::CreateCorpse(Square
);
2338 void mindworm::GetAICommand()
2340 character
* NeighbourEnemy
= GetRandomNeighbour(HOSTILE
);
2343 if(NeighbourEnemy
&& NeighbourEnemy
->IsHumanoid() && NeighbourEnemy
->HasHead()
2344 && !NeighbourEnemy
->IsInfectedByMindWorm())
2346 TryToImplantLarvae(NeighbourEnemy
);
2350 character
* NearestEnemy
= GetNearestEnemy();
2354 PsiAttack(NearestEnemy
);
2364 void mindworm::TryToImplantLarvae(character
* Victim
)
2366 if(Victim
->MindWormCanPenetrateSkull(this))
2368 Victim
->SetCounterToMindWormHatch(100);
2369 if(Victim
->IsPlayer())
2371 ADD_MESSAGE("%s penetrates digs through your skull, lays %s eggs and jumps out.",
2372 CHAR_NAME(DEFINITE
), CHAR_POSSESSIVE_PRONOUN
);
2374 else if(Victim
->CanBeSeenByPlayer())
2376 ADD_MESSAGE("%s penetrates digs through %s's skull, lays %s eggs and jumps out.",
2377 CHAR_NAME(DEFINITE
), Victim
->CHAR_NAME(DEFINITE
), CHAR_POSSESSIVE_PRONOUN
);
2383 void mindworm::PsiAttack(character
* Victim
)
2385 if(Victim
->IsPlayer())
2387 ADD_MESSAGE("Your brain is on fire.");
2389 else if(Victim
->CanBeSeenByPlayer() && PLAYER
->GetAttribute(PERCEPTION
) > RAND_N(20))
2391 ADD_MESSAGE("%s looks scared.", Victim
->CHAR_NAME(DEFINITE
));
2395 Victim
->ReceiveDamage(this, 1 + RAND_N(5), PSI
, ALL
, 8, false, false, false, false);
2396 Victim
->CheckDeath(CONST_S("killed by ") + GetName(INDEFINITE
) + "'s psi attack", this);
2400 ////////////////////////////////////////////////////////////////////////////////
2401 bodypart
*menatrixfusanga::MakeBodyPart (int) const { return menatrixtorso::Spawn(0, NO_MATERIALS
); }
2402 col16
menatrixfusanga::GetSkinColor () const { return MakeRGB16(60+RAND()%190, 60+RAND()%190, 60+RAND()%190); }
2405 void menatrixfusanga::GetAICommand () {
2408 SeekLeader(GetLeader());
2409 if (FollowLeader(GetLeader())) return;
2411 int NumberOfPlants
= RAND()%3+RAND()%3+RAND()%3+RAND()%3;
2412 for (int c1
= 0; c1
< 50 && NumberOfPlants
; ++c1
) {
2413 for (int c2
= 0; c2
< game::GetTeams() && NumberOfPlants
; ++c2
) {
2414 if (GetTeam()->GetRelation(game::GetTeam(c2
)) == HOSTILE
) {
2415 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c2
)->GetMember().begin(); i
!= game::GetTeam(c2
)->GetMember().end() && NumberOfPlants
; ++i
) {
2416 if ((*i
)->IsEnabled()) {
2417 lsquare
*LSquare
= (*i
)->GetNeighbourLSquare(RAND()%GetNeighbourSquares());
2418 if (LSquare
&& (LSquare
->GetWalkability() & WALK
) && !LSquare
->GetCharacter()) {
2419 character
*NewPlant
;
2420 sLong RandomValue
= RAND()%TurnsExisted
;
2421 if (RandomValue
< 250) NewPlant
= mushroom::Spawn();
2422 else if (RandomValue
< 1500) NewPlant
= magicmushroom::Spawn();
2423 else NewPlant
= magicmushroom::Spawn();
2424 for (int c
= 3; c
< TurnsExisted
/ 500; ++c
) NewPlant
->EditAllAttributes(1);
2425 NewPlant
->SetGenerationDanger(GetGenerationDanger());
2426 NewPlant
->SetTeam(GetTeam());
2427 NewPlant
->PutTo(LSquare
->GetPos());
2429 if (NewPlant
->CanBeSeenByPlayer()) {
2430 if ((*i
)->IsPlayer()) ADD_MESSAGE("%s sprouts from the ground near you.", NewPlant
->CHAR_NAME(INDEFINITE
));
2431 else if ((*i
)->CanBeSeenByPlayer()) ADD_MESSAGE("%s sprouts from the ground near %s.", NewPlant
->CHAR_NAME(INDEFINITE
), (*i
)->CHAR_NAME(DEFINITE
));
2432 else ADD_MESSAGE("%s sprouts from the ground.", NewPlant
->CHAR_NAME(INDEFINITE
));
2439 SeekLeader(GetLeader());
2440 if (FollowLeader(GetLeader())) return;
2441 lsquare
*CradleSquare
= GetNeighbourLSquare(RAND()%8);
2442 if (CradleSquare
&& !CradleSquare
->GetCharacter() && (CradleSquare
->GetWalkability() & WALK
)) {
2443 int SpoiledItems
= 0;
2444 int MushroomsNear
= 0;
2445 for (int d
= 0; d
< 8; ++d
) {
2446 lsquare
*Square
= CradleSquare
->GetNeighbourLSquare(d
);
2448 character
*Char
= Square
->GetCharacter();
2449 if (Char
&& Char
->IsMushroom()) ++MushroomsNear
;
2450 SpoiledItems
+= Square
->GetSpoiledItems();
2453 if ((SpoiledItems
&& MushroomsNear
< 1 && !RAND_N(2)) || (MushroomsNear
< 3 && !RAND_N((1+MushroomsNear
)*2))) {
2454 magicmushroom
*Child
= magicmushroom::Spawn(GetConfig());
2456 case 0: SetSpecies(MakeRGB16(125+RAND()%125, RAND()%100, RAND()%100)); break;
2457 case 1: SetSpecies(MakeRGB16(RAND()%100, 125+RAND()%125, RAND()%100)); break;
2458 case 2: SetSpecies(MakeRGB16(RAND()%100, RAND()%100, 125+RAND()%125)); break;
2460 Child
->SetSpecies(Species
);
2461 Child
->SetTeam(GetTeam());
2462 Child
->SetGenerationDanger(GetGenerationDanger());
2463 Child
->PutTo(CradleSquare
->GetPos());
2464 if (Child
->CanBeSeenByPlayer()) ADD_MESSAGE("%s pops out from the ground.", Child
->CHAR_NAME(INDEFINITE
));
2467 if (AttackAdjacentEnemyAI()) return;
2468 if (MoveRandomly()) return;
2474 if (AttackAdjacentEnemyAI()) return;
2475 if (MoveRandomly()) return;
2480 void menatrixfusanga::SetSpecies (int What
) {
2486 void menatrixfusanga::CreateCorpse (lsquare
*Square
) {
2487 for (int c
= 0; c
< 1; ++c
) Square
->AddItem(wand::Spawn(WAND_OF_CLONING
));
2488 Square
->AddItem(wand::Spawn(WAND_OF_MIRRORING
));
2489 Square
->AddItem(solstone::Spawn());
2490 largecreature::CreateCorpse(Square
);
2494 void menatrixfusanga::Save (outputfile
&SaveFile
) const {
2495 nonhumanoid::Save(SaveFile
);
2496 SaveFile
<< TurnsExisted
;
2497 SaveFile
<< Species
;
2501 void menatrixfusanga::Load (inputfile
&SaveFile
) {
2502 nonhumanoid::Load(SaveFile
);
2503 SaveFile
>> TurnsExisted
;
2504 SaveFile
>> Species
;
2508 truth
menatrixfusanga::MustBeRemovedFromBone () const {
2509 return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM
|| GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL
|| GetLevel()->GetIndex() != VESANA_LEVEL
;
2513 void menatrixfusanga::FinalProcessForBone () {
2514 largecreature::FinalProcessForBone();
2519 void solicitus::BeTalkedTo () {
2520 if (GetRelation(PLAYER
) == HOSTILE
) {
2521 ADD_MESSAGE("Oh no. Now is for the figthing!!!");
2525 if (PLAYER
->StateIsActivated(PANIC
) && !game::PlayerIsSolicitusChampion()) {
2526 ADD_MESSAGE("Solicitus perks up. \"Well hullo there mortal! Would you care to be my Champion? I'll give you a free copy of my celestial monograph on Atheism!\"");
2527 if (game::TruthQuestion(CONST_S("Do you choose to become the Champion of Solicitus? [y/n]"), REQUIRES_ANSWER
)) {
2528 game::TextScreen(CONST_S(
2529 "Solicitus speaks:\n"
2530 "\"Becoming my champion involves my changing your sweat material into pure liquified fear.\"\n"
2531 "\"Now, hold still while I administer to your body what powers I have left!\"\n"));
2532 game::TextScreen(CONST_S("You feel Solicitus changing your sweat glands. It feels disgusting."));
2533 game::MakePlayerSolicitusChampion();
2534 PLAYER
->EditCurrentSweatMaterial(LIQUID_HORROR
);
2535 (celestialmonograph::Spawn())->MoveTo(PLAYER
->GetStack());
2536 //pantheonbook* NewBook = pantheonbook::Spawn();
2537 //AddPlace->AddItem(NewBook);
2538 ADD_MESSAGE("\"Go forth, you are anointed! And here's your personal copy of my monograph, mortal. Enjoy!\"");
2539 GetArea()->SendNewDrawRequest();
2541 ADD_MESSAGE("\"Not a problem, perhaps another time. It's not for everyone, you know.\"");
2544 } else if (PLAYER
->StateIsActivated(PANIC
) && game::PlayerIsSolicitusChampion()) {
2545 ADD_MESSAGE("\"I suppose you want to hear my life story?\"");
2547 ADD_MESSAGE("\"Maybe you should empathise with my situation first. Go drink some liquified fear and then we'll talk.\"");
2552 void solicitus::GetAICommand () {
2553 if (MoveRandomly()) return;
2559 void solicitus::CreateCorpse (lsquare
*Square
) {
2560 //for(int c = 0; c < 3; ++c) Square->AddItem(pineapple::Spawn());
2561 //largecreature::CreateCorpse(Square);
2562 ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\"");
2563 game::GetCurrentLevel()->Explosion(this, CONST_S("killed by an explosion of the toppled-god Solicitus"), PLAYER
->GetPos(), 1300>>3, false);
2568 truth
solicitus::MustBeRemovedFromBone () const {
2569 return !IsEnabled() || GetTeam()->GetID() != MONSTER_TEAM
|| GetDungeon()->GetIndex() != UNDER_WATER_TUNNEL
|| GetLevel()->GetIndex() != VESANA_LEVEL
;
2573 void solicitus::Save (outputfile
&SaveFile
) const {
2574 nonhumanoid::Save(SaveFile
);
2578 void solicitus::Load (inputfile
&SaveFile
) {
2579 nonhumanoid::Load(SaveFile
);
2583 void solicitus::FinalProcessForBone () {
2584 largecreature::FinalProcessForBone();
2589 col16
noxiousorchid::GetTorsoSpecialColor() const {
2590 if (!GetConfig()) return MakeRGB16(125+RAND()%100, RAND()%125, RAND()%100);
2591 if (GetConfig() == GREATER
) return MakeRGB16(100+RAND()%100, RAND()%100, 155+RAND()%100);
2593 return MakeRGB16(200+RAND()%55, RAND()%60, 150);
2596 void noxiousorchid::PostConstruct () {
2597 //GetTorso()->GetMainMaterial()->SetSpoilCounter(200+RAND_N(100));
2600 void noxiousorchid::CreateCorpse (lsquare
*Square
) {
2601 //int Amount = !GetConfig() ? (RAND() % 7 ? 0 : 1) : GetConfig() == GREATER ? (RAND() & 1 ? 0 : (RAND() % 5 ? 1 : (RAND() % 5 ? 2 : 3))) : (!(RAND() % 3) ? 0 : (RAND() % 3 ? 1 : (RAND() % 3 ? 2 : 3)));
2602 //for(int c = 0; c < Amount; ++c) Square->AddItem(kiwi::Spawn());
2603 nonhumanoid::CreateCorpse(Square
);
2606 truth
noxiousorchid::Hit (character
*Enemy
, v2 HitPos
, int Direction
, int Flags
) {
2608 liquid
*Fluid
= 0; // = liquid::Spawn(WATER, 25+RAND()%25);
2611 ADD_MESSAGE("You hit %s.", Enemy
->CHAR_DESCRIPTION(DEFINITE
));
2612 } else if (Enemy
->IsPlayer() || CanBeSeenByPlayer() || Enemy
->CanBeSeenByPlayer()) {
2613 ADD_MESSAGE("%s hits %s.", CHAR_DESCRIPTION(DEFINITE
), Enemy
->CHAR_DESCRIPTION(DEFINITE
));
2616 switch (GetConfig()) {
2618 switch (RAND() % 48) {
2619 case 0: Fluid
= liquid::Spawn(ANTIDOTE_LIQUID
, 15+RAND()%25); break;
2620 case 1: case 2: case 3: case 4: Fluid
= liquid::Spawn(POISON_LIQUID
, 15+RAND()%25); break;
2621 case 5: Fluid
= liquid::Spawn(LIQUID_HORROR
, 15+RAND()%25); break;
2622 case 6: case 7: case 8: Fluid
= liquid::Spawn(SULPHURIC_ACID
, 15+RAND()%25); break;
2627 switch (RAND()%24) {
2628 case 0: Fluid
= liquid::Spawn(ANTIDOTE_LIQUID
, 25+RAND()%25); break;
2629 case 1: case 2: case 3: case 4: Fluid
= liquid::Spawn(POISON_LIQUID
, 25+RAND()%25); break;
2630 case 5: Fluid
= liquid::Spawn(LIQUID_HORROR
, 25+RAND()%25); break;
2631 case 6: case 7: case 8: Fluid
= liquid::Spawn(SULPHURIC_ACID
, 25+RAND()%25); break;
2636 switch (RAND()%24) {
2637 case 0: Fluid
= liquid::Spawn(ANTIDOTE_LIQUID
, 50+RAND()%50); break;
2638 case 1: Fluid
= liquid::Spawn(YELLOW_SLIME
, 50+RAND()%50); break;
2639 case 2: case 3: case 4: Fluid
= liquid::Spawn(POISON_LIQUID
, 50+RAND()%50); break;
2640 case 5: Fluid
= liquid::Spawn(LIQUID_HORROR
, 50+RAND()%50); break;
2641 case 6: case 7: case 8: Fluid
= liquid::Spawn(SULPHURIC_ACID
, 50+RAND()%50); break;
2642 case 9: Fluid
= liquid::Spawn(MUSTARD_GAS_LIQUID
, 50+RAND()%50); break;
2646 default: break; //Fluid = liquid::Spawn(WATER, 25+RAND()%25); break;
2650 Enemy
->SpillFluid(Enemy
, Fluid
);
2652 ADD_MESSAGE("You spill %s on %s.", Fluid
->GetName(false, false).CStr(), Enemy
->CHAR_DESCRIPTION(DEFINITE
));
2653 } else if (Enemy
->IsPlayer() || CanBeSeenByPlayer() || Enemy
->CanBeSeenByPlayer()) {
2654 ADD_MESSAGE("%s spills %s on %s.", CHAR_DESCRIPTION(DEFINITE
), Fluid
->GetName(false, false).CStr(), Enemy
->CHAR_DESCRIPTION(DEFINITE
));
2657 } else if (nonhumanoid::Hit(Enemy
, HitPos
, Direction
, Flags
)) {
2659 } else if (IsPlayer()) {
2660 ADD_MESSAGE("You miss %s.", Enemy
->CHAR_DESCRIPTION(DEFINITE
));
2666 //k8: plant? 'follow leader'? pick up items? move? wow!
2667 void noxiousorchid::GetAICommand () {
2668 SeekLeader(GetLeader());
2669 if (FollowLeader(GetLeader())) return;
2670 if (AttackAdjacentEnemyAI()) return;
2671 if (CheckForUsefulItemsOnGround()) return;
2672 if (MoveRandomly()) return;