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
12 /* Compiled through charset.cpp */
15 #include <sys/types.h>
19 /* These statedata structs contain functions and values used for handling
20 * states. Remember to update them. All normal states must have
21 * PrintBeginMessage and PrintEndMessage functions and a Description string.
22 * BeginHandler, EndHandler, Handler (called each tick) and IsAllowed are
23 * optional, enter zero if the state doesn't need one. If the SECRET flag
24 * is set, Description is not shown in the panel without magical means.
25 * You can also set some source (SRC_*) and duration (DUR_*) flags, which
26 * control whether the state can be randomly activated in certain situations.
27 * These flags can be found in ivandef.h. RANDOMIZABLE sets all source
28 * & duration flags at once. */
30 const char *Description
;
32 void (character::*PrintBeginMessage
) () const;
33 void (character::*PrintEndMessage
) () const;
34 void (character::*BeginHandler
) ();
35 void (character::*EndHandler
) ();
36 void (character::*Handler
) ();
37 truth (character::*IsAllowed
) () const;
38 void (character::*SituationDangerModifier
) (double &) const;
42 const statedata StateData
[STATES
] =
50 &character::EndPolymorph
,
56 RANDOMIZABLE
&~(SRC_MUSHROOM
|SRC_EVIL
),
57 &character::PrintBeginHasteMessage
,
58 &character::PrintEndHasteMessage
,
66 RANDOMIZABLE
&~SRC_GOOD
,
67 &character::PrintBeginSlowMessage
,
68 &character::PrintEndSlowMessage
,
76 RANDOMIZABLE
&~(SRC_MUSHROOM
|SRC_EVIL
|SRC_GOOD
),
77 &character::PrintBeginPolymorphControlMessage
,
78 &character::PrintEndPolymorphControlMessage
,
87 &character::PrintBeginLifeSaveMessage
,
88 &character::PrintEndLifeSaveMessage
,
96 SECRET
|SRC_FOUNTAIN
|SRC_CONFUSE_READ
|DUR_FLAGS
,
97 &character::PrintBeginLycanthropyMessage
,
98 &character::PrintEndLycanthropyMessage
,
101 &character::LycanthropyHandler
,
103 &character::LycanthropySituationDangerModifier
106 RANDOMIZABLE
&~(SRC_MUSHROOM
|SRC_EVIL
),
107 &character::PrintBeginInvisibilityMessage
,
108 &character::PrintEndInvisibilityMessage
,
109 &character::BeginInvisibility
, &character::EndInvisibility
,
115 RANDOMIZABLE
&~(SRC_MUSHROOM
|SRC_EVIL
),
116 &character::PrintBeginInfraVisionMessage
,
117 &character::PrintEndInfraVisionMessage
,
118 &character::BeginInfraVision
,
119 &character::EndInfraVision
,
125 RANDOMIZABLE
&~SRC_EVIL
,
126 &character::PrintBeginESPMessage
,
127 &character::PrintEndESPMessage
,
128 &character::BeginESP
,
136 &character::PrintBeginPoisonedMessage
,
137 &character::PrintEndPoisonedMessage
,
140 &character::PoisonedHandler
,
141 &character::CanBePoisoned
,
142 &character::PoisonedSituationDangerModifier
145 SECRET
|(RANDOMIZABLE
&~(SRC_MUSHROOM
|SRC_GOOD
)),
146 &character::PrintBeginTeleportMessage
,
147 &character::PrintEndTeleportMessage
,
150 &character::TeleportHandler
,
155 SECRET
|(RANDOMIZABLE
&~(SRC_MUSHROOM
|SRC_GOOD
)),
156 &character::PrintBeginPolymorphMessage
,
157 &character::PrintEndPolymorphMessage
,
160 &character::PolymorphHandler
,
162 &character::PolymorphingSituationDangerModifier
165 RANDOMIZABLE
&~(SRC_MUSHROOM
|SRC_EVIL
),
166 &character::PrintBeginTeleportControlMessage
,
167 &character::PrintEndTeleportControlMessage
,
176 &character::PrintBeginPanicMessage
,
177 &character::PrintEndPanicMessage
,
178 &character::BeginPanic
,
179 &character::EndPanic
,
181 &character::CanPanic
,
182 &character::PanicSituationDangerModifier
185 SECRET
|(RANDOMIZABLE
&~(DUR_PERMANENT
|SRC_GOOD
)),
186 &character::PrintBeginConfuseMessage
,
187 &character::PrintEndConfuseMessage
,
191 &character::CanBeConfused
,
192 &character::ConfusedSituationDangerModifier
195 SECRET
|(RANDOMIZABLE
&~DUR_TEMPORARY
),
196 &character::PrintBeginParasitizedMessage
,
197 &character::PrintEndParasitizedMessage
,
200 &character::ParasitizedHandler
,
201 &character::CanBeParasitized
,
202 &character::ParasitizedSituationDangerModifier
206 &character::PrintBeginSearchingMessage
,
207 &character::PrintEndSearchingMessage
,
210 &character::SearchingHandler
,
215 SECRET
|(RANDOMIZABLE
&~(SRC_GOOD
|SRC_EVIL
)),
216 &character::PrintBeginGasImmunityMessage
,
217 &character::PrintEndGasImmunityMessage
,
225 RANDOMIZABLE
&~SRC_EVIL
,
226 &character::PrintBeginLevitationMessage
,
227 &character::PrintEndLevitationMessage
,
229 &character::EndLevitation
,
235 SECRET
|(RANDOMIZABLE
&~DUR_TEMPORARY
),
236 &character::PrintBeginLeprosyMessage
,
237 &character::PrintEndLeprosyMessage
,
238 &character::BeginLeprosy
,
239 &character::EndLeprosy
,
240 &character::LeprosyHandler
,
242 &character::LeprosySituationDangerModifier
245 SRC_FOUNTAIN
|SRC_CONFUSE_READ
|DUR_FLAGS
,
246 &character::PrintBeginHiccupsMessage
,
247 &character::PrintEndHiccupsMessage
,
250 &character::HiccupsHandler
,
252 &character::HiccupsSituationDangerModifier
257 characterprototype::characterprototype (const characterprototype
*Base
, characterspawner Spawner
,
258 charactercloner Cloner
, cchar
*ClassID
)
264 Index
= protocontainer
<character
>::Add(this);
268 std::list
<character
*>::iterator
character::GetTeamIterator () { return TeamIterator
; }
269 void character::SetTeamIterator (std::list
<character
*>::iterator What
) { TeamIterator
= What
; }
270 void character::CreateInitialEquipment (int SpecialFlags
) { AddToInventory(DataBase
->Inventory
, SpecialFlags
); }
271 void character::EditAP (sLong What
) { AP
= Limit
<sLong
>(AP
+What
, -12000, 1200); }
272 int character::GetRandomStepperBodyPart () const { return TORSO_INDEX
; }
273 void character::GainIntrinsic (sLong What
) { BeginTemporaryState(What
, PERMANENT
); }
274 truth
character::IsUsingArms () const { return GetAttackStyle() & USE_ARMS
; }
275 truth
character::IsUsingLegs () const { return GetAttackStyle() & USE_LEGS
; }
276 truth
character::IsUsingHead () const { return GetAttackStyle() & USE_HEAD
; }
277 void character::CalculateAllowedWeaponSkillCategories () { AllowedWeaponSkillCategories
= MARTIAL_SKILL_CATEGORIES
; }
278 festring
character::GetBeVerb () const { return IsPlayer() ? CONST_S("are") : CONST_S("is"); }
279 void character::SetEndurance (int What
) { BaseExperience
[ENDURANCE
] = What
* EXP_MULTIPLIER
; }
280 void character::SetPerception (int What
) { BaseExperience
[PERCEPTION
] = What
* EXP_MULTIPLIER
; }
281 void character::SetIntelligence (int What
) { BaseExperience
[INTELLIGENCE
] = What
* EXP_MULTIPLIER
; }
282 void character::SetWisdom (int What
) { BaseExperience
[WISDOM
] = What
* EXP_MULTIPLIER
; }
283 void character::SetWillPower (int What
) { BaseExperience
[WILL_POWER
] = What
* EXP_MULTIPLIER
; }
284 void character::SetCharisma (int What
) { BaseExperience
[CHARISMA
] = What
* EXP_MULTIPLIER
; }
285 void character::SetMana (int What
) { BaseExperience
[MANA
] = What
* EXP_MULTIPLIER
; }
286 truth
character::IsOnGround () const { return MotherEntity
&& MotherEntity
->IsOnGround(); }
287 truth
character::LeftOversAreUnique () const { return GetArticleMode() || AssignedName
.GetSize(); }
288 truth
character::HomeDataIsValid () const { return (HomeData
&& HomeData
->Level
== GetLSquareUnder()->GetLevelIndex() && HomeData
->Dungeon
== GetLSquareUnder()->GetDungeonIndex()); }
289 void character::SetHomePos (v2 Pos
) { HomeData
->Pos
= Pos
; }
290 cchar
*character::FirstPersonUnarmedHitVerb () const { return "hit"; }
291 cchar
*character::FirstPersonCriticalUnarmedHitVerb () const { return "critically hit"; }
292 cchar
*character::ThirdPersonUnarmedHitVerb () const { return "hits"; }
293 cchar
*character::ThirdPersonCriticalUnarmedHitVerb () const { return "critically hits"; }
294 cchar
*character::FirstPersonKickVerb () const { return "kick"; }
295 cchar
*character::FirstPersonCriticalKickVerb () const { return "critically kick"; }
296 cchar
*character::ThirdPersonKickVerb () const { return "kicks"; }
297 cchar
*character::ThirdPersonCriticalKickVerb () const { return "critically kicks"; }
298 cchar
*character::FirstPersonBiteVerb () const { return "bite"; }
299 cchar
*character::FirstPersonCriticalBiteVerb () const { return "critically bite"; }
300 cchar
*character::ThirdPersonBiteVerb () const { return "bites"; }
301 cchar
*character::ThirdPersonCriticalBiteVerb () const { return "critically bites"; }
302 cchar
*character::UnarmedHitNoun () const { return "attack"; }
303 cchar
*character::KickNoun () const { return "kick"; }
304 cchar
*character::BiteNoun () const { return "attack"; }
305 cchar
*character::GetEquipmentName (int) const { return ""; }
306 const std::list
<uLong
> &character::GetOriginalBodyPartID (int I
) const { return OriginalBodyPartID
[I
]; }
307 square
*character::GetNeighbourSquare (int I
) const { return GetSquareUnder()->GetNeighbourSquare(I
); }
308 lsquare
*character::GetNeighbourLSquare (int I
) const { return static_cast<lsquare
*>(GetSquareUnder())->GetNeighbourLSquare(I
); }
309 wsquare
*character::GetNeighbourWSquare (int I
) const { return static_cast<wsquare
*>(GetSquareUnder())->GetNeighbourWSquare(I
); }
310 god
*character::GetMasterGod () const { return game::GetGod(GetConfig()); }
311 col16
character::GetBodyPartColorA (int, truth
) const { return GetSkinColor(); }
312 col16
character::GetBodyPartColorB (int, truth
) const { return GetTorsoMainColor(); }
313 col16
character::GetBodyPartColorC (int, truth
) const { return GetBeltColor(); } // sorry...
314 col16
character::GetBodyPartColorD (int, truth
) const { return GetTorsoSpecialColor(); }
315 int character::GetRandomApplyBodyPart () const { return TORSO_INDEX
; }
316 truth
character::MustBeRemovedFromBone () const { return IsUnique() && !CanBeGenerated(); }
317 truth
character::IsPet () const { return GetTeam()->GetID() == PLAYER_TEAM
; }
318 character
* character::GetLeader () const { return GetTeam()->GetLeader(); }
319 int character::GetMoveType () const { return (!StateIsActivated(LEVITATION
) ? DataBase
->MoveType
: DataBase
->MoveType
| FLY
); }
320 festring
character::GetZombieDescription () const { return " of "+GetName(INDEFINITE
); }
321 truth
character::BodyPartCanBeSevered (int I
) const { return I
; }
322 truth
character::HasBeenSeen () const { return DataBase
->Flags
& HAS_BEEN_SEEN
; }
323 truth
character::IsTemporary () const { return GetTorso()->GetLifeExpectancy(); }
324 cchar
*character::GetNormalDeathMessage () const { return "killed @k"; }
327 int characterdatabase::*ExpPtr
[ATTRIBUTES
] = {
328 &characterdatabase::DefaultEndurance
,
329 &characterdatabase::DefaultPerception
,
330 &characterdatabase::DefaultIntelligence
,
331 &characterdatabase::DefaultWisdom
,
332 &characterdatabase::DefaultWillPower
,
333 &characterdatabase::DefaultCharisma
,
334 &characterdatabase::DefaultMana
,
335 &characterdatabase::DefaultArmStrength
,
336 &characterdatabase::DefaultLegStrength
,
337 &characterdatabase::DefaultDexterity
,
338 &characterdatabase::DefaultAgility
342 contentscript
<item
> characterdatabase::*EquipmentDataPtr
[EQUIPMENT_DATAS
] = {
343 &characterdatabase::Helmet
,
344 &characterdatabase::Amulet
,
345 &characterdatabase::Cloak
,
346 &characterdatabase::BodyArmor
,
347 &characterdatabase::Belt
,
348 &characterdatabase::RightWielded
,
349 &characterdatabase::LeftWielded
,
350 &characterdatabase::RightRing
,
351 &characterdatabase::LeftRing
,
352 &characterdatabase::RightGauntlet
,
353 &characterdatabase::LeftGauntlet
,
354 &characterdatabase::RightBoot
,
355 &characterdatabase::LeftBoot
359 character::character (ccharacter
&Char
) :
360 entity(Char
), id(Char
), NP(Char
.NP
), AP(Char
.AP
),
361 TemporaryState(Char
.TemporaryState
&~POLYMORPHED
),
362 Team(Char
.Team
), GoingTo(ERROR_V2
), Money(0),
363 AssignedName(Char
.AssignedName
), Action(0),
364 DataBase(Char
.DataBase
), MotherEntity(0),
365 PolymorphBackup(0), EquipmentState(0), SquareUnder(0),
366 AllowedWeaponSkillCategories(Char
.AllowedWeaponSkillCategories
),
367 BodyParts(Char
.BodyParts
),
368 RegenerationCounter(Char
.RegenerationCounter
),
369 SquaresUnder(Char
.SquaresUnder
), LastAcidMsgMin(0),
370 Stamina(Char
.Stamina
), MaxStamina(Char
.MaxStamina
),
371 BlocksSinceLastTurn(0), GenerationDanger(Char
.GenerationDanger
),
372 CommandFlags(Char
.CommandFlags
), WarnFlags(0),
373 ScienceTalks(Char
.ScienceTalks
), TrapData(0), CounterToMindWormHatch(0)
377 Flags
|= C_INITIALIZING
|C_IN_NO_MSG_MODE
;
378 Stack
= new stack(0, this, HIDDEN
);
379 for (c
= 0; c
< STATES
; ++c
) TemporaryStateCounter
[c
] = Char
.TemporaryStateCounter
[c
];
380 if (Team
) TeamIterator
= Team
->Add(this);
381 for (c
= 0; c
< BASE_ATTRIBUTES
; ++c
) BaseExperience
[c
] = Char
.BaseExperience
[c
];
382 BodyPartSlot
= new bodypartslot
[BodyParts
];
383 OriginalBodyPartID
= new std::list
<uLong
>[BodyParts
];
384 CWeaponSkill
= new cweaponskill
[AllowedWeaponSkillCategories
];
385 SquareUnder
= new square
*[SquaresUnder
];
386 if (SquaresUnder
== 1) *SquareUnder
= 0; else memset(SquareUnder
, 0, SquaresUnder
*sizeof(square
*));
387 for (c
= 0; c
< BodyParts
; ++c
) {
388 BodyPartSlot
[c
].SetMaster(this);
389 bodypart
*CharBodyPart
= Char
.GetBodyPart(c
);
390 OriginalBodyPartID
[c
] = Char
.OriginalBodyPartID
[c
];
392 bodypart
*BodyPart
= static_cast<bodypart
*>(CharBodyPart
->Duplicate());
393 SetBodyPart(c
, BodyPart
);
394 BodyPart
->CalculateEmitation();
397 for (c
= 0; c
< AllowedWeaponSkillCategories
; ++c
) CWeaponSkill
[c
] = Char
.CWeaponSkill
[c
];
398 HomeData
= Char
.HomeData
? new homedata(*Char
.HomeData
) : 0;
399 ID
= game::CreateNewCharacterID(this);
403 character::character () :
404 entity(HAS_BE
), NP(50000), AP(0), TemporaryState(0), Team(0),
405 GoingTo(ERROR_V2
), Money(0), Action(0), MotherEntity(0),
406 PolymorphBackup(0), EquipmentState(0), SquareUnder(0),
407 RegenerationCounter(0), HomeData(0), LastAcidMsgMin(0),
408 BlocksSinceLastTurn(0), GenerationDanger(DEFAULT_GENERATION_DANGER
),
409 WarnFlags(0), ScienceTalks(0), TrapData(0), CounterToMindWormHatch(0)
411 Stack
= new stack(0, this, HIDDEN
);
415 character::~character () {
416 if (Action
) delete Action
;
417 if (Team
) Team
->Remove(GetTeamIterator());
419 for (int c
= 0; c
< BodyParts
; ++c
) delete GetBodyPart(c
);
420 delete [] BodyPartSlot
;
421 delete [] OriginalBodyPartID
;
422 delete PolymorphBackup
;
423 delete [] SquareUnder
;
424 delete [] CWeaponSkill
;
426 for (trapdata
*T
= TrapData
; T
;) {
431 game::RemoveCharacterID(ID
);
435 void character::Hunger () {
436 switch (GetBurdenState()) {
440 EditExperience(LEG_STRENGTH
, 150, 1 << 2);
441 EditExperience(AGILITY
, -50, 1 << 2);
445 EditExperience(LEG_STRENGTH
, 75, 1 << 1);
446 EditExperience(AGILITY
, -25, 1 << 1);
453 switch (GetHungerState()) {
455 EditExperience(ARM_STRENGTH
, -75, 1 << 3);
456 EditExperience(LEG_STRENGTH
, -75, 1 << 3);
459 EditExperience(ARM_STRENGTH
, -50, 1 << 2);
460 EditExperience(LEG_STRENGTH
, -50, 1 << 2);
463 EditExperience(ARM_STRENGTH
, -25, 1 << 1);
464 EditExperience(LEG_STRENGTH
, -25, 1 << 1);
467 EditExperience(AGILITY
, -25, 1 << 1);
470 EditExperience(AGILITY
, -50, 1 << 2);
473 EditExperience(AGILITY
, -75, 1 << 3);
476 CheckStarvationDeath(CONST_S("starved to death"));
480 int character::TakeHit (character
*Enemy
, item
*Weapon
, bodypart
*EnemyBodyPart
, v2 HitPos
, double Damage
,
481 double ToHitValue
, int Success
, int Type
, int GivenDir
, truth Critical
, truth ForceHit
)
484 game::mActor
= Enemy
;
485 game::RunOnCharEvent(this, CONST_S("take_hit"));
487 int Dir
= Type
== BITE_ATTACK
? YOURSELF
: GivenDir
;
488 double DodgeValue
= GetDodgeValue();
489 if (!Enemy
->IsPlayer() && GetAttackWisdomLimit() != NO_LIMIT
) Enemy
->EditExperience(WISDOM
, 75, 1 << 13);
490 if (!Enemy
->CanBeSeenBy(this)) ToHitValue
*= 2;
491 if (!CanBeSeenBy(Enemy
)) DodgeValue
*= 2;
492 if (Enemy
->StateIsActivated(CONFUSED
)) ToHitValue
*= 0.75;
494 switch (Enemy
->GetTirednessState()) {
500 switch (GetTirednessState()) {
508 if (!IsRetreating()) SetGoingTo(Enemy
->GetPos());
509 else SetGoingTo(GetPos()-((Enemy
->GetPos()-GetPos())<<4));
510 if (!Enemy
->IsRetreating()) Enemy
->SetGoingTo(GetPos());
511 else Enemy
->SetGoingTo(Enemy
->GetPos()-((GetPos()-Enemy
->GetPos())<<4));
514 /* Effectively, the average chance to hit is 100% / (DV/THV + 1). */
515 if (RAND() % int(100+ToHitValue
/DodgeValue
*(100+Success
)) < 100 && !Critical
&& !ForceHit
) {
516 Enemy
->AddMissMessage(this);
517 EditExperience(AGILITY
, 150, 1 << 7);
518 EditExperience(PERCEPTION
, 75, 1 << 7);
519 if (Enemy
->CanBeSeenByPlayer())
520 DeActivateVoluntaryAction(CONST_S("The attack of ")+Enemy
->GetName(DEFINITE
)+CONST_S(" interrupts you."));
522 DeActivateVoluntaryAction(CONST_S("The attack interrupts you."));
526 int TrueDamage
= int(Damage
*(100+Success
)/100)+(RAND()%3 ? 1 : 0);
528 TrueDamage
+= TrueDamage
>> 1;
532 int BodyPart
= ChooseBodyPartToReceiveHit(ToHitValue
, DodgeValue
);
536 Enemy
->AddPrimitiveHitMessage(this, Enemy
->FirstPersonCriticalUnarmedHitVerb(), Enemy
->ThirdPersonCriticalUnarmedHitVerb(), BodyPart
);
539 Enemy
->AddWeaponHitMessage(this, Weapon
, BodyPart
, true);
542 Enemy
->AddPrimitiveHitMessage(this, Enemy
->FirstPersonCriticalKickVerb(), Enemy
->ThirdPersonCriticalKickVerb(), BodyPart
);
545 Enemy
->AddPrimitiveHitMessage(this, Enemy
->FirstPersonCriticalBiteVerb(), Enemy
->ThirdPersonCriticalBiteVerb(), BodyPart
);
551 Enemy
->AddPrimitiveHitMessage(this, Enemy
->FirstPersonUnarmedHitVerb(), Enemy
->ThirdPersonUnarmedHitVerb(), BodyPart
);
554 Enemy
->AddWeaponHitMessage(this, Weapon
, BodyPart
, false);
557 Enemy
->AddPrimitiveHitMessage(this, Enemy
->FirstPersonKickVerb(), Enemy
->ThirdPersonKickVerb(), BodyPart
);
560 Enemy
->AddPrimitiveHitMessage(this, Enemy
->FirstPersonBiteVerb(), Enemy
->ThirdPersonBiteVerb(), BodyPart
);
565 if (!Critical
&& TrueDamage
&& Enemy
->AttackIsBlockable(Type
)) {
566 TrueDamage
= CheckForBlock(Enemy
, Weapon
, ToHitValue
, TrueDamage
, Success
, Type
);
567 if (!TrueDamage
|| (Weapon
&& !Weapon
->Exists())) {
568 if (Enemy
->CanBeSeenByPlayer())
569 DeActivateVoluntaryAction(CONST_S("The attack of ")+Enemy
->GetName(DEFINITE
)+CONST_S(" interrupts you."));
571 DeActivateVoluntaryAction(CONST_S("The attack interrupts you."));
576 int WeaponSkillHits
= CalculateWeaponSkillHits(Enemy
);
577 int DoneDamage
= ReceiveBodyPartDamage(Enemy
, TrueDamage
, PHYSICAL_DAMAGE
, BodyPart
, Dir
, false, Critical
, true, Type
== BITE_ATTACK
&& Enemy
->BiteCapturesBodyPart());
578 truth Succeeded
= (GetBodyPart(BodyPart
) && HitEffect(Enemy
, Weapon
, HitPos
, Type
, BodyPart
, Dir
, !DoneDamage
)) || DoneDamage
;
579 if (Succeeded
) Enemy
->WeaponSkillHit(Weapon
, Type
, WeaponSkillHits
);
582 if (Weapon
->Exists() && DoneDamage
< TrueDamage
) Weapon
->ReceiveDamage(Enemy
, TrueDamage
-DoneDamage
, PHYSICAL_DAMAGE
);
583 if (Weapon
->Exists() && DoneDamage
&& SpillsBlood() && GetBodyPart(BodyPart
) &&
584 (GetBodyPart(BodyPart
)->IsAlive() || GetBodyPart(BodyPart
)->GetMainMaterial()->IsLiquid()))
585 Weapon
->SpillFluid(0, CreateBlood(15+RAND()%15));
588 if (Enemy
->AttackIsBlockable(Type
)) SpecialBodyDefenceEffect(Enemy
, EnemyBodyPart
, Type
);
591 if (Enemy
->CanBeSeenByPlayer())
592 DeActivateVoluntaryAction(CONST_S("The attack of ")+Enemy
->GetName(DEFINITE
)+CONST_S(" interrupts you."));
594 DeActivateVoluntaryAction(CONST_S("The attack interrupts you."));
596 return DID_NO_DAMAGE
;
599 if (CheckDeath(GetNormalDeathMessage(), Enemy
, Enemy
->IsPlayer() ? FORCE_MSG
: 0)) return HAS_DIED
;
601 if (Enemy
->CanBeSeenByPlayer())
602 DeActivateVoluntaryAction(CONST_S("The attack of ")+Enemy
->GetName(DEFINITE
)+CONST_S(" interrupts you."));
604 DeActivateVoluntaryAction(CONST_S("The attack interrupts you."));
610 struct svpriorityelement
{
611 svpriorityelement (int BodyPart
, int StrengthValue
) : BodyPart(BodyPart
), StrengthValue(StrengthValue
) {}
612 bool operator < (const svpriorityelement
&AnotherPair
) const { return StrengthValue
> AnotherPair
.StrengthValue
; }
618 int character::ChooseBodyPartToReceiveHit (double ToHitValue
, double DodgeValue
) {
619 if (BodyParts
== 1) return 0;
620 std::priority_queue
<svpriorityelement
> SVQueue
;
621 for (int c
= 0; c
< BodyParts
; ++c
) {
622 bodypart
*BodyPart
= GetBodyPart(c
);
623 if (BodyPart
&& (BodyPart
->GetHP() != 1 || BodyPart
->CanBeSevered(PHYSICAL_DAMAGE
)))
624 SVQueue
.push(svpriorityelement(c
, ModifyBodyPartHitPreference(c
, BodyPart
->GetStrengthValue()+BodyPart
->GetHP())));
626 while (SVQueue
.size()) {
627 svpriorityelement E
= SVQueue
.top();
628 int ToHitPercentage
= int(GLOBAL_WEAK_BODYPART_HIT_MODIFIER
*ToHitValue
*GetBodyPart(E
.BodyPart
)->GetBodyPartVolume()/(DodgeValue
*GetBodyVolume()));
629 ToHitPercentage
= ModifyBodyPartToHitChance(E
.BodyPart
, ToHitPercentage
);
630 if (ToHitPercentage
< 1) ToHitPercentage
= 1;
631 else if (ToHitPercentage
> 95) ToHitPercentage
= 95;
632 if (ToHitPercentage
> RAND()%100) return E
.BodyPart
;
639 void character::Be () {
640 if (game::ForceJumpToPlayerBe()) {
641 if (!IsPlayer()) return;
642 game::SetForceJumpToPlayerBe(false);
644 truth ForceBe
= HP
!= MaxHP
|| AllowSpoil();
645 for (int c
= 0; c
< BodyParts
; ++c
) {
646 bodypart
*BodyPart
= GetBodyPart(c
);
647 if (BodyPart
&& (ForceBe
|| BodyPart
->NeedsBe())) BodyPart
->Be();
650 if (!IsEnabled()) return;
651 if (GetTeam() == PLAYER
->GetTeam()) {
652 for (int c
= 0; c
< AllowedWeaponSkillCategories
; ++c
) {
653 if (CWeaponSkill
[c
].Tick() && IsPlayer()) CWeaponSkill
[c
].AddLevelDownMessage(c
);
658 if (GetHungerState() == STARVING
&& !(RAND()%50)) LoseConsciousness(250+RAND_N(250), true);
659 if (!Action
|| Action
->AllowFoodConsumption()) Hunger();
661 if (Stamina
!= MaxStamina
) RegenerateStamina();
662 if (HP
!= MaxHP
) Regenerate();
663 if (Action
&& AP
>= 1000) ActionAutoTermination();
664 if (Action
&& AP
>= 1000) {
666 if (!IsEnabled()) return;
668 EditAP(GetStateAPGain(100));
672 SpecialTurnHandler();
673 BlocksSinceLastTurn
= 0;
675 static int Timer
= 0;
676 if (ivanconfig::GetAutoSaveInterval() && !GetAction() && ++Timer
>= ivanconfig::GetAutoSaveInterval()) {
677 game::Save(game::GetAutoSaveFileName());
680 game::CalculateNextDanger();
681 if (!StateIsActivated(POLYMORPHED
)) game::UpdatePlayerAttributeAverage();
682 if (!game::IsInWilderness()) Search(GetAttribute(PERCEPTION
));
686 if (Action
->ShowEnvironment()) {
687 static int Counter
= 0;
688 if (++Counter
== 10) {
689 game::DrawEverything();
693 msgsystem::ThyMessagesAreNowOld();
694 if (Action
->IsVoluntary() && READ_KEY()) Action
->Terminate(false);
697 if (!Action
&& !game::IsInWilderness()) GetAICommand();
703 void character::Move (v2 MoveTo
, truth TeleportMove
, truth Run
) {
704 if (!IsEnabled()) return;
705 /* Test whether the player is stuck to something */
706 if (!TeleportMove
&& !TryToUnStickTraps(MoveTo
-GetPos())) return;
707 if (Run
&& !IsPlayer() && TorsoIsAlive() && (Stamina
<= 10000 / Max(GetAttribute(LEG_STRENGTH
), 1) ||
708 (!StateIsActivated(PANIC
) && Stamina
< MaxStamina
>> 2)))
711 if (GetBurdenState() != OVER_LOADED
|| TeleportMove
) {
712 lsquare
*OldSquareUnder
[MAX_SQUARES_UNDER
];
713 if (!game::IsInWilderness()) {
714 for (int c
= 0; c
< GetSquaresUnder(); ++c
) OldSquareUnder
[c
] = GetLSquareUnder(c
);
719 /* Multitiled creatures should behave differently, maybe? */
721 int ED
= GetSquareUnder()->GetEntryDifficulty();
722 EditAP(-GetMoveAPRequirement(ED
) >> 1);
724 EditExperience(AGILITY
, 125, ED
<< 7);
727 switch (GetHungerState()) {
728 case SATIATED
: Base
= 11000; break;
729 case BLOATED
: Base
= 12500; break;
730 case OVER_FED
: Base
= 15000; break;
733 EditStamina(-Base
/ Max(GetAttribute(LEG_STRENGTH
), 1), true);
735 int ED
= GetSquareUnder()->GetEntryDifficulty();
736 EditAP(-GetMoveAPRequirement(ED
));
738 EditExperience(AGILITY
, 75, ED
<< 7);
741 if (IsPlayer()) ShowNewPosInfo();
742 if (IsPlayer() && !game::IsInWilderness()) GetStackUnder()->SetSteppedOn(true);
743 if (!game::IsInWilderness()) SignalStepFrom(OldSquareUnder
);
746 cchar
*CrawlVerb
= StateIsActivated(LEVITATION
) ? "float" : "crawl";
747 ADD_MESSAGE("You try very hard to %s forward. But your load is too heavy.", CrawlVerb
);
754 void character::GetAICommand () {
755 SeekLeader(GetLeader());
756 if (FollowLeader(GetLeader())) return;
757 if (CheckForEnemies(true, true, true)) return;
758 if (CheckForUsefulItemsOnGround()) return;
759 if (CheckForDoors()) return;
760 if (CheckSadism()) return;
761 if (MoveRandomly()) return;
766 truth
character::MoveTowardsTarget (truth Run
) {
771 if (!Route
.empty()) {
774 } else TPos
= GoingTo
;
776 MoveTo
[0] = v2(0, 0);
777 MoveTo
[1] = v2(0, 0);
778 MoveTo
[2] = v2(0, 0);
780 if (TPos
.X
< Pos
.X
) {
781 if (TPos
.Y
< Pos
.Y
) {
782 MoveTo
[0] = v2(-1, -1);
783 MoveTo
[1] = v2(-1, 0);
784 MoveTo
[2] = v2( 0, -1);
785 } else if (TPos
.Y
== Pos
.Y
) {
786 MoveTo
[0] = v2(-1, 0);
787 MoveTo
[1] = v2(-1, -1);
788 MoveTo
[2] = v2(-1, 1);
789 } else if (TPos
.Y
> Pos
.Y
) {
790 MoveTo
[0] = v2(-1, 1);
791 MoveTo
[1] = v2(-1, 0);
792 MoveTo
[2] = v2( 0, 1);
794 } else if (TPos
.X
== Pos
.X
) {
795 if (TPos
.Y
< Pos
.Y
) {
796 MoveTo
[0] = v2( 0, -1);
797 MoveTo
[1] = v2(-1, -1);
798 MoveTo
[2] = v2( 1, -1);
799 } else if (TPos
.Y
== Pos
.Y
) {
802 } else if (TPos
.Y
> Pos
.Y
) {
803 MoveTo
[0] = v2( 0, 1);
804 MoveTo
[1] = v2(-1, 1);
805 MoveTo
[2] = v2( 1, 1);
807 } else if (TPos
.X
> Pos
.X
) {
808 if (TPos
.Y
< Pos
.Y
) {
809 MoveTo
[0] = v2(1, -1);
810 MoveTo
[1] = v2(1, 0);
811 MoveTo
[2] = v2(0, -1);
812 } else if (TPos
.Y
== Pos
.Y
) {
813 MoveTo
[0] = v2(1, 0);
814 MoveTo
[1] = v2(1, -1);
815 MoveTo
[2] = v2(1, 1);
816 } else if (TPos
.Y
> Pos
.Y
) {
817 MoveTo
[0] = v2(1, 1);
818 MoveTo
[1] = v2(1, 0);
819 MoveTo
[2] = v2(0, 1);
823 v2 ModifiedMoveTo
= ApplyStateModification(MoveTo
[0]);
825 if (TryMove(ModifiedMoveTo
, true, Run
)) return true;
827 int L
= (Pos
-TPos
).GetManhattanLength();
829 if (RAND()&1) Swap(MoveTo
[1], MoveTo
[2]);
831 if (Pos
.IsAdjacent(TPos
)) {
836 if ((Pos
+MoveTo
[1]-TPos
).GetManhattanLength() <= L
&& TryMove(ApplyStateModification(MoveTo
[1]), true, Run
)) return true;
837 if ((Pos
+MoveTo
[2]-TPos
).GetManhattanLength() <= L
&& TryMove(ApplyStateModification(MoveTo
[2]), true, Run
)) return true;
838 Illegal
.insert(Pos
+ModifiedMoveTo
);
839 if (CreateRoute()) return true;
844 int character::CalculateNewSquaresUnder (lsquare
**NewSquare
, v2 Pos
) const {
845 if (GetLevel()->IsValidPos(Pos
)) {
846 *NewSquare
= GetNearLSquare(Pos
);
853 truth
character::TryMove (v2 MoveVector
, truth Important
, truth Run
) {
854 lsquare
*MoveToSquare
[MAX_SQUARES_UNDER
];
855 character
*Pet
[MAX_SQUARES_UNDER
];
856 character
*Neutral
[MAX_SQUARES_UNDER
];
857 character
*Hostile
[MAX_SQUARES_UNDER
];
858 v2 PetPos
[MAX_SQUARES_UNDER
];
859 v2 NeutralPos
[MAX_SQUARES_UNDER
];
860 v2 HostilePos
[MAX_SQUARES_UNDER
];
861 v2 MoveTo
= GetPos()+MoveVector
;
862 int Direction
= game::GetDirectionForVector(MoveVector
);
863 if (Direction
== DIR_ERROR
) ABORT("Direction fault.");
864 if (!game::IsInWilderness()) {
865 int Squares
= CalculateNewSquaresUnder(MoveToSquare
, MoveTo
);
870 for (int c
= 0; c
< Squares
; ++c
) {
871 character
* Char
= MoveToSquare
[c
]->GetCharacter();
872 if (Char
&& Char
!= this) {
873 v2 Pos
= MoveToSquare
[c
]->GetPos();
876 PetPos
[Pets
++] = Pos
;
877 } else if (Char
->GetRelation(this) != HOSTILE
) {
878 Neutral
[Neutrals
] = Char
;
879 NeutralPos
[Neutrals
++] = Pos
;
881 Hostile
[Hostiles
] = Char
;
882 HostilePos
[Hostiles
++] = Pos
;
887 if (Hostiles
== 1) return Hit(Hostile
[0], HostilePos
[0], Direction
);
889 int Index
= RAND() % Hostiles
;
890 return Hit(Hostile
[Index
], HostilePos
[Index
], Direction
);
894 if (!IsPlayer() && !Pets
&& Important
&& CanMoveOn(MoveToSquare
[0]))
895 return HandleCharacterBlockingTheWay(Neutral
[0], NeutralPos
[0], Direction
);
897 return IsPlayer() && Hit(Neutral
[0], NeutralPos
[0], Direction
);
898 } else if (Neutrals
) {
900 int Index
= RAND() % Neutrals
;
901 return Hit(Neutral
[Index
], NeutralPos
[Index
], Direction
);
907 for (int c
= 0; c
< Squares
; ++c
) if (MoveToSquare
[c
]->IsScary(this)) return false;
911 if (IsPlayer() && !ivanconfig::GetBeNice() && Pet
[0]->IsMasochist() && HasSadistAttackMode() &&
912 game::TruthQuestion("Do you want to punish " + Pet
[0]->GetObjectPronoun() + "? [y/N]"))
913 return Hit(Pet
[0], PetPos
[0], Direction
, SADIST_HIT
);
915 return (Important
&& (CanMoveOn(MoveToSquare
[0]) ||
916 (IsPlayer() && game::GoThroughWallsCheatIsActive())) && Displace(Pet
[0]));
917 } else if (Pets
) return false;
919 if ((CanMove() && CanMoveOn(MoveToSquare
[0])) || ((game::GoThroughWallsCheatIsActive() && IsPlayer()))) {
920 Move(MoveTo
, false, Run
);
921 if (IsEnabled() && GetPos() == GoingTo
) TerminateGoingTo();
924 for (int c
= 0; c
< Squares
; ++c
) {
925 olterrain
*Terrain
= MoveToSquare
[c
]->GetOLTerrain();
926 if (Terrain
&& Terrain
->CanBeOpened()) {
928 if (Terrain
->IsLocked()) {
931 if (ivanconfig::GetKickDownDoors()) {
932 Kick(MoveToSquare
[c
], Direction
);
936 ADD_MESSAGE("The %s is locked.", Terrain
->GetNameSingular().CStr()); /* not sure if this is better than "the door is locked", but I guess it _might_ be slighltly better */
938 } else if (Important
&& CheckKick()) {
939 room
*Room
= MoveToSquare
[c
]->GetRoom();
940 if (!Room
|| Room
->AllowKick(this, MoveToSquare
[c
])) {
941 int HP
= Terrain
->GetHP();
942 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s kicks %s.", CHAR_NAME(DEFINITE
), Terrain
->CHAR_NAME(DEFINITE
));
943 Kick(MoveToSquare
[c
], Direction
);
944 olterrain
*NewTerrain
= MoveToSquare
[c
]->GetOLTerrain();
945 if (NewTerrain
== Terrain
&& Terrain
->GetHP() == HP
) { // BUG!
946 Illegal
.insert(MoveTo
);
952 } else { /* if (Terrain->IsLocked()) */
953 /*if(!IsPlayer() || game::TruthQuestion(CONST_S("Do you want to open ") + Terrain->GetName(DEFINITE) + "? [y/N]", false, game::GetMoveCommandKeyBetweenPoints(PLAYER->GetPos(), MoveToSquare[0]->GetPos()))) return MoveToSquare[c]->Open(this);*/
954 /* Non-players always try to open it */
955 if (!IsPlayer()) return MoveToSquare
[c
]->Open(this);
956 if (game::TruthQuestion(CONST_S("Do you want to ")+(ivanconfig::GetKickDownDoors()?"kick ":"open ")+
957 Terrain
->GetName(DEFINITE
)+"? [y/N]", false, game::GetMoveCommandKeyBetweenPoints(PLAYER
->GetPos(),
958 MoveToSquare
[0]->GetPos()))) {
959 if (ivanconfig::GetKickDownDoors()) {
960 Kick(MoveToSquare
[c
], Direction
);
963 return MoveToSquare
[c
]->Open(this);
966 } /* if (Terrain->IsLocked()) */
967 } else { /* if (CanOpen()) */
969 ADD_MESSAGE("This monster type cannot open doors.");
973 Illegal
.insert(MoveTo
);
974 return CreateRoute();
976 } /* if (CanOpen()) */
977 } /* if (Terrain && Terrain->CanBeOpened()) */
982 if (IsPlayer() && !IsStuck() && GetLevel()->IsOnGround() && game::TruthQuestion(CONST_S("Do you want to leave ")+game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex())+"? [y/N]")) {
983 if (HasPetrussNut() && !HasGoldenEagleShirt()) {
984 game::TextScreen(CONST_S("An undead and sinister voice greets you as you leave the city behind:\n\n\"MoRtAl! ThOu HaSt SlAuGtHeReD pEtRuS aNd PlEaSeD mE!\nfRoM tHiS dAy On, ThOu ArT tHe DeArEsT sErVaNt Of AlL eViL!\"\n\nYou are victorious!"));
985 game::GetCurrentArea()->SendNewDrawRequest();
986 game::DrawEverything();
988 festring Msg
= CONST_S("killed Petrus and became the Avatar of Chaos");
989 PLAYER
->AddScoreEntry(Msg
, 3, false);
993 if (game::TryTravel(WORLD_MAP
, WORLD_MAP
, game::GetCurrentDungeonIndex())) return true;
998 /** No multitile support */
999 if (CanMove() && GetArea()->IsValidPos(MoveTo
) && (CanMoveOn(GetNearWSquare(MoveTo
)) || game::GoThroughWallsCheatIsActive())) {
1000 if (!game::GoThroughWallsCheatIsActive()) {
1001 charactervector
&V
= game::GetWorldMap()->GetPlayerGroup();
1002 truth Discard
= false;
1003 for (uInt c
= 0; c
< V
.size(); ++c
) {
1004 if (!V
[c
]->CanMoveOn(GetNearWSquare(MoveTo
))) {
1006 ADD_MESSAGE("One or more of your team members cannot cross this terrain.");
1007 if (!game::TruthQuestion("Discard them? [y/N]")) return false;
1010 if (Discard
) delete V
[c
];
1011 V
.erase(V
.begin() + c
--);
1015 Move(MoveTo
, false);
1024 void character::CreateCorpse (lsquare
*Square
) {
1025 if (!BodyPartsDisappearWhenSevered() && !game::AllBodyPartsVanish()) {
1026 corpse
*Corpse
= corpse::Spawn(0, NO_MATERIALS
);
1027 Corpse
->SetDeceased(this);
1028 Square
->AddItem(Corpse
);
1036 void character::Die (ccharacter
*Killer
, cfestring
&Msg
, uLong DeathFlags
) {
1037 /* Note: This function musn't delete any objects, since one of these may be
1038 the one currently processed by pool::Be()! */
1039 if (!IsEnabled()) return;
1040 game::mActor
= Killer
;
1041 game::RunOnCharEvent(this, CONST_S("die"));
1045 ADD_MESSAGE("You die.");
1046 game::DrawEverything();
1047 if (game::TruthQuestion(CONST_S("Do you want to save screenshot? [y/n]"), REQUIRES_ANSWER
)) {
1048 festring dir
= inputfile::GetMyDir()+"/DeathShots";
1050 mkdir(dir
.CStr(), 0755);
1055 time_t t
= time(NULL
);
1056 struct tm
*ts
= localtime(&t
);
1058 timestr
<< (int)(ts
->tm_year
%100);
1059 int t
= ts
->tm_mon
+1;
1060 if (t
< 10) timestr
<< '0'; timestr
<< t
;
1061 t
= ts
->tm_mday
; if (t
< 10) timestr
<< '0'; timestr
<< t
;
1063 t
= ts
->tm_hour
; if (t
< 10) timestr
<< '0'; timestr
<< t
;
1064 t
= ts
->tm_min
; if (t
< 10) timestr
<< '0'; timestr
<< t
;
1065 t
= ts
->tm_sec
; if (t
< 10) timestr
<< '0'; timestr
<< t
;
1069 #if defined(HAVE_IMLIB2) || defined(HAVE_LIBPNG)
1070 festring ext
= ".png";
1072 festring ext
= ".bmp";
1074 festring fname
= dir
+"/deathshot_"+timestr
;
1075 if (inputfile::fileExists(fname
+ext
)) {
1076 for (int f
= 0; f
< 1000; f
++) {
1078 sprintf(buf
, "%03d", f
);
1079 festring fn
= fname
+buf
;
1080 if (!inputfile::fileExists(fn
+ext
)) {
1087 fprintf(stderr
, "deathshot: %s\n", fname
.CStr());
1088 #if defined(HAVE_IMLIB2) || defined(HAVE_LIBPNG)
1089 DOUBLE_BUFFER
->SavePNG(fname
);
1091 DOUBLE_BUFFER
->SaveBMP(fname
);
1094 if (game::WizardModeIsActive()) {
1095 game::DrawEverything();
1096 if (!game::TruthQuestion(CONST_S("Do you want to do this, cheater? [y/n]"), REQUIRES_ANSWER
)) {
1102 SetNP(SATIATED_LEVEL
);
1103 SendNewDrawRequest();
1107 } else if (CanBeSeenByPlayer() && !(DeathFlags
& DISALLOW_MSG
)) {
1108 ProcessAndAddMessage(GetDeathMessage());
1109 } else if (DeathFlags
& FORCE_MSG
) {
1110 ADD_MESSAGE("You sense the death of something.");
1113 if (!(DeathFlags
& FORBID_REINCARNATION
)) {
1114 if (StateIsActivated(LIFE_SAVED
) && CanMoveOn(!game::IsInWilderness() ? GetSquareUnder() : PLAYER
->GetSquareUnder())) {
1118 if (SpecialSaveLife()) return;
1119 } else if (StateIsActivated(LIFE_SAVED
)) {
1123 Flags
|= C_IN_NO_MSG_MODE
;
1124 character
*Ghost
= 0;
1126 game::RemoveSaves();
1127 if (!game::IsInWilderness()) {
1128 Ghost
= game::CreateGhost();
1133 square
*SquareUnder
[MAX_SQUARES_UNDER
];
1134 memset(SquareUnder
, 0, sizeof(SquareUnder
));
1136 if (IsPlayer() || !game::IsInWilderness()) {
1137 for (int c
= 0; c
< SquaresUnder
; ++c
) SquareUnder
[c
] = GetSquareUnder(c
);
1140 charactervector
& V
= game::GetWorldMap()->GetPlayerGroup();
1141 V
.erase(std::find(V
.begin(), V
.end(), this));
1143 //lsquare **LSquareUnder = reinterpret_cast<lsquare **>(SquareUnder); /* warning; wtf? */
1144 lsquare
*LSquareUnder
[MAX_SQUARES_UNDER
];
1145 memcpy(LSquareUnder
, SquareUnder
, sizeof(SquareUnder
));
1147 if (!game::IsInWilderness()) {
1148 if (!StateIsActivated(POLYMORPHED
)) {
1149 if (!IsPlayer() && !IsTemporary() && !Msg
.IsEmpty()) game::SignalDeath(this, Killer
, Msg
);
1150 if (!(DeathFlags
& DISALLOW_CORPSE
)) CreateCorpse(LSquareUnder
[0]); else SendToHell();
1152 if (!IsPlayer() && !IsTemporary() && !Msg
.IsEmpty()) game::SignalDeath(GetPolymorphBackup(), Killer
, Msg
);
1153 GetPolymorphBackup()->CreateCorpse(LSquareUnder
[0]);
1154 GetPolymorphBackup()->Flags
&= ~C_POLYMORPHED
;
1155 SetPolymorphBackup(0);
1159 if (!IsPlayer() && !IsTemporary() && !Msg
.IsEmpty()) game::SignalDeath(this, Killer
, Msg
);
1164 if (!game::IsInWilderness()) {
1165 for (int c
= 0; c
< GetSquaresUnder(); ++c
) LSquareUnder
[c
]->SetTemporaryEmitation(GetEmitation());
1167 ShowAdventureInfo();
1168 if (!game::IsInWilderness()) {
1169 for(int c
= 0; c
< GetSquaresUnder(); ++c
) LSquareUnder
[c
]->SetTemporaryEmitation(0);
1173 if (!game::IsInWilderness()) {
1174 if (GetSquaresUnder() == 1) {
1175 stack
*StackUnder
= LSquareUnder
[0]->GetStack();
1176 GetStack()->MoveItemsTo(StackUnder
);
1177 doforbodypartswithparam
<stack
*>()(this, &bodypart::DropEquipment
, StackUnder
);
1179 while (GetStack()->GetItems()) GetStack()->GetBottom()->MoveTo(LSquareUnder
[RAND_N(GetSquaresUnder())]->GetStack());
1180 for (int c
= 0; c
< BodyParts
; ++c
) {
1181 bodypart
*BodyPart
= GetBodyPart(c
);
1182 if (BodyPart
) BodyPart
->DropEquipment(LSquareUnder
[RAND_N(GetSquaresUnder())]->GetStack());
1187 if (GetTeam()->GetLeader() == this) GetTeam()->SetLeader(0);
1189 Flags
&= ~C_IN_NO_MSG_MODE
;
1193 if (!game::IsInWilderness()) {
1194 Ghost
->PutTo(LSquareUnder
[0]->GetPos());
1198 game::TextScreen(CONST_S("Unfortunately you died."), ZERO_V2
, WHITE
, true, true, &game::ShowDeathSmiley
);
1204 void character::AddMissMessage (ccharacter
*Enemy
) const {
1206 if (Enemy
->IsPlayer()) Msg
= GetDescription(DEFINITE
)+" misses you!";
1207 else if (IsPlayer()) Msg
= CONST_S("You miss ")+Enemy
->GetDescription(DEFINITE
)+'!';
1208 else if (CanBeSeenByPlayer() || Enemy
->CanBeSeenByPlayer()) Msg
= GetDescription(DEFINITE
)+" misses "+Enemy
->GetDescription(DEFINITE
)+'!';
1210 ADD_MESSAGE("%s", Msg
.CStr());
1214 void character::AddBlockMessage (ccharacter
*Enemy
, citem
*Blocker
, cfestring
&HitNoun
, truth Partial
) const {
1216 festring BlockVerb
= (Partial
? " to partially block the " : " to block the ")+HitNoun
;
1218 Msg
<< "You manage" << BlockVerb
<< " with your " << Blocker
->GetName(UNARTICLED
) << '!';
1219 } else if (Enemy
->IsPlayer() || Enemy
->CanBeSeenByPlayer()) {
1220 if (CanBeSeenByPlayer())
1221 Msg
<< GetName(DEFINITE
) << " manages" << BlockVerb
<< " with " << GetPossessivePronoun() << ' ' << Blocker
->GetName(UNARTICLED
) << '!';
1223 Msg
<< "Something manages" << BlockVerb
<< " with something!";
1227 ADD_MESSAGE("%s", Msg
.CStr());
1231 void character::AddPrimitiveHitMessage (ccharacter
*Enemy
, cfestring
&FirstPersonHitVerb
,
1232 cfestring
&ThirdPersonHitVerb
, int BodyPart
) const
1235 festring BodyPartDescription
;
1236 if (BodyPart
&& (Enemy
->CanBeSeenByPlayer() || Enemy
->IsPlayer()))
1237 BodyPartDescription
<< " in the " << Enemy
->GetBodyPartName(BodyPart
);
1238 if (Enemy
->IsPlayer())
1239 Msg
<< GetDescription(DEFINITE
) << ' ' << ThirdPersonHitVerb
<< " you" << BodyPartDescription
<< '!';
1240 else if (IsPlayer())
1241 Msg
<< "You " << FirstPersonHitVerb
<< ' ' << Enemy
->GetDescription(DEFINITE
) << BodyPartDescription
<< '!';
1242 else if (CanBeSeenByPlayer() || Enemy
->CanBeSeenByPlayer())
1243 Msg
<< GetDescription(DEFINITE
) << ' ' << ThirdPersonHitVerb
<< ' ' << Enemy
->GetDescription(DEFINITE
) + BodyPartDescription
<< '!';
1246 ADD_MESSAGE("%s", Msg
.CStr());
1250 cchar
*const HitVerb
[] = { "strike", "slash", "stab" };
1251 cchar
*const HitVerb3rdPersonEnd
[] = { "s", "es", "s" };
1254 void character::AddWeaponHitMessage (ccharacter
*Enemy
, citem
*Weapon
, int BodyPart
, truth Critical
) const {
1256 festring BodyPartDescription
;
1258 if (BodyPart
&& (Enemy
->CanBeSeenByPlayer() || Enemy
->IsPlayer()))
1259 BodyPartDescription
<< " in the " << Enemy
->GetBodyPartName(BodyPart
);
1261 int FittingTypes
= 0;
1262 int DamageFlags
= Weapon
->GetDamageFlags();
1265 for (int c
= 0; c
< DAMAGE_TYPES
; ++c
) {
1266 if (1 << c
& DamageFlags
) {
1267 if (!FittingTypes
|| !RAND_N(FittingTypes
+1)) DamageType
= c
;
1272 if (!FittingTypes
) ABORT("No damage flags specified for %s!", Weapon
->CHAR_NAME(UNARTICLED
));
1274 festring NewHitVerb
= Critical
? " critically " : " ";
1275 NewHitVerb
<< HitVerb
[DamageType
];
1276 cchar
*const E
= HitVerb3rdPersonEnd
[DamageType
];
1278 if (Enemy
->IsPlayer()) {
1279 Msg
<< GetDescription(DEFINITE
) << NewHitVerb
<< E
<< " you" << BodyPartDescription
;
1280 if (CanBeSeenByPlayer()) Msg
<< " with " << GetPossessivePronoun() << ' ' << Weapon
->GetName(UNARTICLED
);
1282 } else if (IsPlayer()) {
1283 Msg
<< "You" << NewHitVerb
<< ' ' << Enemy
->GetDescription(DEFINITE
) << BodyPartDescription
<< '!';
1284 } else if(CanBeSeenByPlayer() || Enemy
->CanBeSeenByPlayer()) {
1285 Msg
<< GetDescription(DEFINITE
) << NewHitVerb
<< E
<< ' ' << Enemy
->GetDescription(DEFINITE
) << BodyPartDescription
;
1286 if (CanBeSeenByPlayer()) Msg
<< " with " << GetPossessivePronoun() << ' ' << Weapon
->GetName(UNARTICLED
);
1291 ADD_MESSAGE("%s", Msg
.CStr());
1295 item
*character::GeneralFindItem (ItemCheckerCB chk
) const {
1296 for (stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) {
1298 if (it
&& chk(it
)) return it
;
1304 static truth
isElpuriHead (item
*i
) { return i
->IsHeadOfElpuri(); }
1305 truth
character::HasHeadOfElpuri () const {
1306 if (GeneralFindItem(::isElpuriHead
)) return true;
1307 return combineequipmentpredicates()(this, &item::IsHeadOfElpuri
, 1);
1311 static truth
isPetrussNut (item
*i
) { return i
->IsPetrussNut(); }
1312 truth
character::HasPetrussNut () const {
1313 if (GeneralFindItem(::isPetrussNut
)) return true;
1314 return combineequipmentpredicates()(this, &item::IsPetrussNut
, 1);
1318 static truth
isGoldenEagleShirt (item
*i
) { return i
->IsGoldenEagleShirt(); }
1319 truth
character::HasGoldenEagleShirt () const {
1320 if (GeneralFindItem(::isGoldenEagleShirt
)) return true;
1321 return combineequipmentpredicates()(this, &item::IsGoldenEagleShirt
, 1);
1325 int character::GeneralRemoveItem (ItemCheckerCB chk
, truth allItems
) {
1331 for (stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) {
1333 if (Item
&& chk(Item
)) {
1334 Item
->RemoveFromSlot();
1337 if (!allItems
) return cnt
;
1346 for (int c
= 0; c
< GetEquipments(); ++c
) {
1347 item
*Item
= GetEquipment(c
);
1348 if (Item
&& chk(Item
)) {
1349 Item
->RemoveFromSlot();
1352 if (!allItems
) return cnt
;
1362 static truth
isEncryptedScroll (item
*i
) { return i
->IsEncryptedScroll(); }
1363 truth
character::RemoveEncryptedScroll () { return GeneralRemoveItem(::isEncryptedScroll
) != 0; }
1366 static truth
isMondedrPass (item
*i
) { return i
->IsMondedrPass(); }
1367 truth
character::RemoveMondedrPass () { return GeneralRemoveItem(::isMondedrPass
) != 0; }
1370 static truth
isRingOfThieves (item
*i
) { return i
->IsRingOfThieves(); }
1371 truth
character::RemoveRingOfThieves () { return GeneralRemoveItem(::isRingOfThieves
) != 0; }
1374 truth
character::ReadItem (item
*ToBeRead
) {
1375 if (!ToBeRead
->CanBeRead(this)) {
1376 if (IsPlayer()) ADD_MESSAGE("You can't read this.");
1379 if (!GetLSquareUnder()->IsDark() || game::GetSeeWholeMapCheatMode()) {
1380 if (StateIsActivated(CONFUSED
) && !(RAND()&7)) {
1381 if (!ToBeRead
->IsDestroyable(this)) {
1382 ADD_MESSAGE("You read some words of %s and understand exactly nothing.", ToBeRead
->CHAR_NAME(DEFINITE
));
1384 ADD_MESSAGE("%s is very confusing. Or perhaps you are just too confused?", ToBeRead
->CHAR_NAME(DEFINITE
));
1385 ActivateRandomState(SRC_CONFUSE_READ
, 1000+RAND()%1500);
1386 ToBeRead
->RemoveFromSlot();
1387 ToBeRead
->SendToHell();
1392 if (ToBeRead
->Read(this)) {
1393 if (!game::WizardModeIsActive()) {
1394 /* This AP is used to take the stuff out of backpack */
1401 if (IsPlayer()) ADD_MESSAGE("It's too dark here to read.");
1406 void character::CalculateBurdenState () {
1407 int OldBurdenState
= BurdenState
;
1408 sLong SumOfMasses
= GetCarriedWeight();
1409 sLong CarryingStrengthUnits
= sLong(GetCarryingStrength())*2500;
1410 if (SumOfMasses
> (CarryingStrengthUnits
<< 1) + CarryingStrengthUnits
) BurdenState
= OVER_LOADED
;
1411 else if (SumOfMasses
> CarryingStrengthUnits
<< 1) BurdenState
= STRESSED
;
1412 else if (SumOfMasses
> CarryingStrengthUnits
) BurdenState
= BURDENED
;
1413 else BurdenState
= UNBURDENED
;
1414 if (!IsInitializing() && BurdenState
!= OldBurdenState
) CalculateBattleInfo();
1418 void character::Save (outputfile
&SaveFile
) const {
1419 SaveFile
<< (uShort
)GetType();
1420 Stack
->Save(SaveFile
);
1422 for (int c
= 0; c
< BASE_ATTRIBUTES
; ++c
) SaveFile
<< BaseExperience
[c
];
1424 SaveFile
<< ExpModifierMap
;
1425 SaveFile
<< NP
<< AP
<< Stamina
<< GenerationDanger
<< ScienceTalks
<< CounterToMindWormHatch
;
1426 SaveFile
<< TemporaryState
<< EquipmentState
<< Money
<< GoingTo
<< RegenerationCounter
<< Route
<< Illegal
;
1427 SaveFile
.Put(!!IsEnabled());
1428 SaveFile
<< HomeData
<< BlocksSinceLastTurn
<< CommandFlags
;
1429 SaveFile
<< WarnFlags
<< (uShort
)Flags
;
1431 for (int c
= 0; c
< BodyParts
; ++c
) SaveFile
<< BodyPartSlot
[c
] << OriginalBodyPartID
[c
];
1433 SaveLinkedList(SaveFile
, TrapData
);
1436 for (int c
= 0; c
< STATES
; ++c
) SaveFile
<< TemporaryStateCounter
[c
];
1440 SaveFile
<< Team
->GetID(); // uLong
1442 SaveFile
.Put(false);
1445 if (GetTeam() && GetTeam()->GetLeader() == this) SaveFile
.Put(true); else SaveFile
.Put(false);
1447 SaveFile
<< AssignedName
<< PolymorphBackup
;
1449 for (int c
= 0; c
< AllowedWeaponSkillCategories
; ++c
) SaveFile
<< CWeaponSkill
[c
];
1451 SaveFile
<< (uShort
)GetConfig();
1455 void character::Load (inputfile
&SaveFile
) {
1457 Stack
->Load(SaveFile
);
1459 game::AddCharacterID(this, ID
);
1461 for (int c
= 0; c
< BASE_ATTRIBUTES
; ++c
) SaveFile
>> BaseExperience
[c
];
1463 SaveFile
>> ExpModifierMap
;
1464 SaveFile
>> NP
>> AP
>> Stamina
>> GenerationDanger
>> ScienceTalks
>> CounterToMindWormHatch
;
1465 SaveFile
>> TemporaryState
>> EquipmentState
>> Money
>> GoingTo
>> RegenerationCounter
>> Route
>> Illegal
;
1467 if (!SaveFile
.Get()) Disable();
1469 SaveFile
>> HomeData
>> BlocksSinceLastTurn
>> CommandFlags
;
1470 SaveFile
>> WarnFlags
;
1471 WarnFlags
&= ~WARNED
;
1472 Flags
|= ReadType
<uShort
>(SaveFile
) & ~ENTITY_FLAGS
;
1474 for (int c
= 0; c
< BodyParts
; ++c
) {
1475 SaveFile
>> BodyPartSlot
[c
] >> OriginalBodyPartID
[c
];
1476 item
*BodyPart
= *BodyPartSlot
[c
];
1477 if (BodyPart
) BodyPart
->Disable();
1480 LoadLinkedList(SaveFile
, TrapData
);
1483 if (Action
) Action
->SetActor(this);
1485 for (int c
= 0; c
< STATES
; ++c
) SaveFile
>> TemporaryStateCounter
[c
];
1487 if (SaveFile
.Get()) SetTeam(game::GetTeam(ReadType
<uLong
>(SaveFile
)));
1489 if (SaveFile
.Get()) GetTeam()->SetLeader(this);
1491 SaveFile
>> AssignedName
>> PolymorphBackup
;
1493 for (int c
= 0; c
< AllowedWeaponSkillCategories
; ++c
) SaveFile
>> CWeaponSkill
[c
];
1495 databasecreator
<character
>::InstallDataBase(this, ReadType
<uShort
>(SaveFile
));
1497 if (IsEnabled() && !game::IsInWilderness()) {
1498 for (int c
= 1; c
< GetSquaresUnder(); ++c
) GetSquareUnder(c
)->SetCharacter(this);
1503 truth
character::Engrave (cfestring
&What
) {
1504 GetLSquareUnder()->Engrave(What
);
1508 truth
character::MoveRandomly () {
1509 if (!IsEnabled()) return false;
1510 for (int c
= 0; c
< 10; ++c
) {
1511 v2 ToTry
= game::GetMoveVector(RAND()&7);
1512 if (GetLevel()->IsValidPos(GetPos()+ToTry
)) {
1513 lsquare
*Square
= GetNearLSquare(GetPos()+ToTry
);
1514 if (!Square
->IsDangerous(this) && !Square
->IsScary(this) && TryMove(ToTry
, false, false)) return true;
1521 truth
character::TestForPickup (item
*ToBeTested
) const {
1522 if (MakesBurdened(ToBeTested
->GetWeight()+GetCarriedWeight())) return false;
1527 void character::AddScoreEntry (cfestring
&Description
, double Multiplier
, truth AddEndLevel
) const {
1528 if (!game::WizardModeIsReallyActive()) {
1530 if (!HScore
.CheckVersion()) {
1531 if (game::Menu(0, v2(RES
.X
>> 1, RES
.Y
>> 1), CONST_S("The highscore version doesn't match.\rDo you want to erase previous records and start a new file?\rNote, if you answer no, the score of your current game will be lost!\r"), CONST_S("Yes\rNo\r"), LIGHT_GRAY
)) return;
1534 festring Desc
= game::GetPlayerName();
1535 Desc
<< ", " << Description
;
1536 if (AddEndLevel
) Desc
<< " in "+(game::IsInWilderness() ? "the world map" : game::GetCurrentDungeon()->GetLevelDescription(game::GetCurrentLevelIndex()));
1537 HScore
.Add(sLong(game::GetScore()*Multiplier
), Desc
);
1543 truth
character::CheckDeath (cfestring
&Msg
, ccharacter
*Murderer
, uLong DeathFlags
) {
1544 if (!IsEnabled()) return true;
1545 if (game::IsSumoWrestling() && IsDead()) {
1546 game::EndSumoWrestling(!!IsPlayer());
1549 if (DeathFlags
& FORCE_DEATH
|| IsDead()) {
1550 if (Murderer
&& Murderer
->IsPlayer() && GetTeam()->GetKillEvilness()) game::DoEvilDeed(GetTeam()->GetKillEvilness());
1551 festring SpecifierMsg
;
1552 int SpecifierParts
= 0;
1553 if (GetPolymorphBackup()) {
1554 SpecifierMsg
<< " polymorphed into ";
1555 id::AddName(SpecifierMsg
, INDEFINITE
);
1558 if (!(DeathFlags
& IGNORE_TRAPS
) && IsStuck()) {
1559 if (SpecifierParts
++) SpecifierMsg
<< " and";
1560 SpecifierMsg
<< " caught in " << GetTrapDescription();
1562 if (GetAction() && !(DeathFlags
& IGNORE_UNCONSCIOUSNESS
&& GetAction()->IsUnconsciousness())) {
1563 festring ActionMsg
= GetAction()->GetDeathExplanation();
1564 if (!ActionMsg
.IsEmpty()) {
1565 if (SpecifierParts
> 1) {
1566 SpecifierMsg
= ActionMsg
<< ',' << SpecifierMsg
;
1568 if (SpecifierParts
) SpecifierMsg
<< " and";
1569 SpecifierMsg
<< ActionMsg
;
1574 festring NewMsg
= Msg
;
1575 if (Murderer
== this) {
1576 SEARCH_N_REPLACE(NewMsg
, "@bkp", CONST_S("by ") + GetPossessivePronoun(false) + " own");
1577 SEARCH_N_REPLACE(NewMsg
, "@bk", CONST_S("by ") + GetObjectPronoun(false) + "self");
1578 SEARCH_N_REPLACE(NewMsg
, "@k", GetObjectPronoun(false) + "self");
1580 SEARCH_N_REPLACE(NewMsg
, "@bkp", CONST_S("by ") + Murderer
->GetName(INDEFINITE
) + "'s");
1581 SEARCH_N_REPLACE(NewMsg
, "@bk", CONST_S("by ") + Murderer
->GetName(INDEFINITE
));
1582 SEARCH_N_REPLACE(NewMsg
, "@k", CONST_S("by ") + Murderer
->GetName(INDEFINITE
));
1584 if (SpecifierParts
) NewMsg
<< " while" << SpecifierMsg
;
1585 if (IsPlayer() && game::WizardModeIsActive()) ADD_MESSAGE("Death message: %s. Score: %d.", NewMsg
.CStr(), game::GetScore());
1586 Die(Murderer
, NewMsg
, DeathFlags
);
1593 truth
character::CheckStarvationDeath (cfestring
&Msg
) {
1594 if (GetNP() < 1 && UsesNutrition()) return CheckDeath(Msg
, 0, FORCE_DEATH
);
1599 void character::ThrowItem (int Direction
, item
*ToBeThrown
) {
1600 if (Direction
> 7) ABORT("Throw in TOO odd direction...");
1601 ToBeThrown
->Fly(this, Direction
, GetAttribute(ARM_STRENGTH
));
1605 void character::HasBeenHitByItem (character
*Thrower
, item
*Thingy
, int Damage
, double ToHitValue
, int Direction
) {
1606 if (IsPlayer()) ADD_MESSAGE("%s hits you.", Thingy
->CHAR_NAME(DEFINITE
));
1607 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s hits %s.", Thingy
->CHAR_NAME(DEFINITE
), CHAR_NAME(DEFINITE
));
1608 int BodyPart
= ChooseBodyPartToReceiveHit(ToHitValue
, DodgeValue
);
1609 int WeaponSkillHits
= Thrower
? CalculateWeaponSkillHits(Thrower
) : 0;
1610 int DoneDamage
= ReceiveBodyPartDamage(Thrower
, Damage
, PHYSICAL_DAMAGE
, BodyPart
, Direction
);
1611 truth Succeeded
= (GetBodyPart(BodyPart
) && HitEffect(Thrower
, Thingy
, Thingy
->GetPos(), THROW_ATTACK
, BodyPart
, Direction
, !DoneDamage
)) || DoneDamage
;
1612 if (Succeeded
&& Thrower
) Thrower
->WeaponSkillHit(Thingy
, THROW_ATTACK
, WeaponSkillHits
);
1613 festring DeathMsg
= CONST_S("killed by a flying ")+Thingy
->GetName(UNARTICLED
);
1614 if (CheckDeath(DeathMsg
, Thrower
)) return;
1616 if (Thrower
->CanBeSeenByPlayer())
1617 DeActivateVoluntaryAction(CONST_S("The attack of ")+Thrower
->GetName(DEFINITE
)+CONST_S(" interrupts you."));
1619 DeActivateVoluntaryAction(CONST_S("The attack interrupts you."));
1621 DeActivateVoluntaryAction(CONST_S("The hit interrupts you."));
1626 truth
character::DodgesFlyingItem (item
*Item
, double ToHitValue
) {
1627 return !Item
->EffectIsGood() && RAND() % int(100+ToHitValue
/DodgeValue
*100) < 100;
1631 void character::GetPlayerCommand () {
1633 truth HasActed
= false;
1635 game::DrawEverything();
1636 if (game::GetDangerFound()) {
1637 if (game::GetDangerFound() > 500.) {
1638 if (game::GetCausePanicFlag()) {
1639 game::SetCausePanicFlag(false);
1640 BeginTemporaryState(PANIC
, 500+RAND_N(500));
1642 game::AskForEscPress(CONST_S("You are horrified by your situation!"));
1643 } else if (ivanconfig::GetWarnAboutDanger()) {
1644 if (game::GetDangerFound() > 50.) game::AskForEscPress(CONST_S("You sense great danger!"));
1645 else game::AskForEscPress(CONST_S("You sense danger!"));
1647 game::SetDangerFound(0);
1649 game::SetIsInGetCommand(true);
1650 int Key
= GET_KEY();
1651 game::SetIsInGetCommand(false);
1652 if (Key
!= '+' && Key
!= '-' && Key
!= 'M') msgsystem::ThyMessagesAreNowOld(); // gum
1653 truth ValidKeyPressed
= false;
1655 for (c
= 0; c
< DIRECTION_COMMAND_KEYS
; ++c
) {
1656 if (Key
== game::GetMoveCommandKey(c
)) {
1657 HasActed
= TryMove(ApplyStateModification(game::GetMoveVector(c
)), true, game::PlayerIsRunning());
1658 ValidKeyPressed
= true;
1661 for (c
= 1; (cmd
= commandsystem::GetCommand(c
)); ++c
) {
1663 /* Numpad aliases for most commonly used commands */
1664 if (Key
== KEY_DEL
&& cmd
->GetLinkedFunction() == commandsystem::Eat
) Key
= cmd
->GetKey();
1665 if (Key
== KEY_INS
&& cmd
->GetLinkedFunction() == commandsystem::PickUp
) Key
= cmd
->GetKey();
1666 if (Key
== KEY_PLUS
&& cmd
->GetLinkedFunction() == commandsystem::EquipmentScreen
) Key
= cmd
->GetKey();
1668 if (Key
== cmd
->GetKey()) {
1669 if (game::IsInWilderness() && !commandsystem::GetCommand(c
)->IsUsableInWilderness())
1670 ADD_MESSAGE("This function cannot be used while in wilderness.");
1671 else if (!game::WizardModeIsActive() && commandsystem::GetCommand(c
)->IsWizardModeFunction())
1672 ADD_MESSAGE("Activate wizardmode to use this function.");
1674 HasActed
= commandsystem::GetCommand(c
)->GetLinkedFunction()(this);
1675 ValidKeyPressed
= true;
1679 if (!ValidKeyPressed
) ADD_MESSAGE("Unknown key. Press '?' for a list of commands.");
1681 game::IncreaseTurn();
1685 void character::Vomit (v2 Pos
, int Amount
, truth ShowMsg
) {
1686 if (!CanVomit()) return;
1688 if (IsPlayer()) ADD_MESSAGE("You vomit.");
1689 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s vomits.", CHAR_NAME(DEFINITE
));
1691 if (VomittingIsUnhealthy()) {
1692 EditExperience(ARM_STRENGTH
, -75, 1 << 9);
1693 EditExperience(LEG_STRENGTH
, -75, 1 << 9);
1696 EditNP(-2500-RAND()%2501);
1697 CheckStarvationDeath(CONST_S("vomited himself to death"));
1699 if (StateIsActivated(PARASITIZED
) && !(RAND() & 7)) {
1700 if (IsPlayer()) ADD_MESSAGE("You notice a dead broad tapeworm among your former stomach contents.");
1701 DeActivateTemporaryState(PARASITIZED
);
1703 if (!game::IsInWilderness()) {
1704 GetNearLSquare(Pos
)->ReceiveVomit(this, liquid::Spawn(GetVomitMaterial(), sLong(sqrt(GetBodyVolume())*Amount
/1000)));
1709 truth
character::Polymorph (character
*NewForm
, int Counter
) {
1710 if (!IsPolymorphable() || (!IsPlayer() && game::IsInWilderness())) {
1715 if (GetAction()) GetAction()->Terminate(false);
1716 NewForm
->SetAssignedName("");
1718 ADD_MESSAGE("Your body glows in a crimson light. You transform into %s!", NewForm
->CHAR_NAME(INDEFINITE
));
1719 else if (CanBeSeenByPlayer())
1720 ADD_MESSAGE("%s glows in a crimson light and %s transforms into %s!", CHAR_NAME(DEFINITE
), GetPersonalPronoun().CStr(), NewForm
->CHAR_NAME(INDEFINITE
));
1722 Flags
|= C_IN_NO_MSG_MODE
;
1723 NewForm
->Flags
|= C_IN_NO_MSG_MODE
;
1724 NewForm
->ChangeTeam(GetTeam());
1725 NewForm
->GenerationDanger
= GenerationDanger
;
1726 NewForm
->mOnEvents
= this->mOnEvents
;
1728 if (GetTeam()->GetLeader() == this) GetTeam()->SetLeader(NewForm
);
1732 NewForm
->PutToOrNear(Pos
);
1733 NewForm
->SetAssignedName(GetAssignedName());
1734 NewForm
->ActivateTemporaryState(POLYMORPHED
);
1735 NewForm
->SetTemporaryStateCounter(POLYMORPHED
, Counter
);
1737 if (TemporaryStateIsActivated(POLYMORPHED
)) {
1738 NewForm
->SetPolymorphBackup(GetPolymorphBackup());
1739 SetPolymorphBackup(0);
1742 NewForm
->SetPolymorphBackup(this);
1743 Flags
|= C_POLYMORPHED
;
1747 GetStack()->MoveItemsTo(NewForm
->GetStack());
1748 NewForm
->SetMoney(GetMoney());
1749 DonateEquipmentTo(NewForm
);
1750 Flags
&= ~C_IN_NO_MSG_MODE
;
1751 NewForm
->Flags
&= ~C_IN_NO_MSG_MODE
;
1752 NewForm
->CalculateAll();
1756 game::SetPlayer(NewForm
);
1757 game::SendLOSUpdateRequest();
1761 NewForm
->TestWalkability();
1766 void character::BeKicked (character
*Kicker
, item
*Boot
, bodypart
*Leg
, v2 HitPos
, double KickDamage
,
1767 double ToHitValue
, int Success
, int Direction
, truth Critical
, truth ForceHit
)
1770 game::mActor
= Kicker
;
1771 game::RunOnCharEvent(this, CONST_S("before_be_kicked"));
1773 switch (TakeHit(Kicker
, Boot
, Leg
, HitPos
, KickDamage
, ToHitValue
, Success
, KICK_ATTACK
, Direction
, Critical
, ForceHit
)) {
1777 if (IsEnabled() && !CheckBalance(KickDamage
)) {
1778 if (IsPlayer()) ADD_MESSAGE("The kick throws you off balance.");
1779 else if (Kicker
->IsPlayer()) ADD_MESSAGE("The kick throws %s off balance.", CHAR_DESCRIPTION(DEFINITE
));
1780 v2 FallToPos
= GetPos()+game::GetMoveVector(Direction
);
1781 FallTo(Kicker
, FallToPos
);
1787 /* Return true if still in balance */
1788 truth
character::CheckBalance (double KickDamage
) {
1789 return !CanMove() || IsStuck() || !KickDamage
|| (!IsFlying() && KickDamage
*5 < RAND()%GetSize());
1793 void character::FallTo (character
*GuiltyGuy
, v2 Where
) {
1795 lsquare
*MoveToSquare
[MAX_SQUARES_UNDER
];
1796 int Squares
= CalculateNewSquaresUnder(MoveToSquare
, Where
);
1798 truth NoRoom
= false;
1799 for (int c
= 0; c
< Squares
; ++c
) {
1800 olterrain
*Terrain
= MoveToSquare
[c
]->GetOLTerrain();
1801 if (Terrain
&& !CanMoveOn(Terrain
)) { NoRoom
= true; break; }
1805 if (IsPlayer()) ADD_MESSAGE("You hit your head on the wall.");
1806 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s hits %s head on the wall.", CHAR_NAME(DEFINITE
), GetPossessivePronoun().CStr());
1808 ReceiveDamage(GuiltyGuy
, 1+RAND()%5, PHYSICAL_DAMAGE
, HEAD
);
1809 CheckDeath(CONST_S("killed by hitting a wall due to being kicked @bk"), GuiltyGuy
);
1811 if (IsFreeForMe(MoveToSquare
[0])) Move(Where
, true);
1812 // Place code that handles characters bouncing to each other here
1818 truth
character::CheckCannibalism (cmaterial
*What
) const {
1819 return GetTorso()->GetMainMaterial()->IsSameAs(What
);
1823 void character::StandIdleAI () {
1824 SeekLeader(GetLeader());
1825 if (CheckForEnemies(true, true, true)) return;
1826 if (CheckForUsefulItemsOnGround()) return;
1827 if (FollowLeader(GetLeader())) return;
1828 if (CheckForDoors()) return;
1829 if (MoveTowardsHomePos()) return;
1830 if (CheckSadism()) return;
1835 truth
character::LoseConsciousness (int Counter
, truth HungerFaint
) {
1836 if (!AllowUnconsciousness()) return false;
1837 action
*Action
= GetAction();
1839 if (HungerFaint
&& !Action
->AllowUnconsciousness()) return false;
1840 if (Action
->IsUnconsciousness()) {
1841 static_cast<unconsciousness
*>(Action
)->RaiseCounterTo(Counter
);
1844 Action
->Terminate(false);
1846 if (IsPlayer()) ADD_MESSAGE("You lose consciousness.");
1847 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s loses consciousness.", CHAR_NAME(DEFINITE
));
1848 unconsciousness
*Unconsciousness
= unconsciousness::Spawn(this);
1849 Unconsciousness
->SetCounter(Counter
);
1850 SetAction(Unconsciousness
);
1855 void character::DeActivateVoluntaryAction (cfestring
&Reason
) {
1856 if (GetAction() && GetAction()->IsVoluntary()) {
1858 if (Reason
.GetSize()) ADD_MESSAGE("%s", Reason
.CStr());
1859 if (game::TruthQuestion(CONST_S("Continue ") + GetAction()->GetDescription()+"? [y/N]")) GetAction()->ActivateInDNDMode();
1860 else GetAction()->Terminate(false);
1863 GetAction()->Terminate(false);
1869 void character::ActionAutoTermination () {
1870 if (!GetAction() || !GetAction()->IsVoluntary() || GetAction()->InDNDMode()) return;
1872 for (int c
= 0; c
< game::GetTeams(); ++c
) {
1873 if (GetTeam()->GetRelation(game::GetTeam(c
)) == HOSTILE
) {
1874 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
) {
1876 if (ch
->IsEnabled() && ch
->CanBeSeenBy(this, false, true) && (ch
->CanMove() || ch
->GetPos().IsAdjacent(Pos
)) && ch
->CanAttack()) {
1878 ADD_MESSAGE("%s seems to be hostile.", ch
->CHAR_NAME(DEFINITE
));
1879 if (game::TruthQuestion(CONST_S("Continue ")+GetAction()->GetDescription()+"? [y/N]")) GetAction()->ActivateInDNDMode();
1880 else GetAction()->Terminate(false);
1882 GetAction()->Terminate(false);
1892 truth
character::CheckForEnemies (truth CheckDoors
, truth CheckGround
, truth MayMoveRandomly
, truth RunTowardsTarget
) {
1893 if (!IsEnabled()) return false;
1894 truth HostileCharsNear
= false;
1895 character
*NearestChar
= 0;
1896 sLong NearestDistance
= 0x7FFFFFFF;
1898 for (int c
= 0; c
< game::GetTeams(); ++c
) {
1899 if (GetTeam()->GetRelation(game::GetTeam(c
)) == HOSTILE
) {
1900 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
) {
1902 if (ch
->IsEnabled() && GetAttribute(WISDOM
) < ch
->GetAttackWisdomLimit()) {
1903 sLong ThisDistance
= Max
<sLong
>(abs(ch
->GetPos().X
- Pos
.X
), abs(ch
->GetPos().Y
- Pos
.Y
));
1904 if (ThisDistance
<= GetLOSRangeSquare()) HostileCharsNear
= true;
1905 if ((ThisDistance
< NearestDistance
|| (ThisDistance
== NearestDistance
&& !(RAND() % 3))) &&
1906 ch
->CanBeSeenBy(this, false, IsGoingSomeWhere()) &&
1907 (!IsGoingSomeWhere() || HasClearRouteTo(ch
->GetPos()))) {
1909 NearestDistance
= ThisDistance
;
1917 if (GetAttribute(INTELLIGENCE
) >= 10 || IsSpy()) game::CallForAttention(GetPos(), 100);
1918 if (SpecialEnemySightedReaction(NearestChar
)) return true;
1919 if (IsExtraCoward() && !StateIsActivated(PANIC
) && NearestChar
->GetRelativeDanger(this) >= 0.5) {
1920 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s sees %s.", CHAR_NAME(DEFINITE
), NearestChar
->CHAR_DESCRIPTION(DEFINITE
));
1921 BeginTemporaryState(PANIC
, 500+RAND()%500);
1923 if (!IsRetreating()) {
1924 if (CheckGround
&& NearestDistance
> 2 && CheckForUsefulItemsOnGround(false)) return true;
1925 SetGoingTo(NearestChar
->GetPos());
1927 SetGoingTo(Pos
-((NearestChar
->GetPos()-Pos
)<<4));
1929 return MoveTowardsTarget(true);
1931 character
*Leader
= GetLeader();
1932 if (Leader
== this) Leader
= 0;
1933 if (!Leader
&& IsGoingSomeWhere()) {
1934 if (!MoveTowardsTarget(RunTowardsTarget
)) {
1938 if (!IsEnabled()) return true;
1939 if (GetPos() == GoingTo
) TerminateGoingTo();
1943 if ((!Leader
|| (Leader
&& !IsGoingSomeWhere())) && HostileCharsNear
) {
1944 if (CheckDoors
&& CheckForDoors()) return true;
1945 if (CheckGround
&& CheckForUsefulItemsOnGround()) return true;
1946 if (MayMoveRandomly
&& MoveRandomly()) return true; // one has heard that an enemy is near but doesn't know where
1954 truth
character::CheckForDoors () {
1955 if (!CanOpen() || !IsEnabled()) return false;
1956 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
1957 lsquare
*Square
= GetNeighbourLSquare(d
);
1958 if (Square
&& Square
->GetOLTerrain() && Square
->GetOLTerrain()->Open(this)) return true;
1964 truth
character::CheckForUsefulItemsOnGround (truth CheckFood
) {
1965 if (StateIsActivated(PANIC
) || !IsEnabled()) return false;
1966 itemvector ItemVector
;
1967 GetStackUnder()->FillItemVector(ItemVector
);
1968 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) {
1969 if (ItemVector
[c
]->CanBeSeenBy(this) && ItemVector
[c
]->IsPickable(this)) {
1970 if (!(CommandFlags
& DONT_CHANGE_EQUIPMENT
) && TryToEquip(ItemVector
[c
])) return true;
1971 if (CheckFood
&& UsesNutrition() && !CheckIfSatiated() && TryToConsume(ItemVector
[c
])) return true;
1972 if ((ItemVector
[c
])->IsThrowingWeapon() && IsRangedAttacker() && TryToAddToInventory(ItemVector
[c
])) return true;
1979 truth
character::TryToAddToInventory (item
*Item
) {
1980 if (!(GetBurdenState() > STRESSED
) || !CanUseEquipment() || Item
->GetSquaresUnder() != 1) return false;
1981 room
*Room
= GetRoom();
1982 if (!Room
|| Room
->PickupItem(this, Item
, 1)) {
1983 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s picks up %s from the ground.", CHAR_NAME(DEFINITE
), Item
->CHAR_NAME(INDEFINITE
));
1984 Item
->MoveTo(GetStack());
1992 truth
character::CheckThrowItemOpportunity () {
1993 if (!IsRangedAttacker() || !CanThrow() || !IsHumanoid() || !IsSmall() || !IsEnabled()) return false; // total gum
1995 // (1) - Acquire target as nearest enemy
1996 // (2) - Check that this enemy is in range, and is in appropriate direction
1997 // No friendly fire!
1998 // (3) - check inventory for throwing weapon, select this weapon
1999 // (4) - throw item in direction where the enemy is
2001 //Check the visible area for hostiles
2002 int ThrowDirection
= 0;
2003 int TargetFound
= 0;
2006 int RangeMax
= GetLOSRange();
2007 int CandidateDirections
[7] = {0, 0, 0, 0, 0, 0, 0};
2008 int HostileFound
= 0;
2009 item
*ToBeThrown
= 0;
2010 itemvector ItemVector
;
2012 for (int r
= 1; r
<= RangeMax
; ++r
) {
2013 for (int dir
= 0; dir
< 8; ++dir
) {
2014 level
*Level
= GetLevel();
2017 case 0: TestPos
= v2(Pos
.X
-r
, Pos
.Y
-r
); break;
2018 case 1: TestPos
= v2(Pos
.X
, Pos
.Y
-r
); break;
2019 case 2: TestPos
= v2(Pos
.X
+r
, Pos
.Y
-r
); break;
2020 case 3: TestPos
= v2(Pos
.X
-r
, Pos
.Y
); break;
2021 case 4: TestPos
= v2(Pos
.X
+r
, Pos
.Y
); break;
2022 case 5: TestPos
= v2(Pos
.X
-r
, Pos
.Y
+r
); break;
2023 case 6: TestPos
= v2(Pos
.X
, Pos
.Y
+r
); break;
2024 case 7: TestPos
= v2(Pos
.X
+r
, Pos
.Y
+r
); break;
2026 if (Level
->IsValidPos(TestPos
)) {
2027 square
*TestSquare
= GetNearSquare(TestPos
);
2028 character
*Dude
= TestSquare
->GetCharacter();
2030 if (Dude
&& Dude
->IsEnabled() && GetRelation(Dude
) != HOSTILE
&& Dude
->CanBeSeenBy(this, false, true)) {
2031 CandidateDirections
[dir
] = BLOCKED
;
2033 if (Dude
&& Dude
->IsEnabled() && GetRelation(Dude
) == HOSTILE
&& Dude
->CanBeSeenBy(this, false, true) && CandidateDirections
[dir
] != BLOCKED
) {
2034 //then load this candidate position direction into the vector of possible throw directions
2035 CandidateDirections
[dir
] = SUCCESS
;
2043 for (int dir
= 0; dir
< 8; ++dir
) {
2044 if (CandidateDirections
[dir
] == SUCCESS
&& !TargetFound
) {
2045 ThrowDirection
= dir
;
2049 if (!TargetFound
) return false;
2053 //check inventory for throwing weapon
2054 GetStack()->FillItemVector(ItemVector
);
2055 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) {
2056 if (ItemVector
[c
]->IsThrowingWeapon()) {
2057 ToBeThrown
= ItemVector
[c
];
2061 if (!ToBeThrown
) return false;
2062 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s throws %s.", CHAR_NAME(DEFINITE
), ToBeThrown
->CHAR_NAME(INDEFINITE
));
2063 ThrowItem(ThrowDirection
, ToBeThrown
);
2064 EditExperience(ARM_STRENGTH
, 75, 1 << 8);
2065 EditExperience(DEXTERITY
, 75, 1 << 8);
2066 EditExperience(PERCEPTION
, 75, 1 << 8);
2074 truth
character::FollowLeader (character
*Leader
) {
2075 if (!Leader
|| Leader
== this || !IsEnabled()) return false;
2076 if (CommandFlags
& FOLLOW_LEADER
&& Leader
->CanBeSeenBy(this) && Leader
->SquareUnderCanBeSeenBy(this, true)) {
2077 v2 Distance
= GetPos()-GoingTo
;
2078 if (abs(Distance
.X
) <= 2 && abs(Distance
.Y
) <= 2) return false;
2079 return MoveTowardsTarget(false);
2081 if (IsGoingSomeWhere()) {
2082 if (!MoveTowardsTarget(true)) {
2092 void character::SeekLeader (ccharacter
*Leader
) {
2093 if (Leader
&& Leader
!= this) {
2094 if (Leader
->CanBeSeenBy(this) && (Leader
->SquareUnderCanBeSeenBy(this, true) || !IsGoingSomeWhere())) {
2095 if (CommandFlags
& FOLLOW_LEADER
) SetGoingTo(Leader
->GetPos());
2096 } else if (!IsGoingSomeWhere()) {
2097 team
*Team
= GetTeam();
2098 for (std::list
<character
*>::const_iterator i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
) {
2100 if (ch
->IsEnabled() && ch
->GetID() != GetID() &&
2101 (CommandFlags
& FOLLOW_LEADER
) == (ch
->CommandFlags
& FOLLOW_LEADER
) && ch
->CanBeSeenBy(this)) {
2102 v2 Pos
= ch
->GetPos();
2103 v2 Distance
= GetPos()-Pos
;
2104 if (abs(Distance
.X
) > 2 && abs(Distance
.Y
) > 2) {
2115 int character::GetMoveEase () const {
2116 switch (BurdenState
) {
2118 case STRESSED
: return 50;
2119 case BURDENED
: return 75;
2120 case UNBURDENED
: return 100;
2126 int character::GetLOSRange () const {
2127 if (!game::IsInWilderness()) return GetAttribute(PERCEPTION
)*GetLevel()->GetLOSModifier()/48;
2132 truth
character::Displace (character
*Who
, truth Forced
) {
2133 if (GetBurdenState() == OVER_LOADED
) {
2135 cchar
*CrawlVerb
= StateIsActivated(LEVITATION
) ? "float" : "crawl";
2136 ADD_MESSAGE("You try very hard to %s forward. But your load is too heavy.", CrawlVerb
);
2143 double Danger
= GetRelativeDanger(Who
);
2144 int PriorityDifference
= Limit(GetDisplacePriority()-Who
->GetDisplacePriority(), -31, 31);
2146 if (IsPlayer()) ++PriorityDifference
;
2147 else if (Who
->IsPlayer()) --PriorityDifference
;
2149 if (PriorityDifference
>= 0) Danger
*= 1 << PriorityDifference
;
2150 else Danger
/= 1 << -PriorityDifference
;
2152 if (IsSmall() && Who
->IsSmall() &&
2153 (Forced
|| Danger
> 1.0 || !(Who
->IsPlayer() || Who
->IsBadPath(GetPos()))) &&
2154 !IsStuck() && !Who
->IsStuck() && (!Who
->GetAction() || Who
->GetAction()->TryDisplace()) &&
2155 CanMove() && Who
->CanMove() && Who
->CanMoveOn(GetLSquareUnder())) {
2156 if (IsPlayer()) ADD_MESSAGE("You displace %s!", Who
->CHAR_DESCRIPTION(DEFINITE
));
2157 else if (Who
->IsPlayer()) ADD_MESSAGE("%s displaces you!", CHAR_DESCRIPTION(DEFINITE
));
2158 else if (CanBeSeenByPlayer() || Who
->CanBeSeenByPlayer()) ADD_MESSAGE("%s displaces %s!", CHAR_DESCRIPTION(DEFINITE
), Who
->CHAR_DESCRIPTION(DEFINITE
));
2159 lsquare
*OldSquareUnder1
[MAX_SQUARES_UNDER
];
2160 lsquare
*OldSquareUnder2
[MAX_SQUARES_UNDER
];
2161 for (int c
= 0; c
< GetSquaresUnder(); ++c
) OldSquareUnder1
[c
] = GetLSquareUnder(c
);
2162 for (int c
= 0; c
< Who
->GetSquaresUnder(); ++c
) OldSquareUnder2
[c
] = Who
->GetLSquareUnder(c
);
2164 v2 WhoPos
= Who
->GetPos();
2169 EditAP(-GetMoveAPRequirement(GetSquareUnder()->GetEntryDifficulty()) - 500);
2170 EditNP(-12*GetSquareUnder()->GetEntryDifficulty());
2171 EditExperience(AGILITY
, 75, GetSquareUnder()->GetEntryDifficulty() << 7);
2172 if (IsPlayer()) ShowNewPosInfo();
2173 if (Who
->IsPlayer()) Who
->ShowNewPosInfo();
2174 SignalStepFrom(OldSquareUnder1
);
2175 Who
->SignalStepFrom(OldSquareUnder2
);
2179 ADD_MESSAGE("%s resists!", Who
->CHAR_DESCRIPTION(DEFINITE
));
2188 void character::SetNP (sLong What
) {
2189 int OldState
= GetHungerState();
2192 int NewState
= GetHungerState();
2193 if (NewState
== STARVING
&& OldState
> STARVING
) DeActivateVoluntaryAction(CONST_S("You are getting really hungry."));
2194 else if (NewState
== VERY_HUNGRY
&& OldState
> VERY_HUNGRY
) DeActivateVoluntaryAction(CONST_S("You are getting very hungry."));
2195 else if (NewState
== HUNGRY
&& OldState
> HUNGRY
) DeActivateVoluntaryAction(CONST_S("You are getting hungry."));
2200 void character::ShowNewPosInfo () const {
2201 msgsystem::EnterBigMessageMode();
2204 if (ivanconfig::GetAutoCenterMap()) {
2205 game::UpdateCameraX();
2206 game::UpdateCameraY();
2208 if (Pos
.X
< game::GetCamera().X
+3 || Pos
.X
>= game::GetCamera().X
+game::GetScreenXSize()-3) game::UpdateCameraX();
2209 if (Pos
.Y
< game::GetCamera().Y
+3 || Pos
.Y
>= game::GetCamera().Y
+game::GetScreenYSize()-3) game::UpdateCameraY();
2212 game::SendLOSUpdateRequest();
2213 game::DrawEverythingNoBlit();
2216 if (!game::IsInWilderness()) {
2217 if (GetLSquareUnder()->IsDark() && !game::GetSeeWholeMapCheatMode()) ADD_MESSAGE("It's dark in here!");
2219 GetLSquareUnder()->ShowSmokeMessage();
2220 itemvectorvector PileVector
;
2221 GetStackUnder()->Pile(PileVector
, this, CENTER
);
2223 if (PileVector
.size()) {
2224 truth Feel
= !GetLSquareUnder()->IsTransparent() || GetLSquareUnder()->IsDark();
2225 if (PileVector
.size() == 1) {
2226 if (Feel
) ADD_MESSAGE("You feel %s lying here.", PileVector
[0][0]->GetName(INDEFINITE
, PileVector
[0].size()).CStr());
2227 else ADD_MESSAGE("%s %s lying here.", PileVector
[0][0]->GetName(INDEFINITE
, PileVector
[0].size()).CStr(), PileVector
[0].size() == 1 ? "is" : "are");
2230 for (uInt c
= 0; c
< PileVector
.size(); ++c
) {
2231 if ((Items
+= PileVector
[c
].size()) > 3) break;
2234 if (Feel
) ADD_MESSAGE("You feel several items lying here.");
2235 else ADD_MESSAGE("Several items are lying here.");
2237 if (Feel
) ADD_MESSAGE("You feel a few items lying here.");
2238 else ADD_MESSAGE("A few items are lying here.");
2244 GetLSquareUnder()->GetSideItemDescription(SideItems
);
2246 if (!SideItems
.IsEmpty()) ADD_MESSAGE("There is %s.", SideItems
.CStr());
2248 if (GetLSquareUnder()->HasEngravings()) {
2249 if (CanRead()) ADD_MESSAGE("Something has been engraved here: \"%s\"", GetLSquareUnder()->GetEngraved());
2250 else ADD_MESSAGE("Something has been engraved here.");
2254 msgsystem::LeaveBigMessageMode();
2258 void character::Hostility (character
*Enemy
) {
2259 if (Enemy
== this || !Enemy
|| !Team
|| !Enemy
->Team
) return;
2260 if (Enemy
->IsMasochist() && GetRelation(Enemy
) == FRIEND
) return;
2261 if (!IsAlly(Enemy
)) {
2262 GetTeam()->Hostility(Enemy
->GetTeam());
2263 } else if (IsPlayer() && !Enemy
->IsPlayer()) {
2264 // I believe both may be players due to polymorph feature...
2265 if (Enemy
->CanBeSeenByPlayer()) ADD_MESSAGE("%s becomes enraged.", Enemy
->CHAR_NAME(DEFINITE
));
2266 Enemy
->ChangeTeam(game::GetTeam(BETRAYED_TEAM
));
2271 stack
*character::GetGiftStack () const {
2272 if (GetLSquareUnder()->GetRoomIndex() && !GetLSquareUnder()->GetRoom()->AllowDropGifts()) return GetStack();
2273 return GetStackUnder();
2277 truth
character::MoveRandomlyInRoom () {
2278 for (int c
= 0; c
< 10; ++c
) {
2279 v2 ToTry
= game::GetMoveVector(RAND()&7);
2280 if (GetLevel()->IsValidPos(GetPos()+ToTry
)) {
2281 lsquare
*Square
= GetNearLSquare(GetPos()+ToTry
);
2282 if (!Square
->IsDangerous(this) && !Square
->IsScary(this) &&
2283 (!Square
->GetOLTerrain() || !Square
->GetOLTerrain()->IsDoor()) &&
2284 TryMove(ToTry
, false, false)) return true;
2291 truth
character::IsPassableSquare (int x
, int y
) const {
2292 area
*ca
= GetSquareUnder()->GetArea();
2293 if (x
< 0 || y
< 0 || x
>= ca
->GetXSize() || y
>= ca
->GetYSize()) return false;
2294 lsquare
*sq
= static_cast<lsquare
*>(ca
->GetSquare(x
, y
));
2295 return sq
&& CanMoveOn(sq
);
2299 truth
character::IsInCorridor () const { return IsInCorridor(GetPos().X
, GetPos().Y
); }
2301 truth
character::IsInCorridor (int x
, int y
) const {
2302 if (!IsPassableSquare(x
, y
-1) && !IsPassableSquare(x
, y
+1)) return true;
2303 if (!IsPassableSquare(x
-1, y
) && !IsPassableSquare(x
+1, y
)) return true;
2304 if (IsPassableSquare(x
, y
-1) && IsPassableSquare(x
, y
+1) &&
2305 (IsPassableSquare(x
-1, y
) || IsPassableSquare(x
+1, y
))) return false;
2306 if (IsPassableSquare(x
-1, y
) && IsPassableSquare(x
+1, y
) &&
2307 (IsPassableSquare(x
, y
-1) || IsPassableSquare(x
, y
+1))) return false;
2308 if (!IsPassableSquare(x
-1, y
-1) && !IsPassableSquare(x
+1, y
-1) &&
2309 !IsPassableSquare(x
-1, y
+1) && !IsPassableSquare(x
+1, y
+1)) return true;
2314 truth
character::IsInCorridor (const v2 dir
) const {
2315 v2 nxy
= GetPos()+dir
;
2316 return IsInCorridor(nxy
.X
, nxy
.Y
);
2320 void character::GoOn (go
*Go
, truth FirstStep
) {
2321 //fprintf(stderr, "corridor: %s\n", IsInCorridor() ? "tan" : "ona");
2322 if (FirstStep
) Go
->SetIsWalkingInOpen(!IsInCorridor());
2324 v2 MoveVector
= ApplyStateModification(game::GetMoveVector(Go
->GetDirection()));
2325 lsquare
*MoveToSquare
[MAX_SQUARES_UNDER
];
2327 int Squares
= CalculateNewSquaresUnder(MoveToSquare
, GetPos()+MoveVector
);
2328 if (!Squares
|| !CanMoveOn(MoveToSquare
[0])) {
2329 Go
->Terminate(false);
2333 if (!FirstStep
&& !Go
->IsWalkingInOpen() && !IsInCorridor(MoveVector
)) {
2334 Go
->Terminate(false);
2338 uInt OldRoomIndex
= GetLSquareUnder()->GetRoomIndex();
2339 uInt CurrentRoomIndex
= MoveToSquare
[0]->GetRoomIndex();
2341 if ((OldRoomIndex
&& (CurrentRoomIndex
!= OldRoomIndex
)) && !FirstStep
) {
2342 Go
->Terminate(false);
2346 for (int c
= 0; c
< Squares
; ++c
) {
2347 if ((MoveToSquare
[c
]->GetCharacter() && GetTeam() != MoveToSquare
[c
]->GetCharacter()->GetTeam()) || MoveToSquare
[c
]->IsDangerous(this)) {
2348 Go
->Terminate(false);
2364 static const int revDir
[8] = { 7, 6, 5, 4, 3, 3, 1, 0 };
2365 static const bool orthoDir
[8] = { false, true, false, true, true, false, true, false };
2367 int OKDirectionsCounter
= 0, gd
= Go
->GetDirection(), odc
= 0;
2368 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
2369 lsquare
*Square
= GetNeighbourLSquare(d
);
2370 if (Square
&& CanMoveOn(Square
)) ++OKDirectionsCounter
;
2374 if (orthoDir
[gd
] && !Go
->IsWalkingInOpen()) {
2375 // check if there is dead end forward and we have only one turn
2376 v2 nxy
= GetPos()+MoveVector
;
2377 v2 nxyff
= nxy
+MoveVector
;
2379 lsquare
*sqf
= static_cast<lsquare
*>(GetSquareUnder()->GetArea()->GetSquare(nxy
));
2380 lsquare
*sqff
= static_cast<lsquare
*>(GetSquareUnder()->GetArea()->GetSquare(nxyff
));
2381 if (sqf
&& CanMoveOn(sqf
) && (!sqff
|| !CanMoveOn(sqff
))) {
2383 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
2384 if (d
== gd
|| d
== revDir
[gd
] || !orthoDir
[d
]) continue;
2385 v2 sqxy
= nxy
+game::GetMoveVector(d
);
2386 lsquare
*sq
= static_cast<lsquare
*>(GetSquareUnder()->GetArea()->GetSquare(sqxy
));
2387 if (sq
&& CanMoveOn(sq
)) {
2393 //fprintf(stderr, "*: %d; nDir: %d\n", odc, nDir);
2396 bool doStop
= false;
2397 if (!Go
->IsWalkingInOpen()) {
2399 //fprintf(stderr, "dc: %d\n", OKDirectionsCounter);
2400 if (Go
->prevWasTurn()) {
2401 Go
->SetPrevWasTurn(false);
2402 } else if (odc
== 1) {
2403 // if we will step on something, do it
2404 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
2405 lsquare
*Square
= GetNeighbourLSquare(d
);
2406 if (Square
&& Square
->GetStack()->HasSomethingFunny(this, ivanconfig::GetStopOnCorpses(), ivanconfig::GetStopOnSeenItems())) {
2412 // follow the turn; 3: back, forward and turn
2417 if (gd
== 3) newDir
= 0;
2418 else if (gd
== 4) newDir
= 2;
2421 if (gd
== 1) newDir
= 0;
2422 else if (gd
== 6) newDir
= 5;
2425 if (gd
== 1) newDir
= 2;
2426 else if (gd
== 6) newDir
= 7;
2429 if (gd
== 3) newDir
= 5;
2430 else if (gd
== 4) newDir
= 7;
2433 //if (newDir < 0) ABORT("go error");
2434 if (newDir
< 0) { Go
->Terminate(false); return; }
2435 lsquare
*Square
= GetNeighbourLSquare(newDir
);
2436 if (Square
&& CanMoveOn(Square
)) {
2437 // fuckin' copypasta
2438 MoveVector
= ApplyStateModification(game::GetMoveVector(newDir
));
2439 int squares
= CalculateNewSquaresUnder(MoveToSquare
, GetPos()+MoveVector
);
2441 for (int c
= 0; c
< squares
; ++c
) {
2442 if ((MoveToSquare
[c
]->GetCharacter() && GetTeam() != MoveToSquare
[c
]->GetCharacter()->GetTeam()) || MoveToSquare
[c
]->IsDangerous(this)) {
2453 if (newDir
< 0) { Go
->Terminate(false); return; }
2455 //fprintf(stderr, "newDir: %d\n", newDir);
2456 Go
->SetDirection(newDir
);
2457 Go
->SetPrevWasTurn(true);
2458 v2 nxyf
= GetPos()+MoveVector
+game::GetMoveVector(newDir
);
2459 v2 nxyff
= nxyf
+game::GetMoveVector(newDir
);
2460 lsquare
*sqf
= static_cast<lsquare
*>(GetSquareUnder()->GetArea()->GetSquare(nxyf
));
2461 lsquare
*sqff
= static_cast<lsquare
*>(GetSquareUnder()->GetArea()->GetSquare(nxyff
));
2462 if (sqf
&& CanMoveOn(sqf
)) {
2463 Go
->SetPrevWasTurn(sqff
&& CanMoveOn(sqff
));
2466 } else if (!IsInCorridor()) {
2467 Go
->Terminate(false);
2471 Go
->SetPrevWasTurn(false);
2472 //if (OKDirectionsCounter <= 2) Go->SetIsWalkingInOpen(false);
2473 Go
->SetIsWalkingInOpen(!IsInCorridor());
2476 square
*BeginSquare
= GetSquareUnder();
2479 for (int c
= 0; c
< Squares
; ++c
) {
2480 if (MoveToSquare
[c
]->GetStack()->HasSomethingFunny(this, ivanconfig::GetStopOnCorpses(), ivanconfig::GetStopOnSeenItems())) {
2487 truth moveOk
= TryMove(MoveVector
, true, game::PlayerIsRunning());
2488 if (!moveOk
|| BeginSquare
== GetSquareUnder() || (CurrentRoomIndex
&& (OldRoomIndex
!= CurrentRoomIndex
))) {
2490 if (ivanconfig::GetGoingDelay()) DELAY(ivanconfig::GetGoingDelay());
2491 game::DrawEverything();
2493 Go
->Terminate(false);
2497 if (doStop
/* || GetStackUnder()->HasSomethingFunny(this, ivanconfig::GetStopOnCorpses(), ivanconfig::GetStopOnSeenItems())*/) {
2498 Go
->Terminate(false);
2500 if (ivanconfig::GetGoingDelay()) DELAY(ivanconfig::GetGoingDelay());
2502 game::DrawEverything();
2506 void character::SetTeam (team
*What
) {
2507 /*k8 if(Team) int esko = esko = 2; */
2509 SetTeamIterator(What
->Add(this));
2513 void character::ChangeTeam (team
*What
) {
2514 if (Team
) Team
->Remove(GetTeamIterator());
2516 SendNewDrawRequest();
2517 if (Team
) SetTeamIterator(Team
->Add(this));
2521 truth
character::ChangeRandomAttribute (int HowMuch
) {
2522 for (int c
= 0; c
< 50; ++c
) {
2523 int AttribID
= RAND()%ATTRIBUTES
;
2524 if (EditAttribute(AttribID
, HowMuch
)) return true;
2530 int character::RandomizeReply (sLong
&Said
, int Replies
) {
2531 truth NotSaid
= false;
2532 for (int c
= 0; c
< Replies
; ++c
) {
2533 if (!(Said
& (1 << c
))) {
2538 if (!NotSaid
) Said
= 0;
2540 while (Said
& 1 << (ToSay
= RAND() % Replies
));
2546 void character::DisplayInfo (festring
&Msg
) {
2548 Msg
<< " You are " << GetStandVerb() << " here.";
2550 Msg
<< ' ' << GetName(INDEFINITE
).CapitalizeCopy() << " is " << GetStandVerb() << " here. " << GetPersonalPronoun().CapitalizeCopy();
2551 cchar
*Separator1
= GetAction() ? "," : " and";
2552 cchar
*Separator2
= " and";
2553 if (GetTeam() == PLAYER
->GetTeam()) {
2556 int Relation
= GetRelation(PLAYER
);
2557 if (Relation
== HOSTILE
) Msg
<< " is hostile";
2558 else if (Relation
== UNCARING
) {
2559 Msg
<< " does not care about you";
2560 Separator1
= Separator2
= " and is";
2562 Msg
<< " is friendly";
2565 if (StateIsActivated(PANIC
)) {
2566 Msg
<< Separator1
<< " panicked";
2567 Separator2
= " and";
2569 if (GetAction()) Msg
<< Separator2
<< ' ' << GetAction()->GetDescription();
2575 void character::TestWalkability () {
2576 if (!IsEnabled()) return;
2577 square
*SquareUnder
= !game::IsInWilderness() ? GetSquareUnder() : PLAYER
->GetSquareUnder();
2578 if (SquareUnder
->IsFatalToStay() && !CanMoveOn(SquareUnder
)) {
2579 truth Alive
= false;
2580 if (!game::IsInWilderness() || IsPlayer()) {
2581 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
2582 square
*Square
= GetNeighbourSquare(d
);
2583 if (Square
&& CanMoveOn(Square
) && IsFreeForMe(Square
)) {
2584 if (IsPlayer()) ADD_MESSAGE("%s.", SquareUnder
->SurviveMessage(this));
2585 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s %s.", CHAR_NAME(DEFINITE
), SquareUnder
->MonsterSurviveMessage(this));
2586 Move(Square
->GetPos(), true); // actually, this shouldn't be a teleport move
2587 SquareUnder
->SurviveEffect(this);
2597 festring DeathMsg
= festring(SquareUnder
->DeathMessage(this));
2598 game::AskForEscPress(DeathMsg
+".");
2599 festring Msg
= SquareUnder
->ScoreEntry(this);
2600 PLAYER
->AddScoreEntry(Msg
);
2603 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s %s.", CHAR_NAME(DEFINITE
), SquareUnder
->MonsterDeathVerb(this));
2604 Die(0, SquareUnder
->ScoreEntry(this), DISALLOW_MSG
);
2611 int character::GetSize () const {
2612 return GetTorso()->GetSize();
2616 void character::SetMainMaterial (material
*NewMaterial
, int SpecialFlags
) {
2617 NewMaterial
->SetVolume(GetBodyPart(0)->GetMainMaterial()->GetVolume());
2618 GetBodyPart(0)->SetMainMaterial(NewMaterial
, SpecialFlags
);
2619 for (int c
= 1; c
< BodyParts
; ++c
) {
2620 NewMaterial
= NewMaterial
->SpawnMore(GetBodyPart(c
)->GetMainMaterial()->GetVolume());
2621 GetBodyPart(c
)->SetMainMaterial(NewMaterial
, SpecialFlags
);
2626 void character::ChangeMainMaterial (material
*NewMaterial
, int SpecialFlags
) {
2627 NewMaterial
->SetVolume(GetBodyPart(0)->GetMainMaterial()->GetVolume());
2628 GetBodyPart(0)->ChangeMainMaterial(NewMaterial
, SpecialFlags
);
2629 for (int c
= 1; c
< BodyParts
; ++c
) {
2630 NewMaterial
= NewMaterial
->SpawnMore(GetBodyPart(c
)->GetMainMaterial()->GetVolume());
2631 GetBodyPart(c
)->ChangeMainMaterial(NewMaterial
, SpecialFlags
);
2636 void character::SetSecondaryMaterial (material
*, int) {
2637 ABORT("Illegal character::SetSecondaryMaterial call!");
2641 void character::ChangeSecondaryMaterial (material
*, int) {
2642 ABORT("Illegal character::ChangeSecondaryMaterial call!");
2646 void character::TeleportRandomly (truth Intentional
) {
2647 v2 TelePos
= ERROR_V2
;
2648 if (StateIsActivated(TELEPORT_CONTROL
)) {
2650 v2 Input
= game::PositionQuestion(CONST_S("Where do you wish to teleport? [direction keys move cursor, space accepts]"), GetPos(), &game::TeleportHandler
, 0, false);
2651 if (Input
== ERROR_V2
) Input
= GetPos(); // esc pressed
2652 lsquare
*Square
= GetNearLSquare(Input
);
2653 if (CanMoveOn(Square
) || game::GoThroughWallsCheatIsActive()) {
2654 if (Square
->GetPos() == GetPos()) {
2655 ADD_MESSAGE("You disappear and reappear.");
2658 if (IsFreeForMe(Square
)) {
2659 if ((Input
-GetPos()).GetLengthSquare() <= GetTeleportRangeSquare()) {
2660 EditExperience(INTELLIGENCE
, 100, 1 << 10);
2663 ADD_MESSAGE("You cannot concentrate yourself enough to control a teleport that far.");
2666 character
*C
= Square
->GetCharacter();
2667 if (C
) ADD_MESSAGE("For a moment you feel very much like %s.", C
->CHAR_NAME(INDEFINITE
));
2668 else ADD_MESSAGE("You feel that something weird has happened, but can't really tell what exactly.");
2671 ADD_MESSAGE("You feel like having been hit by something really hard from the inside.");
2673 } else if (!Intentional
) {
2674 if (IsGoingSomeWhere() && GetLevel()->IsValidPos(GoingTo
)) {
2675 v2 Where
= GetLevel()->GetNearestFreeSquare(this, GoingTo
);
2676 if (Where
!= ERROR_V2
&& (Where
-GetPos()).GetLengthSquare() <= GetTeleportRangeSquare()) {
2677 EditExperience(INTELLIGENCE
, 100, 1 << 10);
2685 ADD_MESSAGE("A rainbow-colored whirlpool twists the existence around you. You are sucked through a tunnel piercing a myriad of surreal universes. Luckily you return to this dimension in one piece.");
2688 if (TelePos
!= ERROR_V2
) Move(TelePos
, true);
2689 else Move(GetLevel()->GetRandomSquare(this), true);
2691 if (!IsPlayer() && CanBeSeenByPlayer()) ADD_MESSAGE("%s appears.", CHAR_NAME(INDEFINITE
));
2693 if (GetAction() && GetAction()->IsVoluntary()) GetAction()->Terminate(false);
2697 void character::RestoreHP () {
2698 doforbodyparts()(this, &bodypart::FastRestoreHP
);
2703 void character::RestoreLivingHP () {
2705 for (int c
= 0; c
< BodyParts
; ++c
) {
2706 bodypart
*BodyPart
= GetBodyPart(c
);
2707 if (BodyPart
&& BodyPart
->CanRegenerate()) {
2708 BodyPart
->FastRestoreHP();
2709 HP
+= BodyPart
->GetHP();
2715 truth
character::AllowDamageTypeBloodSpill (int Type
) {
2716 switch (Type
&0xFFF) {
2717 case PHYSICAL_DAMAGE
:
2726 case MUSTARD_GAS_DAMAGE
:
2730 ABORT("Unknown blood effect destroyed the dungeon!");
2735 /* Returns truly done damage */
2736 int character::ReceiveBodyPartDamage (character
*Damager
, int Damage
, int Type
, int BodyPartIndex
,
2737 int Direction
, truth PenetrateResistance
, truth Critical
, truth ShowNoDamageMsg
, truth CaptureBodyPart
)
2739 bodypart
*BodyPart
= GetBodyPart(BodyPartIndex
);
2740 if (!Damager
|| Damager
->AttackMayDamageArmor()) BodyPart
->DamageArmor(Damager
, Damage
, Type
);
2741 if (!PenetrateResistance
) {
2742 Damage
-= (BodyPart
->GetTotalResistance(Type
)>>1)+RAND()%((BodyPart
->GetTotalResistance(Type
)>>1)+1);
2744 if (int(Damage
) < 1) {
2748 if (ShowNoDamageMsg
) {
2749 if (IsPlayer()) ADD_MESSAGE("You are not hurt.");
2750 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s is not hurt.", GetPersonalPronoun().CStr());
2756 if (Critical
&& AllowDamageTypeBloodSpill(Type
) && !game::IsInWilderness()) {
2757 BodyPart
->SpillBlood(2+(RAND()&1));
2758 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
2759 lsquare
*Square
= GetNeighbourLSquare(d
);
2760 if (Square
&& Square
->IsFlyable()) BodyPart
->SpillBlood(1, Square
->GetPos());
2764 if (BodyPart
->ReceiveDamage(Damager
, Damage
, Type
, Direction
) && BodyPartCanBeSevered(BodyPartIndex
)) {
2765 if (DamageTypeDestroysBodyPart(Type
)) {
2766 if (IsPlayer()) ADD_MESSAGE("Your %s is destroyed!", BodyPart
->GetBodyPartName().CStr());
2767 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s %s is destroyed!", GetPossessivePronoun().CStr(), BodyPart
->GetBodyPartName().CStr());
2768 GetBodyPart(BodyPartIndex
)->DropEquipment();
2769 item
*Severed
= SevereBodyPart(BodyPartIndex
);
2770 if (Severed
) Severed
->DestroyBodyPart(!game::IsInWilderness() ? GetStackUnder() : GetStack());
2771 SendNewDrawRequest();
2772 if (IsPlayer()) game::AskForEscPress(CONST_S("Bodypart destroyed!"));
2774 if (IsPlayer()) ADD_MESSAGE("Your %s is severed off!", BodyPart
->GetBodyPartName().CStr());
2775 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s %s is severed off!", GetPossessivePronoun().CStr(), BodyPart
->GetBodyPartName().CStr());
2776 item
*Severed
= SevereBodyPart(BodyPartIndex
);
2777 SendNewDrawRequest();
2779 if (CaptureBodyPart
) {
2780 Damager
->GetLSquareUnder()->AddItem(Severed
);
2781 } else if (!game::IsInWilderness()) {
2782 /** No multi-tile humanoid support! */
2783 GetStackUnder()->AddItem(Severed
);
2784 if (Direction
!= YOURSELF
) Severed
->Fly(0, Direction
, Damage
);
2786 GetStack()->AddItem(Severed
);
2788 Severed
->DropEquipment();
2789 } else if (IsPlayer() || CanBeSeenByPlayer()) {
2790 ADD_MESSAGE("It vanishes.");
2792 if (IsPlayer()) game::AskForEscPress(CONST_S("Bodypart severed!"));
2794 if (CanPanicFromSeveredBodyPart() && RAND()%100 < GetPanicLevel() && !StateIsActivated(PANIC
) && !IsDead()) {
2795 BeginTemporaryState(PANIC
, 1000+RAND()%1001);
2797 SpecialBodyPartSeverReaction();
2800 if (!IsDead()) CheckPanic(500);
2806 /* Returns 0 if bodypart disappears */
2807 item
*character::SevereBodyPart (int BodyPartIndex
, truth ForceDisappearance
, stack
*EquipmentDropStack
) {
2808 bodypart
*BodyPart
= GetBodyPart(BodyPartIndex
);
2809 if (StateIsActivated(LEPROSY
)) BodyPart
->GetMainMaterial()->SetIsInfectedByLeprosy(true);
2810 if (ForceDisappearance
|| BodyPartsDisappearWhenSevered() || StateIsActivated(POLYMORPHED
) || game::AllBodyPartsVanish()) {
2811 BodyPart
->DropEquipment(EquipmentDropStack
);
2812 BodyPart
->RemoveFromSlot();
2813 CalculateAttributeBonuses();
2814 CalculateBattleInfo();
2815 BodyPart
->SendToHell();
2816 SignalPossibleTransparencyChange();
2817 RemoveTraps(BodyPartIndex
);
2820 BodyPart
->SetOwnerDescription("of " + GetName(INDEFINITE
));
2821 BodyPart
->SetIsUnique(LeftOversAreUnique());
2822 UpdateBodyPartPicture(BodyPartIndex
, true);
2823 BodyPart
->RemoveFromSlot();
2824 BodyPart
->RandomizePosition();
2825 CalculateAttributeBonuses();
2826 CalculateBattleInfo();
2828 SignalPossibleTransparencyChange();
2829 RemoveTraps(BodyPartIndex
);
2834 /* The second int is actually TargetFlags, which is not used here, but seems to be used in humanoid::ReceiveDamage.
2835 * Returns true if the character really receives damage */
2836 truth
character::ReceiveDamage (character
*Damager
, int Damage
, int Type
, int, int Direction
,
2837 truth
, truth PenetrateArmor
, truth Critical
, truth ShowMsg
)
2839 truth Affected
= ReceiveBodyPartDamage(Damager
, Damage
, Type
, 0, Direction
, PenetrateArmor
, Critical
, ShowMsg
);
2840 if (DamageTypeAffectsInventory(Type
)) {
2841 for (int c
= 0; c
< GetEquipments(); ++c
) {
2842 item
*Equipment
= GetEquipment(c
);
2843 if (Equipment
) Equipment
->ReceiveDamage(Damager
, Damage
, Type
);
2845 GetStack()->ReceiveDamage(Damager
, Damage
, Type
);
2851 festring
character::GetDescription (int Case
) const {
2852 if (IsPlayer()) return CONST_S("you");
2853 if (CanBeSeenByPlayer()) return GetName(Case
);
2854 return CONST_S("something");
2858 festring
character::GetPersonalPronoun (truth PlayersView
) const {
2859 if (IsPlayer() && PlayersView
) return CONST_S("you");
2860 if (GetSex() == UNDEFINED
|| (PlayersView
&& !CanBeSeenByPlayer() && !game::GetSeeWholeMapCheatMode())) return CONST_S("it");
2861 if (GetSex() == MALE
) return CONST_S("he");
2862 return CONST_S("she");
2866 festring
character::GetPossessivePronoun (truth PlayersView
) const {
2867 if (IsPlayer() && PlayersView
) return CONST_S("your");
2868 if (GetSex() == UNDEFINED
|| (PlayersView
&& !CanBeSeenByPlayer() && !game::GetSeeWholeMapCheatMode())) return CONST_S("its");
2869 if (GetSex() == MALE
) return CONST_S("his");
2870 return CONST_S("her");
2874 festring
character::GetObjectPronoun (truth PlayersView
) const {
2875 if (IsPlayer() && PlayersView
) return CONST_S("you");
2876 if (GetSex() == UNDEFINED
|| (PlayersView
&& !CanBeSeenByPlayer() && !game::GetSeeWholeMapCheatMode())) return CONST_S("it");
2877 if (GetSex() == MALE
) return CONST_S("him");
2878 return CONST_S("her");
2882 void character::AddName (festring
&String
, int Case
) const {
2883 if (AssignedName
.IsEmpty()) {
2884 id::AddName(String
, Case
);
2885 } else if (!(Case
& PLURAL
)) {
2886 if (!ShowClassDescription()) {
2887 String
<< AssignedName
;
2889 String
<< AssignedName
<< ' ';
2890 id::AddName(String
, (Case
|ARTICLE_BIT
)&~INDEFINE_BIT
);
2893 id::AddName(String
, Case
);
2894 String
<< " named " << AssignedName
;
2899 int character::GetHungerState () const {
2900 if (!UsesNutrition()) return NOT_HUNGRY
;
2901 if (GetNP() > OVER_FED_LEVEL
) return OVER_FED
;
2902 if (GetNP() > BLOATED_LEVEL
) return BLOATED
;
2903 if (GetNP() > SATIATED_LEVEL
) return SATIATED
;
2904 if (GetNP() > NOT_HUNGER_LEVEL
) return NOT_HUNGRY
;
2905 if (GetNP() > HUNGER_LEVEL
) return HUNGRY
;
2906 if (GetNP() > VERY_HUNGER_LEVEL
) return VERY_HUNGRY
;
2911 truth
character::CanConsume (material
*Material
) const {
2912 return GetConsumeFlags() & Material
->GetConsumeType();
2916 void character::SetTemporaryStateCounter (sLong State
, int What
) {
2917 for (int c
= 0; c
< STATES
; ++c
) {
2918 if ((1 << c
) & State
) TemporaryStateCounter
[c
] = What
;
2923 void character::EditTemporaryStateCounter (sLong State
, int What
) {
2924 for (int c
= 0; c
< STATES
; ++c
) {
2925 if ((1 << c
) & State
) TemporaryStateCounter
[c
] += What
;
2930 int character::GetTemporaryStateCounter (sLong State
) const {
2931 for (int c
= 0; c
< STATES
; ++c
) {
2932 if ((1 << c
) & State
) return TemporaryStateCounter
[c
];
2934 ABORT("Illegal GetTemporaryStateCounter request!");
2939 truth
character::CheckKick () const {
2941 if (IsPlayer()) ADD_MESSAGE("This race can't kick.");
2948 int character::GetResistance (int Type
) const {
2949 switch (Type
&0xFFF) {
2950 case PHYSICAL_DAMAGE
:
2953 case MUSTARD_GAS_DAMAGE
:
2956 case ENERGY
: return GetEnergyResistance();
2957 case FIRE
: return GetFireResistance();
2958 case POISON
: return GetPoisonResistance();
2959 case ELECTRICITY
: return GetElectricityResistance();
2960 case ACID
: return GetAcidResistance();
2962 ABORT("Resistance lack detected!");
2967 void character::Regenerate () {
2968 if (HP
== MaxHP
) return;
2969 sLong RegenerationBonus
= 0;
2970 truth NoHealableBodyParts
= true;
2971 for (int c
= 0; c
< BodyParts
; ++c
) {
2972 bodypart
*BodyPart
= GetBodyPart(c
);
2973 if (BodyPart
&& BodyPart
->CanRegenerate()) {
2974 RegenerationBonus
+= BodyPart
->GetMaxHP();
2975 if (NoHealableBodyParts
&& BodyPart
->GetHP() < BodyPart
->GetMaxHP()) NoHealableBodyParts
= false;
2978 if (!RegenerationBonus
|| NoHealableBodyParts
) return;
2979 RegenerationBonus
*= (50+GetAttribute(ENDURANCE
));
2981 if (Action
&& Action
->IsRest()) {
2982 if (SquaresUnder
== 1) RegenerationBonus
*= GetSquareUnder()->GetRestModifier() << 1;
2984 int Lowest
= GetSquareUnder(0)->GetRestModifier();
2985 for (int c
= 1; c
< GetSquaresUnder(); ++c
) {
2986 int Mod
= GetSquareUnder(c
)->GetRestModifier();
2987 if (Mod
< Lowest
) Lowest
= Mod
;
2989 RegenerationBonus
*= Lowest
<< 1;
2993 RegenerationCounter
+= RegenerationBonus
;
2995 while (RegenerationCounter
> 1250000) {
2996 bodypart
*BodyPart
= HealHitPoint();
2997 if (!BodyPart
) break;
2998 EditNP(-Max(7500/MaxHP
, 1));
2999 RegenerationCounter
-= 1250000;
3000 int HP
= BodyPart
->GetHP();
3001 EditExperience(ENDURANCE
, Min(1000*BodyPart
->GetMaxHP()/(HP
*HP
), 300), 1000);
3006 void character::PrintInfo () const {
3007 felist
Info(CONST_S("Information about ")+GetName(DEFINITE
));
3008 for (int c
= 0; c
< GetEquipments(); ++c
) {
3009 item
*Equipment
= GetEquipment(c
);
3010 if ((EquipmentEasilyRecognized(c
) || game::WizardModeIsActive()) && Equipment
) {
3011 int ImageKey
= game::AddToItemDrawVector(itemvector(1, Equipment
));
3012 Info
.AddEntry(festring(GetEquipmentName(c
))+": "+Equipment
->GetName(INDEFINITE
), LIGHT_GRAY
, 0, ImageKey
, true);
3015 if (Info
.IsEmpty()) {
3016 ADD_MESSAGE("There's nothing special to tell about %s.", CHAR_NAME(DEFINITE
));
3018 game::SetStandardListAttributes(Info
);
3019 Info
.SetEntryDrawer(game::ItemEntryDrawer
);
3022 game::ClearItemDrawVector();
3026 truth
character::TryToRiseFromTheDead () {
3027 for (int c
= 0; c
< BodyParts
; ++c
) {
3028 bodypart
*BodyPart
= GetBodyPart(c
);
3030 BodyPart
->ResetSpoiling();
3031 if (BodyPart
->CanRegenerate() || BodyPart
->GetHP() < 1) BodyPart
->SetHP(1);
3039 truth
character::RaiseTheDead (character
*) {
3040 truth Useful
= false;
3041 for (int c
= 0; c
< BodyParts
; ++c
) {
3042 bodypart
*BodyPart
= GetBodyPart(c
);
3043 if (!BodyPart
&& CanCreateBodyPart(c
)) {
3044 CreateBodyPart(c
)->SetHP(1);
3045 if (IsPlayer()) ADD_MESSAGE("Suddenly you grow a new %s.", GetBodyPartName(c
).CStr());
3046 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s grows a new %s.", CHAR_NAME(DEFINITE
), GetBodyPartName(c
).CStr());
3048 } else if (BodyPart
&& BodyPart
->CanRegenerate() && BodyPart
->GetHP() < 1) {
3053 if (IsPlayer()) ADD_MESSAGE("You shudder.");
3054 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s shudders.", CHAR_NAME(DEFINITE
));
3060 void character::SetSize (int Size
) {
3061 for (int c
= 0; c
< BodyParts
; ++c
) {
3062 bodypart
*BodyPart
= GetBodyPart(c
);
3063 if (BodyPart
) BodyPart
->SetSize(GetBodyPartSize(c
, Size
));
3068 sLong
character::GetBodyPartSize (int I
, int TotalSize
) const {
3069 if (I
== TORSO_INDEX
) return TotalSize
;
3070 ABORT("Weird bodypart size request for a character!");
3075 sLong
character::GetBodyPartVolume (int I
) const {
3076 if (I
== TORSO_INDEX
) return GetTotalVolume();
3077 ABORT("Weird bodypart volume request for a character!");
3082 void character::CreateBodyParts (int SpecialFlags
) {
3083 for (int c
= 0; c
< BodyParts
; ++c
) if (CanCreateBodyPart(c
)) CreateBodyPart(c
, SpecialFlags
);
3087 void character::RestoreBodyParts () {
3088 for (int c
= 0; c
< BodyParts
; ++c
) if (!GetBodyPart(c
) && CanCreateBodyPart(c
)) CreateBodyPart(c
);
3092 void character::UpdatePictures () {
3093 if (!PictureUpdatesAreForbidden()) for (int c
= 0; c
< BodyParts
; ++c
) UpdateBodyPartPicture(c
, false);
3097 bodypart
*character::MakeBodyPart (int I
) const {
3098 if (I
== TORSO_INDEX
) return normaltorso::Spawn(0, NO_MATERIALS
);
3099 ABORT("Weird bodypart to make for a character!");
3104 bodypart
*character::CreateBodyPart (int I
, int SpecialFlags
) {
3105 bodypart
*BodyPart
= MakeBodyPart(I
);
3106 material
*Material
= CreateBodyPartMaterial(I
, GetBodyPartVolume(I
));
3107 BodyPart
->InitMaterials(Material
, false);
3108 BodyPart
->SetSize(GetBodyPartSize(I
, GetTotalSize()));
3109 BodyPart
->SetBloodMaterial(GetBloodMaterial());
3110 BodyPart
->SetNormalMaterial(Material
->GetConfig());
3111 SetBodyPart(I
, BodyPart
);
3112 BodyPart
->InitSpecialAttributes();
3113 if (!(SpecialFlags
& NO_PIC_UPDATE
)) UpdateBodyPartPicture(I
, false);
3114 if (!IsInitializing()) {
3115 CalculateBattleInfo();
3116 SendNewDrawRequest();
3117 SignalPossibleTransparencyChange();
3123 v2
character::GetBodyPartBitmapPos (int I
, truth
) const {
3124 if (I
== TORSO_INDEX
) return GetTorsoBitmapPos();
3125 ABORT("Weird bodypart BitmapPos request for a character!");
3130 void character::UpdateBodyPartPicture (int I
, truth Severed
) {
3131 bodypart
*BP
= GetBodyPart(I
);
3133 BP
->SetBitmapPos(GetBodyPartBitmapPos(I
, Severed
));
3134 BP
->GetMainMaterial()->SetSkinColor(GetBodyPartColorA(I
, Severed
));
3135 BP
->GetMainMaterial()->SetSkinColorIsSparkling(GetBodyPartSparkleFlags(I
) & SPARKLING_A
);
3136 BP
->SetMaterialColorB(GetBodyPartColorB(I
, Severed
));
3137 BP
->SetMaterialColorC(GetBodyPartColorC(I
, Severed
));
3138 BP
->SetMaterialColorD(GetBodyPartColorD(I
, Severed
));
3139 BP
->SetSparkleFlags(GetBodyPartSparkleFlags(I
));
3140 BP
->SetSpecialFlags(GetSpecialBodyPartFlags(I
));
3141 BP
->SetWobbleData(GetBodyPartWobbleData(I
));
3142 BP
->UpdatePictures();
3147 void character::LoadDataBaseStats () {
3148 for (int c
= 0; c
< BASE_ATTRIBUTES
; ++c
) {
3149 BaseExperience
[c
] = DataBase
->NaturalExperience
[c
];
3150 if (BaseExperience
[c
]) LimitRef(BaseExperience
[c
], MIN_EXP
, MAX_EXP
);
3152 SetMoney(GetDefaultMoney());
3153 const fearray
<sLong
> &Skills
= GetKnownCWeaponSkills();
3155 const fearray
<sLong
> &Hits
= GetCWeaponSkillHits();
3156 if (Hits
.Size
== 1) {
3157 for (uInt c
= 0; c
< Skills
.Size
; ++c
) {
3158 if (Skills
[c
] < AllowedWeaponSkillCategories
) CWeaponSkill
[Skills
[c
]].AddHit(Hits
[0]*100);
3160 } else if (Hits
.Size
== Skills
.Size
) {
3161 for (uInt c
= 0; c
< Skills
.Size
; ++c
) {
3162 if (Skills
[c
] < AllowedWeaponSkillCategories
) CWeaponSkill
[Skills
[c
]].AddHit(Hits
[c
]*100);
3165 ABORT("Illegal weapon skill hit array size detected!");
3171 character
*characterprototype::SpawnAndLoad (inputfile
&SaveFile
) const {
3172 character
*Char
= Spawner(0, LOAD
);
3173 Char
->Load(SaveFile
);
3174 Char
->CalculateAll();
3179 void character::Initialize (int NewConfig
, int SpecialFlags
) {
3180 Flags
|= C_INITIALIZING
|C_IN_NO_MSG_MODE
;
3181 CalculateBodyParts();
3182 CalculateAllowedWeaponSkillCategories();
3183 CalculateSquaresUnder();
3184 BodyPartSlot
= new bodypartslot
[BodyParts
];
3185 OriginalBodyPartID
= new std::list
<uLong
>[BodyParts
];
3186 CWeaponSkill
= new cweaponskill
[AllowedWeaponSkillCategories
];
3187 SquareUnder
= new square
*[SquaresUnder
];
3189 if (SquaresUnder
== 1) *SquareUnder
= 0; else memset(SquareUnder
, 0, SquaresUnder
*sizeof(square
*));
3191 for (int c
= 0; c
< BodyParts
; ++c
) BodyPartSlot
[c
].SetMaster(this);
3193 if (!(SpecialFlags
& LOAD
)) {
3194 ID
= game::CreateNewCharacterID(this);
3195 databasecreator
<character
>::InstallDataBase(this, NewConfig
);
3196 LoadDataBaseStats();
3197 TemporaryState
|= GetClassStates();
3198 if (TemporaryState
) {
3199 for (int c
= 0; c
< STATES
; ++c
) if (TemporaryState
& (1 << c
)) TemporaryStateCounter
[c
] = PERMANENT
;
3202 CreateBodyParts(SpecialFlags
| NO_PIC_UPDATE
);
3203 InitSpecialAttributes();
3204 CommandFlags
= GetDefaultCommandFlags();
3206 if (GetAttribute(INTELLIGENCE
, false) < 8) CommandFlags
&= ~DONT_CONSUME_ANYTHING_VALUABLE
; // gum
3207 if (!GetDefaultName().IsEmpty()) SetAssignedName(GetDefaultName());
3210 if (!(SpecialFlags
& LOAD
)) PostConstruct();
3212 if (!(SpecialFlags
& LOAD
)) {
3213 if (!(SpecialFlags
& NO_EQUIPMENT
)) CreateInitialEquipment((SpecialFlags
& NO_EQUIPMENT_PIC_UPDATE
) >> 1);
3214 if (!(SpecialFlags
& NO_PIC_UPDATE
)) UpdatePictures();
3220 Flags
&= ~(C_INITIALIZING
|C_IN_NO_MSG_MODE
);
3224 truth
character::TeleportNear (character
*Caller
) {
3225 v2 Where
= GetLevel()->GetNearestFreeSquare(this, Caller
->GetPos());
3226 if (Where
== ERROR_V2
) return false;
3232 void character::ReceiveHeal (sLong Amount
) {
3234 for (c
= 0; c
< Amount
/ 10; ++c
) if (!HealHitPoint()) break;
3236 if (RAND()%10 < Amount
) HealHitPoint();
3237 if (Amount
>= 250 || RAND()%250 < Amount
) {
3238 bodypart
*NewBodyPart
= GenerateRandomBodyPart();
3239 if (!NewBodyPart
) return;
3240 NewBodyPart
->SetHP(1);
3241 if (IsPlayer()) ADD_MESSAGE("You grow a new %s.", NewBodyPart
->GetBodyPartName().CStr());
3242 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s grows a new %s.", CHAR_NAME(DEFINITE
), NewBodyPart
->GetBodyPartName().CStr());
3247 void character::AddHealingLiquidConsumeEndMessage () const {
3248 if (IsPlayer()) ADD_MESSAGE("You feel better.");
3249 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s looks healthier.", CHAR_NAME(DEFINITE
));
3253 void character::ReceiveSchoolFood (sLong SizeOfEffect
) {
3254 SizeOfEffect
+= RAND()%SizeOfEffect
;
3255 if (SizeOfEffect
>= 250) VomitAtRandomDirection(SizeOfEffect
);
3256 if (!(RAND() % 3) && SizeOfEffect
>= 500 && EditAttribute(ENDURANCE
, SizeOfEffect
/500)) {
3257 if (IsPlayer()) ADD_MESSAGE("You gain a little bit of toughness for surviving this stuff.");
3258 else if (CanBeSeenByPlayer()) ADD_MESSAGE("Suddenly %s looks tougher.", CHAR_NAME(DEFINITE
));
3260 BeginTemporaryState(POISONED
, (SizeOfEffect
>>1));
3264 void character::AddSchoolFoodConsumeEndMessage () const {
3265 if (IsPlayer()) ADD_MESSAGE("Yuck! This stuff tasted like vomit and old mousepads.");
3269 void character::AddSchoolFoodHitMessage () const {
3270 if (IsPlayer()) ADD_MESSAGE("Yuck! This stuff feels like vomit and old mousepads.");
3274 void character::ReceiveNutrition (sLong SizeOfEffect
) {
3275 EditNP(SizeOfEffect
);
3279 void character::ReceiveOmmelUrine (sLong Amount
) {
3280 EditExperience(ARM_STRENGTH
, 500, Amount
<<4);
3281 EditExperience(LEG_STRENGTH
, 500, Amount
<<4);
3282 if (IsPlayer()) game::DoEvilDeed(Amount
/25);
3286 void character::ReceiveOmmelCerumen (sLong Amount
) {
3287 EditExperience(INTELLIGENCE
, 500, Amount
<< 5);
3288 EditExperience(WISDOM
, 500, Amount
<< 5);
3289 if (IsPlayer()) game::DoEvilDeed(Amount
/ 25);
3293 void character::ReceiveOmmelSweat (sLong Amount
) {
3294 EditExperience(AGILITY
, 500, Amount
<< 4);
3295 EditExperience(DEXTERITY
, 500, Amount
<< 4);
3297 if (IsPlayer()) game::DoEvilDeed(Amount
/ 25);
3301 void character::ReceiveOmmelTears (sLong Amount
) {
3302 EditExperience(PERCEPTION
, 500, Amount
<< 4);
3303 EditExperience(CHARISMA
, 500, Amount
<< 4);
3304 if (IsPlayer()) game::DoEvilDeed(Amount
/ 25);
3308 void character::ReceiveOmmelSnot (sLong Amount
) {
3309 EditExperience(ENDURANCE
, 500, Amount
<< 5);
3311 if (IsPlayer()) game::DoEvilDeed(Amount
/ 25);
3315 void character::ReceiveOmmelBone (sLong Amount
) {
3316 EditExperience(ARM_STRENGTH
, 500, Amount
<< 6);
3317 EditExperience(LEG_STRENGTH
, 500, Amount
<< 6);
3318 EditExperience(DEXTERITY
, 500, Amount
<< 6);
3319 EditExperience(AGILITY
, 500, Amount
<< 6);
3320 EditExperience(ENDURANCE
, 500, Amount
<< 6);
3321 EditExperience(PERCEPTION
, 500, Amount
<< 6);
3322 EditExperience(INTELLIGENCE
, 500, Amount
<< 6);
3323 EditExperience(WISDOM
, 500, Amount
<< 6);
3324 EditExperience(CHARISMA
, 500, Amount
<< 6);
3327 if (IsPlayer()) game::DoEvilDeed(Amount
/ 25);
3331 void character::AddOmmelConsumeEndMessage () const {
3332 if (IsPlayer()) ADD_MESSAGE("You feel a primitive force coursing through your veins.");
3333 else if (CanBeSeenByPlayer()) ADD_MESSAGE("Suddenly %s looks more powerful.", CHAR_NAME(DEFINITE
));
3337 void character::ReceivePepsi (sLong Amount
) {
3338 ReceiveDamage(0, Amount
/ 100, POISON
, TORSO
);
3339 EditExperience(PERCEPTION
, Amount
, 1 << 14);
3340 if (CheckDeath(CONST_S("was poisoned by pepsi"), 0)) return;
3341 if (IsPlayer()) game::DoEvilDeed(Amount
/ 10);
3345 void character::AddPepsiConsumeEndMessage () const {
3346 if (IsPlayer()) ADD_MESSAGE("Urgh. You feel your guruism fading away.");
3347 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s looks very lame.", CHAR_NAME(DEFINITE
));
3351 void character::ReceiveDarkness (sLong Amount
) {
3352 EditExperience(INTELLIGENCE
, -Amount
/ 5, 1 << 13);
3353 EditExperience(WISDOM
, -Amount
/ 5, 1 << 13);
3354 EditExperience(CHARISMA
, -Amount
/ 5, 1 << 13);
3355 if (IsPlayer()) game::DoEvilDeed(int(Amount
/ 50));
3359 void character::AddFrogFleshConsumeEndMessage () const {
3360 if (IsPlayer()) ADD_MESSAGE("Arg. You feel the fate of a navastater placed upon you...");
3361 else if (CanBeSeenByPlayer()) ADD_MESSAGE("Suddenly %s looks like a navastater.", CHAR_NAME(DEFINITE
));
3365 void character::ReceiveKoboldFlesh (sLong
) {
3366 /* As it is commonly known, the possibility of fainting per 500 cubic
3367 centimeters of kobold flesh is exactly 5%. */
3368 if (!(RAND() % 20)) {
3369 if (IsPlayer()) ADD_MESSAGE("You lose control of your legs and fall down.");
3370 LoseConsciousness(250 + RAND_N(250));
3375 void character::AddKoboldFleshConsumeEndMessage () const {
3376 if (IsPlayer()) ADD_MESSAGE("This stuff tasted really funny.");
3380 void character::AddKoboldFleshHitMessage () const {
3381 if (IsPlayer()) ADD_MESSAGE("You feel very funny.");
3385 void character::AddBoneConsumeEndMessage () const {
3386 if (IsPlayer()) ADD_MESSAGE("You feel like a hippie.");
3387 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s barks happily.", CHAR_NAME(DEFINITE
)); // this suspects that nobody except dogs can eat bones
3390 truth
character::RawEditAttribute (double &Experience
, int Amount
) const {
3391 /* Check if the attribute is disabled for creature */
3392 if (!Experience
) return false;
3393 if ((Amount
< 0 && Experience
< 2 * EXP_MULTIPLIER
) || (Amount
> 0 && Experience
> 999 * EXP_MULTIPLIER
)) return false;
3394 Experience
+= Amount
* EXP_MULTIPLIER
;
3395 LimitRef
<double>(Experience
, MIN_EXP
, MAX_EXP
);
3400 void character::DrawPanel (truth AnimationDraw
) const {
3401 if (AnimationDraw
) { DrawStats(true); return; }
3402 igraph::BlitBackGround(v2(19 + (game::GetScreenXSize() << 4), 0), v2(RES
.X
- 19 - (game::GetScreenXSize() << 4), RES
.Y
));
3403 igraph::BlitBackGround(v2(16, 45 + (game::GetScreenYSize() << 4)), v2(game::GetScreenXSize() << 4, 9));
3404 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 45 + (game::GetScreenYSize() << 4)), WHITE
, "%s", GetPanelName().CStr());
3405 game::UpdateAttributeMemory();
3406 int PanelPosX
= RES
.X
- 96;
3407 int PanelPosY
= DrawStats(false);
3408 PrintAttribute("End", ENDURANCE
, PanelPosX
, PanelPosY
++);
3409 PrintAttribute("Per", PERCEPTION
, PanelPosX
, PanelPosY
++);
3410 PrintAttribute("Int", INTELLIGENCE
, PanelPosX
, PanelPosY
++);
3411 PrintAttribute("Wis", WISDOM
, PanelPosX
, PanelPosY
++);
3412 PrintAttribute("Wil", WILL_POWER
, PanelPosX
, PanelPosY
++);
3413 PrintAttribute("Cha", CHARISMA
, PanelPosX
, PanelPosY
++);
3414 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Siz %d", GetSize());
3415 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), IsInBadCondition() ? RED
: WHITE
, "HP %d/%d", GetHP(), GetMaxHP());
3417 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Gold: %d", GetMoney());
3420 if (game::IsInWilderness())
3421 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Worldmap");
3423 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "%s", game::GetCurrentDungeon()->GetShortLevelDescription(game::GetCurrentLevelIndex()).CapitalizeCopy().CStr());
3426 game::GetTime(Time
);
3427 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Day %d", Time
.Day
);
3428 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Time %d:%s%d", Time
.Hour
, Time
.Min
< 10 ? "0" : "", Time
.Min
);
3429 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Turn %d", game::GetTurn());
3434 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "%s", festring(GetAction()->GetDescription()).CapitalizeCopy().CStr());
3436 for (int c
= 0; c
< STATES
; ++c
)
3437 if (!(StateData
[c
].Flags
& SECRET
) && StateIsActivated(1 << c
) && (1 << c
!= HASTE
|| !StateIsActivated(SLOW
)) && (1 << c
!= SLOW
|| !StateIsActivated(HASTE
)))
3438 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), (1 << c
) & EquipmentState
|| TemporaryStateCounter
[c
] == PERMANENT
? BLUE
: WHITE
, "%s", StateData
[c
].Description
);
3440 /* Make this more elegant!!! */
3441 if (GetHungerState() == STARVING
)
3442 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), RED
, "Starving");
3443 else if (GetHungerState() == VERY_HUNGRY
)
3444 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), BLUE
, "Very hungry");
3445 else if (GetHungerState() == HUNGRY
)
3446 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), BLUE
, "Hungry");
3447 else if (GetHungerState() == SATIATED
)
3448 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Satiated");
3449 else if (GetHungerState() == BLOATED
)
3450 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Bloated");
3451 else if (GetHungerState() == OVER_FED
)
3452 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Overfed!");
3454 switch (GetBurdenState()) {
3456 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), RED
, "Overload!");
3459 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), BLUE
, "Stressed");
3462 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), BLUE
, "Burdened");
3466 switch (GetTirednessState()) {
3468 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), RED
, "Fainting");
3471 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, "Exhausted");
3475 if (game::PlayerIsRunning()) {
3476 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, GetRunDescriptionLine(0));
3477 cchar
*SecondLine
= GetRunDescriptionLine(1);
3478 if (strlen(SecondLine
)) FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
++ * 10), WHITE
, SecondLine
);
3483 void character::CalculateDodgeValue () {
3484 DodgeValue
= 0.05 * GetMoveEase() * GetAttribute(AGILITY
) / sqrt(GetSize());
3485 if (IsFlying()) DodgeValue
*= 2;
3486 if (DodgeValue
< 1) DodgeValue
= 1;
3490 truth
character::DamageTypeAffectsInventory (int Type
) {
3491 switch (Type
&0xFFF) {
3498 case PHYSICAL_DAMAGE
:
3501 case MUSTARD_GAS_DAMAGE
:
3505 ABORT("Unknown reaping effect destroyed dungeon!");
3510 int character::CheckForBlockWithArm (character
*Enemy
, item
*Weapon
, arm
*Arm
,
3511 double WeaponToHitValue
, int Damage
, int Success
, int Type
)
3513 int BlockStrength
= Arm
->GetBlockCapability();
3514 double BlockValue
= Arm
->GetBlockValue();
3515 if (BlockStrength
&& BlockValue
) {
3516 item
*Blocker
= Arm
->GetWielded();
3517 if (RAND() % int(100+WeaponToHitValue
/BlockValue
/(1<<BlocksSinceLastTurn
)*(100+Success
)) < 100) {
3518 int NewDamage
= BlockStrength
< Damage
? Damage
-BlockStrength
: 0;
3520 case UNARMED_ATTACK
: AddBlockMessage(Enemy
, Blocker
, Enemy
->UnarmedHitNoun(), NewDamage
); break;
3521 case WEAPON_ATTACK
: AddBlockMessage(Enemy
, Blocker
, "attack", NewDamage
); break;
3522 case KICK_ATTACK
: AddBlockMessage(Enemy
, Blocker
, Enemy
->KickNoun(), NewDamage
); break;
3523 case BITE_ATTACK
: AddBlockMessage(Enemy
, Blocker
, Enemy
->BiteNoun(), NewDamage
); break;
3525 sLong Weight
= Blocker
->GetWeight();
3526 sLong StrExp
= Limit(15 * Weight
/ 200, 75, 300);
3527 sLong DexExp
= Weight
? Limit(75000 / Weight
, 75, 300) : 300;
3528 Arm
->EditExperience(ARM_STRENGTH
, StrExp
, 1 << 8);
3529 Arm
->EditExperience(DEXTERITY
, DexExp
, 1 << 8);
3530 EditStamina(-10000 / GetAttribute(ARM_STRENGTH
), false);
3531 if (Arm
->TwoHandWieldIsActive()) {
3532 arm
*PairArm
= Arm
->GetPairArm();
3533 PairArm
->EditExperience(ARM_STRENGTH
, StrExp
, 1 << 8);
3534 PairArm
->EditExperience(DEXTERITY
, DexExp
, 1 << 8);
3536 Blocker
->WeaponSkillHit(Enemy
->CalculateWeaponSkillHits(this));
3537 Blocker
->ReceiveDamage(this, Damage
, PHYSICAL_DAMAGE
);
3538 Blocker
->BlockEffect(this, Enemy
, Weapon
, Type
);
3539 if (Weapon
) Weapon
->ReceiveDamage(Enemy
, Damage
- NewDamage
, PHYSICAL_DAMAGE
);
3540 if (BlocksSinceLastTurn
< 16) ++BlocksSinceLastTurn
;
3548 sLong
character::GetStateAPGain (sLong BaseAPGain
) const {
3549 if (!StateIsActivated(HASTE
) == !StateIsActivated(SLOW
)) return BaseAPGain
;
3550 if (StateIsActivated(HASTE
)) return (BaseAPGain
* 5) >> 2;
3551 return (BaseAPGain
<< 2) / 5;
3555 void character::SignalEquipmentAdd (int EquipmentIndex
) {
3556 item
*Equipment
= GetEquipment(EquipmentIndex
);
3557 if (Equipment
->IsInCorrectSlot(EquipmentIndex
)) {
3558 sLong AddedStates
= Equipment
->GetGearStates();
3560 for (int c
= 0; c
< STATES
; ++c
) {
3561 if (AddedStates
& (1 << c
)) {
3562 if (!StateIsActivated(1 << c
)) {
3563 if (!IsInNoMsgMode()) (this->*StateData
[c
].PrintBeginMessage
)();
3564 EquipmentState
|= 1 << c
;
3565 if (StateData
[c
].BeginHandler
) (this->*StateData
[c
].BeginHandler
)();
3567 EquipmentState
|= 1 << c
;
3573 if (!IsInitializing() && Equipment
->IsInCorrectSlot(EquipmentIndex
)) ApplyEquipmentAttributeBonuses(Equipment
);
3577 void character::SignalEquipmentRemoval (int, citem
*Item
) {
3578 CalculateEquipmentState();
3579 if (CalculateAttributeBonuses()) CheckDeath(festring("lost ")+GetPossessivePronoun(false)+" vital "+Item
->GetName(INDEFINITE
));
3583 void character::CalculateEquipmentState () {
3584 sLong Back
= EquipmentState
;
3586 for (int c
= 0; c
< GetEquipments(); ++c
) {
3587 item
*Equipment
= GetEquipment(c
);
3588 if (Equipment
&& Equipment
->IsInCorrectSlot(c
)) EquipmentState
|= Equipment
->GetGearStates();
3590 for (int c
= 0; c
< STATES
; ++c
) {
3591 if (Back
& (1 << c
) && !StateIsActivated(1 << c
)) {
3592 if (StateData
[c
].EndHandler
) {
3593 (this->*StateData
[c
].EndHandler
)();
3594 if (!IsEnabled()) return;
3596 if (!IsInNoMsgMode()) (this->*StateData
[c
].PrintEndMessage
)();
3602 /* Counter = duration in ticks */
3603 void character::BeginTemporaryState (sLong State
, int Counter
) {
3604 if (!Counter
) return;
3606 if (State
== POLYMORPHED
) ABORT("No Polymorphing with BeginTemporaryState!");
3607 for (Index
= 0; Index
< STATES
; ++Index
) if (1 << Index
== State
) break;
3608 if (Index
== STATES
) ABORT("BeginTemporaryState works only when State == 2Â ^ n!");
3609 if (TemporaryStateIsActivated(State
)) {
3610 int OldCounter
= GetTemporaryStateCounter(State
);
3611 if (OldCounter
!= PERMANENT
) EditTemporaryStateCounter(State
, Max(Counter
, 50-OldCounter
));
3612 } else if (StateData
[Index
].IsAllowed
== 0 || (this->*StateData
[Index
].IsAllowed
)()) {
3613 SetTemporaryStateCounter(State
, Max(Counter
, 50));
3614 if (!EquipmentStateIsActivated(State
)) {
3615 if (!IsInNoMsgMode()) (this->*StateData
[Index
].PrintBeginMessage
)();
3616 ActivateTemporaryState(State
);
3617 if (StateData
[Index
].BeginHandler
) (this->*StateData
[Index
].BeginHandler
)();
3619 ActivateTemporaryState(State
);
3625 void character::HandleStates () {
3626 if (!TemporaryState
&& !EquipmentState
) return;
3627 for (int c
= 0; c
< STATES
; ++c
) {
3628 if (TemporaryState
& (1 << c
) && TemporaryStateCounter
[c
] != PERMANENT
) {
3629 if (!--TemporaryStateCounter
[c
]) {
3630 TemporaryState
&= ~(1 << c
);
3631 if (!(EquipmentState
& (1 << c
))) {
3632 if (StateData
[c
].EndHandler
) {
3633 (this->*StateData
[c
].EndHandler
)();
3634 if (!IsEnabled()) return;
3636 if (!TemporaryStateCounter
[c
]) (this->*StateData
[c
].PrintEndMessage
)();
3640 if (StateIsActivated(1 << c
)) {
3641 if (StateData
[c
].Handler
) (this->*StateData
[c
].Handler
)();
3643 if (!IsEnabled()) return;
3648 void character::PrintBeginPolymorphControlMessage () const {
3649 if (IsPlayer()) ADD_MESSAGE("You feel your mind has total control over your body.");
3653 void character::PrintEndPolymorphControlMessage () const {
3654 if (IsPlayer()) ADD_MESSAGE("You are somehow uncertain of your willpower.");
3658 void character::PrintBeginLifeSaveMessage () const {
3659 if (IsPlayer()) ADD_MESSAGE("You hear Hell's gates being locked just now.");
3663 void character::PrintEndLifeSaveMessage () const {
3664 if (IsPlayer()) ADD_MESSAGE("You feel the Afterlife is welcoming you once again.");
3668 void character::PrintBeginLycanthropyMessage () const {
3669 if (IsPlayer()) ADD_MESSAGE("You suddenly notice you've always loved full moons.");
3673 void character::PrintEndLycanthropyMessage () const {
3674 if (IsPlayer()) ADD_MESSAGE("You feel the wolf inside you has had enough of your bad habits.");
3678 void character::PrintBeginInvisibilityMessage () const {
3679 if ((PLAYER
->StateIsActivated(INFRA_VISION
) && IsWarm()) || (PLAYER
->StateIsActivated(ESP
) && GetAttribute(INTELLIGENCE
) >= 5)) {
3680 if (IsPlayer()) ADD_MESSAGE("You seem somehow transparent.");
3681 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s seems somehow transparent.", CHAR_NAME(DEFINITE
));
3683 if (IsPlayer()) ADD_MESSAGE("You fade away.");
3684 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears!", CHAR_NAME(DEFINITE
));
3689 void character::PrintEndInvisibilityMessage () const {
3690 if ((PLAYER
->StateIsActivated(INFRA_VISION
) && IsWarm()) || (PLAYER
->StateIsActivated(ESP
) && GetAttribute(INTELLIGENCE
) >= 5)) {
3691 if (IsPlayer()) ADD_MESSAGE("Your notice your transparency has ended.");
3692 else if (CanBeSeenByPlayer()) ADD_MESSAGE("The appearance of %s seems far more solid now.", CHAR_NAME(INDEFINITE
));
3694 if (IsPlayer()) ADD_MESSAGE("You reappear.");
3695 else if (CanBeSeenByPlayer()) ADD_MESSAGE("Suddenly %s appears from nowhere!", CHAR_NAME(INDEFINITE
));
3700 void character::PrintBeginInfraVisionMessage () const {
3702 if (StateIsActivated(INVISIBLE
) && IsWarm() && !(StateIsActivated(ESP
) && GetAttribute(INTELLIGENCE
) >= 5))
3703 ADD_MESSAGE("You reappear.");
3705 ADD_MESSAGE("You feel your perception being magically altered.");
3710 void character::PrintEndInfraVisionMessage () const {
3712 if (StateIsActivated(INVISIBLE
) && IsWarm() && !(StateIsActivated(ESP
) && GetAttribute(INTELLIGENCE
) >= 5))
3713 ADD_MESSAGE("You disappear.");
3715 ADD_MESSAGE("You feel your perception returning to normal.");
3720 void character::PrintBeginESPMessage () const {
3721 if (IsPlayer()) ADD_MESSAGE("You suddenly feel like being only a tiny part of a great network of intelligent minds.");
3725 void character::PrintEndESPMessage () const {
3726 if (IsPlayer()) ADD_MESSAGE("You are filled with desire to be just yourself from now on.");
3730 void character::PrintBeginHasteMessage () const {
3731 if (IsPlayer()) ADD_MESSAGE("Time slows down to a crawl.");
3732 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s looks faster!", CHAR_NAME(DEFINITE
));
3736 void character::PrintEndHasteMessage () const {
3737 if (IsPlayer()) ADD_MESSAGE("Everything seems to move much faster now.");
3738 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s looks slower!", CHAR_NAME(DEFINITE
));
3742 void character::PrintBeginSlowMessage () const {
3743 if (IsPlayer()) ADD_MESSAGE("Everything seems to move much faster now.");
3744 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s looks slower!", CHAR_NAME(DEFINITE
));
3748 void character::PrintEndSlowMessage () const {
3749 if (IsPlayer()) ADD_MESSAGE("Time slows down to a crawl.");
3750 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s looks faster!", CHAR_NAME(DEFINITE
));
3754 void character::EndPolymorph () {
3755 ForceEndPolymorph();
3759 character
*character::ForceEndPolymorph () {
3761 ADD_MESSAGE("You return to your true form.");
3762 } else if (game::IsInWilderness()) {
3763 ActivateTemporaryState(POLYMORPHED
);
3764 SetTemporaryStateCounter(POLYMORPHED
, 10);
3765 return this; // fast gum solution, state ends when the player enters a dungeon
3767 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s returns to %s true form.", CHAR_NAME(DEFINITE
), GetPossessivePronoun().CStr());
3769 if (GetAction()) GetAction()->Terminate(false);
3773 character
*Char
= GetPolymorphBackup();
3774 Flags
|= C_IN_NO_MSG_MODE
;
3775 Char
->Flags
|= C_IN_NO_MSG_MODE
;
3776 Char
->ChangeTeam(GetTeam());
3777 if (GetTeam()->GetLeader() == this) GetTeam()->SetLeader(Char
);
3778 SetPolymorphBackup(0);
3779 Char
->PutToOrNear(Pos
);
3781 Char
->Flags
&= ~C_POLYMORPHED
;
3782 GetStack()->MoveItemsTo(Char
->GetStack());
3783 DonateEquipmentTo(Char
);
3784 Char
->SetMoney(GetMoney());
3785 Flags
&= ~C_IN_NO_MSG_MODE
;
3786 Char
->Flags
&= ~C_IN_NO_MSG_MODE
;
3787 Char
->CalculateAll();
3788 Char
->SetAssignedName(GetAssignedName());
3791 game::SetPlayer(Char
);
3792 game::SendLOSUpdateRequest();
3795 Char
->TestWalkability();
3800 void character::LycanthropyHandler () {
3801 if (!(RAND() % 2000)) {
3802 if (StateIsActivated(POLYMORPH_CONTROL
) && !game::TruthQuestion(CONST_S("Do you wish to change into a werewolf? [y/N]"))) return;
3803 Polymorph(werewolfwolf::Spawn(), 1000 + RAND() % 2000);
3808 void character::SaveLife () {
3809 if (TemporaryStateIsActivated(LIFE_SAVED
)) {
3811 ADD_MESSAGE("But wait! You glow briefly red and seem to be in a better shape!");
3812 else if (CanBeSeenByPlayer())
3813 ADD_MESSAGE("But wait, suddenly %s glows briefly red and seems to be in a better shape!", GetPersonalPronoun().CStr());
3814 DeActivateTemporaryState(LIFE_SAVED
);
3816 item
*LifeSaver
= 0;
3817 for (int c
= 0; c
< GetEquipments(); ++c
) {
3818 item
*Equipment
= GetEquipment(c
);
3819 if (Equipment
&& Equipment
->IsInCorrectSlot(c
) && Equipment
->GetGearStates() & LIFE_SAVED
) LifeSaver
= Equipment
;
3821 if (!LifeSaver
) ABORT("The Universe can only kill you once!");
3823 ADD_MESSAGE("But wait! Your %s glows briefly red and disappears and you seem to be in a better shape!", LifeSaver
->CHAR_NAME(UNARTICLED
));
3824 else if (CanBeSeenByPlayer())
3825 ADD_MESSAGE("But wait, suddenly %s %s glows briefly red and disappears and %s seems to be in a better shape!", GetPossessivePronoun().CStr(), LifeSaver
->CHAR_NAME(UNARTICLED
), GetPersonalPronoun().CStr());
3826 LifeSaver
->RemoveFromSlot();
3827 LifeSaver
->SendToHell();
3830 if (IsPlayer()) game::AskForEscPress(CONST_S("Life saved!"));
3838 if (GetNP() < SATIATED_LEVEL
) SetNP(SATIATED_LEVEL
);
3840 SendNewDrawRequest();
3842 if (GetAction()) GetAction()->Terminate(false);
3846 character
*character::PolymorphRandomly (int MinDanger
, int MaxDanger
, int Time
) {
3847 character
*NewForm
= 0;
3848 if (StateIsActivated(POLYMORPH_CONTROL
)) {
3850 if (!GetNewFormForPolymorphWithControl(NewForm
)) return NewForm
;
3853 NewForm
= protosystem::CreateMonster(MinDanger
* 10, MaxDanger
* 10, NO_EQUIPMENT
);
3856 NewForm
= protosystem::CreateMonster(MinDanger
, MaxDanger
, NO_EQUIPMENT
);
3858 Polymorph(NewForm
, Time
);
3863 /* In reality, the reading takes Time / (Intelligence * 10) turns */
3864 void character::StartReading (item
*Item
, sLong Time
) {
3865 study
*Read
= study::Spawn(this);
3866 Read
->SetLiteratureID(Item
->GetID());
3867 if (game::WizardModeIsActive()) Time
= 1;
3868 Read
->SetCounter(Time
);
3870 if (IsPlayer()) ADD_MESSAGE("You start reading %s.", Item
->CHAR_NAME(DEFINITE
));
3871 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s starts reading %s.", CHAR_NAME(DEFINITE
), Item
->CHAR_NAME(DEFINITE
));
3875 /* Call when one makes something with his/her/its hands.
3876 * Difficulty of 5 takes about one turn, so it's the most common to use. */
3877 void character::DexterityAction (int Difficulty
) {
3878 EditAP(-20000 * Difficulty
/ APBonus(GetAttribute(DEXTERITY
)));
3879 EditExperience(DEXTERITY
, Difficulty
* 15, 1 << 7);
3883 /* If Theoretically != false, range is not a factor. */
3884 truth
character::CanBeSeenByPlayer (truth Theoretically
, truth IgnoreESP
) const {
3885 if (IsEnabled() && !game::IsGenerating() && (Theoretically
|| GetSquareUnder())) {
3886 truth MayBeESPSeen
= PLAYER
->IsEnabled() && !IgnoreESP
&& PLAYER
->StateIsActivated(ESP
) && GetAttribute(INTELLIGENCE
) >= 5;
3887 truth MayBeInfraSeen
= PLAYER
->IsEnabled() && PLAYER
->StateIsActivated(INFRA_VISION
) && IsWarm();
3888 truth Visible
= !StateIsActivated(INVISIBLE
) || MayBeESPSeen
|| MayBeInfraSeen
;
3889 if (game::IsInWilderness()) return Visible
;
3890 if (MayBeESPSeen
&& (Theoretically
|| GetDistanceSquareFrom(PLAYER
) <= PLAYER
->GetESPRangeSquare())) return true;
3891 if (!Visible
) return false;
3892 return Theoretically
|| SquareUnderCanBeSeenByPlayer(MayBeInfraSeen
);
3898 truth
character::CanBeSeenBy (ccharacter
*Who
, truth Theoretically
, truth IgnoreESP
) const {
3899 if (Who
->IsPlayer()) return CanBeSeenByPlayer(Theoretically
, IgnoreESP
);
3900 if (IsEnabled() && !game::IsGenerating() && (Theoretically
|| GetSquareUnder())) {
3901 truth MayBeESPSeen
= Who
->IsEnabled() && !IgnoreESP
&& Who
->StateIsActivated(ESP
) && GetAttribute(INTELLIGENCE
) >= 5;
3902 truth MayBeInfraSeen
= Who
->IsEnabled() && Who
->StateIsActivated(INFRA_VISION
) && IsWarm();
3903 truth Visible
= !StateIsActivated(INVISIBLE
) || MayBeESPSeen
|| MayBeInfraSeen
;
3904 if (game::IsInWilderness()) return Visible
;
3905 if (MayBeESPSeen
&& (Theoretically
|| GetDistanceSquareFrom(Who
) <= Who
->GetESPRangeSquare())) return true;
3906 if (!Visible
) return false;
3907 return Theoretically
|| SquareUnderCanBeSeenBy(Who
, MayBeInfraSeen
);
3913 truth
character::SquareUnderCanBeSeenByPlayer (truth IgnoreDarkness
) const {
3914 if (!GetSquareUnder()) return false;
3915 int S1
= SquaresUnder
, S2
= PLAYER
->SquaresUnder
;
3916 if (S1
== 1 && S2
== 1) {
3917 if (GetSquareUnder()->CanBeSeenByPlayer(IgnoreDarkness
)) return true;
3918 if (IgnoreDarkness
) {
3919 int LOSRangeSquare
= PLAYER
->GetLOSRangeSquare();
3920 if ((GetPos() - PLAYER
->GetPos()).GetLengthSquare() <= LOSRangeSquare
) {
3921 eyecontroller::Map
= GetLevel()->GetMap();
3922 return mapmath
<eyecontroller
>::DoLine(PLAYER
->GetPos().X
, PLAYER
->GetPos().Y
, GetPos().X
, GetPos().Y
, SKIP_FIRST
);
3927 for (int c1
= 0; c1
< S1
; ++c1
) {
3928 lsquare
*Square
= GetLSquareUnder(c1
);
3929 if (Square
->CanBeSeenByPlayer(IgnoreDarkness
)) return true;
3930 else if (IgnoreDarkness
) {
3931 v2 Pos
= Square
->GetPos();
3932 int LOSRangeSquare
= PLAYER
->GetLOSRangeSquare();
3933 for (int c2
= 0; c2
< S2
; ++c2
) {
3934 v2 PlayerPos
= PLAYER
->GetPos(c2
);
3935 if ((Pos
-PlayerPos
).GetLengthSquare() <= LOSRangeSquare
) {
3936 eyecontroller::Map
= GetLevel()->GetMap();
3937 if (mapmath
<eyecontroller
>::DoLine(PlayerPos
.X
, PlayerPos
.Y
, Pos
.X
, Pos
.Y
, SKIP_FIRST
)) return true;
3947 truth
character::SquareUnderCanBeSeenBy (ccharacter
*Who
, truth IgnoreDarkness
) const {
3948 int S1
= SquaresUnder
, S2
= Who
->SquaresUnder
;
3949 int LOSRangeSquare
= Who
->GetLOSRangeSquare();
3950 if (S1
== 1 && S2
== 1) return GetSquareUnder()->CanBeSeenFrom(Who
->GetPos(), LOSRangeSquare
, IgnoreDarkness
);
3951 for (int c1
= 0; c1
< S1
; ++c1
) {
3952 lsquare
*Square
= GetLSquareUnder(c1
);
3953 for (int c2
= 0; c2
< S2
; ++c2
) if (Square
->CanBeSeenFrom(Who
->GetPos(c2
), LOSRangeSquare
, IgnoreDarkness
)) return true;
3959 int character::GetDistanceSquareFrom (ccharacter
*Who
) const {
3960 int S1
= SquaresUnder
, S2
= Who
->SquaresUnder
;
3961 if (S1
== 1 && S2
== 1) return (GetPos() - Who
->GetPos()).GetLengthSquare();
3962 v2
MinDist(0x7FFF, 0x7FFF);
3963 int MinLength
= 0xFFFF;
3964 for (int c1
= 0; c1
< S1
; ++c1
) {
3965 for (int c2
= 0; c2
< S2
; ++c2
) {
3966 v2 Dist
= GetPos(c1
)-Who
->GetPos(c2
);
3967 if (Dist
.X
< 0) Dist
.X
= -Dist
.X
;
3968 if (Dist
.Y
< 0) Dist
.Y
= -Dist
.Y
;
3969 if (Dist
.X
<= MinDist
.X
&& Dist
.Y
<= MinDist
.Y
) {
3971 MinLength
= Dist
.GetLengthSquare();
3972 } else if (Dist
.X
< MinDist
.X
|| Dist
.Y
< MinDist
.Y
) {
3973 int Length
= Dist
.GetLengthSquare();
3974 if (Length
< MinLength
) {
3985 void character::AttachBodyPart (bodypart
*BodyPart
) {
3986 SetBodyPart(BodyPart
->GetBodyPartIndex(), BodyPart
);
3987 if (!AllowSpoil()) BodyPart
->ResetSpoiling();
3988 BodyPart
->ResetPosition();
3989 BodyPart
->UpdatePictures();
3990 CalculateAttributeBonuses();
3991 CalculateBattleInfo();
3992 SendNewDrawRequest();
3993 SignalPossibleTransparencyChange();
3997 /* Returns true if the character has all bodyparts, false if not. */
3998 truth
character::HasAllBodyParts () const {
3999 for (int c
= 0; c
< BodyParts
; ++c
) if (!GetBodyPart(c
) && CanCreateBodyPart(c
)) return false;
4004 bodypart
*character::GenerateRandomBodyPart () {
4005 int NeededBodyPart
[MAX_BODYPARTS
];
4007 for (int c
= 0; c
< BodyParts
; ++c
) if (!GetBodyPart(c
) && CanCreateBodyPart(c
)) NeededBodyPart
[Index
++] = c
;
4008 return Index
? CreateBodyPart(NeededBodyPart
[RAND() % Index
]) : 0;
4012 /* Searches the character's Stack and if it find some bodyparts there that are the character's
4013 * old bodyparts returns a stackiterator to one of them (choosen in random).
4014 * If no fitting bodyparts are found the function returns 0 */
4015 bodypart
*character::FindRandomOwnBodyPart (truth AllowNonLiving
) const {
4016 itemvector LostAndFound
;
4017 for (int c
= 0; c
< BodyParts
; ++c
) {
4018 if (!GetBodyPart(c
)) {
4019 for (std::list
<uLong
>::iterator i
= OriginalBodyPartID
[c
].begin(); i
!= OriginalBodyPartID
[c
].end(); ++i
) {
4020 bodypart
*Found
= static_cast<bodypart
*>(SearchForItem(*i
));
4021 if (Found
&& (AllowNonLiving
|| Found
->CanRegenerate())) LostAndFound
.push_back(Found
);
4025 if (LostAndFound
.empty()) return 0;
4026 return static_cast<bodypart
*>(LostAndFound
[RAND() % LostAndFound
.size()]);
4030 void character::PrintBeginPoisonedMessage () const {
4031 if (IsPlayer()) ADD_MESSAGE("You seem to be very ill.");
4032 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s looks very ill.", CHAR_NAME(DEFINITE
));
4036 void character::PrintEndPoisonedMessage () const {
4037 if (IsPlayer()) ADD_MESSAGE("You feel better again.");
4038 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s looks better.", CHAR_NAME(DEFINITE
));
4042 void character::PoisonedHandler () {
4043 if (!(RAND() % 100)) VomitAtRandomDirection(500 + RAND_N(250));
4045 for (int Used
= 0; Used
< GetTemporaryStateCounter(POISONED
); Used
+= 100) if (!(RAND() % 100)) ++Damage
;
4047 ReceiveDamage(0, Damage
, POISON
, ALL
, 8, false, false, false, false);
4048 CheckDeath(CONST_S("died of acute poisoning"), 0);
4053 truth
character::IsWarm () const {
4054 return combinebodypartpredicates()(this, &bodypart::IsWarm
, 1);
4058 void character::BeginInvisibility () {
4060 SendNewDrawRequest();
4061 SignalPossibleTransparencyChange();
4065 void character::BeginInfraVision () {
4066 if (IsPlayer()) GetArea()->SendNewDrawRequest();
4070 void character::BeginESP () {
4071 if (IsPlayer()) GetArea()->SendNewDrawRequest();
4075 void character::EndInvisibility () {
4077 SendNewDrawRequest();
4078 SignalPossibleTransparencyChange();
4082 void character::EndInfraVision () {
4083 if (IsPlayer() && IsEnabled()) GetArea()->SendNewDrawRequest();
4087 void character::EndESP () {
4088 if (IsPlayer() && IsEnabled()) GetArea()->SendNewDrawRequest();
4092 void character::Draw (blitdata
&BlitData
) const {
4093 col24 L
= BlitData
.Luminance
;
4094 if (PLAYER
->IsEnabled() &&
4095 ((PLAYER
->StateIsActivated(ESP
) && GetAttribute(INTELLIGENCE
) >= 5 &&
4096 (PLAYER
->GetPos() - GetPos()).GetLengthSquare() <= PLAYER
->GetESPRangeSquare()) ||
4097 (PLAYER
->StateIsActivated(INFRA_VISION
) && IsWarm())))
4098 BlitData
.Luminance
= ivanconfig::GetContrastLuminance();
4100 DrawBodyParts(BlitData
);
4101 BlitData
.Luminance
= ivanconfig::GetContrastLuminance();
4102 BlitData
.Src
.Y
= 16;
4103 cint SquareIndex
= BlitData
.CustomData
& SQUARE_INDEX_MASK
;
4105 if (GetTeam() == PLAYER
->GetTeam() && !IsPlayer() && SquareIndex
== GetTameSymbolSquareIndex()) {
4106 BlitData
.Src
.X
= 32;
4107 igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData
);
4110 if (IsFlying() && SquareIndex
== GetFlySymbolSquareIndex()) {
4111 BlitData
.Src
.X
= 128;
4112 igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData
);
4115 if (IsSwimming() && SquareIndex
== GetSwimmingSymbolSquareIndex()) {
4116 BlitData
.Src
.X
= 240;
4117 igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData
);
4120 if (GetAction() && GetAction()->IsUnconsciousness() && SquareIndex
== GetUnconsciousSymbolSquareIndex()) {
4121 BlitData
.Src
.X
= 224;
4122 igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData
);
4125 BlitData
.Src
.X
= BlitData
.Src
.Y
= 0;
4126 BlitData
.Luminance
= L
;
4130 void character::DrawBodyParts (blitdata
&BlitData
) const {
4131 GetTorso()->Draw(BlitData
);
4135 void character::PrintBeginTeleportMessage () const {
4136 if (IsPlayer()) ADD_MESSAGE("You feel jumpy.");
4140 void character::PrintEndTeleportMessage () const {
4141 if (IsPlayer()) ADD_MESSAGE("You suddenly realize you've always preferred walking to jumping.");
4145 void character::TeleportHandler () {
4146 if (!(RAND() % 1500) && !game::IsInWilderness()) {
4147 if (IsPlayer()) ADD_MESSAGE("You feel an urgent spatial relocation is now appropriate.");
4148 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears.", CHAR_NAME(DEFINITE
));
4154 void character::PrintBeginPolymorphMessage () const {
4155 if (IsPlayer()) ADD_MESSAGE("An unconfortable uncertainty of who you really are overwhelms you.");
4159 void character::PrintEndPolymorphMessage () const {
4160 if (IsPlayer()) ADD_MESSAGE("You feel you are you and no one else.");
4164 void character::PolymorphHandler () {
4165 if (!(RAND() % 1500)) PolymorphRandomly(1, 999999, 200 + RAND() % 800);
4168 void character::PrintBeginTeleportControlMessage () const {
4169 if (IsPlayer()) ADD_MESSAGE("You feel very controlled.");
4173 void character::PrintEndTeleportControlMessage () const {
4174 if (IsPlayer()) ADD_MESSAGE("You feel your control slipping.");
4178 void character::DisplayStethoscopeInfo (character
*) const {
4179 felist
Info(CONST_S("Information about ") + GetDescription(DEFINITE
));
4180 AddSpecialStethoscopeInfo(Info
);
4181 Info
.AddEntry(CONST_S("Endurance: ") + GetAttribute(ENDURANCE
), LIGHT_GRAY
);
4182 Info
.AddEntry(CONST_S("Perception: ") + GetAttribute(PERCEPTION
), LIGHT_GRAY
);
4183 Info
.AddEntry(CONST_S("Intelligence: ") + GetAttribute(INTELLIGENCE
), LIGHT_GRAY
);
4184 Info
.AddEntry(CONST_S("Wisdom: ") + GetAttribute(WISDOM
), LIGHT_GRAY
);
4185 //Info.AddEntry(CONST_S("Willpower: ") + GetAttribute(WILL_POWER), LIGHT_GRAY);
4186 Info
.AddEntry(CONST_S("Charisma: ") + GetAttribute(CHARISMA
), LIGHT_GRAY
);
4187 Info
.AddEntry(CONST_S("HP: ") + GetHP() + "/" + GetMaxHP(), IsInBadCondition() ? RED
: LIGHT_GRAY
);
4188 if (GetAction()) Info
.AddEntry(festring(GetAction()->GetDescription()).CapitalizeCopy(), LIGHT_GRAY
);
4189 for (int c
= 0; c
< STATES
; ++c
) {
4190 if (StateIsActivated(1 << c
) && (1 << c
!= HASTE
|| !StateIsActivated(SLOW
)) && (1 << c
!= SLOW
|| !StateIsActivated(HASTE
)))
4191 Info
.AddEntry(StateData
[c
].Description
, LIGHT_GRAY
);
4193 switch (GetTirednessState()) {
4194 case FAINTING
: Info
.AddEntry("Fainting", RED
); break;
4195 case EXHAUSTED
: Info
.AddEntry("Exhausted", LIGHT_GRAY
); break;
4197 game::SetStandardListAttributes(Info
);
4202 truth
character::CanUseStethoscope (truth PrintReason
) const {
4203 if (PrintReason
) ADD_MESSAGE("This type of monster can't use a stethoscope.");
4208 /* Effect used by at least Sophos.
4209 * NOTICE: Doesn't check for death! */
4210 void character::TeleportSomePartsAway (int NumberToTeleport
) {
4211 for (int c
= 0; c
< NumberToTeleport
; ++c
) {
4212 int RandomBodyPart
= GetRandomNonVitalBodyPart();
4213 if (RandomBodyPart
== NONE_INDEX
) {
4214 for (; c
< NumberToTeleport
; ++c
) {
4215 GetTorso()->SetHP((GetTorso()->GetHP() << 2) / 5);
4216 sLong TorsosVolume
= GetTorso()->GetMainMaterial()->GetVolume() / 10;
4217 if (!TorsosVolume
) break;
4218 sLong Amount
= (RAND() % TorsosVolume
)+1;
4219 item
*Lump
= GetTorso()->GetMainMaterial()->CreateNaturalForm(Amount
);
4220 GetTorso()->GetMainMaterial()->EditVolume(-Amount
);
4221 Lump
->MoveTo(GetNearLSquare(GetLevel()->GetRandomSquare())->GetStack());
4222 if (IsPlayer()) ADD_MESSAGE("Parts of you teleport away.");
4223 else if (CanBeSeenByPlayer()) ADD_MESSAGE("Parts of %s teleport away.", CHAR_NAME(DEFINITE
));
4226 item
*SeveredBodyPart
= SevereBodyPart(RandomBodyPart
);
4227 if (SeveredBodyPart
) {
4228 GetNearLSquare(GetLevel()->GetRandomSquare())->AddItem(SeveredBodyPart
);
4229 SeveredBodyPart
->DropEquipment();
4230 if (IsPlayer()) ADD_MESSAGE("Your %s teleports away.", GetBodyPartName(RandomBodyPart
).CStr());
4231 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s %s teleports away.", GetPossessivePronoun().CStr(), GetBodyPartName(RandomBodyPart
).CStr());
4233 if (IsPlayer()) ADD_MESSAGE("Your %s disappears.", GetBodyPartName(RandomBodyPart
).CStr());
4234 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s %s disappears.", GetPossessivePronoun().CStr(), GetBodyPartName(RandomBodyPart
).CStr());
4241 /* Returns an index of a random bodypart that is not vital. If no non-vital bodypart is found returns NONE_INDEX */
4242 int character::GetRandomNonVitalBodyPart () const {
4243 int OKBodyPart
[MAX_BODYPARTS
];
4244 int OKBodyParts
= 0;
4245 for (int c
= 0; c
< BodyParts
; ++c
) if (GetBodyPart(c
) && !BodyPartIsVital(c
)) OKBodyPart
[OKBodyParts
++] = c
;
4246 return OKBodyParts
? OKBodyPart
[RAND() % OKBodyParts
] : NONE_INDEX
;
4250 void character::CalculateVolumeAndWeight () {
4251 Volume
= Stack
->GetVolume();
4252 Weight
= Stack
->GetWeight();
4254 CarriedWeight
= Weight
;
4255 for (int c
= 0; c
< BodyParts
; ++c
) {
4256 bodypart
*BodyPart
= GetBodyPart(c
);
4258 BodyVolume
+= BodyPart
->GetBodyPartVolume();
4259 Volume
+= BodyPart
->GetVolume();
4260 CarriedWeight
+= BodyPart
->GetCarriedWeight();
4261 Weight
+= BodyPart
->GetWeight();
4267 void character::SignalVolumeAndWeightChange () {
4268 if (!IsInitializing()) {
4269 CalculateVolumeAndWeight();
4270 if (IsEnabled()) CalculateBurdenState();
4271 if (MotherEntity
) MotherEntity
->SignalVolumeAndWeightChange();
4276 void character::SignalEmitationIncrease (col24 EmitationUpdate
) {
4277 if (game::CompareLights(EmitationUpdate
, Emitation
) > 0) {
4278 game::CombineLights(Emitation
, EmitationUpdate
);
4279 if (MotherEntity
) MotherEntity
->SignalEmitationIncrease(EmitationUpdate
);
4280 else if (SquareUnder
[0] && !game::IsInWilderness()) {
4281 for(int c
= 0; c
< GetSquaresUnder(); ++c
) GetLSquareUnder()->SignalEmitationIncrease(EmitationUpdate
);
4287 void character::SignalEmitationDecrease (col24 EmitationUpdate
) {
4288 if (game::CompareLights(EmitationUpdate
, Emitation
) >= 0 && Emitation
) {
4289 col24 Backup
= Emitation
;
4290 CalculateEmitation();
4291 if (Backup
!= Emitation
) {
4292 if (MotherEntity
) MotherEntity
->SignalEmitationDecrease(EmitationUpdate
);
4293 else if (SquareUnder
[0] && !game::IsInWilderness()) {
4294 for (int c
= 0; c
< GetSquaresUnder(); ++c
) GetLSquareUnder(c
)->SignalEmitationDecrease(EmitationUpdate
);
4301 void character::CalculateEmitation () {
4302 Emitation
= GetBaseEmitation();
4303 for (int c
= 0; c
< BodyParts
; ++c
) {
4304 bodypart
*BodyPart
= GetBodyPart(c
);
4305 if (BodyPart
) game::CombineLights(Emitation
, BodyPart
->GetEmitation());
4307 game::CombineLights(Emitation
, Stack
->GetEmitation());
4311 void character::CalculateAll () {
4312 Flags
|= C_INITIALIZING
;
4313 CalculateAttributeBonuses();
4314 CalculateVolumeAndWeight();
4315 CalculateEmitation();
4316 CalculateBodyPartMaxHPs(0);
4317 CalculateMaxStamina();
4318 CalculateBurdenState();
4319 CalculateBattleInfo();
4320 Flags
&= ~C_INITIALIZING
;
4324 void character::CalculateHP () {
4325 HP
= sumbodypartproperties()(this, &bodypart::GetHP
);
4329 void character::CalculateMaxHP () {
4330 MaxHP
= sumbodypartproperties()(this, &bodypart::GetMaxHP
);
4334 void character::CalculateBodyPartMaxHPs (uLong Flags
) {
4335 doforbodypartswithparam
<uLong
>()(this, &bodypart::CalculateMaxHP
, Flags
);
4341 truth
character::EditAttribute (int Identifier
, int Value
) {
4342 if (Identifier
== ENDURANCE
&& UseMaterialAttributes()) return false;
4343 if (RawEditAttribute(BaseExperience
[Identifier
], Value
)) {
4344 if (!IsInitializing()) {
4345 if (Identifier
== LEG_STRENGTH
) CalculateBurdenState();
4346 else if (Identifier
== ENDURANCE
) CalculateBodyPartMaxHPs();
4347 else if (IsPlayer() && Identifier
== PERCEPTION
) game::SendLOSUpdateRequest();
4348 else if (IsPlayerKind() && (Identifier
== INTELLIGENCE
|| Identifier
== WISDOM
|| Identifier
== CHARISMA
)) UpdatePictures();
4349 CalculateBattleInfo();
4357 truth
character::ActivateRandomState (int Flags
, int Time
, sLong Seed
) {
4359 if (Seed
) femath::SetSeed(Seed
);
4360 sLong ToBeActivated
= GetRandomState(Flags
|DUR_TEMPORARY
);
4362 if (!ToBeActivated
) return false;
4363 BeginTemporaryState(ToBeActivated
, Time
);
4368 truth
character::GainRandomIntrinsic (int Flags
) {
4369 sLong ToBeActivated
= GetRandomState(Flags
|DUR_PERMANENT
);
4370 if (!ToBeActivated
) return false;
4371 GainIntrinsic(ToBeActivated
);
4376 /* Returns 0 if state not found */
4377 sLong
character::GetRandomState (int Flags
) const {
4378 sLong OKStates
[STATES
];
4379 int NumberOfOKStates
= 0;
4380 for (int c
= 0; c
< STATES
; ++c
) {
4381 if (StateData
[c
].Flags
& Flags
& DUR_FLAGS
&& StateData
[c
].Flags
& Flags
& SRC_FLAGS
) OKStates
[NumberOfOKStates
++] = 1 << c
;
4383 return NumberOfOKStates
? OKStates
[RAND() % NumberOfOKStates
] : 0;
4387 int characterprototype::CreateSpecialConfigurations (characterdatabase
**TempConfig
, int Configs
, int Level
) {
4388 if (Level
== 0 && TempConfig
[0]->CreateDivineConfigurations
) {
4389 Configs
= databasecreator
<character
>::CreateDivineConfigurations(this, TempConfig
, Configs
);
4391 if (Level
== 1 && TempConfig
[0]->CreateUndeadConfigurations
) {
4392 for (int c
= 1; c
< protocontainer
<character
>::GetSize(); ++c
) {
4393 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c
);
4394 const character::database
*const *CharacterConfigData
= Proto
->GetConfigData();
4395 if (!CharacterConfigData
) ABORT("No database entry for character <%s>!", Proto
->GetClassID());
4396 const character::database
*const* End
= CharacterConfigData
+ Proto
->GetConfigSize();
4397 for (++CharacterConfigData
; CharacterConfigData
!= End
; ++CharacterConfigData
) {
4398 const character::database
*CharacterDataBase
= *CharacterConfigData
;
4399 if (CharacterDataBase
->UndeadVersions
) {
4400 character::database
* ConfigDataBase
= new character::database(**TempConfig
);
4401 ConfigDataBase
->InitDefaults(this, (c
<< 8) | CharacterDataBase
->Config
);
4402 ConfigDataBase
->PostFix
<< "of ";
4403 if (CharacterDataBase
->Adjective
.GetSize()) {
4404 if (CharacterDataBase
->UsesLongAdjectiveArticle
) ConfigDataBase
->PostFix
<< "an ";
4405 else ConfigDataBase
->PostFix
<< "a ";
4406 ConfigDataBase
->PostFix
<< CharacterDataBase
->Adjective
<< ' ';
4408 if (CharacterDataBase
->UsesLongArticle
) ConfigDataBase
->PostFix
<< "an ";
4409 else ConfigDataBase
->PostFix
<< "a ";
4411 ConfigDataBase
->PostFix
<< CharacterDataBase
->NameSingular
;
4412 if (CharacterDataBase
->PostFix
.GetSize()) ConfigDataBase
->PostFix
<< ' ' << CharacterDataBase
->PostFix
;
4413 int P1
= TempConfig
[0]->UndeadAttributeModifier
;
4414 int P2
= TempConfig
[0]->UndeadVolumeModifier
;
4416 for (c2
= 0; c2
< ATTRIBUTES
; ++c2
) ConfigDataBase
->*ExpPtr
[c2
] = CharacterDataBase
->*ExpPtr
[c2
] * P1
/ 100;
4417 for (c2
= 0; c2
< EQUIPMENT_DATAS
; ++c2
) ConfigDataBase
->*EquipmentDataPtr
[c2
] = contentscript
<item
>();
4418 ConfigDataBase
->DefaultIntelligence
= 5;
4419 ConfigDataBase
->DefaultWisdom
= 5;
4420 ConfigDataBase
->DefaultCharisma
= 5;
4421 ConfigDataBase
->TotalSize
= CharacterDataBase
->TotalSize
;
4422 ConfigDataBase
->Sex
= CharacterDataBase
->Sex
;
4423 ConfigDataBase
->AttributeBonus
= CharacterDataBase
->AttributeBonus
;
4424 ConfigDataBase
->TotalVolume
= CharacterDataBase
->TotalVolume
* P2
/ 100;
4425 if (TempConfig
[0]->UndeadCopyMaterials
) {
4426 ConfigDataBase
->HeadBitmapPos
= CharacterDataBase
->HeadBitmapPos
;
4427 ConfigDataBase
->HairColor
= CharacterDataBase
->HairColor
;
4428 ConfigDataBase
->EyeColor
= CharacterDataBase
->EyeColor
;
4429 ConfigDataBase
->CapColor
= CharacterDataBase
->CapColor
;
4430 ConfigDataBase
->FleshMaterial
= CharacterDataBase
->FleshMaterial
;
4431 ConfigDataBase
->BloodMaterial
= CharacterDataBase
->BloodMaterial
;
4432 ConfigDataBase
->VomitMaterial
= CharacterDataBase
->VomitMaterial
;
4433 ConfigDataBase
->SweatMaterial
= CharacterDataBase
->SweatMaterial
;
4435 ConfigDataBase
->KnownCWeaponSkills
= CharacterDataBase
->KnownCWeaponSkills
;
4436 ConfigDataBase
->CWeaponSkillHits
= CharacterDataBase
->CWeaponSkillHits
;
4437 ConfigDataBase
->PostProcess();
4438 TempConfig
[Configs
++] = ConfigDataBase
;
4443 if (Level
== 0 && TempConfig
[0]->CreateGolemMaterialConfigurations
) {
4444 for (int c
= 1; c
< protocontainer
<material
>::GetSize(); ++c
) {
4445 const material::prototype
* Proto
= protocontainer
<material
>::GetProto(c
);
4446 const material::database
*const* MaterialConfigData
= Proto
->GetConfigData();
4447 const material::database
*const* End
= MaterialConfigData
+ Proto
->GetConfigSize();
4448 for (++MaterialConfigData
; MaterialConfigData
!= End
; ++MaterialConfigData
) {
4449 const material::database
* MaterialDataBase
= *MaterialConfigData
;
4450 if (MaterialDataBase
->CategoryFlags
& IS_GOLEM_MATERIAL
) {
4451 character::database
* ConfigDataBase
= new character::database(**TempConfig
);
4452 ConfigDataBase
->InitDefaults(this, MaterialDataBase
->Config
);
4453 ConfigDataBase
->Adjective
= MaterialDataBase
->NameStem
;
4454 ConfigDataBase
->UsesLongAdjectiveArticle
= MaterialDataBase
->NameFlags
& USE_AN
;
4455 ConfigDataBase
->AttachedGod
= MaterialDataBase
->AttachedGod
;
4456 TempConfig
[Configs
++] = ConfigDataBase
;
4465 double character::GetTimeToDie (ccharacter
*Enemy
, int Damage
, double ToHitValue
, truth AttackIsBlockable
, truth UseMaxHP
) const {
4466 double DodgeValue
= GetDodgeValue();
4467 if (!Enemy
->CanBeSeenBy(this, true)) ToHitValue
*= 2;
4468 if (!CanBeSeenBy(Enemy
, true)) DodgeValue
*= 2;
4469 double MinHits
= 1000;
4471 for (int c
= 0; c
< BodyParts
; ++c
) {
4472 if (BodyPartIsVital(c
) && GetBodyPart(c
)) {
4473 double Hits
= GetBodyPart(c
)->GetTimeToDie(Damage
, ToHitValue
, DodgeValue
, AttackIsBlockable
, UseMaxHP
);
4474 if (First
) { MinHits
= Hits
; First
= false; } else MinHits
= 1 / (1 / MinHits
+ 1 / Hits
);
4481 double character::GetRelativeDanger (ccharacter
*Enemy
, truth UseMaxHP
) const {
4482 double Danger
= Enemy
->GetTimeToKill(this, UseMaxHP
) / GetTimeToKill(Enemy
, UseMaxHP
);
4483 int EnemyAP
= Enemy
->GetMoveAPRequirement(1);
4484 int ThisAP
= GetMoveAPRequirement(1);
4485 if (EnemyAP
> ThisAP
) Danger
*= 1.25;
4486 else if (ThisAP
> EnemyAP
) Danger
*= 0.80;
4487 if (!Enemy
->CanBeSeenBy(this, true)) Danger
*= Enemy
->IsPlayer() ? 0.2 : 0.5;
4488 if (!CanBeSeenBy(Enemy
, true)) Danger
*= IsPlayer() ? 5. : 2.;
4489 if (GetAttribute(INTELLIGENCE
) < 10 && !IsPlayer()) Danger
*= 0.80;
4490 if (Enemy
->GetAttribute(INTELLIGENCE
) < 10 && !Enemy
->IsPlayer()) Danger
*= 1.25;
4491 return Limit(Danger
, 0.001, 1000.0);
4495 festring
character::GetBodyPartName (int I
, truth Articled
) const {
4496 if (I
== TORSO_INDEX
) return Articled
? CONST_S("a torso") : CONST_S("torso");
4497 ABORT("Illegal character bodypart name request!");
4502 item
*character::SearchForItem(uLong ID
) const {
4503 item
*Equipment
= findequipment
<uLong
>()(this, &item::HasID
, ID
);
4504 if (Equipment
) return Equipment
;
4505 for (stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) if (i
->GetID() == ID
) return *i
;
4510 truth
character::ContentsCanBeSeenBy (ccharacter
*Viewer
) const {
4511 return Viewer
== this;
4515 truth
character::HitEffect (character
*Enemy
, item
* Weapon
, v2 HitPos
, int Type
, int BodyPartIndex
,
4516 int Direction
, truth BlockedByArmour
)
4518 if (Weapon
) return Weapon
->HitEffect(this, Enemy
, HitPos
, BodyPartIndex
, Direction
, BlockedByArmour
);
4520 case UNARMED_ATTACK
: return Enemy
->SpecialUnarmedEffect(this, HitPos
, BodyPartIndex
, Direction
, BlockedByArmour
);
4521 case KICK_ATTACK
: return Enemy
->SpecialKickEffect(this, HitPos
, BodyPartIndex
, Direction
, BlockedByArmour
);
4522 case BITE_ATTACK
: return Enemy
->SpecialBiteEffect(this, HitPos
, BodyPartIndex
, Direction
, BlockedByArmour
);
4528 void character::WeaponSkillHit (item
*Weapon
, int Type
, int Hits
) {
4531 case UNARMED_ATTACK
: Category
= UNARMED
; break;
4532 case WEAPON_ATTACK
: Weapon
->WeaponSkillHit(Hits
); return;
4533 case KICK_ATTACK
: Category
= KICK
; break;
4534 case BITE_ATTACK
: Category
= BITE
; break;
4536 if (!IsHumanoid()) return;
4537 Category
= Weapon
->GetWeaponCategory();
4540 ABORT("Illegal Type %d passed to character::WeaponSkillHit()!", Type
);
4543 if (GetCWeaponSkill(Category
)->AddHit(Hits
)) {
4544 CalculateBattleInfo();
4545 if (IsPlayer()) GetCWeaponSkill(Category
)->AddLevelUpMessage(Category
);
4550 /* Returns 0 if character cannot be duplicated */
4551 character
*character::Duplicate (uLong Flags
) {
4552 if (!(Flags
& IGNORE_PROHIBITIONS
) && !CanBeCloned()) return 0;
4553 character
*Char
= GetProtoType()->Clone(this);
4554 if (Flags
& MIRROR_IMAGE
) {
4555 DuplicateEquipment(Char
, Flags
& ~IGNORE_PROHIBITIONS
);
4556 Char
->SetLifeExpectancy(Flags
>> LE_BASE_SHIFT
& LE_BASE_RANGE
, Flags
>> LE_RAND_SHIFT
& LE_RAND_RANGE
);
4558 Char
->CalculateAll();
4559 Char
->CalculateEmitation();
4560 Char
->UpdatePictures();
4561 Char
->Flags
&= ~(C_INITIALIZING
|C_IN_NO_MSG_MODE
);
4566 truth
character::TryToEquip (item
*Item
) {
4567 if (!Item
->AllowEquip() || !CanUseEquipment() || GetAttribute(WISDOM
) >= Item
->GetWearWisdomLimit() || Item
->GetSquaresUnder() != 1)
4569 for (int e
= 0; e
< GetEquipments(); ++e
) {
4570 if (GetBodyPartOfEquipment(e
) && EquipmentIsAllowed(e
)) {
4571 sorter Sorter
= EquipmentSorter(e
);
4572 if ((Sorter
== 0 || (Item
->*Sorter
)(this)) &&
4573 ((e
!= RIGHT_WIELDED_INDEX
&& e
!= LEFT_WIELDED_INDEX
) ||
4574 Item
->IsWeapon(this) || Item
->IsShield(this)) && AllowEquipment(Item
, e
)) {
4575 item
*OldEquipment
= GetEquipment(e
);
4576 if (BoundToUse(OldEquipment
, e
)) continue;
4577 lsquare
*LSquareUnder
= GetLSquareUnder();
4578 stack
*StackUnder
= LSquareUnder
->GetStack();
4579 msgsystem::DisableMessages();
4580 Flags
|= C_PICTURE_UPDATES_FORBIDDEN
;
4581 LSquareUnder
->Freeze();
4582 StackUnder
->Freeze();
4583 double Danger
= GetRelativeDanger(PLAYER
);
4584 if (OldEquipment
) OldEquipment
->RemoveFromSlot();
4585 Item
->RemoveFromSlot();
4586 SetEquipment(e
, Item
);
4587 double NewDanger
= GetRelativeDanger(PLAYER
);
4588 Item
->RemoveFromSlot();
4589 StackUnder
->AddItem(Item
);
4590 if (OldEquipment
) SetEquipment(e
, OldEquipment
);
4591 msgsystem::EnableMessages();
4592 Flags
&= ~C_PICTURE_UPDATES_FORBIDDEN
;
4593 LSquareUnder
->UnFreeze();
4594 StackUnder
->UnFreeze();
4596 if (NewDanger
> Danger
|| BoundToUse(Item
, e
)) {
4597 room
*Room
= GetRoom();
4598 if (!Room
|| Room
->PickupItem(this, Item
, 1)) {
4599 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s drops %s %s and equips %s instead.", CHAR_NAME(DEFINITE
), CHAR_POSSESSIVE_PRONOUN
, OldEquipment
->CHAR_NAME(UNARTICLED
), Item
->CHAR_NAME(INDEFINITE
));
4600 if (Room
) Room
->DropItem(this, OldEquipment
, 1);
4601 OldEquipment
->MoveTo(StackUnder
);
4602 Item
->RemoveFromSlot();
4603 SetEquipment(e
, Item
);
4609 if (NewDanger
> Danger
|| (NewDanger
== Danger
&& e
!= RIGHT_WIELDED_INDEX
&& e
!= LEFT_WIELDED_INDEX
) || BoundToUse(Item
, e
)) {
4610 room
*Room
= GetRoom();
4611 if (!Room
|| Room
->PickupItem(this, Item
, 1)) {
4612 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s picks up and equips %s.", CHAR_NAME(DEFINITE
), Item
->CHAR_NAME(INDEFINITE
));
4613 Item
->RemoveFromSlot();
4614 SetEquipment(e
, Item
);
4627 truth
character::TryToConsume (item
*Item
) {
4628 return Item
->CanBeEatenByAI(this) && ConsumeItem(Item
, Item
->GetConsumeMaterial(this)->GetConsumeVerb());
4632 void character::UpdateESPLOS () const {
4633 if (StateIsActivated(ESP
) && !game::IsInWilderness()) {
4634 for (int c
= 0; c
< game::GetTeams(); ++c
) {
4635 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
) {
4636 const character
*ch
= *i
;
4637 if (ch
->IsEnabled()) ch
->SendNewDrawRequest();
4644 int character::GetCWeaponSkillLevel (citem
*Item
) const {
4645 if (Item
->GetWeaponCategory() < GetAllowedWeaponSkillCategories()) return GetCWeaponSkill(Item
->GetWeaponCategory())->GetLevel();
4650 void character::PrintBeginPanicMessage () const {
4651 if (IsPlayer()) ADD_MESSAGE("You panic!");
4652 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s panics.", CHAR_NAME(DEFINITE
));
4656 void character::PrintEndPanicMessage () const {
4657 if (IsPlayer()) ADD_MESSAGE("You finally calm down.");
4658 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s calms down.", CHAR_NAME(DEFINITE
));
4662 void character::CheckPanic (int Ticks
) {
4663 if (GetPanicLevel() > 1 && !StateIsActivated(PANIC
) && GetHP() * 100 < RAND() % (GetPanicLevel() * GetMaxHP() << 1))
4664 BeginTemporaryState(PANIC
, ((Ticks
* 3) >> 2) + RAND() % ((Ticks
>> 1) + 1)); // 25% randomness to ticks...
4668 /* returns 0 if fails else the newly created character */
4669 character
*character::DuplicateToNearestSquare (character
*Cloner
, uLong Flags
) {
4670 character
*NewlyCreated
= Duplicate(Flags
);
4671 if (!NewlyCreated
) return 0;
4672 if (Flags
& CHANGE_TEAM
&& Cloner
) NewlyCreated
->ChangeTeam(Cloner
->GetTeam());
4673 NewlyCreated
->PutNear(GetPos());
4674 return NewlyCreated
;
4678 void character::SignalSpoil (material
*m
) {
4679 if (GetMotherEntity()) GetMotherEntity()->SignalSpoil(m
);
4680 else Disappear(0, "spoil", &item::IsVeryCloseToSpoiling
);
4684 truth
character::CanHeal () const {
4685 for (int c
= 0; c
< BodyParts
; ++c
) {
4686 bodypart
*BodyPart
= GetBodyPart(c
);
4687 if (BodyPart
&& BodyPart
->CanRegenerate() && BodyPart
->GetHP() < BodyPart
->GetMaxHP()) return true;
4693 int character::GetRelation (ccharacter
*Who
) const {
4694 return GetTeam()->GetRelation(Who
->GetTeam());
4698 truth (item::*AffectTest
[BASE_ATTRIBUTES
])() const = {
4699 &item::AffectsEndurance
,
4700 &item::AffectsPerception
,
4701 &item::AffectsIntelligence
,
4702 &item::AffectsWisdom
,
4703 &item::AffectsWillPower
,
4704 &item::AffectsCharisma
,
4709 /* Returns nonzero if endurance has decreased and death may occur */
4710 truth
character::CalculateAttributeBonuses () {
4711 doforbodyparts()(this, &bodypart::CalculateAttributeBonuses
);
4712 int BackupBonus
[BASE_ATTRIBUTES
];
4713 int BackupCarryingBonus
= CarryingBonus
;
4716 for (c1
= 0; c1
< BASE_ATTRIBUTES
; ++c1
) {
4717 BackupBonus
[c1
] = AttributeBonus
[c1
];
4718 AttributeBonus
[c1
] = 0;
4720 for (c1
= 0; c1
< GetEquipments(); ++c1
) {
4721 item
*Equipment
= GetEquipment(c1
);
4722 if (!Equipment
|| !Equipment
->IsInCorrectSlot(c1
)) continue;
4723 for (int c2
= 0; c2
< BASE_ATTRIBUTES
; ++c2
) {
4724 if ((Equipment
->*AffectTest
[c2
])()) AttributeBonus
[c2
] += Equipment
->GetEnchantment();
4726 if (Equipment
->AffectsCarryingCapacity()) CarryingBonus
+= Equipment
->GetCarryingBonus();
4729 ApplySpecialAttributeBonuses();
4731 if (IsPlayer() && !IsInitializing() && AttributeBonus
[PERCEPTION
] != BackupBonus
[PERCEPTION
]) game::SendLOSUpdateRequest();
4732 if (IsPlayer() && !IsInitializing() && AttributeBonus
[INTELLIGENCE
] != BackupBonus
[INTELLIGENCE
]) UpdateESPLOS();
4734 if (!IsInitializing() && CarryingBonus
!= BackupCarryingBonus
) CalculateBurdenState();
4736 if (!IsInitializing() && AttributeBonus
[ENDURANCE
] != BackupBonus
[ENDURANCE
]) {
4737 CalculateBodyPartMaxHPs();
4738 CalculateMaxStamina();
4739 return AttributeBonus
[ENDURANCE
] < BackupBonus
[ENDURANCE
];
4746 void character::ApplyEquipmentAttributeBonuses (item
*Equipment
) {
4747 if (Equipment
->AffectsEndurance()) {
4748 AttributeBonus
[ENDURANCE
] += Equipment
->GetEnchantment();
4749 CalculateBodyPartMaxHPs();
4750 CalculateMaxStamina();
4752 if (Equipment
->AffectsPerception()) {
4753 AttributeBonus
[PERCEPTION
] += Equipment
->GetEnchantment();
4754 if (IsPlayer()) game::SendLOSUpdateRequest();
4756 if (Equipment
->AffectsIntelligence()) {
4757 AttributeBonus
[INTELLIGENCE
] += Equipment
->GetEnchantment();
4758 if (IsPlayer()) UpdateESPLOS();
4760 if (Equipment
->AffectsWisdom()) AttributeBonus
[WISDOM
] += Equipment
->GetEnchantment();
4761 if (Equipment
->AffectsWillPower()) AttributeBonus
[WILL_POWER
] += Equipment
->GetEnchantment();
4762 if (Equipment
->AffectsCharisma()) AttributeBonus
[CHARISMA
] += Equipment
->GetEnchantment();
4763 if (Equipment
->AffectsMana()) AttributeBonus
[MANA
] += Equipment
->GetEnchantment();
4764 if (Equipment
->AffectsCarryingCapacity()) {
4765 CarryingBonus
+= Equipment
->GetCarryingBonus();
4766 CalculateBurdenState();
4771 void character::ReceiveAntidote (sLong Amount
) {
4772 if (StateIsActivated(POISONED
)) {
4773 if (GetTemporaryStateCounter(POISONED
) > Amount
) {
4774 EditTemporaryStateCounter(POISONED
, -Amount
);
4777 if (IsPlayer()) ADD_MESSAGE("Aaaah... You feel much better.");
4778 Amount
-= GetTemporaryStateCounter(POISONED
);
4779 DeActivateTemporaryState(POISONED
);
4782 if ((Amount
>= 100 || RAND_N(100) < Amount
) && StateIsActivated(PARASITIZED
)) {
4783 if (IsPlayer()) ADD_MESSAGE("Something in your belly didn't seem to like this stuff.");
4784 DeActivateTemporaryState(PARASITIZED
);
4785 Amount
-= Min(100, Amount
);
4787 if ((Amount
>= 100 || RAND_N(100) < Amount
) && StateIsActivated(LEPROSY
)) {
4788 if (IsPlayer()) ADD_MESSAGE("You are not falling to pieces anymore.");
4789 DeActivateTemporaryState(LEPROSY
);
4790 Amount
-= Min(100, Amount
);
4795 void character::AddAntidoteConsumeEndMessage () const {
4796 if (StateIsActivated(POISONED
)) {
4797 // true only if the antidote didn't cure the poison completely
4798 if (IsPlayer()) ADD_MESSAGE("Your body processes the poison in your veins with rapid speed.");
4803 truth
character::IsDead () const {
4804 for (int c
= 0; c
< BodyParts
; ++c
) {
4805 bodypart
*BodyPart
= GetBodyPart(c
);
4806 if (BodyPartIsVital(c
) && (!BodyPart
|| BodyPart
->GetHP() < 1)) return true;
4812 void character::SignalSpoilLevelChange (material
*m
) {
4813 if (GetMotherEntity()) GetMotherEntity()->SignalSpoilLevelChange(m
); else UpdatePictures();
4817 void character::AddOriginalBodyPartID (int I
, uLong What
) {
4818 if (std::find(OriginalBodyPartID
[I
].begin(), OriginalBodyPartID
[I
].end(), What
) == OriginalBodyPartID
[I
].end()) {
4819 OriginalBodyPartID
[I
].push_back(What
);
4820 if (OriginalBodyPartID
[I
].size() > 100) OriginalBodyPartID
[I
].erase(OriginalBodyPartID
[I
].begin());
4825 void character::AddToInventory (const fearray
<contentscript
<item
> > &ItemArray
, int SpecialFlags
) {
4826 for (uInt c1
= 0; c1
< ItemArray
.Size
; ++c1
) {
4827 if (ItemArray
[c1
].IsValid()) {
4828 const interval
*TimesPtr
= ItemArray
[c1
].GetTimes();
4829 int Times
= TimesPtr
? TimesPtr
->Randomize() : 1;
4830 for (int c2
= 0; c2
< Times
; ++c2
) {
4831 item
*Item
= ItemArray
[c1
].Instantiate(SpecialFlags
);
4833 Stack
->AddItem(Item
);
4834 Item
->SpecialGenerationHandler();
4842 truth
character::HasHadBodyPart (citem
*Item
) const {
4843 for (int c
= 0; c
< BodyParts
; ++c
)
4844 if (std::find(OriginalBodyPartID
[c
].begin(), OriginalBodyPartID
[c
].end(), Item
->GetID()) != OriginalBodyPartID
[c
].end())
4846 return GetPolymorphBackup() && GetPolymorphBackup()->HasHadBodyPart(Item
);
4850 festring
&character::ProcessMessage (festring
&Msg
) const {
4851 SEARCH_N_REPLACE(Msg
, "@nu", GetName(UNARTICLED
));
4852 SEARCH_N_REPLACE(Msg
, "@ni", GetName(INDEFINITE
));
4853 SEARCH_N_REPLACE(Msg
, "@nd", GetName(DEFINITE
));
4854 SEARCH_N_REPLACE(Msg
, "@du", GetDescription(UNARTICLED
));
4855 SEARCH_N_REPLACE(Msg
, "@di", GetDescription(INDEFINITE
));
4856 SEARCH_N_REPLACE(Msg
, "@dd", GetDescription(DEFINITE
));
4857 SEARCH_N_REPLACE(Msg
, "@pp", GetPersonalPronoun());
4858 SEARCH_N_REPLACE(Msg
, "@sp", GetPossessivePronoun());
4859 SEARCH_N_REPLACE(Msg
, "@op", GetObjectPronoun());
4860 SEARCH_N_REPLACE(Msg
, "@Nu", GetName(UNARTICLED
).CapitalizeCopy());
4861 SEARCH_N_REPLACE(Msg
, "@Ni", GetName(INDEFINITE
).CapitalizeCopy());
4862 SEARCH_N_REPLACE(Msg
, "@Nd", GetName(DEFINITE
).CapitalizeCopy());
4863 SEARCH_N_REPLACE(Msg
, "@Du", GetDescription(UNARTICLED
).CapitalizeCopy());
4864 SEARCH_N_REPLACE(Msg
, "@Di", GetDescription(INDEFINITE
).CapitalizeCopy());
4865 SEARCH_N_REPLACE(Msg
, "@Dd", GetDescription(DEFINITE
).CapitalizeCopy());
4866 SEARCH_N_REPLACE(Msg
, "@Pp", GetPersonalPronoun().CapitalizeCopy());
4867 SEARCH_N_REPLACE(Msg
, "@Sp", GetPossessivePronoun().CapitalizeCopy());
4868 SEARCH_N_REPLACE(Msg
, "@Op", GetObjectPronoun().CapitalizeCopy());
4869 SEARCH_N_REPLACE(Msg
, "@Gd", GetMasterGod()->GetName());
4874 void character::ProcessAndAddMessage (festring Msg
) const {
4875 ADD_MESSAGE("%s", ProcessMessage(Msg
).CStr());
4879 void character::BeTalkedTo () {
4881 if (GetRelation(PLAYER
) == HOSTILE
)
4882 ProcessAndAddMessage(GetHostileReplies()[RandomizeReply(Said
, GetHostileReplies().Size
)]);
4884 ProcessAndAddMessage(GetFriendlyReplies()[RandomizeReply(Said
, GetFriendlyReplies().Size
)]);
4888 truth
character::CheckZap () {
4890 ADD_MESSAGE("This monster type can't zap.");
4897 void character::DamageAllItems (character
*Damager
, int Damage
, int Type
) {
4898 GetStack()->ReceiveDamage(Damager
, Damage
, Type
);
4899 for (int c
= 0; c
< GetEquipments(); ++c
) {
4900 item
*Equipment
= GetEquipment(c
);
4901 if (Equipment
) Equipment
->ReceiveDamage(Damager
, Damage
, Type
);
4906 truth
character::Equips (citem
*Item
) const {
4907 return combineequipmentpredicateswithparam
<uLong
>()(this, &item::HasID
, Item
->GetID(), 1);
4911 void character::PrintBeginConfuseMessage () const {
4912 if (IsPlayer()) ADD_MESSAGE("You feel quite happy.");
4916 void character::PrintEndConfuseMessage () const {
4917 if (IsPlayer()) ADD_MESSAGE("The world is boring again.");
4921 v2
character::ApplyStateModification (v2 TryDirection
) const {
4922 if (!StateIsActivated(CONFUSED
) || RAND() & 15 || game::IsInWilderness()) return TryDirection
;
4923 v2 To
= GetLevel()->GetFreeAdjacentSquare(this, GetPos(), true);
4924 if (To
== ERROR_V2
) return TryDirection
;
4926 if (To
!= TryDirection
&& IsPlayer()) ADD_MESSAGE("Whoa! You somehow don't manage to walk straight.");
4931 void character::AddConfuseHitMessage () const {
4932 if (IsPlayer()) ADD_MESSAGE("This stuff is confusing.");
4936 item
*character::SelectFromPossessions (cfestring
&Topic
, sorter Sorter
) {
4937 itemvector ReturnVector
;
4938 SelectFromPossessions(ReturnVector
, Topic
, NO_MULTI_SELECT
, Sorter
);
4939 return !ReturnVector
.empty() ? ReturnVector
[0] : 0;
4943 truth
character::SelectFromPossessions (itemvector
&ReturnVector
, cfestring
&Topic
, int Flags
, sorter Sorter
) {
4945 truth InventoryPossible
= GetStack()->SortedItems(this, Sorter
);
4946 if (InventoryPossible
) List
.AddEntry(CONST_S("choose from inventory"), LIGHT_GRAY
, 20, game::AddToItemDrawVector(itemvector()));
4951 for (c
= 0; c
< BodyParts
; ++c
) {
4952 bodypart
*BodyPart
= GetBodyPart(c
);
4953 if (BodyPart
&& (Sorter
== 0 || (BodyPart
->*Sorter
)(this))) {
4954 Item
.push_back(BodyPart
);
4956 BodyPart
->AddName(Entry
, UNARTICLED
);
4957 int ImageKey
= game::AddToItemDrawVector(itemvector(1, BodyPart
));
4958 List
.AddEntry(Entry
, LIGHT_GRAY
, 20, ImageKey
, true);
4962 for (c
= 0; c
< GetEquipments(); ++c
) {
4963 item
*Equipment
= GetEquipment(c
);
4964 if (Equipment
&& (Sorter
== 0 || (Equipment
->*Sorter
)(this))) {
4965 Item
.push_back(Equipment
);
4966 Entry
= GetEquipmentName(c
);
4969 Equipment
->AddInventoryEntry(this, Entry
, 1, true);
4970 AddSpecialEquipmentInfo(Entry
, c
);
4971 int ImageKey
= game::AddToItemDrawVector(itemvector(1, Equipment
));
4972 List
.AddEntry(Entry
, LIGHT_GRAY
, 20, ImageKey
, true);
4977 game::SetStandardListAttributes(List
);
4978 List
.SetFlags(SELECTABLE
|DRAW_BACKGROUND_AFTERWARDS
);
4979 List
.SetEntryDrawer(game::ItemEntryDrawer
);
4980 game::DrawEverythingNoBlit();
4981 int Chosen
= List
.Draw();
4982 game::ClearItemDrawVector();
4983 if (Chosen
!= ESCAPED
) {
4984 if ((InventoryPossible
&& !Chosen
) || Chosen
& FELIST_ERROR_BIT
) {
4985 GetStack()->DrawContents(ReturnVector
, this, Topic
, Flags
, Sorter
);
4987 ReturnVector
.push_back(Item
[InventoryPossible
? Chosen
- 1 : Chosen
]);
4988 if (Flags
& SELECT_PAIR
&& ReturnVector
[0]->HandleInPairs()) {
4989 item
*PairEquipment
= GetPairEquipment(ReturnVector
[0]->GetEquipmentIndex());
4990 if (PairEquipment
&& PairEquipment
->CanBePiledWith(ReturnVector
[0], this)) ReturnVector
.push_back(PairEquipment
);
4995 if (!GetStack()->SortedItems(this, Sorter
)) return false;
4996 game::ClearItemDrawVector();
4997 GetStack()->DrawContents(ReturnVector
, this, Topic
, Flags
, Sorter
);
5003 truth
character::EquipsSomething (sorter Sorter
) {
5004 for (int c
= 0; c
< GetEquipments(); ++c
) {
5005 item
*Equipment
= GetEquipment(c
);
5006 if (Equipment
&& (Sorter
== 0 || (Equipment
->*Sorter
)(this))) return true;
5012 material
*character::CreateBodyPartMaterial (int, sLong Volume
) const {
5013 return MAKE_MATERIAL(GetFleshMaterial(), Volume
);
5017 truth
character::CheckTalk () {
5019 ADD_MESSAGE("This monster does not know the art of talking.");
5026 truth
character::MoveTowardsHomePos () {
5027 if (HomeDataIsValid() && IsEnabled()) {
5028 SetGoingTo(HomeData
->Pos
);
5029 return MoveTowardsTarget(false) || (!GetPos().IsAdjacent(HomeData
->Pos
) && MoveRandomly());
5035 truth
character::TryToChangeEquipment (stack
*MainStack
, stack
*SecStack
, int Chosen
) {
5036 if (!GetBodyPartOfEquipment(Chosen
)) {
5037 ADD_MESSAGE("Bodypart missing!");
5040 item
*OldEquipment
= GetEquipment(Chosen
);
5041 if (!IsPlayer() && BoundToUse(OldEquipment
, Chosen
)) {
5042 ADD_MESSAGE("%s refuses to unequip %s.", CHAR_DESCRIPTION(DEFINITE
), OldEquipment
->CHAR_NAME(DEFINITE
));
5045 if (OldEquipment
) OldEquipment
->MoveTo(MainStack
);
5046 sorter Sorter
= EquipmentSorter(Chosen
);
5047 if (!MainStack
->SortedItems(this, Sorter
) && (!SecStack
|| !SecStack
->SortedItems(this, Sorter
))) {
5048 ADD_MESSAGE("You haven't got any item that could be used for this purpose.");
5051 game::DrawEverythingNoBlit();
5052 itemvector ItemVector
;
5053 int Return
= MainStack
->DrawContents(ItemVector
, SecStack
, this,
5054 CONST_S("Choose ") + GetEquipmentName(Chosen
) + ':',
5055 SecStack
? CONST_S("Items in your inventory") : CONST_S(""),
5056 SecStack
? festring(CONST_S("Items in ") + GetPossessivePronoun() + " inventory") : CONST_S(""),
5057 SecStack
? festring(GetDescription(DEFINITE
) + " is " + GetVerbalBurdenState()) : CONST_S(""),
5058 GetVerbalBurdenStateColor(),
5059 NONE_AS_CHOICE
|NO_MULTI_SELECT
,
5061 if (Return
== ESCAPED
) {
5063 OldEquipment
->RemoveFromSlot();
5064 SetEquipment(Chosen
, OldEquipment
);
5068 item
*Item
= ItemVector
.empty() ? 0 : ItemVector
[0];
5070 if (!IsPlayer() && !AllowEquipment(Item
, Chosen
)) {
5071 ADD_MESSAGE("%s refuses to equip %s.", CHAR_DESCRIPTION(DEFINITE
), Item
->CHAR_NAME(DEFINITE
));
5074 Item
->RemoveFromSlot();
5075 SetEquipment(Chosen
, Item
);
5076 if (CheckIfEquipmentIsNotUsable(Chosen
)) Item
->MoveTo(MainStack
); // small bug?
5078 return Item
!= OldEquipment
;
5082 void character::PrintBeginParasitizedMessage () const {
5083 if (IsPlayer()) ADD_MESSAGE("You feel you are no longer alone.");
5087 void character::PrintEndParasitizedMessage () const {
5088 if (IsPlayer()) ADD_MESSAGE("A feeling of sLong welcome emptiness overwhelms you.");
5092 void character::ParasitizedHandler () {
5094 if (!(RAND() % 250)) {
5095 if (IsPlayer()) ADD_MESSAGE("Ugh. You feel something violently carving its way through your intestines.");
5096 ReceiveDamage(0, 1, POISON
, TORSO
, 8, false, false, false, false);
5097 CheckDeath(CONST_S("killed by a vile parasite"), 0);
5102 truth
character::CanFollow () const {
5103 return CanMove() && !StateIsActivated(PANIC
) && !IsStuck();
5107 festring
character::GetKillName () const {
5108 if (!GetPolymorphBackup()) return GetName(INDEFINITE
);
5110 GetPolymorphBackup()->AddName(KillName
, INDEFINITE
);
5111 KillName
<< " polymorphed into ";
5112 id::AddName(KillName
, INDEFINITE
);
5117 festring
character::GetPanelName () const {
5119 Name
<< AssignedName
<< " the " << game::GetVerbalPlayerAlignment() << ' ';
5120 id::AddName(Name
, UNARTICLED
);
5125 sLong
character::GetMoveAPRequirement (int Difficulty
) const {
5126 return (!StateIsActivated(PANIC
) ? 10000000 : 8000000) * Difficulty
/ (APBonus(GetAttribute(AGILITY
)) * GetMoveEase());
5130 bodypart
*character::HealHitPoint() {
5131 int NeedHeal
= 0, NeedHealIndex
[MAX_BODYPARTS
];
5132 for (int c
= 0; c
< BodyParts
; ++c
) {
5133 bodypart
*BodyPart
= GetBodyPart(c
);
5134 if (BodyPart
&& BodyPart
->CanRegenerate() && BodyPart
->GetHP() < BodyPart
->GetMaxHP()) NeedHealIndex
[NeedHeal
++] = c
;
5137 bodypart
*BodyPart
= GetBodyPart(NeedHealIndex
[RAND() % NeedHeal
]);
5138 BodyPart
->IncreaseHP();
5146 void character::CreateHomeData () {
5147 HomeData
= new homedata
;
5148 lsquare
*Square
= GetLSquareUnder();
5149 HomeData
->Pos
= Square
->GetPos();
5150 HomeData
->Dungeon
= Square
->GetDungeonIndex();
5151 HomeData
->Level
= Square
->GetLevelIndex();
5152 HomeData
->Room
= Square
->GetRoomIndex();
5156 room
*character::GetHomeRoom() const {
5157 if (HomeDataIsValid() && HomeData
->Room
) return GetLevel()->GetRoom(HomeData
->Room
);
5162 void character::RemoveHomeData () {
5168 void character::AddESPConsumeMessage () const {
5169 if (IsPlayer()) ADD_MESSAGE("You feel a strange mental activity.");
5173 void character::SetBodyPart (int I
, bodypart
*What
) {
5174 BodyPartSlot
[I
].PutInItem(What
);
5176 What
->SignalPossibleUsabilityChange();
5178 AddOriginalBodyPartID(I
, What
->GetID());
5179 if (What
->GetMainMaterial()->IsInfectedByLeprosy()) GainIntrinsic(LEPROSY
);
5180 else if (StateIsActivated(LEPROSY
)) What
->GetMainMaterial()->SetIsInfectedByLeprosy(true);
5185 truth
character::ConsumeItem (item
*Item
, cfestring
&ConsumeVerb
) {
5186 if (IsPlayer() && HasHadBodyPart(Item
) && !game::TruthQuestion(CONST_S("Are you sure? You may be able to put it back... [y/N]")))
5188 if (Item
->IsOnGround() && GetRoom() && !GetRoom()->ConsumeItem(this, Item
, 1))
5190 if (IsPlayer()) ADD_MESSAGE("You begin %s %s.", ConsumeVerb
.CStr(), Item
->CHAR_NAME(DEFINITE
));
5191 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s begins %s %s.", CHAR_NAME(DEFINITE
), ConsumeVerb
.CStr(), Item
->CHAR_NAME(DEFINITE
));
5192 consume
*Consume
= consume::Spawn(this);
5193 Consume
->SetDescription(ConsumeVerb
);
5194 Consume
->SetConsumingID(Item
->GetID());
5201 truth
character::CheckThrow () const {
5203 ADD_MESSAGE("This monster type cannot throw.");
5210 void character::GetHitByExplosion (const explosion
*Explosion
, int Damage
) {
5211 int DamageDirection
= GetPos() == Explosion
->Pos
? RANDOM_DIR
: game::CalculateRoughDirection(GetPos() - Explosion
->Pos
);
5212 if (!IsPet() && Explosion
->Terrorist
&& Explosion
->Terrorist
->IsPet()) Explosion
->Terrorist
->Hostility(this);
5213 GetTorso()->SpillBlood((8 - Explosion
->Size
+ RAND() % (8 - Explosion
->Size
)) >> 1);
5214 v2 SpillPos
= GetPos() + game::GetMoveVector(DamageDirection
);
5215 if (GetArea()->IsValidPos(SpillPos
)) GetTorso()->SpillBlood((8-Explosion
->Size
+RAND()%(8-Explosion
->Size
))>>1, SpillPos
);
5216 if (IsPlayer()) ADD_MESSAGE("You are hit by the explosion!");
5217 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s is hit by the explosion.", CHAR_NAME(DEFINITE
));
5218 truth WasUnconscious
= GetAction() && GetAction()->IsUnconsciousness();
5219 ReceiveDamage(Explosion
->Terrorist
, Damage
>> 1, FIRE
, ALL
, DamageDirection
, true, false, false, false);
5221 ReceiveDamage(Explosion
->Terrorist
, Damage
>> 1, PHYSICAL_DAMAGE
, ALL
, DamageDirection
, true, false, false, false);
5222 CheckDeath(Explosion
->DeathMsg
, Explosion
->Terrorist
, !WasUnconscious
? IGNORE_UNCONSCIOUSNESS
: 0);
5227 void character::SortAllItems (const sortdata
&SortData
) {
5228 GetStack()->SortAllItems(SortData
);
5229 doforequipmentswithparam
<const sortdata
&>()(this, &item::SortAllItems
, SortData
);
5233 void character::PrintBeginSearchingMessage () const {
5234 if (IsPlayer()) ADD_MESSAGE("You feel you can now notice even the very smallest details around you.");
5238 void character::PrintEndSearchingMessage () const {
5239 if (IsPlayer()) ADD_MESSAGE("You feel less perceptive.");
5243 void character::SearchingHandler () {
5244 if (!game::IsInWilderness()) Search(15);
5248 void character::Search (int Perception
) {
5249 for (int d
= 0; d
< GetExtendedNeighbourSquares(); ++d
) {
5250 lsquare
*LSquare
= GetNeighbourLSquare(d
);
5251 if (LSquare
) LSquare
->GetStack()->Search(this, Min(Perception
, 200));
5256 // surprisingly returns 0 if fails
5257 character
*character::GetRandomNeighbour (int RelationFlags
) const {
5258 character
*Chars
[MAX_NEIGHBOUR_SQUARES
];
5260 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
5261 lsquare
*LSquare
= GetNeighbourLSquare(d
);
5263 character
*Char
= LSquare
->GetCharacter();
5264 if (Char
&& (GetRelation(Char
) & RelationFlags
)) Chars
[Index
++] = Char
;
5267 return Index
? Chars
[RAND() % Index
] : 0;
5271 void character::ResetStates () {
5272 for (int c
= 0; c
< STATES
; ++c
) {
5273 if (1 << c
!= POLYMORPHED
&& TemporaryStateIsActivated(1 << c
) && TemporaryStateCounter
[c
] != PERMANENT
) {
5274 TemporaryState
&= ~(1 << c
);
5275 if (StateData
[c
].EndHandler
) {
5276 (this->*StateData
[c
].EndHandler
)();
5277 if (!IsEnabled())return;
5284 void characterdatabase::InitDefaults (const characterprototype
*NewProtoType
, int NewConfig
) {
5286 ProtoType
= NewProtoType
;
5292 void character::PrintBeginGasImmunityMessage () const {
5293 if (IsPlayer()) ADD_MESSAGE("All smells fade away.");
5297 void character::PrintEndGasImmunityMessage () const {
5298 if (IsPlayer()) ADD_MESSAGE("Yuck! The world smells bad again.");
5302 void character::ShowAdventureInfo () const {
5303 static const char *lists
[4][4] = {
5304 { "Show massacre history",
5306 "Show message history",
5309 "Show message history",
5312 { "Show message history",
5316 { "Show massacre history",
5317 "Show message history",
5321 // massacre, inventory, messages
5322 static const int nums
[4][3] = {
5329 if (GetStack()->GetItems()) {
5330 idx
= game::MassacreListsEmpty() ? 1 : 0;
5332 idx
= game::MassacreListsEmpty() ? 2 : 3;
5336 sel
= game::ListSelectorArray(sel
, CONST_S("Do you want to see some funny history?"), lists
[idx
]);
5338 if (sel
== nums
[idx
][0] && !game::MassacreListsEmpty()) game::DisplayMassacreLists();
5339 if (sel
== nums
[idx
][1] && GetStack()->GetItems()) {
5340 GetStack()->DrawContents(this, CONST_S("Your inventory"), NO_SELECT
);
5341 for(stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) i
->DrawContents(this);
5342 doforequipmentswithparam
<ccharacter
*>()(this, &item::DrawContents
, this);
5344 if (sel
== nums
[idx
][2]) msgsystem::DrawMessageHistory();
5349 truth
character::EditAllAttributes (int Amount
) {
5350 if (!Amount
) return true;
5352 truth MayEditMore
= false;
5353 for (c
= 0; c
< BodyParts
; ++c
) {
5354 bodypart
*BodyPart
= GetBodyPart(c
);
5355 if (BodyPart
&& BodyPart
->EditAllAttributes(Amount
)) MayEditMore
= true;
5357 for (c
= 0; c
< BASE_ATTRIBUTES
; ++c
) {
5358 if (BaseExperience
[c
]) {
5359 BaseExperience
[c
] += Amount
* EXP_MULTIPLIER
;
5360 LimitRef(BaseExperience
[c
], MIN_EXP
, MAX_EXP
);
5361 if ((Amount
< 0 && BaseExperience
[c
] != MIN_EXP
) || (Amount
> 0 && BaseExperience
[c
] != MAX_EXP
)) MayEditMore
= true;
5368 game::SendLOSUpdateRequest();
5371 if (IsPlayerKind()) UpdatePictures();
5377 void character::AddAttributeInfo (festring
&Entry
) const {
5379 Entry
<< GetAttribute(ENDURANCE
);
5381 Entry
<< GetAttribute(PERCEPTION
);
5383 Entry
<< GetAttribute(INTELLIGENCE
);
5385 Entry
<< GetAttribute(WISDOM
);
5387 Entry
<< GetAttribute(CHARISMA
);
5389 Entry
<< GetAttribute(MANA
);
5393 void character::AddDefenceInfo (felist
&List
) const {
5395 for (int c
= 0; c
< BodyParts
; ++c
) {
5396 bodypart
*BodyPart
= GetBodyPart(c
);
5398 Entry
= CONST_S(" ");
5399 BodyPart
->AddName(Entry
, UNARTICLED
);
5401 Entry
<< BodyPart
->GetMaxHP();
5403 Entry
<< BodyPart
->GetTotalResistance(PHYSICAL_DAMAGE
);
5404 List
.AddEntry(Entry
, LIGHT_GRAY
);
5410 void character::DetachBodyPart () {
5411 ADD_MESSAGE("You haven't got any extra bodyparts.");
5416 void character::ReceiveHolyBanana (sLong Amount
) {
5418 EditExperience(ARM_STRENGTH
, Amount
, 1 << 13);
5419 EditExperience(LEG_STRENGTH
, Amount
, 1 << 13);
5420 EditExperience(DEXTERITY
, Amount
, 1 << 13);
5421 EditExperience(AGILITY
, Amount
, 1 << 13);
5422 EditExperience(ENDURANCE
, Amount
, 1 << 13);
5423 EditExperience(PERCEPTION
, Amount
, 1 << 13);
5424 EditExperience(INTELLIGENCE
, Amount
, 1 << 13);
5425 EditExperience(WISDOM
, Amount
, 1 << 13);
5426 EditExperience(CHARISMA
, Amount
, 1 << 13);
5431 void character::AddHolyBananaConsumeEndMessage () const {
5432 if (IsPlayer()) ADD_MESSAGE("You feel a mysterious strengthening fire coursing through your body.");
5433 else if (CanBeSeenByPlayer()) ADD_MESSAGE("For a moment %s is surrounded by a swirling fire aura.", CHAR_NAME(DEFINITE
));
5437 truth
character::PreProcessForBone () {
5438 if (IsPet() && IsEnabled()) {
5439 Die(0, CONST_S(""), FORBID_REINCARNATION
);
5442 if (GetAction()) GetAction()->Terminate(false);
5443 if (TemporaryStateIsActivated(POLYMORPHED
)) {
5444 character
*PolymorphBackup
= GetPolymorphBackup();
5446 PolymorphBackup
->PreProcessForBone();
5449 if (MustBeRemovedFromBone()) return false;
5450 if (IsUnique() && !CanBeGenerated()) game::SignalQuestMonsterFound();
5454 GetStack()->PreProcessForBone();
5455 doforequipments()(this, &item::PreProcessForBone
);
5456 doforbodyparts()(this, &bodypart::PreProcessForBone
);
5457 game::RemoveCharacterID(ID
);
5459 game::AddCharacterID(this, ID
);
5464 truth
character::PostProcessForBone (double &DangerSum
, int& Enemies
) {
5465 if (PostProcessForBone()) {
5466 if (GetRelation(PLAYER
) == HOSTILE
) {
5467 double Danger
= GetRelativeDanger(PLAYER
, true);
5468 if (Danger
> 99.0) game::SetTooGreatDangerFound(true);
5469 else if (!IsUnique() && !IgnoreDanger()) {
5470 DangerSum
+= Danger
;
5480 truth
character::PostProcessForBone () {
5481 uLong NewID
= game::CreateNewCharacterID(this);
5482 game::GetBoneCharacterIDMap().insert(std::make_pair(-ID
, NewID
));
5483 game::RemoveCharacterID(ID
);
5485 if (IsUnique() && CanBeGenerated()) {
5486 if (DataBase
->Flags
& HAS_BEEN_GENERATED
) return false;
5489 GetStack()->PostProcessForBone();
5490 doforequipments()(this, &item::PostProcessForBone
);
5491 doforbodyparts()(this, &bodypart::PostProcessForBone
);
5496 void character::FinalProcessForBone () {
5498 GetStack()->FinalProcessForBone();
5499 doforequipments()(this, &item::FinalProcessForBone
);
5501 for (c
= 0; c
< BodyParts
; ++c
) {
5502 for (std::list
<uLong
>::iterator i
= OriginalBodyPartID
[c
].begin(); i
!= OriginalBodyPartID
[c
].end();) {
5503 boneidmap::iterator BI
= game::GetBoneItemIDMap().find(*i
);
5504 if (BI
== game::GetBoneItemIDMap().end()) {
5505 std::list
<uLong
>::iterator Dirt
= i
++;
5506 OriginalBodyPartID
[c
].erase(Dirt
);
5516 void character::SetSoulID (uLong What
) {
5517 if (GetPolymorphBackup()) GetPolymorphBackup()->SetSoulID(What
);
5521 truth
character::SearchForItem (citem
*Item
) const {
5522 if (combineequipmentpredicateswithparam
<uLong
>()(this, &item::HasID
, Item
->GetID(), 1)) return true;
5523 for (stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) if (*i
== Item
) return true;
5528 item
*character::SearchForItem (const sweaponskill
*SWeaponSkill
) const {
5529 for (int c
= 0; c
< GetEquipments(); ++c
) {
5530 item
*Equipment
= GetEquipment(c
);
5531 if (Equipment
&& SWeaponSkill
->IsSkillOf(Equipment
)) return Equipment
;
5533 for (stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) if (SWeaponSkill
->IsSkillOf(*i
)) return *i
;
5538 void character::PutNear (v2 Pos
) {
5539 v2 NewPos
= game::GetCurrentLevel()->GetNearestFreeSquare(this, Pos
, false);
5540 if (NewPos
== ERROR_V2
) {
5541 do { NewPos
= game::GetCurrentLevel()->GetRandomSquare(this); } while(NewPos
== Pos
);
5547 void character::PutToOrNear (v2 Pos
) {
5548 if (game::IsInWilderness() || (CanMoveOn(game::GetCurrentLevel()->GetLSquare(Pos
)) && IsFreeForMe(game::GetCurrentLevel()->GetLSquare(Pos
))))
5555 void character::PutTo (v2 Pos
) {
5556 SquareUnder
[0] = game::GetCurrentArea()->GetSquare(Pos
);
5557 SquareUnder
[0]->AddCharacter(this);
5561 void character::Remove () {
5562 SquareUnder
[0]->RemoveCharacter();
5567 void character::SendNewDrawRequest () const {
5568 for (int c
= 0; c
< SquaresUnder
; ++c
) {
5569 square
*Square
= GetSquareUnder(c
);
5570 if (Square
) Square
->SendNewDrawRequest();
5575 truth
character::IsOver (v2 Pos
) const {
5576 for (int c
= 0; c
< SquaresUnder
; ++c
) {
5577 square
*Square
= GetSquareUnder(c
);
5578 if (Square
&& Square
->GetPos() == Pos
) return true;
5584 truth
character::CanTheoreticallyMoveOn (const lsquare
*LSquare
) const { return GetMoveType() & LSquare
->GetTheoreticalWalkability(); }
5585 truth
character::CanMoveOn (const lsquare
*LSquare
) const { return GetMoveType() & LSquare
->GetWalkability(); }
5586 truth
character::CanMoveOn (const square
*Square
) const { return GetMoveType() & Square
->GetSquareWalkability(); }
5587 truth
character::CanMoveOn (const olterrain
*OLTerrain
) const { return GetMoveType() & OLTerrain
->GetWalkability(); }
5588 truth
character::CanMoveOn (const oterrain
*OTerrain
) const { return GetMoveType() & OTerrain
->GetWalkability(); }
5589 truth
character::IsFreeForMe(square
*Square
) const { return !Square
->GetCharacter() || Square
->GetCharacter() == this; }
5590 void character::LoadSquaresUnder () { SquareUnder
[0] = game::GetSquareInLoad(); }
5592 truth
character::AttackAdjacentEnemyAI () {
5593 if (!IsEnabled()) return false;
5594 character
*Char
[MAX_NEIGHBOUR_SQUARES
];
5595 v2 Pos
[MAX_NEIGHBOUR_SQUARES
];
5596 int Dir
[MAX_NEIGHBOUR_SQUARES
];
5598 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
5599 square
*Square
= GetNeighbourSquare(d
);
5601 character
*Enemy
= Square
->GetCharacter();
5602 if (Enemy
&& (GetRelation(Enemy
) == HOSTILE
|| StateIsActivated(CONFUSED
))) {
5604 Pos
[Index
] = Square
->GetPos();
5605 Char
[Index
++] = Enemy
;
5610 int ChosenIndex
= RAND() % Index
;
5611 Hit(Char
[ChosenIndex
], Pos
[ChosenIndex
], Dir
[ChosenIndex
]);
5618 void character::SignalStepFrom (lsquare
**OldSquareUnder
) {
5620 lsquare
*NewSquareUnder
[MAX_SQUARES_UNDER
];
5621 for (c
= 0; c
< GetSquaresUnder(); ++c
) NewSquareUnder
[c
] = GetLSquareUnder(c
);
5622 for (c
= 0; c
< GetSquaresUnder(); ++c
) {
5623 if (IsEnabled() && GetLSquareUnder(c
) == NewSquareUnder
[c
]) NewSquareUnder
[c
]->StepOn(this, OldSquareUnder
);
5628 int character::GetSumOfAttributes () const {
5629 return GetAttribute(ENDURANCE
) + GetAttribute(PERCEPTION
) + GetAttribute(INTELLIGENCE
) + GetAttribute(WISDOM
) + GetAttribute(CHARISMA
) + GetAttribute(ARM_STRENGTH
) + GetAttribute(AGILITY
);
5633 void character::IntelligenceAction (int Difficulty
) {
5634 EditAP(-20000 * Difficulty
/ APBonus(GetAttribute(INTELLIGENCE
)));
5635 EditExperience(INTELLIGENCE
, Difficulty
* 15, 1 << 7);
5639 struct walkabilitycontroller
{
5640 static truth
Handler (int x
, int y
) {
5641 return x
>= 0 && y
>= 0 && x
< LevelXSize
&& y
< LevelYSize
&& Map
[x
][y
]->GetTheoreticalWalkability() & MoveType
;
5643 static lsquare
***Map
;
5644 static int LevelXSize
, LevelYSize
;
5645 static int MoveType
;
5649 lsquare
***walkabilitycontroller::Map
;
5650 int walkabilitycontroller::LevelXSize
, walkabilitycontroller::LevelYSize
;
5651 int walkabilitycontroller::MoveType
;
5654 truth
character::CreateRoute () {
5656 if (GetAttribute(INTELLIGENCE
) >= 10 && !StateIsActivated(CONFUSED
)) {
5658 walkabilitycontroller::Map
= GetLevel()->GetMap();
5659 walkabilitycontroller::LevelXSize
= GetLevel()->GetXSize();
5660 walkabilitycontroller::LevelYSize
= GetLevel()->GetYSize();
5661 walkabilitycontroller::MoveType
= GetMoveType();
5663 for (int c
= 0; c
< game::GetTeams(); ++c
)
5664 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
) {
5665 character
*Char
= *i
;
5666 if (Char
->IsEnabled() && !Char
->Route
.empty() && (Char
->GetMoveType()&GetMoveType()) == Char
->GetMoveType()) {
5667 v2 CharGoingTo
= Char
->Route
[0];
5668 v2 iPos
= Char
->Route
.back();
5669 if ((GoingTo
-CharGoingTo
).GetLengthSquare() <= 100 && (Pos
- iPos
).GetLengthSquare() <= 100 &&
5670 mapmath
<walkabilitycontroller
>::DoLine(CharGoingTo
.X
, CharGoingTo
.Y
, GoingTo
.X
, GoingTo
.Y
, SKIP_FIRST
) &&
5671 mapmath
<walkabilitycontroller
>::DoLine(Pos
.X
, Pos
.Y
, iPos
.X
, iPos
.Y
, SKIP_FIRST
)) {
5672 if (!Illegal
.empty() && Illegal
.find(Char
->Route
.back()) != Illegal
.end()) continue;
5673 Node
= GetLevel()->FindRoute(CharGoingTo
, GoingTo
, Illegal
, GetMoveType());
5674 if (Node
) { while(Node
->Last
) { Route
.push_back(Node
->Pos
); Node
= Node
->Last
; } }
5675 else { Route
.clear(); continue; }
5676 Route
.insert(Route
.end(), Char
->Route
.begin(), Char
->Route
.end());
5677 Node
= GetLevel()->FindRoute(Pos
, iPos
, Illegal
, GetMoveType());
5678 if (Node
) { while (Node
->Last
) { Route
.push_back(Node
->Pos
); Node
= Node
->Last
; } }
5679 else { Route
.clear(); continue; }
5680 IntelligenceAction(1);
5685 Node
= GetLevel()->FindRoute(Pos
, GoingTo
, Illegal
, GetMoveType());
5686 if (Node
) { while(Node
->Last
) { Route
.push_back(Node
->Pos
); Node
= Node
->Last
; } }
5687 else TerminateGoingTo();
5688 IntelligenceAction(5);
5695 void character::SetGoingTo (v2 What
) {
5696 if (GoingTo
!= What
) {
5704 void character::TerminateGoingTo () {
5711 truth
character::CheckForFood (int Radius
) {
5712 if (StateIsActivated(PANIC
) || !UsesNutrition() || !IsEnabled()) return false;
5715 for (int r
= 1; r
<= Radius
; ++r
) {
5718 for (y
= Pos
.Y
-r
; y
<= Pos
.Y
+r
; ++y
) if (CheckForFoodInSquare(v2(x
, y
))) return true;
5721 if (x
< GetLevel()->GetXSize()) {
5722 for (y
= Pos
.Y
-r
; y
<= Pos
.Y
+r
; ++y
) if (CheckForFoodInSquare(v2(x
, y
))) return true;
5726 for (x
= Pos
.X
-r
; x
<= Pos
.X
+r
; ++x
) if (CheckForFoodInSquare(v2(x
, y
))) return true;
5729 if (y
< GetLevel()->GetYSize()) {
5730 for (x
= Pos
.X
-r
; x
<= Pos
.X
+r
; ++x
) if (CheckForFoodInSquare(v2(x
, y
))) return true;
5737 truth
character::CheckForFoodInSquare (v2 Pos
) {
5738 level
*Level
= GetLevel();
5739 if (Level
->IsValidPos(Pos
)) {
5740 lsquare
*Square
= Level
->GetLSquare(Pos
);
5741 stack
*Stack
= Square
->GetStack();
5742 if (Stack
->GetItems()) {
5743 for (stackiterator i
= Stack
->GetBottom(); i
.HasItem(); ++i
) {
5744 if (i
->IsPickable(this) && i
->CanBeSeenBy(this) && i
->CanBeEatenByAI(this) && (!Square
->GetRoomIndex() || Square
->GetRoom()->AllowFoodSearch())) {
5746 return MoveTowardsTarget(false);
5755 void character::SetConfig (int NewConfig
, int SpecialFlags
) {
5756 databasecreator
<character
>::InstallDataBase(this, NewConfig
);
5759 if (!(SpecialFlags
& NO_PIC_UPDATE
)) UpdatePictures();
5763 truth
character::IsOver (citem
*Item
) const {
5764 for (int c1
= 0; c1
< Item
->GetSquaresUnder(); ++c1
)
5765 for (int c2
= 0; c2
< SquaresUnder
; ++c2
)
5766 if (Item
->GetPos(c1
) == GetPos(c2
)) return true;
5771 truth
character::CheckConsume (cfestring
&Verb
) const {
5772 if (!UsesNutrition()) {
5773 if (IsPlayer()) ADD_MESSAGE("In this form you can't and don't need to %s.", Verb
.CStr());
5780 void character::PutTo (lsquare
*To
) {
5781 PutTo(To
->GetPos());
5785 double character::RandomizeBabyExperience (double SumE
) {
5786 if (!SumE
) return 0;
5787 double E
= (SumE
/ 4) - (SumE
/ 32) + (double(RAND()) / MAX_RAND
) * (SumE
/ 16 + 1);
5788 return Limit(E
, MIN_EXP
, MAX_EXP
);
5792 liquid
*character::CreateBlood (sLong Volume
) const {
5793 return liquid::Spawn(GetBloodMaterial(), Volume
);
5797 void character::SpillFluid (character
*Spiller
, liquid
*Liquid
, int SquareIndex
) {
5798 sLong ReserveVolume
= Liquid
->GetVolume() >> 1;
5799 Liquid
->EditVolume(-ReserveVolume
);
5800 GetStack()->SpillFluid(Spiller
, Liquid
, sLong(Liquid
->GetVolume() * sqrt(double(GetStack()->GetVolume()) / GetVolume())));
5801 Liquid
->EditVolume(ReserveVolume
);
5803 sLong Modifier
[MAX_BODYPARTS
], ModifierSum
= 0;
5804 for (c
= 0; c
< BodyParts
; ++c
) {
5805 if (GetBodyPart(c
)) {
5806 Modifier
[c
] = sLong(sqrt(GetBodyPart(c
)->GetVolume()));
5807 if (Modifier
[c
]) Modifier
[c
] *= 1 + (RAND() & 3);
5808 ModifierSum
+= Modifier
[c
];
5813 for (c
= 1; c
< GetBodyParts(); ++c
) {
5814 if (GetBodyPart(c
) && IsEnabled())
5815 GetBodyPart(c
)->SpillFluid(Spiller
, Liquid
->SpawnMoreLiquid(Liquid
->GetVolume() * Modifier
[c
] / ModifierSum
), SquareIndex
);
5818 Liquid
->SetVolume(Liquid
->GetVolume() * Modifier
[TORSO_INDEX
] / ModifierSum
);
5819 GetTorso()->SpillFluid(Spiller
, Liquid
, SquareIndex
);
5824 void character::StayOn (liquid
*Liquid
) {
5825 Liquid
->TouchEffect(this, TORSO_INDEX
);
5829 truth
character::IsAlly (ccharacter
*Char
) const {
5830 return Char
->GetTeam()->GetID() == GetTeam()->GetID();
5834 void character::ResetSpoiling () {
5835 doforbodyparts()(this, &bodypart::ResetSpoiling
);
5839 item
*character::SearchForItem (ccharacter
*Char
, sorter Sorter
) const {
5840 item
*Equipment
= findequipment
<ccharacter
*>()(this, Sorter
, Char
);
5841 if (Equipment
) return Equipment
;
5842 for (stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) if (((*i
)->*Sorter
)(Char
)) return *i
;
5847 truth
character::DetectMaterial (cmaterial
*Material
) const {
5848 return GetStack()->DetectMaterial(Material
) ||
5849 combinebodypartpredicateswithparam
<cmaterial
*>()(this, &bodypart::DetectMaterial
, Material
, 1) ||
5850 combineequipmentpredicateswithparam
<cmaterial
*>()(this, &item::DetectMaterial
, Material
, 1);
5854 truth
character::DamageTypeDestroysBodyPart (int Type
) {
5855 return (Type
&0xFFF) != PHYSICAL_DAMAGE
;
5859 truth
character::CheckIfTooScaredToHit (ccharacter
*Enemy
) const {
5860 if (IsPlayer() && StateIsActivated(PANIC
)) {
5861 for (int d
= 0; d
< GetNeighbourSquares(); ++d
) {
5862 square
*Square
= GetNeighbourSquare(d
);
5864 if(CanMoveOn(Square
) && (!Square
->GetCharacter() || Square
->GetCharacter()->IsPet())) {
5865 ADD_MESSAGE("You are too scared to attack %s.", Enemy
->CHAR_DESCRIPTION(DEFINITE
));
5875 void character::PrintBeginLevitationMessage () const {
5877 if (IsPlayer()) ADD_MESSAGE("You rise into the air like a small hot-air balloon.");
5878 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s begins to float.", CHAR_NAME(DEFINITE
));
5883 void character::PrintEndLevitationMessage () const {
5885 if (IsPlayer()) ADD_MESSAGE("You descend gently onto the ground.");
5886 else if (CanBeSeenByPlayer()) ADD_MESSAGE("%s drops onto the ground.", CHAR_NAME(DEFINITE
));
5891 truth
character::IsLimbIndex (int I
) {
5893 case RIGHT_ARM_INDEX
:
5894 case LEFT_ARM_INDEX
:
5895 case RIGHT_LEG_INDEX
:
5896 case LEFT_LEG_INDEX
:
5903 void character::EditExperience (int Identifier
, double Value
, double Speed
) {
5904 if (!AllowExperience() || (Identifier
== ENDURANCE
&& UseMaterialAttributes())) return;
5905 int Change
= RawEditExperience(BaseExperience
[Identifier
], GetNaturalExperience(Identifier
), Value
, Speed
);
5906 if (!Change
) return;
5907 cchar
*PlayerMsg
= 0, *NPCMsg
= 0;
5908 switch (Identifier
) {
5911 PlayerMsg
= "You feel tougher than anything!";
5912 if (IsPet()) NPCMsg
= "Suddenly %s looks tougher.";
5914 PlayerMsg
= "You feel less healthy.";
5915 if (IsPet()) NPCMsg
= "Suddenly %s looks less healthy.";
5917 CalculateBodyPartMaxHPs();
5918 CalculateMaxStamina();
5923 PlayerMsg
= "You now see the world in much better detail than before.";
5925 PlayerMsg
= "You feel very guru.";
5926 game::GetGod(VALPURUS
)->AdjustRelation(100);
5928 game::SendLOSUpdateRequest();
5933 if (Change
> 0) PlayerMsg
= "Suddenly the inner structure of the Multiverse around you looks quite simple.";
5934 else PlayerMsg
= "It surely is hard to think today.";
5937 if (IsPlayerKind()) UpdatePictures();
5941 if (Change
> 0) PlayerMsg
= "You feel your life experience increasing all the time.";
5942 else PlayerMsg
= "You feel like having done something unwise.";
5944 if (IsPlayerKind()) UpdatePictures();
5948 PlayerMsg
= "You feel very confident of your social skills.";
5950 if (GetAttribute(CHARISMA
) <= 15) NPCMsg
= "%s looks less ugly.";
5951 else NPCMsg
= "%s looks more attractive.";
5954 PlayerMsg
= "You feel somehow disliked.";
5956 if (GetAttribute(CHARISMA
) < 15) NPCMsg
= "%s looks more ugly.";
5957 else NPCMsg
= "%s looks less attractive.";
5960 if (IsPlayerKind()) UpdatePictures();
5964 PlayerMsg
= "You feel magical forces coursing through your body!";
5965 NPCMsg
= "You notice an odd glow around %s.";
5967 PlayerMsg
= "You feel your magical abilities withering slowly.";
5968 NPCMsg
= "You notice strange vibrations in the air around %s. But they disappear rapidly.";
5973 if (IsPlayer()) ADD_MESSAGE(PlayerMsg
);
5974 else if (NPCMsg
&& CanBeSeenByPlayer()) ADD_MESSAGE(NPCMsg
, CHAR_NAME(DEFINITE
));
5976 CalculateBattleInfo();
5980 int character::RawEditExperience (double &Exp
, double NaturalExp
, double Value
, double Speed
) const {
5981 double OldExp
= Exp
;
5986 if(!OldExp
|| !Value
|| (Value
> 0 && OldExp
>= NaturalExp
* (100 + Value
) / 100) ||
5987 (Value
< 0 && OldExp
<= NaturalExp
* (100 + Value
) / 100)) return 0;
5988 if (!IsPlayer()) Speed
*= 1.5;
5989 Exp
+= (NaturalExp
* (100 + Value
) - 100 * OldExp
) * Speed
* EXP_DIVISOR
;
5990 LimitRef(Exp
, MIN_EXP
, MAX_EXP
);
5991 int NewA
= int(Exp
* EXP_DIVISOR
);
5992 int OldA
= int(OldExp
* EXP_DIVISOR
);
5993 int Delta
= NewA
- OldA
;
5994 if (Delta
> 0) Exp
= Max(Exp
, (NewA
+ 0.05) * EXP_MULTIPLIER
);
5995 else if (Delta
< 0) Exp
= Min(Exp
, (NewA
+ 0.95) * EXP_MULTIPLIER
);
5996 LimitRef(Exp
, MIN_EXP
, MAX_EXP
);
6001 int character::GetAttribute (int Identifier
, truth AllowBonus
) const {
6002 int A
= int(BaseExperience
[Identifier
] * EXP_DIVISOR
);
6003 if (AllowBonus
&& Identifier
== INTELLIGENCE
&& BrainsHurt()) return Max((A
+ AttributeBonus
[INTELLIGENCE
]) / 3, 1);
6004 return A
&& AllowBonus
? Max(A
+ AttributeBonus
[Identifier
], 1) : A
;
6008 void characterdatabase::PostProcess () {
6009 double AM
= (100 + AttributeBonus
) * EXP_MULTIPLIER
/ 100;
6010 for (int c
= 0; c
< ATTRIBUTES
; ++c
) NaturalExperience
[c
] = this->*ExpPtr
[c
] * AM
;
6014 void character::EditDealExperience (sLong Price
) {
6015 EditExperience(CHARISMA
, sqrt(Price
) / 5, 1 << 9);
6019 void character::PrintBeginLeprosyMessage () const {
6020 if (IsPlayer()) ADD_MESSAGE("You feel you're falling in pieces.");
6024 void character::PrintEndLeprosyMessage () const {
6025 if (IsPlayer()) ADD_MESSAGE("You feel your limbs are stuck in place tightly."); // CHANGE OR DIE
6029 void character::TryToInfectWithLeprosy (ccharacter
*Infector
) {
6030 if (!IsImmuneToLeprosy() &&
6031 ((GetRelation(Infector
) == HOSTILE
&& !RAND_N(50 * GetAttribute(ENDURANCE
))) ||
6032 !RAND_N(500 * GetAttribute(ENDURANCE
)))) GainIntrinsic(LEPROSY
);
6036 void character::SignalGeneration () {
6037 const_cast<database
*>(DataBase
)->Flags
|= HAS_BEEN_GENERATED
;
6041 void character::CheckIfSeen () {
6042 if (IsPlayer() || CanBeSeenByPlayer()) SignalSeen();
6046 void character::SignalSeen () {
6047 if (!(WarnFlags
& WARNED
) && GetRelation(PLAYER
) == HOSTILE
) {
6048 double Danger
= GetRelativeDanger(PLAYER
);
6050 game::SetDangerFound(Max(game::GetDangerFound(), Danger
));
6051 if (Danger
> 500.0 && !(WarnFlags
& HAS_CAUSED_PANIC
)) {
6052 WarnFlags
|= HAS_CAUSED_PANIC
;
6053 game::SetCausePanicFlag(true);
6055 WarnFlags
|= WARNED
;
6058 const_cast<database
*>(DataBase
)->Flags
|= HAS_BEEN_SEEN
;
6062 int character::GetPolymorphIntelligenceRequirement () const {
6063 if (DataBase
->PolymorphIntelligenceRequirement
== DEPENDS_ON_ATTRIBUTES
) return Max(GetAttributeAverage() - 5, 0);
6064 return DataBase
->PolymorphIntelligenceRequirement
;
6068 void character::RemoveAllItems () {
6069 GetStack()->Clean();
6070 for (int c
= 0; c
< GetEquipments(); ++c
) {
6071 item
*Equipment
= GetEquipment(c
);
6073 Equipment
->RemoveFromSlot();
6074 Equipment
->SendToHell();
6080 int character::CalculateWeaponSkillHits (ccharacter
*Enemy
) const {
6081 if (Enemy
->IsPlayer()) {
6082 configid
ConfigID(GetType(), GetConfig());
6083 const dangerid
& DangerID
= game::GetDangerMap().find(ConfigID
)->second
;
6084 return Min(int(DangerID
.EquippedDanger
* 2000), 1000);
6086 return Min(int(GetRelativeDanger(Enemy
, true) * 2000), 1000);
6090 truth
character::CanUseEquipment (int I
) const {
6091 return CanUseEquipment() && I
< GetEquipments() && GetBodyPartOfEquipment(I
) && EquipmentIsAllowed(I
);
6095 /* Target mustn't have any equipment */
6096 void character::DonateEquipmentTo (character
*Character
) {
6098 uLong
*EquipmentMemory
= game::GetEquipmentMemory();
6099 for (int c
= 0; c
< MAX_EQUIPMENT_SLOTS
; ++c
) {
6100 item
*Item
= GetEquipment(c
);
6102 if (Character
->CanUseEquipment(c
)) {
6103 Item
->RemoveFromSlot();
6104 Character
->SetEquipment(c
, Item
);
6106 EquipmentMemory
[c
] = Item
->GetID();
6107 Item
->MoveTo(Character
->GetStack());
6109 } else if (CanUseEquipment(c
)) {
6110 EquipmentMemory
[c
] = 0;
6111 } else if (EquipmentMemory
[c
] && Character
->CanUseEquipment(c
)) {
6112 for (stackiterator i
= Character
->GetStack()->GetBottom(); i
.HasItem(); ++i
) {
6113 if (i
->GetID() == EquipmentMemory
[c
]) {
6115 Item
->RemoveFromSlot();
6116 Character
->SetEquipment(c
, Item
);
6120 EquipmentMemory
[c
] = 0;
6124 for (int c
= 0; c
< GetEquipments(); ++c
) {
6125 item
*Item
= GetEquipment(c
);
6127 if (Character
->CanUseEquipment(c
)) {
6128 Item
->RemoveFromSlot();
6129 Character
->SetEquipment(c
, Item
);
6131 Item
->MoveTo(Character
->GetStackUnder());
6139 void character::ReceivePeaSoup (sLong
) {
6140 lsquare
*Square
= GetLSquareUnder();
6141 if (Square
->IsFlyable()) Square
->AddSmoke(gas::Spawn(FART
, 250));
6145 void character::AddPeaSoupConsumeEndMessage () const {
6147 if (CanHear()) ADD_MESSAGE("Mmmh! The soup is very tasty. You hear a small puff.");
6148 else ADD_MESSAGE("Mmmh! The soup is very tasty.");
6149 } else if (CanBeSeenByPlayer() && PLAYER
->CanHear()) {
6151 ADD_MESSAGE("You hear a small puff.");
6156 void character::CalculateMaxStamina () {
6157 MaxStamina
= TorsoIsAlive() ? GetAttribute(ENDURANCE
) * 10000 : 0;
6161 void character::EditStamina (int Amount
, truth CanCauseUnconsciousness
) {
6162 if (!TorsoIsAlive()) return;
6163 int UnconsciousnessStamina
= MaxStamina
>> 3;
6164 if (!CanCauseUnconsciousness
&& Amount
< 0) {
6165 if (Stamina
> UnconsciousnessStamina
) {
6167 if (Stamina
< UnconsciousnessStamina
) Stamina
= UnconsciousnessStamina
;
6171 int OldStamina
= Stamina
;
6173 if (Stamina
> MaxStamina
) {
6174 Stamina
= MaxStamina
;
6175 } else if (Stamina
< 0) {
6177 LoseConsciousness(250 + RAND_N(250));
6178 } else if (IsPlayer()) {
6179 if (OldStamina
>= MaxStamina
>> 2 && Stamina
< MaxStamina
>> 2) {
6180 ADD_MESSAGE("You are getting a little tired.");
6181 } else if(OldStamina
>= UnconsciousnessStamina
&& Stamina
< UnconsciousnessStamina
) {
6182 ADD_MESSAGE("You are seriously out of breath!");
6183 game::SetPlayerIsRunning(false);
6186 if (IsPlayer() && StateIsActivated(PANIC
) && GetTirednessState() != FAINTING
) game::SetPlayerIsRunning(true);
6190 void character::RegenerateStamina () {
6191 if (GetTirednessState() != UNTIRED
) {
6192 EditExperience(ENDURANCE
, 50, 1);
6193 if (Sweats() && TorsoIsAlive() && !RAND_N(30) && !game::IsInWilderness()) {
6194 sLong Volume
= sLong(0.05 * sqrt(GetBodyVolume()));
6195 if (GetTirednessState() == FAINTING
) Volume
<<= 1;
6196 for (int c
= 0; c
< SquaresUnder
; ++c
) GetLSquareUnder(c
)->SpillFluid(0, CreateSweat(Volume
), false, false);
6201 if (Action
->IsRest()) {
6202 if (SquaresUnder
== 1) {
6203 Bonus
= GetSquareUnder()->GetRestModifier() << 1;
6205 int Lowest
= GetSquareUnder(0)->GetRestModifier();
6206 for (int c
= 1; c
< GetSquaresUnder(); ++c
) {
6207 int Mod
= GetSquareUnder(c
)->GetRestModifier();
6208 if (Mod
< Lowest
) Lowest
= Mod
;
6210 Bonus
= Lowest
<< 1;
6212 } else if (Action
->IsUnconsciousness()) Bonus
= 2;
6215 switch (GetBurdenState()) {
6216 case OVER_LOADED
: Plus1
= 25; break;
6217 case STRESSED
: Plus1
= 50; break;
6218 case BURDENED
: Plus1
= 75; break;
6222 switch (GetHungerState()) {
6223 case STARVING
: Plus2
= 25; break;
6224 case VERY_HUNGRY
: Plus2
= 50; break;
6225 case HUNGRY
: Plus2
= 75; break;
6228 Stamina
+= Plus1
* Plus2
* Bonus
/ 1000;
6229 if (Stamina
> MaxStamina
) Stamina
= MaxStamina
;
6230 if (IsPlayer() && StateIsActivated(PANIC
) && GetTirednessState() != FAINTING
) game::SetPlayerIsRunning(true);
6234 void character::BeginPanic () {
6235 if (IsPlayer() && GetTirednessState() != FAINTING
) game::SetPlayerIsRunning(true);
6236 DeActivateVoluntaryAction();
6240 void character::EndPanic () {
6241 if (IsPlayer()) game::SetPlayerIsRunning(false);
6245 int character::GetTirednessState () const {
6246 if (Stamina
>= MaxStamina
>> 2) return UNTIRED
;
6247 if (Stamina
>= MaxStamina
>> 3) return EXHAUSTED
;
6252 void character::ReceiveBlackUnicorn (sLong Amount
) {
6253 if (!(RAND() % 160)) game::DoEvilDeed(Amount
/ 50);
6254 BeginTemporaryState(TELEPORT
, Amount
/ 100);
6255 for (int c
= 0; c
< STATES
; ++c
) {
6256 if (StateData
[c
].Flags
& DUR_TEMPORARY
) {
6257 BeginTemporaryState(1 << c
, Amount
/ 100);
6258 if (!IsEnabled()) return;
6259 } else if (StateData
[c
].Flags
& DUR_PERMANENT
) {
6260 GainIntrinsic(1 << c
);
6261 if (!IsEnabled()) return;
6267 void character::ReceiveGrayUnicorn (sLong Amount
) {
6268 if (!(RAND() % 80)) game::DoEvilDeed(Amount
/ 50);
6269 BeginTemporaryState(TELEPORT
, Amount
/ 100);
6270 for (int c
= 0; c
< STATES
; ++c
) {
6271 if (1 << c
!= TELEPORT
) {
6272 DecreaseStateCounter(1 << c
, -Amount
/ 100);
6273 if (!IsEnabled()) return;
6279 void character::ReceiveWhiteUnicorn (sLong Amount
) {
6280 if (!(RAND() % 40)) game::DoEvilDeed(Amount
/ 50);
6281 BeginTemporaryState(TELEPORT
, Amount
/ 100);
6282 DecreaseStateCounter(LYCANTHROPY
, -Amount
/ 100);
6283 DecreaseStateCounter(POISONED
, -Amount
/ 100);
6284 DecreaseStateCounter(PARASITIZED
, -Amount
/ 100);
6285 DecreaseStateCounter(LEPROSY
, -Amount
/ 100);
6289 /* Counter should be negative. Removes intrinsics. */
6290 void character::DecreaseStateCounter (sLong State
, int Counter
) {
6292 for (Index
= 0; Index
< STATES
; ++Index
) if (1 << Index
== State
) break;
6293 if (Index
== STATES
) ABORT("DecreaseTemporaryStateCounter works only when State == 2Â ^ n!");
6294 if (TemporaryState
& State
) {
6295 if (TemporaryStateCounter
[Index
] == PERMANENT
|| (TemporaryStateCounter
[Index
] += Counter
) <= 0) {
6296 TemporaryState
&= ~State
;
6297 if (!(EquipmentState
& State
)) {
6298 if (StateData
[Index
].EndHandler
) {
6299 (this->*StateData
[Index
].EndHandler
)();
6300 if (!IsEnabled()) return;
6302 (this->*StateData
[Index
].PrintEndMessage
)();
6309 truth
character::IsImmuneToLeprosy () const {
6310 return DataBase
->IsImmuneToLeprosy
|| UseMaterialAttributes();
6314 void character::LeprosyHandler () {
6315 EditExperience(ARM_STRENGTH
, -25, 1 << 1);
6316 EditExperience(LEG_STRENGTH
, -25, 1 << 1);
6317 EditExperience(DEXTERITY
, -25, 1 << 1);
6318 EditExperience(AGILITY
, -25, 1 << 1);
6319 EditExperience(ENDURANCE
, -25, 1 << 1);
6320 EditExperience(CHARISMA
, -25, 1 << 1);
6321 CheckDeath(CONST_S("killed by leprosy"));
6325 bodypart
*character::SearchForOriginalBodyPart (int I
) const {
6326 for (stackiterator i1
= GetStackUnder()->GetBottom(); i1
.HasItem(); ++i1
) {
6327 for (std::list
<uLong
>::iterator i2
= OriginalBodyPartID
[I
].begin(); i2
!= OriginalBodyPartID
[I
].end(); ++i2
)
6328 if (i1
->GetID() == *i2
) return static_cast<bodypart
*>(*i1
);
6334 void character::SetLifeExpectancy (int Base
, int RandPlus
) {
6336 for (c
= 0; c
< BodyParts
; ++c
) {
6337 bodypart
*BodyPart
= GetBodyPart(c
);
6338 if (BodyPart
) BodyPart
->SetLifeExpectancy(Base
, RandPlus
);
6340 for (c
= 0; c
< GetEquipments(); ++c
) {
6341 item
*Equipment
= GetEquipment(c
);
6342 if (Equipment
) Equipment
->SetLifeExpectancy(Base
, RandPlus
);
6347 /* Receiver should be a fresh duplicate of this */
6348 void character::DuplicateEquipment (character
*Receiver
, uLong Flags
) {
6349 for (int c
= 0; c
< GetEquipments(); ++c
) {
6350 item
*Equipment
= GetEquipment(c
);
6352 item
*Duplicate
= Equipment
->Duplicate(Flags
);
6353 Receiver
->SetEquipment(c
, Duplicate
);
6359 void character::Disappear (corpse
*Corpse
, cchar
*Verb
, truth (item::*ClosePredicate
)() const) {
6360 truth TorsoDisappeared
= false;
6361 truth CanBeSeen
= Corpse
? Corpse
->CanBeSeenByPlayer() : IsPlayer() || CanBeSeenByPlayer();
6363 if ((GetTorso()->*ClosePredicate
)()) {
6365 if (Corpse
) ADD_MESSAGE("%s %ss.", Corpse
->CHAR_NAME(DEFINITE
), Verb
);
6366 else if (IsPlayer()) ADD_MESSAGE("You %s.", Verb
);
6367 else ADD_MESSAGE("%s %ss.", CHAR_NAME(DEFINITE
), Verb
);
6369 TorsoDisappeared
= true;
6370 for (c
= 0; c
< GetEquipments(); ++c
) {
6371 item
*Equipment
= GetEquipment(c
);
6372 if (Equipment
&& (Equipment
->*ClosePredicate
)()) {
6373 Equipment
->RemoveFromSlot();
6374 Equipment
->SendToHell();
6377 itemvector ItemVector
;
6378 GetStack()->FillItemVector(ItemVector
);
6379 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) {
6380 if (ItemVector
[c
] && (ItemVector
[c
]->*ClosePredicate
)()) {
6381 ItemVector
[c
]->RemoveFromSlot();
6382 ItemVector
[c
]->SendToHell();
6386 for (c
= 1; c
< GetBodyParts(); ++c
) {
6387 bodypart
*BodyPart
= GetBodyPart(c
);
6389 if ((BodyPart
->*ClosePredicate
)()) {
6390 if (!TorsoDisappeared
&& CanBeSeen
) {
6391 if(IsPlayer()) ADD_MESSAGE("Your %s %ss.", GetBodyPartName(c
).CStr(), Verb
);
6392 else ADD_MESSAGE("The %s of %s %ss.", GetBodyPartName(c
).CStr(), CHAR_NAME(DEFINITE
), Verb
);
6394 BodyPart
->DropEquipment();
6395 item
*BodyPart
= SevereBodyPart(c
);
6396 if (BodyPart
) BodyPart
->SendToHell();
6397 } else if (TorsoDisappeared
) {
6398 BodyPart
->DropEquipment();
6399 item
*BodyPart
= SevereBodyPart(c
);
6401 if (Corpse
) Corpse
->GetSlot()->AddFriendItem(BodyPart
);
6402 else if (!game::IsInWilderness()) GetStackUnder()->AddItem(BodyPart
);
6403 else BodyPart
->SendToHell();
6408 if (TorsoDisappeared
) {
6410 Corpse
->RemoveFromSlot();
6411 Corpse
->SendToHell();
6413 CheckDeath(festring(Verb
) + "ed", 0, FORCE_DEATH
|DISALLOW_CORPSE
|DISALLOW_MSG
);
6416 CheckDeath(festring(Verb
) + "ed", 0, DISALLOW_MSG
);
6421 void character::SignalDisappearance () {
6422 if (GetMotherEntity()) GetMotherEntity()->SignalDisappearance();
6423 else Disappear(0, "disappear", &item::IsVeryCloseToDisappearance
);
6427 truth
character::HornOfFearWorks () const {
6428 return CanHear() && GetPanicLevel() > RAND() % 33;
6432 void character::BeginLeprosy () {
6433 doforbodypartswithparam
<truth
>()(this, &bodypart::SetIsInfectedByLeprosy
, true);
6437 void character::EndLeprosy () {
6438 doforbodypartswithparam
<truth
>()(this, &bodypart::SetIsInfectedByLeprosy
, false);
6442 truth
character::IsSameAs (ccharacter
*What
) const {
6443 return What
->GetType() == GetType() && What
->GetConfig() == GetConfig();
6447 uLong
character::GetCommandFlags () const {
6448 return !StateIsActivated(PANIC
) ? CommandFlags
: CommandFlags
|FLEE_FROM_ENEMIES
;
6452 uLong
character::GetConstantCommandFlags () const {
6453 return !StateIsActivated(PANIC
) ? DataBase
->ConstantCommandFlags
: DataBase
->ConstantCommandFlags
|FLEE_FROM_ENEMIES
;
6457 uLong
character::GetPossibleCommandFlags () const {
6458 int Int
= GetAttribute(INTELLIGENCE
);
6459 uLong Flags
= ALL_COMMAND_FLAGS
;
6460 if (!CanMove() || Int
< 4) Flags
&= ~FOLLOW_LEADER
;
6461 if (!CanMove() || Int
< 6) Flags
&= ~FLEE_FROM_ENEMIES
;
6462 if (!CanUseEquipment() || Int
< 8) Flags
&= ~DONT_CHANGE_EQUIPMENT
;
6463 if (!UsesNutrition() || Int
< 8) Flags
&= ~DONT_CONSUME_ANYTHING_VALUABLE
;
6468 truth
character::IsRetreating () const {
6469 return StateIsActivated(PANIC
) || (CommandFlags
& FLEE_FROM_ENEMIES
&& IsPet());
6473 truth
character::ChatMenu () {
6474 if (GetAction() && !GetAction()->CanBeTalkedTo()) {
6475 ADD_MESSAGE("%s is silent.", CHAR_DESCRIPTION(DEFINITE
));
6476 PLAYER
->EditAP(-200);
6479 uLong ManagementFlags
= GetManagementFlags();
6480 if (ManagementFlags
== CHAT_IDLY
|| !IsPet()) return ChatIdly();
6481 static cchar
*const ChatMenuEntry
[CHAT_MENU_ENTRIES
] = {
6488 static const petmanagementfunction PMF
[CHAT_MENU_ENTRIES
] = {
6489 &character::ChangePetEquipment
,
6490 &character::TakePetItems
,
6491 &character::GivePetItems
,
6492 &character::IssuePetCommands
,
6493 &character::ChatIdly
6495 felist
List(CONST_S("Choose action:"));
6496 game::SetStandardListAttributes(List
);
6497 List
.AddFlags(SELECTABLE
);
6499 for (c
= 0; c
< CHAT_MENU_ENTRIES
; ++c
) if (1 << c
& ManagementFlags
) List
.AddEntry(ChatMenuEntry
[c
], LIGHT_GRAY
);
6500 int Chosen
= List
.Draw();
6501 if (Chosen
& FELIST_ERROR_BIT
) return false;
6502 for (c
= 0, i
= 0; c
< CHAT_MENU_ENTRIES
; ++c
) {
6503 if (1 << c
& ManagementFlags
&& i
++ == Chosen
) return (this->*PMF
[c
])();
6505 return false; // dummy
6509 truth
character::ChangePetEquipment () {
6510 if (EquipmentScreen(PLAYER
->GetStack(), GetStack())) {
6518 truth
character::TakePetItems () {
6519 truth Success
= false;
6520 stack::SetSelected(0);
6523 game::DrawEverythingNoBlit();
6524 GetStack()->DrawContents(
6528 CONST_S("What do you want to take from ") + CHAR_DESCRIPTION(DEFINITE
) + '?',
6531 GetDescription(DEFINITE
) + " is " + GetVerbalBurdenState(),
6532 GetVerbalBurdenStateColor(),
6534 if (ToTake
.empty()) break;
6535 for (uInt c
= 0; c
< ToTake
.size(); ++c
) ToTake
[c
]->MoveTo(PLAYER
->GetStack());
6536 ADD_MESSAGE("You take %s.", ToTake
[0]->GetName(DEFINITE
, ToTake
.size()).CStr());
6541 PLAYER
->DexterityAction(2);
6547 truth
character::GivePetItems () {
6548 truth Success
= false;
6549 stack::SetSelected(0);
6552 game::DrawEverythingNoBlit();
6553 PLAYER
->GetStack()->DrawContents(
6557 CONST_S("What do you want to give to ") + CHAR_DESCRIPTION(DEFINITE
) + '?',
6560 GetDescription(DEFINITE
) + " is " + GetVerbalBurdenState(),
6561 GetVerbalBurdenStateColor(),
6563 if (ToGive
.empty()) break;
6564 for (uInt c
= 0; c
< ToGive
.size(); ++c
) ToGive
[c
]->MoveTo(GetStack());
6565 ADD_MESSAGE("You give %s to %s.", ToGive
[0]->GetName(DEFINITE
, ToGive
.size()).CStr(), CHAR_DESCRIPTION(DEFINITE
));
6570 PLAYER
->DexterityAction(2);
6576 truth
character::IssuePetCommands () {
6577 if (!IsConscious()) {
6578 ADD_MESSAGE("%s is unconscious.", CHAR_DESCRIPTION(DEFINITE
));
6581 uLong PossibleC
= GetPossibleCommandFlags();
6583 ADD_MESSAGE("%s cannot be commanded.", CHAR_DESCRIPTION(DEFINITE
));
6586 uLong OldC
= GetCommandFlags();
6587 uLong NewC
= OldC
, VaryFlags
= 0;
6588 game::CommandScreen(CONST_S("Issue commands to ")+GetDescription(DEFINITE
), PossibleC
, GetConstantCommandFlags(), VaryFlags
, NewC
);
6589 if (NewC
== OldC
) return false;
6590 SetCommandFlags(NewC
);
6591 PLAYER
->EditAP(-500);
6592 PLAYER
->EditExperience(CHARISMA
, 25, 1 << 7);
6597 truth
character::ChatIdly () {
6598 if (!TryToTalkAboutScience()) {
6600 PLAYER
->EditExperience(CHARISMA
, 75, 1 << 7);
6602 PLAYER
->EditAP(-1000);
6607 truth
character::EquipmentScreen (stack
*MainStack
, stack
*SecStack
) {
6608 if (!CanUseEquipment()) {
6609 ADD_MESSAGE("%s cannot use equipment.", CHAR_DESCRIPTION(DEFINITE
));
6613 truth EquipmentChanged
= false;
6614 felist
List(CONST_S("Equipment menu [ESC exits]"));
6618 List
.EmptyDescription();
6620 List
.AddDescription(CONST_S(""));
6621 List
.AddDescription(festring(GetDescription(DEFINITE
) + " is " + GetVerbalBurdenState()).CapitalizeCopy(), GetVerbalBurdenStateColor());
6623 for (int c
= 0; c
< GetEquipments(); ++c
) {
6624 Entry
= GetEquipmentName(c
);
6627 item
*Equipment
= GetEquipment(c
);
6629 Equipment
->AddInventoryEntry(this, Entry
, 1, true);
6630 AddSpecialEquipmentInfo(Entry
, c
);
6631 int ImageKey
= game::AddToItemDrawVector(itemvector(1, Equipment
));
6632 List
.AddEntry(Entry
, LIGHT_GRAY
, 20, ImageKey
, true);
6634 Entry
<< (GetBodyPartOfEquipment(c
) ? "-" : "can't use");
6635 List
.AddEntry(Entry
, LIGHT_GRAY
, 20, game::AddToItemDrawVector(itemvector()));
6638 game::DrawEverythingNoBlit();
6639 game::SetStandardListAttributes(List
);
6640 List
.SetFlags(SELECTABLE
|DRAW_BACKGROUND_AFTERWARDS
);
6641 List
.SetEntryDrawer(game::ItemEntryDrawer
);
6642 Chosen
= List
.Draw();
6643 game::ClearItemDrawVector();
6644 if (Chosen
>= GetEquipments()) break;
6645 EquipmentChanged
= TryToChangeEquipment(MainStack
, SecStack
, Chosen
);
6647 if (EquipmentChanged
) DexterityAction(5);
6648 return EquipmentChanged
;
6652 uLong
character::GetManagementFlags () const {
6653 uLong Flags
= ALL_MANAGEMENT_FLAGS
;
6654 if (!CanUseEquipment() || !AllowPlayerToChangeEquipment()) Flags
&= ~CHANGE_EQUIPMENT
;
6655 if (!GetStack()->GetItems()) Flags
&= ~TAKE_ITEMS
;
6656 if (!WillCarryItems()) Flags
&= ~GIVE_ITEMS
;
6657 if (!GetPossibleCommandFlags()) Flags
&= ~ISSUE_COMMANDS
;
6662 cchar
*VerbalBurdenState
[] = { "overloaded", "stressed", "burdened", "unburdened" };
6663 col16 VerbalBurdenStateColor
[] = { RED
, BLUE
, BLUE
, WHITE
};
6665 cchar
*character::GetVerbalBurdenState () const { return VerbalBurdenState
[BurdenState
]; }
6666 col16
character::GetVerbalBurdenStateColor () const { return VerbalBurdenStateColor
[BurdenState
]; }
6667 int character::GetAttributeAverage () const { return GetSumOfAttributes()/7; }
6669 cfestring
&character::GetStandVerb() const {
6670 if (ForceCustomStandVerb()) return DataBase
->StandVerb
;
6671 static festring Hovering
= "hovering";
6672 static festring Swimming
= "swimming";
6673 if (StateIsActivated(LEVITATION
)) return Hovering
;
6674 if (IsSwimming()) return Swimming
;
6675 return DataBase
->StandVerb
;
6679 truth
character::CheckApply () const {
6681 ADD_MESSAGE("This monster type cannot apply.");
6688 void character::EndLevitation () {
6689 if (!IsFlying() && GetSquareUnder()) {
6690 if (!game::IsInWilderness()) SignalStepFrom(0);
6691 if (game::IsInWilderness() || !GetLSquareUnder()->IsFreezed()) TestWalkability();
6696 truth
character::CanMove () const {
6697 return !IsRooted() || StateIsActivated(LEVITATION
);
6701 void character::CalculateEnchantments () {
6702 doforequipments()(this, &item::CalculateEnchantment
);
6703 GetStack()->CalculateEnchantments();
6707 truth
character::GetNewFormForPolymorphWithControl (character
*&NewForm
) {
6708 festring Topic
, Temp
;
6711 festring Temp
= game::DefaultQuestion(CONST_S("What do you want to become? [press '?' for a list]"), game::GetDefaultPolymorphTo(), &game::PolymorphControlKeyHandler
);
6712 NewForm
= protosystem::CreateMonster(Temp
);
6714 if (NewForm
->IsSameAs(this)) {
6716 ADD_MESSAGE("You choose not to polymorph.");
6720 if (PolymorphBackup
&& NewForm
->IsSameAs(PolymorphBackup
)) {
6722 NewForm
= ForceEndPolymorph();
6725 if (NewForm
->GetPolymorphIntelligenceRequirement() > GetAttribute(INTELLIGENCE
) && !game::WizardModeIsActive()) {
6726 ADD_MESSAGE("You feel your mind isn't yet powerful enough to call forth the form of %s.", NewForm
->CHAR_NAME(INDEFINITE
));
6730 NewForm
->RemoveAllItems();
6738 liquid
*character::CreateSweat(sLong Volume
) const {
6739 return liquid::Spawn(GetSweatMaterial(), Volume
);
6743 truth
character::TeleportRandomItem (truth TryToHinderVisibility
) {
6744 if (IsImmuneToItemTeleport()) return false;
6745 itemvector ItemVector
;
6746 std::vector
<sLong
> PossibilityVector
;
6747 int TotalPossibility
= 0;
6748 for (stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) {
6749 ItemVector
.push_back(*i
);
6750 int Possibility
= i
->GetTeleportPriority();
6751 if (TryToHinderVisibility
) Possibility
+= i
->GetHinderVisibilityBonus(this);
6752 PossibilityVector
.push_back(Possibility
);
6753 TotalPossibility
+= Possibility
;
6755 for (int c
= 0; c
< GetEquipments(); ++c
) {
6756 item
*Equipment
= GetEquipment(c
);
6758 ItemVector
.push_back(Equipment
);
6759 int Possibility
= Equipment
->GetTeleportPriority();
6760 if (TryToHinderVisibility
) Possibility
+= Equipment
->GetHinderVisibilityBonus(this);
6761 PossibilityVector
.push_back(Possibility
<<= 1);
6762 TotalPossibility
+= Possibility
;
6765 if (!TotalPossibility
) return false;
6766 int Chosen
= femath::WeightedRand(PossibilityVector
, TotalPossibility
);
6767 item
*Item
= ItemVector
[Chosen
];
6768 truth Equipped
= PLAYER
->Equips(Item
);
6769 truth Seen
= Item
->CanBeSeenByPlayer();
6770 Item
->RemoveFromSlot();
6771 if (Seen
) ADD_MESSAGE("%s disappears.", Item
->CHAR_NAME(DEFINITE
));
6772 if (Equipped
) game::AskForEscPress(CONST_S("Equipment lost!"));
6774 int Range
= Item
->GetEmitation() && TryToHinderVisibility
? 25 : 5;
6775 rect
Border(Pos
+ v2(-Range
, -Range
), Pos
+ v2(Range
, Range
));
6776 Pos
= GetLevel()->GetRandomSquare(this, 0, &Border
);
6777 if (Pos
== ERROR_V2
) Pos
= GetLevel()->GetRandomSquare();
6778 GetNearLSquare(Pos
)->GetStack()->AddItem(Item
);
6779 if (Item
->CanBeSeenByPlayer()) ADD_MESSAGE("%s appears.", Item
->CHAR_NAME(INDEFINITE
));
6784 truth
character::HasClearRouteTo (v2 Pos
) const {
6785 pathcontroller::Map
= GetLevel()->GetMap();
6786 pathcontroller::Character
= this;
6787 v2 ThisPos
= GetPos();
6788 return mapmath
<pathcontroller
>::DoLine(ThisPos
.X
, ThisPos
.Y
, Pos
.X
, Pos
.Y
, SKIP_FIRST
);
6792 truth
character::IsTransparent () const {
6793 return !IsEnormous() || GetTorso()->GetMainMaterial()->IsTransparent() || StateIsActivated(INVISIBLE
);
6797 void character::SignalPossibleTransparencyChange () {
6798 if (!game::IsInWilderness()) {
6799 for (int c
= 0; c
< SquaresUnder
; ++c
) {
6800 lsquare
*Square
= GetLSquareUnder(c
);
6801 if (Square
) Square
->SignalPossibleTransparencyChange();
6807 int character::GetCursorData () const {
6809 int Color
= game::PlayerIsRunning() ? BLUE_CURSOR
: DARK_CURSOR
;
6810 for (int c
= 0; c
< BodyParts
; ++c
) {
6811 bodypart
*BodyPart
= GetBodyPart(c
);
6812 if (BodyPart
&& BodyPart
->IsUsable()) {
6813 int ConditionColorIndex
= BodyPart
->GetConditionColorIndex();
6814 if ((BodyPartIsVital(c
) && !ConditionColorIndex
) || (ConditionColorIndex
<= 1 && ++Bad
== 2)) return Color
|CURSOR_FLASH
;
6815 } else if (++Bad
== 2) return Color
|CURSOR_FLASH
;
6817 Color
= game::PlayerIsRunning() ? YELLOW_CURSOR
: RED_CURSOR
;
6818 return Bad
? Color
|CURSOR_FLASH
: Color
;
6822 void character::TryToName () {
6823 if (!IsPet()) ADD_MESSAGE("%s refuses to let YOU decide what %s's called.", CHAR_NAME(DEFINITE
), CHAR_PERSONAL_PRONOUN
);
6824 else if (IsPlayer()) ADD_MESSAGE("You can't rename yourself.");
6825 else if (!IsNameable()) ADD_MESSAGE("%s refuses to be called anything else but %s.", CHAR_NAME(DEFINITE
), CHAR_NAME(DEFINITE
));
6827 festring Topic
= CONST_S("What name will you give to ")+GetName(DEFINITE
)+'?';
6828 festring Name
= game::StringQuestion(Topic
, WHITE
, 0, 80, true);
6829 if (Name
.GetSize()) SetAssignedName(Name
);
6834 double character::GetSituationDanger (ccharacter
*Enemy
, v2 ThisPos
, v2 EnemyPos
, truth SeesEnemy
) const {
6836 if (IgnoreDanger() && !IsPlayer()) {
6837 if (Enemy
->IgnoreDanger() && !Enemy
->IsPlayer()) {
6838 Danger
= double(GetHP())*GetHPRequirementForGeneration()/(Enemy
->GetHP()*Enemy
->GetHPRequirementForGeneration());
6841 Danger
= 0.25*GetHPRequirementForGeneration()/Enemy
->GetHP();
6843 } else if (Enemy
->IgnoreDanger() && !Enemy
->IsPlayer()) {
6844 Danger
= 4.0*GetHP()/Enemy
->GetHPRequirementForGeneration();
6846 Danger
= GetRelativeDanger(Enemy
);
6848 Danger
*= 3.0/((EnemyPos
-ThisPos
).GetManhattanLength()+2);
6849 if (!SeesEnemy
) Danger
*= 0.2;
6850 if (StateIsActivated(PANIC
)) Danger
*= 0.2;
6851 Danger
*= double(GetHP())*Enemy
->GetMaxHP()/(Enemy
->GetHP()*GetMaxHP());
6856 void character::ModifySituationDanger (double &Danger
) const {
6857 switch (GetTirednessState()) {
6858 case FAINTING
: Danger
*= 1.5;
6859 case EXHAUSTED
: Danger
*= 1.25;
6861 for (int c
= 0; c
< STATES
; ++c
) {
6862 if (StateIsActivated(1 << c
) && StateData
[c
].SituationDangerModifier
!= 0) (this->*StateData
[c
].SituationDangerModifier
)(Danger
);
6867 void character::LycanthropySituationDangerModifier (double &Danger
) const {
6868 character
*Wolf
= werewolfwolf::Spawn();
6869 double DangerToWolf
= GetRelativeDanger(Wolf
);
6870 Danger
*= pow(DangerToWolf
, 0.1);
6875 void character::PoisonedSituationDangerModifier (double &Danger
) const {
6876 int C
= GetTemporaryStateCounter(POISONED
);
6877 Danger
*= (1+(C
*C
)/(GetHP()*10000.0*(GetGlobalResistance(POISON
)+1)));
6881 void character::PolymorphingSituationDangerModifier (double &Danger
) const {
6882 if (!StateIsActivated(POLYMORPH_CONTROL
)) Danger
*= 1.5;
6886 void character::PanicSituationDangerModifier (double &Danger
) const {
6891 void character::ConfusedSituationDangerModifier (double &Danger
) const {
6896 void character::ParasitizedSituationDangerModifier (double &Danger
) const {
6901 void character::LeprosySituationDangerModifier (double &Danger
) const {
6906 void character::AddRandomScienceName (festring
&String
) const {
6907 festring Science
= GetScienceTalkName().GetRandomElement().CStr();
6908 if (Science
[0] == '!') {
6909 String
<< Science
.CStr()+1;
6912 festring Attribute
= GetScienceTalkAdjectiveAttribute().GetRandomElement();
6914 truth NoAttrib
= Attribute
.IsEmpty(), NoSecondAdjective
= false;
6915 if (!Attribute
.IsEmpty() && Attribute
[0] == '!') {
6916 NoSecondAdjective
= true;
6917 Attribute
.Erase(0, 1);
6919 if (!Science
.Find("the ")) {
6920 Science
.Erase(0, 4);
6921 if (!Attribute
.Find("the ", 0, 4)) Attribute
<< " the"; else Attribute
.Insert(0, "the ", 4);
6923 if (islower(Science
[0]) && Science
.Find(' ') == festring::NPos
&& Science
.Find('-') == festring::NPos
&&
6924 Science
.Find("phobia") == festring::NPos
) {
6925 Prefix
= GetScienceTalkPrefix().GetRandomElement();
6926 if (!Prefix
.IsEmpty() && Science
.Find(Prefix
) != festring::NPos
) Prefix
.Empty();
6928 int L
= Prefix
.GetSize();
6929 if (L
&& Prefix
[L
-1] == Science
[0]) Science
.Erase(0, 1);
6930 if (!NoAttrib
&& !NoSecondAdjective
== !RAND_GOOD(3)) {
6931 int S1
= NoSecondAdjective
? 0 : GetScienceTalkAdjectiveAttribute().Size
;
6932 int S2
= GetScienceTalkSubstantiveAttribute().Size
;
6933 festring OtherAttribute
;
6934 int Chosen
= RAND_GOOD(S1
+S2
);
6935 if (Chosen
< S1
) OtherAttribute
= GetScienceTalkAdjectiveAttribute()[Chosen
];
6936 else OtherAttribute
= GetScienceTalkSubstantiveAttribute()[Chosen
- S1
];
6937 if (!OtherAttribute
.IsEmpty() && OtherAttribute
.Find("the ", 0, 4) && Attribute
.Find(OtherAttribute
) == festring::NPos
) {
6938 String
<< Attribute
<< ' ' << OtherAttribute
<< ' ' << Prefix
<< Science
;
6942 String
<< Attribute
;
6943 if (!NoAttrib
) String
<< ' ';
6944 String
<< Prefix
<< Science
;
6948 truth
character::TryToTalkAboutScience () {
6949 if (GetRelation(PLAYER
) == HOSTILE
||
6950 GetScienceTalkPossibility() <= RAND_GOOD(100) ||
6951 PLAYER
->GetAttribute(INTELLIGENCE
) < GetScienceTalkIntelligenceRequirement() ||
6952 PLAYER
->GetAttribute(WISDOM
) < GetScienceTalkWisdomRequirement() ||
6953 PLAYER
->GetAttribute(CHARISMA
) < GetScienceTalkCharismaRequirement())
6957 AddRandomScienceName(Science
);
6960 AddRandomScienceName(S1
);
6961 AddRandomScienceName(S2
);
6962 if (S1
.Find(S2
) == festring::NPos
&& S2
.Find(S1
) == festring::NPos
) {
6963 switch (RAND_GOOD(3)) {
6964 case 0: Science
= "the relation of "; break;
6965 case 1: Science
= "the differences of "; break;
6966 case 2: Science
= "the similarities of "; break;
6968 Science
<< S1
<< " and " << S2
;
6971 AddRandomScienceName(Science
);
6974 switch ((RAND() + GET_TICK()) % 10) {
6976 ADD_MESSAGE("You have a rather pleasant chat about %s with %s.", Science
.CStr(), CHAR_DESCRIPTION(DEFINITE
));
6979 ADD_MESSAGE("%s explains a few of %s opinions regarding %s to you.", CHAR_DESCRIPTION(DEFINITE
), CHAR_POSSESSIVE_PRONOUN
, Science
.CStr());
6982 ADD_MESSAGE("%s reveals a number of %s insightful views of %s to you.", CHAR_DESCRIPTION(DEFINITE
), CHAR_POSSESSIVE_PRONOUN
, Science
.CStr());
6985 ADD_MESSAGE("You exhange some information pertaining to %s with %s.", Science
.CStr(), CHAR_DESCRIPTION(DEFINITE
));
6988 ADD_MESSAGE("You engage in a pretty intriguing conversation about %s with %s.", Science
.CStr(), CHAR_DESCRIPTION(DEFINITE
));
6991 ADD_MESSAGE("You discuss at length about %s with %s.", Science
.CStr(), CHAR_DESCRIPTION(DEFINITE
));
6994 ADD_MESSAGE("You have a somewhat boring talk concerning %s with %s.", Science
.CStr(), CHAR_DESCRIPTION(DEFINITE
));
6997 ADD_MESSAGE("You are drawn into a heated argument regarding %s with %s.", Science
.CStr(), CHAR_DESCRIPTION(DEFINITE
));
7000 ADD_MESSAGE("%s delivers a sLong monologue concerning eg. %s.", CHAR_DESCRIPTION(DEFINITE
), Science
.CStr());
7003 ADD_MESSAGE("You dive into a brief but thought-provoking debate over %s with %s", Science
.CStr(), CHAR_DESCRIPTION(DEFINITE
));
7006 PLAYER
->EditExperience(INTELLIGENCE
, 1000, 50. * GetScienceTalkIntelligenceModifier() / ++ScienceTalks
);
7007 PLAYER
->EditExperience(WISDOM
, 1000, 50. * GetScienceTalkWisdomModifier() / ++ScienceTalks
);
7008 PLAYER
->EditExperience(CHARISMA
, 1000, 50. * GetScienceTalkCharismaModifier() / ++ScienceTalks
);
7013 truth
character::IsUsingWeaponOfCategory (int Category
) const {
7015 ((GetMainWielded() && GetMainWielded()->GetWeaponCategory() == Category
) ||
7016 (GetSecondaryWielded() && GetSecondaryWielded()->GetWeaponCategory() == Category
));
7020 truth
character::TryToUnStickTraps (v2 Dir
) {
7021 if (!TrapData
) return true;
7022 std::vector
<trapdata
> TrapVector
;
7023 for (const trapdata
*T
= TrapData
; T
; T
= T
->Next
) TrapVector
.push_back(*TrapData
);
7024 for (uInt c
= 0; c
< TrapVector
.size(); ++c
) {
7026 entity
*Trap
= game::SearchTrap(TrapVector
[c
].TrapID
);
7027 /*k8:??? if(!Trap->Exists()) int esko = esko = 2; */
7028 if (!Trap
->Exists()) continue; /*k8: ??? added by me; what this means? */
7029 if (Trap
->GetVictimID() == GetID() && Trap
->TryToUnStick(this, Dir
)) break;
7032 return !TrapData
&& IsEnabled();
7036 struct trapidcomparer
{
7037 trapidcomparer (uLong ID
) : ID(ID
) {}
7038 truth
operator () (const trapdata
*T
) const { return T
->TrapID
== ID
; }
7043 void character::RemoveTrap (uLong ID
) {
7044 trapdata
*&T
= ListFind(TrapData
, trapidcomparer(ID
));
7046 doforbodyparts()(this, &bodypart::SignalPossibleUsabilityChange
);
7050 void character::AddTrap (uLong ID
, uLong BodyParts
) {
7051 trapdata
*&T
= ListFind(TrapData
, trapidcomparer(ID
));
7052 if (T
) T
->BodyParts
|= BodyParts
;
7053 else T
= new trapdata(ID
, GetID(), BodyParts
);
7054 doforbodyparts()(this, &bodypart::SignalPossibleUsabilityChange
);
7058 truth
character::IsStuckToTrap (uLong ID
) const {
7059 for (const trapdata
*T
= TrapData
; T
; T
= T
->Next
) if (T
->TrapID
== ID
) return true;
7064 void character::RemoveTraps () {
7065 for (trapdata
*T
= TrapData
; T
;) {
7066 entity
*Trap
= game::SearchTrap(T
->TrapID
);
7067 if (Trap
) Trap
->UnStick();
7068 trapdata
*ToDel
= T
;
7073 doforbodyparts()(this, &bodypart::SignalPossibleUsabilityChange
);
7077 void character::RemoveTraps (int BodyPartIndex
) {
7078 uLong Flag
= 1 << BodyPartIndex
;
7079 for (trapdata
**T
= &TrapData
; *T
;) {
7080 if ((*T
)->BodyParts
& Flag
) {
7081 entity
*Trap
= game::SearchTrap((*T
)->TrapID
);
7082 if (!((*T
)->BodyParts
&= ~Flag
)) {
7083 if (Trap
) Trap
->UnStick();
7084 trapdata
*ToDel
= *T
;
7088 if (Trap
) Trap
->UnStick(BodyPartIndex
);
7096 if (GetBodyPart(BodyPartIndex
)) GetBodyPart(BodyPartIndex
)->SignalPossibleUsabilityChange();
7100 festring
character::GetTrapDescription () const {
7102 std::pair
<entity
*, int> TrapStack
[3];
7104 for (const trapdata
*T
= TrapData
; T
; T
= T
->Next
) {
7106 entity
*Trap
= game::SearchTrap(T
->TrapID
);
7109 for (c
= 0; c
< Index
; ++c
) if (TrapStack
[c
].first
->GetTrapType() == Trap
->GetTrapType()) ++TrapStack
[c
].second
;
7110 if (c
== Index
) TrapStack
[Index
++] = std::make_pair(Trap
, 1);
7118 TrapStack
[0].first
->AddTrapName(Desc
, TrapStack
[0].second
);
7121 TrapStack
[1].first
->AddTrapName(Desc
, TrapStack
[1].second
);
7122 } else if (Index
== 3) {
7124 TrapStack
[1].first
->AddTrapName(Desc
, TrapStack
[1].second
);
7126 TrapStack
[2].first
->AddTrapName(Desc
, TrapStack
[2].second
);
7129 Desc
<< "lots of traps";
7135 int character::RandomizeHurtBodyPart (uLong BodyParts
) const {
7136 int BodyPartIndex
[MAX_BODYPARTS
];
7138 for (int c
= 0; c
< GetBodyParts(); ++c
) {
7139 if (1 << c
& BodyParts
) {
7140 /*k8: ??? if(!GetBodyPart(c)) int esko = esko = 2; */
7141 if (!GetBodyPart(c
)) continue;
7142 BodyPartIndex
[Index
++] = c
;
7144 /*k8: ??? if(!Index) int esko = esko = 2;*/
7146 if (!Index
) abort();
7147 return BodyPartIndex
[RAND_N(Index
)];
7151 truth
character::BodyPartIsStuck (int I
) const {
7152 for (const trapdata
*T
= TrapData
; T
; T
= T
->Next
) if (1 << I
& T
->BodyParts
) return true;
7157 void character::PrintAttribute (cchar
*Desc
, int I
, int PanelPosX
, int PanelPosY
) const {
7158 int Attribute
= GetAttribute(I
);
7159 int NoBonusAttribute
= GetAttribute(I
, false);
7160 col16 C
= game::GetAttributeColor(I
);
7161 festring String
= Desc
;
7163 String
<< Attribute
;
7165 FONT
->Printf(DOUBLE_BUFFER
, v2(PanelPosX
, PanelPosY
* 10), C
, String
.CStr());
7166 if (Attribute
!= NoBonusAttribute
) {
7167 int Where
= PanelPosX
+ ((String
.GetSize() + 1) << 3);
7168 FONT
->Printf(DOUBLE_BUFFER
, v2(Where
, PanelPosY
* 10), LIGHT_GRAY
, "%d", NoBonusAttribute
);
7173 truth
character::AllowUnconsciousness () const {
7174 return DataBase
->AllowUnconsciousness
&& TorsoIsAlive();
7178 truth
character::CanPanic () const {
7179 return !Action
|| !Action
->IsUnconsciousness();
7183 int character::GetRandomBodyPart (uLong Possible
) const {
7184 int OKBodyPart
[MAX_BODYPARTS
];
7185 int OKBodyParts
= 0;
7186 for (int c
= 0; c
< BodyParts
; ++c
) if (1 << c
& Possible
&& GetBodyPart(c
)) OKBodyPart
[OKBodyParts
++] = c
;
7187 return OKBodyParts
? OKBodyPart
[RAND_N(OKBodyParts
)] : NONE_INDEX
;
7191 void character::EditNP (sLong What
) {
7192 int OldState
= GetHungerState();
7194 int NewState
= GetHungerState();
7195 if (OldState
> VERY_HUNGRY
&& NewState
== VERY_HUNGRY
) DeActivateVoluntaryAction(CONST_S("You are getting really hungry."));
7196 if (OldState
> STARVING
&& NewState
== STARVING
) DeActivateVoluntaryAction(CONST_S("You are getting extremely hungry."));
7200 truth
character::IsSwimming () const {
7201 return !IsFlying() && GetSquareUnder() && GetSquareUnder()->GetSquareWalkability() & SWIM
;
7205 void character::AddBlackUnicornConsumeEndMessage () const {
7206 if (IsPlayer()) ADD_MESSAGE("You feel dirty and loathsome.");
7210 void character::AddGrayUnicornConsumeEndMessage () const {
7211 if (IsPlayer()) ADD_MESSAGE("You feel neutralized.");
7215 void character::AddWhiteUnicornConsumeEndMessage () const {
7216 if (IsPlayer()) ADD_MESSAGE("You feel purified.");
7220 void character::AddOmmelBoneConsumeEndMessage () const {
7221 if (IsPlayer()) ADD_MESSAGE("You feel the power of all your canine ancestors combining in your body.");
7222 else if (CanBeSeenByPlayer()) ADD_MESSAGE("For a moment %s looks extremely ferocious. You shudder.", CHAR_NAME(DEFINITE
));
7226 int character::GetBodyPartSparkleFlags (int) const {
7228 ((GetNaturalSparkleFlags() & SKIN_COLOR
? SPARKLING_A
: 0) |
7229 (GetNaturalSparkleFlags() & TORSO_MAIN_COLOR
? SPARKLING_B
: 0) |
7230 (GetNaturalSparkleFlags() & TORSO_SPECIAL_COLOR
? SPARKLING_D
: 0));
7234 truth
character::IsAnimated () const {
7235 return combinebodypartpredicates()(this, &bodypart::IsAnimated
, 1);
7239 double character::GetNaturalExperience (int Identifier
) const {
7240 return DataBase
->NaturalExperience
[Identifier
];
7244 truth
character::HasBodyPart (sorter Sorter
) const {
7245 if (Sorter
== 0) return true;
7246 return combinebodypartpredicateswithparam
<ccharacter
*>()(this, Sorter
, this, 1);
7250 truth
character::PossessesItem (sorter Sorter
) const {
7251 if (Sorter
== 0) return true;
7253 (GetStack()->SortedItems(this, Sorter
) ||
7254 combinebodypartpredicateswithparam
<ccharacter
*>()(this, Sorter
, this, 1) ||
7255 combineequipmentpredicateswithparam
<ccharacter
*>()(this, Sorter
, this, 1));
7260 cchar
*character::GetRunDescriptionLine (int I
) const {
7261 if (!GetRunDescriptionLineOne().IsEmpty()) return !I
? GetRunDescriptionLineOne().CStr() : GetRunDescriptionLineTwo().CStr();
7262 if (IsFlying()) return !I
? "Flying" : "very fast";
7263 if (IsSwimming()) return !I
? "Swimming" : "very fast";
7264 return !I
? "Running" : "";
7268 void character::VomitAtRandomDirection (int Amount
) {
7269 if (game::IsInWilderness()) return;
7270 /* Lacks support of multitile monsters */
7273 for (int d
= 0; d
< 9; ++d
) {
7274 lsquare
*Square
= GetLSquareUnder()->GetNeighbourLSquare(d
);
7275 if (Square
&& !Square
->VomitingIsDangerous(this)) Possible
[Index
++] = Square
->GetPos();
7277 if (Index
) Vomit(Possible
[RAND_N(Index
)], Amount
);
7278 else Vomit(GetPos(), Amount
);
7282 void character::RemoveLifeSavers () {
7283 for (int c
= 0; c
< GetEquipments(); ++c
) {
7284 item
*Equipment
= GetEquipment(c
);
7285 if (Equipment
&& Equipment
->IsInCorrectSlot(c
) && Equipment
->GetGearStates() & LIFE_SAVED
) {
7286 Equipment
->SendToHell();
7287 Equipment
->RemoveFromSlot();
7293 ccharacter
*character::FindCarrier () const {
7294 return this; //check
7298 void character::PrintBeginHiccupsMessage () const {
7299 if (IsPlayer()) ADD_MESSAGE("Your diaphragm is spasming vehemently.");
7303 void character::PrintEndHiccupsMessage () const {
7304 if (IsPlayer()) ADD_MESSAGE("You feel your annoying hiccoughs have finally subsided.");
7308 void character::HiccupsHandler () {
7310 if (!(RAND() % 2000)) {
7311 if (IsPlayer()) ADD_MESSAGE("");
7312 else if (CanBeSeenByPlayer()) ADD_MESSAGE("");
7313 else if ((PLAYER->GetPos()-GetPos()).GetLengthSquare() <= 400) ADD_MESSAGE("");
7314 game::CallForAttention(GetPos(), 400);
7320 void character::HiccupsSituationDangerModifier (double &Danger
) const {
7325 bool character::IsConscious () const {
7326 return !Action
|| !Action
->IsUnconsciousness();
7330 wsquare
*character::GetNearWSquare (v2 Pos
) const {
7331 return static_cast<wsquare
*>(GetSquareUnder()->GetArea()->GetSquare(Pos
));
7335 wsquare
*character::GetNearWSquare (int x
, int y
) const {
7336 return static_cast<wsquare
*>(GetSquareUnder()->GetArea()->GetSquare(x
, y
));
7340 void character::ForcePutNear (v2 Pos
) {
7341 /* GUM SOLUTION!!! */
7342 v2 NewPos
= game::GetCurrentLevel()->GetNearestFreeSquare(PLAYER
, Pos
, false);
7343 if (NewPos
== ERROR_V2
) do { NewPos
= game::GetCurrentLevel()->GetRandomSquare(this); } while(NewPos
== Pos
);
7348 void character::ReceiveMustardGas (int BodyPart
, sLong Volume
) {
7349 if (Volume
) GetBodyPart(BodyPart
)->AddFluid(liquid::Spawn(MUSTARD_GAS_LIQUID
, Volume
), CONST_S("skin"), 0, true);
7353 void character::ReceiveMustardGasLiquid (int BodyPartIndex
, sLong Modifier
) {
7354 bodypart
*BodyPart
= GetBodyPart(BodyPartIndex
);
7355 if (BodyPart
->GetMainMaterial()->GetInteractionFlags() & IS_AFFECTED_BY_MUSTARD_GAS
) {
7356 sLong Tries
= Modifier
;
7357 Modifier
-= Tries
; //opt%?
7359 for (sLong c
= 0; c
< Tries
; ++c
) if (!(RAND() % 100)) ++Damage
;
7360 if (Modifier
&& !(RAND() % 1000 / Modifier
)) ++Damage
;
7362 uLong Minute
= game::GetTotalMinutes();
7363 if (GetLastAcidMsgMin() != Minute
&& (CanBeSeenByPlayer() || IsPlayer())) {
7364 SetLastAcidMsgMin(Minute
);
7365 if (IsPlayer()) ADD_MESSAGE("Mustard gas dissolves the skin of your %s.", BodyPart
->GetBodyPartName().CStr());
7366 else ADD_MESSAGE("Mustard gas dissolves %s.", CHAR_NAME(DEFINITE
));
7368 ReceiveBodyPartDamage(0, Damage
, MUSTARD_GAS_DAMAGE
, BodyPartIndex
, YOURSELF
, false, false, false);
7369 CheckDeath(CONST_S("killed by a fatal exposure to mustard gas"));
7375 truth
character::IsBadPath (v2 Pos
) const {
7376 if (!IsGoingSomeWhere()) return false;
7377 v2 TPos
= !Route
.empty() ? Route
.back() : GoingTo
;
7378 return ((TPos
- Pos
).GetManhattanLength() > (TPos
- GetPos()).GetManhattanLength());
7382 double &character::GetExpModifierRef (expid E
) {
7383 return ExpModifierMap
.insert(std::make_pair(E
, 1.)).first
->second
;
7387 /* Should probably do more. Now only makes Player forget gods */
7388 truth
character::ForgetRandomThing () {
7390 /* hopefully this code isn't some where else */
7391 std::vector
<god
*> Known
;
7392 for (int c
= 1; c
<= GODS
; ++c
) if (game::GetGod(c
)->IsKnown()) Known
.push_back(game::GetGod(c
));
7393 if (Known
.empty()) return false;
7394 int RandomGod
= RAND_N(Known
.size());
7395 Known
.at(RAND_N(Known
.size()))->SetIsKnown(false);
7396 ADD_MESSAGE("You forget how to pray to %s.",
7397 Known
.at(RandomGod
)->GetName());
7404 int character::CheckForBlock (character
*Enemy
, item
*Weapon
, double ToHitValue
, int Damage
, int Success
, int Type
) {
7409 void character::ApplyAllGodsKnownBonus () {
7410 stack
*AddPlace
= GetStackUnder();
7411 if (game::IsInWilderness()) AddPlace
= GetStack(); else AddPlace
= GetStackUnder();
7412 pantheonbook
*NewBook
= pantheonbook::Spawn();
7413 AddPlace
->AddItem(NewBook
);
7414 ADD_MESSAGE("\"MORTAL! BEHOLD THE HOLY SAGA\"");
7415 ADD_MESSAGE("%s materializes near your feet.", NewBook
->CHAR_NAME(INDEFINITE
));
7419 void character::ReceiveSirenSong (character
*Siren
) {
7420 if (Siren
->GetTeam() == GetTeam()) return;
7422 if (IsPlayer()) ADD_MESSAGE("The beautiful melody of %s makes you feel sleepy.", Siren
->CHAR_NAME(DEFINITE
));
7423 else if (CanBeSeenByPlayer()) ADD_MESSAGE("The beautiful melody of %s makes %s look sleepy.", Siren
->CHAR_NAME(DEFINITE
), CHAR_NAME(DEFINITE
)); /*k8*/
7424 Stamina
-= (1 + RAND_N(4)) * 10000;
7427 if (!IsPlayer() && IsCharmable() && !RAND_N(5)) {
7428 ChangeTeam(Siren
->GetTeam());
7429 ADD_MESSAGE("%s seems to be totally brainwashed by %s melodies.", CHAR_NAME(DEFINITE
), Siren
->CHAR_NAME(DEFINITE
));
7433 item
*What
= GiveMostExpensiveItem(Siren
);
7436 ADD_MESSAGE("%s music persuades you to give %s to %s as a present.", Siren
->CHAR_NAME(DEFINITE
), What
->CHAR_NAME(DEFINITE
), Siren
->CHAR_OBJECT_PRONOUN
);
7438 ADD_MESSAGE("%s is persuated to give %s to %s because of %s beautiful singing.", CHAR_NAME(DEFINITE
), What
->CHAR_NAME(INDEFINITE
), Siren
->CHAR_NAME(DEFINITE
), Siren
->CHAR_OBJECT_PRONOUN
);
7441 if (IsPlayer()) ADD_MESSAGE("You would like to give something to %s.", Siren
->CHAR_NAME(DEFINITE
));
7448 // return 0, if no item found
7449 item
*character::FindMostExpensiveItem () const {
7451 item
*MostExpensive
= 0;
7452 for (stackiterator i
= GetStack()->GetBottom(); i
.HasItem(); ++i
) {
7453 if ((*i
)->GetPrice() > MaxPrice
) {
7454 MaxPrice
= (*i
)->GetPrice();
7455 MostExpensive
= (*i
);
7458 for (int c
= 0; c
< GetEquipments(); ++c
) {
7459 item
*Equipment
= GetEquipment(c
);
7460 if (Equipment
&& Equipment
->GetPrice() > MaxPrice
) {
7461 MaxPrice
= Equipment
->GetPrice();
7462 MostExpensive
= Equipment
;
7465 return MostExpensive
;
7469 // returns 0 if no items available
7470 item
*character::GiveMostExpensiveItem(character
*ToWhom
) {
7471 item
*ToGive
= FindMostExpensiveItem();
7472 if (!ToGive
) return 0;
7473 truth Equipped
= PLAYER
->Equips(ToGive
);
7474 ToGive
->RemoveFromSlot();
7475 if (Equipped
) game::AskForEscPress(CONST_S("Equipment lost!"));
7476 ToWhom
->ReceiveItemAsPresent(ToGive
);
7482 void character::ReceiveItemAsPresent (item
*Present
) {
7483 if (TestForPickup(Present
)) GetStack()->AddItem(Present
); else GetStackUnder()->AddItem(Present
);
7487 /* returns 0 if no enemies in sight */
7488 character
*character::GetNearestEnemy () const {
7489 character
*NearestEnemy
= 0;
7490 sLong NearestEnemyDistance
= 0x7FFFFFFF;
7492 for (int c
= 0; c
< game::GetTeams(); ++c
) {
7493 if (GetTeam()->GetRelation(game::GetTeam(c
)) == HOSTILE
) {
7494 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
) {
7495 if ((*i
)->IsEnabled()) {
7496 sLong ThisDistance
= Max
<sLong
>(abs((*i
)->GetPos().X
- Pos
.X
), abs((*i
)->GetPos().Y
- Pos
.Y
));
7497 if ((ThisDistance
< NearestEnemyDistance
|| (ThisDistance
== NearestEnemyDistance
&& !(RAND() % 3))) && (*i
)->CanBeSeenBy(this)) {
7499 NearestEnemyDistance
= ThisDistance
;
7505 return NearestEnemy
;
7509 truth
character::MindWormCanPenetrateSkull (mindworm
*) const {
7514 truth
character::CanTameWithDulcis (const character
*Tamer
) const {
7515 int TamingDifficulty
= GetTamingDifficulty();
7516 if (TamingDifficulty
== NO_TAMING
) return false;
7517 if (GetAttachedGod() == DULCIS
) return true;
7518 int Modifier
= Tamer
->GetAttribute(WISDOM
) + Tamer
->GetAttribute(CHARISMA
);
7519 if (Tamer
->IsPlayer()) Modifier
+= game::GetGod(DULCIS
)->GetRelation() / 20;
7520 else if (Tamer
->GetAttachedGod() == DULCIS
) Modifier
+= 50;
7521 if (TamingDifficulty
== 0) {
7522 if (!IgnoreDanger()) TamingDifficulty
= int(10 * GetRelativeDanger(Tamer
));
7523 else TamingDifficulty
= 10 * GetHPRequirementForGeneration()/Max(Tamer
->GetHP(), 1);
7525 return Modifier
>= TamingDifficulty
* 3;
7529 truth
character::CanTameWithLyre (const character
*Tamer
) const {
7530 int TamingDifficulty
= GetTamingDifficulty();
7531 if (TamingDifficulty
== NO_TAMING
) return false;
7532 if (TamingDifficulty
== 0) {
7533 if (!IgnoreDanger()) TamingDifficulty
= int(10 * GetRelativeDanger(Tamer
));
7534 else TamingDifficulty
= 10*GetHPRequirementForGeneration()/Max(Tamer
->GetHP(), 1);
7536 return Tamer
->GetAttribute(CHARISMA
) >= TamingDifficulty
;
7540 truth
character::CanTameWithScroll (const character
*Tamer
) const {
7541 int TamingDifficulty
= GetTamingDifficulty();
7543 (TamingDifficulty
!= NO_TAMING
&&
7544 (TamingDifficulty
== 0 ||
7545 Tamer
->GetAttribute(INTELLIGENCE
) * 4 + Tamer
->GetAttribute(CHARISMA
) >= TamingDifficulty
* 5));
7549 truth
character::CheckSadism () {
7550 if (!IsSadist() || !HasSadistAttackMode() || !IsSmall()) return false; // gum
7552 for (int d
= 0; d
< 8; ++d
) {
7553 square
*Square
= GetNeighbourSquare(d
);
7555 character
*Char
= Square
->GetCharacter();
7556 if (Char
&& Char
->IsMasochist() && GetRelation(Char
) == FRIEND
&&
7557 Char
->GetHP() * 3 >= Char
->GetMaxHP() * 2 && Hit(Char
, Square
->GetPos(), d
, SADIST_HIT
)) {
7568 truth
character::CheckForBeverage () {
7569 if (StateIsActivated(PANIC
) || !IsEnabled() || !UsesNutrition() || CheckIfSatiated()) return false;
7570 itemvector ItemVector
;
7571 GetStack()->FillItemVector(ItemVector
);
7572 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) if (ItemVector
[c
]->IsBeverage(this) && TryToConsume(ItemVector
[c
])) return true;
7577 void character::Haste () {
7578 doforbodyparts()(this, &bodypart::Haste
);
7579 doforequipments()(this, &item::Haste
);
7580 BeginTemporaryState(HASTE
, 500 + RAND() % 1000);
7584 void character::Slow () {
7585 doforbodyparts()(this, &bodypart::Slow
);
7586 doforequipments()(this, &item::Slow
);
7587 //BeginTemporaryState(HASTE, 500 + RAND() % 1000); // this seems to be a bug
7588 BeginTemporaryState(SLOW
, 500 + RAND() % 1000);