1 /*
2  *  Copyright (C) 2011-2016  OpenDungeons Team
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "entities/Creature.h"
19 
20 #include "creatureaction/CreatureAction.h"
21 #include "creatureaction/CreatureActionClaimGroundTile.h"
22 #include "creatureaction/CreatureActionClaimWallTile.h"
23 #include "creatureaction/CreatureActionDigTile.h"
24 #include "creatureaction/CreatureActionFight.h"
25 #include "creatureaction/CreatureActionFindHome.h"
26 #include "creatureaction/CreatureActionFlee.h"
27 #include "creatureaction/CreatureActionGetFee.h"
28 #include "creatureaction/CreatureActionGrabEntity.h"
29 #include "creatureaction/CreatureActionLeaveDungeon.h"
30 #include "creatureaction/CreatureActionSearchEntityToCarry.h"
31 #include "creatureaction/CreatureActionSearchFood.h"
32 #include "creatureaction/CreatureActionSearchGroundTileToClaim.h"
33 #include "creatureaction/CreatureActionSearchJob.h"
34 #include "creatureaction/CreatureActionSearchTileToDig.h"
35 #include "creatureaction/CreatureActionSearchWallTileToClaim.h"
36 #include "creatureaction/CreatureActionSleep.h"
37 #include "creatureaction/CreatureActionStealFreeGold.h"
38 #include "creatureaction/CreatureActionWalkToTile.h"
39 #include "creaturebehaviour/CreatureBehaviour.h"
40 #include "creatureeffect/CreatureEffect.h"
41 #include "creatureeffect/CreatureEffectManager.h"
42 #include "creatureeffect/CreatureEffectSlap.h"
43 #include "creaturemood/CreatureMood.h"
44 #include "creaturemood/CreatureMoodManager.h"
45 #include "creatureskill/CreatureSkill.h"
46 #include "entities/ChickenEntity.h"
47 #include "entities/CreatureDefinition.h"
48 #include "entities/GameEntityType.h"
49 #include "entities/Tile.h"
50 #include "entities/TreasuryObject.h"
51 #include "entities/Weapon.h"
52 #include "game/Player.h"
53 #include "game/Skill.h"
54 #include "game/SkillType.h"
55 #include "game/Seat.h"
56 #include "gamemap/GameMap.h"
57 #include "gamemap/Pathfinding.h"
58 #include "giftboxes/GiftBoxSkill.h"
59 #include "network/ODClient.h"
60 #include "network/ODServer.h"
61 #include "network/ServerNotification.h"
62 #include "render/CreatureOverlayStatus.h"
63 #include "render/RenderManager.h"
64 #include "rooms/RoomCrypt.h"
65 #include "rooms/RoomDormitory.h"
66 #include "sound/SoundEffectsManager.h"
67 #include "spells/Spell.h"
68 #include "spells/SpellType.h"
69 #include "traps/Trap.h"
70 #include "utils/ConfigManager.h"
71 #include "utils/Helper.h"
72 #include "utils/LogManager.h"
73 #include "utils/MakeUnique.h"
74 #include "utils/Random.h"
75 
76 #include <CEGUI/Event.h>
77 #include <CEGUI/System.h>
78 #include <CEGUI/UDim.h>
79 #include <CEGUI/Vector.h>
80 #include <CEGUI/WindowManager.h>
81 #include <CEGUI/Window.h>
82 #include <CEGUI/widgets/FrameWindow.h>
83 #include <CEGUI/widgets/PushButton.h>
84 
85 #include <OgreQuaternion.h>
86 #include <OgreVector3.h>
87 #include <OgreVector2.h>
88 
89 #include <cmath>
90 #include <algorithm>
91 
92 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
93 #define snprintf_is_banned_in_OD_code _snprintf
94 #endif
95 
96 static const Ogre::Real CANNON_MISSILE_HEIGHT = 0.3;
97 
98 const int32_t Creature::NB_TURNS_BEFORE_CHECKING_TASK = 15;
99 const uint32_t Creature::NB_OVERLAY_HEALTH_VALUES = 8;
100 
101 enum CreatureMoodEnum
102 {
103     Angry = 0x0001,
104     Furious = 0x0002,
105     GetFee = 0x0004,
106     LeaveDungeon = 0x0008,
107     KoDeath = 0x0010,
108     Hungry = 0x0020,
109     Tired = 0x0040,
110     KoTemp = 0x0080,
111     InJail = 0x0100,
112     // To know if a creature is KO
113     KoDeathOrTemp = KoTemp | KoDeath,
114     // Mood filters for creatures in prison that every player will see
115     MoodPrisonFiltersAllPlayers = InJail,
116     // Mood filters for creatures in prison that prison allied will see
117     MoodPrisonFiltersPrisonAllies = KoTemp | InJail
118 };
119 
CreatureParticuleEffect(Creature & creature,const std::string & name,const std::string & script,uint32_t nbTurnsEffect,CreatureEffect * effect)120 CreatureParticuleEffect::CreatureParticuleEffect(Creature& creature, const std::string& name, const std::string& script, uint32_t nbTurnsEffect,
121         CreatureEffect* effect) :
122     EntityParticleEffect(name, script, nbTurnsEffect),
123     mEffect(effect),
124     mCreature(creature)
125 {
126     if(mEffect == nullptr)
127     {
128         OD_LOG_ERR("null effect on creature=" + mCreature.getName() + ", name=" + name + ", script=" + script);
129         return;
130     }
131 
132     mEffect->startEffect(mCreature);
133 }
134 
~CreatureParticuleEffect()135 CreatureParticuleEffect::~CreatureParticuleEffect()
136 {
137     if(mEffect != nullptr)
138     {
139         mEffect->releaseEffect(mCreature);
140         delete mEffect;
141     }
142 
143     mEffect = nullptr;
144 }
145 
Creature(GameMap * gameMap,const CreatureDefinition * definition,Seat * seat,Ogre::Vector3 position)146 Creature::Creature(GameMap* gameMap, const CreatureDefinition* definition, Seat* seat, Ogre::Vector3 position) :
147     MovableGameEntity        (gameMap),
148     mPhysicalDefense         (3.0),
149     mMagicalDefense          (1.5),
150     mElementDefense          (0.0),
151     mModifierStrength        (1.0),
152     mWeaponL                 (nullptr),
153     mWeaponR                 (nullptr),
154     mHomeTile                (nullptr),
155     mDefinition              (definition),
156     mHasVisualDebuggingEntities (false),
157     mWakefulness             (100.0),
158     mHunger                  (0.0),
159     mLevel                   (1),
160     mHp                      (10.0),
161     mMaxHP                   (10.0),
162     mExp                     (0.0),
163     mGroundSpeed             (1.0),
164     mWaterSpeed              (0.0),
165     mLavaSpeed               (0.0),
166     mDigRate                 (0.0),
167     mClaimRate               (0.0),
168     mDeathCounter            (0),
169     mJobCooldown             (0),
170     mGoldFee                 (0),
171     mGoldCarried             (0),
172     mSkillTypeDropDeath      (SkillType::nullSkillType),
173     mWeaponDropDeath         ("none"),
174     mStatsWindow             (nullptr),
175     mNbTurnsWithoutBattle    (0),
176     mCarriedEntity           (nullptr),
177     mMoodCooldownTurns       (0),
178     mMoodValue               (CreatureMoodLevel::Neutral),
179     mMoodPoints              (0),
180     mNbTurnFurious           (-1),
181     mOverlayHealthValue      (0),
182     mOverlayMoodValue        (0),
183     mOverlayStatus           (nullptr),
184     mNeedFireRefresh         (false),
185     mDropCooldown            (0),
186     mSpeedModifier           (1.0),
187     mKoTurnCounter           (0),
188     mSeatPrison              (nullptr),
189     mNbTurnsTorture          (0),
190     mNbTurnsPrison           (0),
191     mActiveSlapsCount        (0)
192 
193 {
194     //TODO: This should be set in initialiser list in parent classes
195     setSeat(seat);
196     mPosition = position;
197     setMeshName(definition->getMeshName());
198     setName(getGameMap()->nextUniqueNameCreature(definition->getClassName()));
199 
200     mMaxHP = mDefinition->getMinHp();
201     setHP(mMaxHP);
202 
203     mGroundSpeed = mDefinition->getMoveSpeedGround();
204     mWaterSpeed = mDefinition->getMoveSpeedWater();
205     mLavaSpeed = mDefinition->getMoveSpeedLava();
206 
207     mDigRate = mDefinition->getDigRate();
208     mClaimRate = mDefinition->getClaimRate();
209 
210     // Fighting stats
211     mPhysicalDefense = mDefinition->getPhysicalDefense();
212     mMagicalDefense = mDefinition->getMagicalDefense();
213     mElementDefense = mDefinition->getElementDefense();
214 
215     if(mDefinition->getWeaponSpawnL().compare("none") != 0)
216         mWeaponL = gameMap->getWeapon(mDefinition->getWeaponSpawnL());
217 
218     if(mDefinition->getWeaponSpawnR().compare("none") != 0)
219         mWeaponR = gameMap->getWeapon(mDefinition->getWeaponSpawnR());
220 
221     setupDefinition(*gameMap, *ConfigManager::getSingleton().getCreatureDefinitionDefaultWorker());
222 }
223 
Creature(GameMap * gameMap)224 Creature::Creature(GameMap* gameMap) :
225     MovableGameEntity        (gameMap),
226     mPhysicalDefense         (3.0),
227     mMagicalDefense          (1.5),
228     mElementDefense          (0.0),
229     mModifierStrength        (1.0),
230     mWeaponL                 (nullptr),
231     mWeaponR                 (nullptr),
232     mHomeTile                (nullptr),
233     mDefinition              (nullptr),
234     mHasVisualDebuggingEntities (false),
235     mWakefulness             (100.0),
236     mHunger                  (0.0),
237     mLevel                   (1),
238     mHp                      (10.0),
239     mMaxHP                   (10.0),
240     mExp                     (0.0),
241     mGroundSpeed             (1.0),
242     mWaterSpeed              (0.0),
243     mLavaSpeed               (0.0),
244     mDigRate                 (0.0),
245     mClaimRate               (0.0),
246     mDeathCounter            (0),
247     mJobCooldown             (0),
248     mGoldFee                 (0),
249     mGoldCarried             (0),
250     mSkillTypeDropDeath      (SkillType::nullSkillType),
251     mWeaponDropDeath         ("none"),
252     mStatsWindow             (nullptr),
253     mNbTurnsWithoutBattle    (0),
254     mCarriedEntity           (nullptr),
255     mMoodCooldownTurns       (0),
256     mMoodValue               (CreatureMoodLevel::Neutral),
257     mMoodPoints              (0),
258     mNbTurnFurious           (-1),
259     mOverlayHealthValue      (0),
260     mOverlayMoodValue        (0),
261     mOverlayStatus           (nullptr),
262     mNeedFireRefresh         (false),
263     mDropCooldown            (0),
264     mSpeedModifier           (1.0),
265     mKoTurnCounter           (0),
266     mSeatPrison              (nullptr),
267     mNbTurnsTorture          (0),
268     mNbTurnsPrison           (0),
269     mActiveSlapsCount        (0)
270 {
271 }
272 
~Creature()273 Creature::~Creature()
274 {
275 }
276 
createMeshLocal()277 void Creature::createMeshLocal()
278 {
279     MovableGameEntity::createMeshLocal();
280     if(!getIsOnServerMap())
281     {
282         RenderManager::getSingleton().rrCreateCreature(this);
283 
284         // By default, we set the creature in idle state
285         RenderManager::getSingleton().rrSetObjectAnimationState(this, EntityAnimation::idle_anim, true);
286     }
287 
288     createMeshWeapons();
289 }
290 
destroyMeshLocal()291 void Creature::destroyMeshLocal()
292 {
293     destroyMeshWeapons();
294     MovableGameEntity::destroyMeshLocal();
295     if(getIsOnServerMap())
296         return;
297 
298     destroyStatsWindow();
299     RenderManager::getSingleton().rrDestroyCreature(this);
300 }
301 
createMeshWeapons()302 void Creature::createMeshWeapons()
303 {
304     if(getIsOnServerMap())
305         return;
306 
307     if(mWeaponL != nullptr)
308         RenderManager::getSingleton().rrCreateWeapon(this, mWeaponL, "L");
309 
310     if(mWeaponR != nullptr)
311         RenderManager::getSingleton().rrCreateWeapon(this, mWeaponR, "R");
312 }
313 
destroyMeshWeapons()314 void Creature::destroyMeshWeapons()
315 {
316     if(getIsOnServerMap())
317         return;
318 
319     if(mWeaponL != nullptr)
320         RenderManager::getSingleton().rrDestroyWeapon(this, mWeaponL, "L");
321 
322     if(mWeaponR != nullptr)
323         RenderManager::getSingleton().rrDestroyWeapon(this, mWeaponR, "R");
324 }
325 
getObjectType() const326 GameEntityType Creature::getObjectType() const
327 {
328     return GameEntityType::creature;
329 }
330 
addToGameMap()331 void Creature::addToGameMap()
332 {
333     getGameMap()->addCreature(this);
334     getGameMap()->addAnimatedObject(this);
335     getGameMap()->addClientUpkeepEntity(this);
336 
337     if(!getIsOnServerMap())
338         return;
339 
340     getGameMap()->addActiveObject(this);
341 }
342 
removeFromGameMap()343 void Creature::removeFromGameMap()
344 {
345     fireEntityRemoveFromGameMap();
346     removeEntityFromPositionTile();
347     getGameMap()->removeCreature(this);
348     getGameMap()->removeAnimatedObject(this);
349     getGameMap()->removeClientUpkeepEntity(this);
350 
351     if(!getIsOnServerMap())
352         return;
353 
354     // If the creature has a homeTile where it sleeps, its bed needs to be destroyed.
355     if (getHomeTile() != nullptr)
356     {
357         RoomDormitory* home = static_cast<RoomDormitory*>(getHomeTile()->getCoveringBuilding());
358         home->releaseTileForSleeping(getHomeTile(), this);
359     }
360 
361     fireRemoveEntityToSeatsWithVision();
362     getGameMap()->removeActiveObject(this);
363 }
364 
getCreatureStreamFormat()365 std::string Creature::getCreatureStreamFormat()
366 {
367     std::string format = MovableGameEntity::getMovableGameEntityStreamFormat();
368     if(!format.empty())
369         format += "\t";
370 
371     format += "ClassName\tLevel\tCurrentXP\tCurrentHP\tCurrentWakefulness"
372             "\tCurrentHunger\tGoldToDeposit\tLeftWeapon\tRightWeapon\tCarriedSkill\tCarriedWeapon"
373             "\tNbCreatureEffects\tN*CreatureEffects";
374 
375     return format;
376 }
377 
exportToStream(std::ostream & os) const378 void Creature::exportToStream(std::ostream& os) const
379 {
380     MovableGameEntity::exportToStream(os);
381     os << mDefinition->getClassName() << "\t";
382     os << getLevel() << "\t" << mExp << "\t";
383     if(getHP() < mMaxHP)
384         os << getHP();
385     else
386         os << "max";
387     os << "\t" << mWakefulness << "\t" << mHunger << "\t" << mGoldCarried;
388 
389     // Check creature weapons
390     if(mWeaponL != nullptr)
391         os << "\t" << mWeaponL->getName();
392     else
393         os << "\tnone";
394 
395     if(mWeaponR != nullptr)
396         os << "\t" << mWeaponR->getName();
397     else
398         os << "\tnone";
399 
400     os << "\t" << Skills::toString(mSkillTypeDropDeath);
401 
402     os << "\t" << mWeaponDropDeath;
403 
404     uint32_t nbEffects = mEntityParticleEffects.size();
405     os << "\t" << nbEffects;
406     for(EntityParticleEffect* effect : mEntityParticleEffects)
407     {
408         // We only save creature particle effects. The other are expected to be re-created
409         // automatically (for example if it is a permanent effect for a creature)
410         if(effect->getEntityParticleEffectType() != EntityParticleEffectType::creature)
411             continue;
412 
413         CreatureParticuleEffect* creatureParticuleEffect = static_cast<CreatureParticuleEffect*>(effect);
414         os << "\t";
415         CreatureEffectManager::write(*creatureParticuleEffect->mEffect, os);
416     }
417 }
418 
importFromStream(std::istream & is)419 bool Creature::importFromStream(std::istream& is)
420 {
421     // Beware: A generic class name might be used here so we shouldn't use mDefinition
422     // here as it is not set yet (for example, default worker will be available only after
423     // seat lobby configuration)
424     if(!MovableGameEntity::importFromStream(is))
425         return false;
426     std::string tempString;
427 
428     if(!(is >> mDefinitionString))
429         return false;
430     if(!(is >> mLevel))
431         return false;
432     if(!(is >> mExp))
433         return false;
434     if(!(is >> mHpString))
435         return false;
436     if(!(is >> mWakefulness))
437         return false;
438     if(!(is >> mHunger))
439         return false;
440     if(!(is >> mGoldCarried))
441         return false;
442     if(!(is >> tempString))
443         return false;
444     if(tempString != "none")
445     {
446         mWeaponL = getGameMap()->getWeapon(tempString);
447         if(mWeaponL == nullptr)
448         {
449             OD_LOG_ERR("Unknown weapon name=" + tempString);
450         }
451     }
452 
453     if(!(is >> tempString))
454         return false;
455     if(tempString != "none")
456     {
457         mWeaponR = getGameMap()->getWeapon(tempString);
458         if(mWeaponR == nullptr)
459         {
460             OD_LOG_ERR("Unknown weapon name=" + tempString);
461         }
462     }
463 
464     if(!(is >> tempString))
465         return false;
466     mSkillTypeDropDeath = Skills::fromString(tempString);
467 
468     if(!(is >> mWeaponDropDeath))
469         return false;
470 
471     mLevel = std::min(MAX_LEVEL, mLevel);
472 
473     uint32_t nbEffects;
474     if(!(is >> nbEffects))
475         return false;
476     while(nbEffects > 0)
477     {
478         --nbEffects;
479         CreatureEffect* effect = CreatureEffectManager::load(is);
480         if(effect == nullptr)
481             continue;
482 
483         addCreatureEffect(effect);
484     }
485 
486     return true;
487 }
488 
buildStats()489 void Creature::buildStats()
490 {
491     // Get the base value
492     mMaxHP = mDefinition->getMinHp();
493     mDigRate = mDefinition->getDigRate();
494     mClaimRate = mDefinition->getClaimRate();
495     mGroundSpeed = mDefinition->getMoveSpeedGround();
496     mWaterSpeed = mDefinition->getMoveSpeedWater();
497     mLavaSpeed  = mDefinition->getMoveSpeedLava();
498 
499     mPhysicalDefense = mDefinition->getPhysicalDefense();
500     mMagicalDefense = mDefinition->getMagicalDefense();
501     mElementDefense = mDefinition->getElementDefense();
502 
503     // Improve the stats to the current level
504     double multiplier = mLevel - 1;
505     if (multiplier <= 0.0)
506         return;
507 
508     mMaxHP += mDefinition->getHpPerLevel() * multiplier;
509     mDigRate += mDefinition->getDigRatePerLevel() * multiplier;
510     mClaimRate += mDefinition->getClaimRatePerLevel() * multiplier;
511     mGroundSpeed += mDefinition->getGroundSpeedPerLevel() * multiplier;
512     mWaterSpeed += mDefinition->getWaterSpeedPerLevel() * multiplier;
513     mLavaSpeed += mDefinition->getLavaSpeedPerLevel() * multiplier;
514 
515     mPhysicalDefense += mDefinition->getPhysicalDefPerLevel() * multiplier;
516     mMagicalDefense += mDefinition->getMagicalDefPerLevel() * multiplier;
517     mElementDefense += mDefinition->getElementDefPerLevel() * multiplier;
518 }
519 
getCreatureFromStream(GameMap * gameMap,std::istream & is)520 Creature* Creature::getCreatureFromStream(GameMap* gameMap, std::istream& is)
521 {
522     Creature* creature = new Creature(gameMap);
523     if(!creature->importFromStream(is))
524     {
525         delete creature;
526         return nullptr;
527     }
528     return creature;
529 }
530 
getCreatureFromPacket(GameMap * gameMap,ODPacket & is)531 Creature* Creature::getCreatureFromPacket(GameMap* gameMap, ODPacket& is)
532 {
533     Creature* creature = new Creature(gameMap);
534     creature->importFromPacket(is);
535     return creature;
536 }
537 
exportToPacket(ODPacket & os,const Seat * seat) const538 void Creature::exportToPacket(ODPacket& os, const Seat* seat) const
539 {
540     MovableGameEntity::exportToPacket(os, seat);
541     const std::string& className = mDefinition->getClassName();
542     os << className;
543     os << mLevel;
544     os << mExp;
545 
546     os << mHp;
547     os << mMaxHP;
548 
549     os << mDigRate;
550     os << mClaimRate;
551     os << mWakefulness;
552     os << mHunger;
553 
554     os << mGroundSpeed;
555     os << mWaterSpeed;
556     os << mLavaSpeed;
557 
558     os << mPhysicalDefense;
559     os << mMagicalDefense;
560     os << mElementDefense;
561     os << mOverlayHealthValue;
562 
563     // Only allied players should see creature mood (except some states)
564     uint32_t moodValue = 0;
565     if(seat->isAlliedSeat(getSeat()))
566         moodValue = mOverlayMoodValue;
567     else if(mSeatPrison != nullptr)
568     {
569         if(mSeatPrison->isAlliedSeat(seat))
570             moodValue = mOverlayMoodValue & CreatureMoodEnum::MoodPrisonFiltersPrisonAllies;
571         else
572             moodValue = mOverlayMoodValue & CreatureMoodEnum::MoodPrisonFiltersAllPlayers;
573     }
574 
575     os << moodValue;
576     os << mSpeedModifier;
577 
578     if(mWeaponL != nullptr)
579         os << mWeaponL->getName();
580     else
581         os << "none";
582 
583     if(mWeaponR != nullptr)
584         os << mWeaponR->getName();
585     else
586         os << "none";
587 }
588 
importFromPacket(ODPacket & is)589 void Creature::importFromPacket(ODPacket& is)
590 {
591     MovableGameEntity::importFromPacket(is);
592     std::string tempString;
593 
594     OD_ASSERT_TRUE(is >> mDefinitionString);
595 
596     OD_ASSERT_TRUE(is >> mLevel);
597     OD_ASSERT_TRUE(is >> mExp);
598 
599     OD_ASSERT_TRUE(is >> mHp);
600     OD_ASSERT_TRUE(is >> mMaxHP);
601 
602     OD_ASSERT_TRUE(is >> mDigRate);
603     OD_ASSERT_TRUE(is >> mClaimRate);
604     OD_ASSERT_TRUE(is >> mWakefulness);
605     OD_ASSERT_TRUE(is >> mHunger);
606 
607     OD_ASSERT_TRUE(is >> mGroundSpeed);
608     OD_ASSERT_TRUE(is >> mWaterSpeed);
609     OD_ASSERT_TRUE(is >> mLavaSpeed);
610 
611     OD_ASSERT_TRUE(is >> mPhysicalDefense);
612     OD_ASSERT_TRUE(is >> mMagicalDefense);
613     OD_ASSERT_TRUE(is >> mElementDefense);
614 
615     OD_ASSERT_TRUE(is >> mOverlayHealthValue);
616     OD_ASSERT_TRUE(is >> mOverlayMoodValue);
617     OD_ASSERT_TRUE(is >> mSpeedModifier);
618 
619     OD_ASSERT_TRUE(is >> tempString);
620     if(tempString != "none")
621     {
622         mWeaponL = getGameMap()->getWeapon(tempString);
623         if(mWeaponL == nullptr)
624         {
625             OD_LOG_ERR("Unknown weapon name=" + tempString);
626         }
627     }
628 
629     OD_ASSERT_TRUE(is >> tempString);
630     if(tempString != "none")
631     {
632         mWeaponR = getGameMap()->getWeapon(tempString);
633         if(mWeaponR == nullptr)
634         {
635             OD_LOG_ERR("Unknown weapon name=" + tempString);
636         }
637     }
638 
639     setupDefinition(*getGameMap(), *ConfigManager::getSingleton().getCreatureDefinitionDefaultWorker());
640 }
641 
setPosition(const Ogre::Vector3 & v)642 void Creature::setPosition(const Ogre::Vector3& v)
643 {
644     MovableGameEntity::setPosition(v);
645     if(mCarriedEntity != nullptr)
646         mCarriedEntity->notifyCarryMove(v);
647 }
648 
setHP(double nHP)649 void Creature::setHP(double nHP)
650 {
651     if (nHP > mMaxHP)
652         mHp = mMaxHP;
653     else
654         mHp = nHP;
655 
656     computeCreatureOverlayHealthValue();
657 }
658 
heal(double hp)659 void Creature::heal(double hp)
660 {
661     mHp = std::min(mHp + hp, mMaxHP);
662 
663     computeCreatureOverlayHealthValue();
664 }
665 
isAlive() const666 bool Creature::isAlive() const
667 {
668     if(!getIsOnServerMap())
669         return mOverlayHealthValue < (NB_OVERLAY_HEALTH_VALUES - 1);
670 
671     return mHp > 0.0;
672 }
673 
update(Ogre::Real timeSinceLastFrame)674 void Creature::update(Ogre::Real timeSinceLastFrame)
675 {
676     Tile* previousPositionTile = getPositionTile();
677     // Update movements, direction, ...
678     MovableGameEntity::update(timeSinceLastFrame);
679 
680     // Update the visual debugging entities
681     //if we are standing in a different tile than we were last turn
682     if (mHasVisualDebuggingEntities &&
683         getIsOnServerMap() &&
684         (getPositionTile() != previousPositionTile))
685     {
686         computeVisualDebugEntities();
687     }
688 
689     if(getOverlayStatus() != nullptr)
690     {
691         getOverlayStatus()->update(timeSinceLastFrame);
692     }
693 }
694 
computeVisibleTiles()695 void Creature::computeVisibleTiles()
696 {
697     // dead Creatures do not give vision
698     if (getHP() <= 0.0)
699         return;
700 
701     // KO Creatures do not give vision
702     if (isKo())
703         return;
704 
705     // creatures in jail do not give vision
706     if (mSeatPrison != nullptr)
707         return;
708 
709     if (!getIsOnMap())
710         return;
711 
712     // Look at the surrounding area
713     updateTilesInSight();
714     for(Tile* tile : mVisibleTiles)
715         tile->notifyVision(getSeat());
716 }
717 
setLevel(unsigned int level)718 void Creature::setLevel(unsigned int level)
719 {
720     // Reset XP once the level has been acquired.
721     mLevel = std::min(MAX_LEVEL, level);
722     mExp = 0.0;
723 
724     buildStats();
725 
726     mNeedFireRefresh = true;
727 }
728 
dropCarriedEquipment()729 void Creature::dropCarriedEquipment()
730 {
731     fireCreatureSound(CreatureSound::Die);
732     clearActionQueue();
733     clearDestinations(EntityAnimation::die_anim, false, false);
734 
735     // We drop what we are carrying
736     Tile* myTile = getPositionTile();
737     if(myTile == nullptr)
738     {
739         OD_LOG_ERR("name=" + getName() + ", position=" + Helper::toString(getPosition()));
740         return;
741     }
742 
743     if(mGoldCarried > 0)
744     {
745         TreasuryObject* obj = new TreasuryObject(getGameMap(), mGoldCarried);
746         obj->addToGameMap();
747         Ogre::Vector3 spawnPosition(static_cast<Ogre::Real>(myTile->getX()),
748                                     static_cast<Ogre::Real>(myTile->getY()), 0.0f);
749         obj->createMesh();
750         obj->setPosition(spawnPosition);
751         mGoldCarried = 0;
752     }
753 
754     if(mSkillTypeDropDeath != SkillType::nullSkillType)
755     {
756         GiftBoxSkill* skillEntity = new GiftBoxSkill(getGameMap(),
757             "DroppedBy" + getName(), mSkillTypeDropDeath);
758         skillEntity->addToGameMap();
759         Ogre::Vector3 spawnPosition(static_cast<Ogre::Real>(myTile->getX()),
760                                     static_cast<Ogre::Real>(myTile->getY()), 0.0f);
761         skillEntity->createMesh();
762         skillEntity->setPosition(spawnPosition);
763         mSkillTypeDropDeath = SkillType::nullSkillType;
764     }
765 
766     // TODO: drop weapon when available
767 }
768 
doUpkeep()769 void Creature::doUpkeep()
770 {
771     // If the creature is in jail, we check if it is still standing on it (if not picked up). If
772     // not, it is free
773     if((mSeatPrison != nullptr) &&
774        getIsOnMap())
775     {
776         Tile* myTile = getPositionTile();
777         if(myTile == nullptr)
778         {
779             OD_LOG_ERR("name=" + getName() + ", position=" + Helper::toString(getPosition()));
780             return;
781         }
782 
783         // We check if the creature is in containment
784         Room* roomPrison = myTile->getCoveringRoom();
785         if((roomPrison == nullptr) ||
786            (!roomPrison->isInContainment(*this)))
787         {
788             // it is not standing on a jail. It is free
789             mSeatPrison = nullptr;
790             mNeedFireRefresh = true;
791         }
792     }
793 
794     // The creature may be killed while temporary KO
795     if((mKoTurnCounter != 0) && !isAlive())
796         mKoTurnCounter = 0;
797 
798     // If the creature is KO to death or dead, we remove its particle effects
799     if(!mEntityParticleEffects.empty() &&
800        ((mKoTurnCounter < 0) || !isAlive()))
801     {
802         for(EntityParticleEffect* effect : mEntityParticleEffects)
803         {
804             delete effect;
805         }
806         mEntityParticleEffects.clear();
807     }
808 
809     // We apply creature effects if any
810     for(auto it = mEntityParticleEffects.begin(); it != mEntityParticleEffects.end();)
811     {
812         CreatureParticuleEffect* effect = static_cast<CreatureParticuleEffect*>(*it);
813         if(effect->mEffect->upkeepEffect(*this))
814         {
815             ++it;
816             continue;
817         }
818 
819         delete effect;
820         it = mEntityParticleEffects.erase(it);
821     }
822 
823     // if creature is not on map (picked up or being carried), we do nothing
824     if(!getIsOnMap())
825         return;
826 
827     // If the creature is temporary KO, it should do nothing
828     if(mKoTurnCounter > 0)
829     {
830         --mKoTurnCounter;
831         if(mKoTurnCounter > 0)
832             return;
833 
834         computeCreatureOverlayMoodValue();
835         return;
836     }
837 
838     if(mKoTurnCounter < 0)
839     {
840         // If the counter reaches 0, the creature is dead
841         ++mKoTurnCounter;
842         if(mKoTurnCounter < 0)
843             return;
844 
845         mHp = 0;
846         computeCreatureOverlayHealthValue();
847         computeCreatureOverlayMoodValue();
848     }
849 
850     // Handle creature death
851     if (!isAlive())
852     {
853         // Let the creature lay dead on the ground for a few turns before removing it from the GameMap.
854         if (mDeathCounter == 0)
855         {
856             OD_LOG_INF("Creature=" + getName() + " RIP");
857 
858             dropCarriedEquipment();
859         }
860         else if (mDeathCounter >= ConfigManager::getSingleton().getCreatureDeathCounter())
861         {
862             // Remove the creature from the game map and into the deletion queue, it will be deleted
863             // when it is safe, i.e. all other pointers to it have been wiped from the program.
864             removeFromGameMap();
865             deleteYourself();
866         }
867 
868         ++mDeathCounter;
869         return;
870     }
871 
872     // If the creature is in jail, it should not auto heal or do anything
873     if(mSeatPrison != nullptr)
874     {
875         Tile* myTile = getPositionTile();
876         if(myTile == nullptr)
877         {
878             OD_LOG_ERR("name=" + getName() + ", position=" + Helper::toString(getPosition()));
879             return;
880         }
881 
882         // If the creature is in a containment room, it should use it
883         Room* roomPrison = myTile->getCoveringRoom();
884         if((roomPrison != nullptr) && (decreaseJobCooldown()))
885             roomPrison->useRoom(*this, true);
886 
887         return;
888     }
889 
890     // If we are not standing somewhere on the map, do nothing.
891     if (getPositionTile() == nullptr)
892     {
893         OD_LOG_ERR("creature=" + getName() + " not on map position=" + Helper::toString(getPosition()));
894         return;
895     }
896 
897     // Check to see if we have earned enough experience to level up.
898     checkLevelUp();
899 
900 
901     // Heal.
902     mHp += mDefinition->getHpHealPerTurn();
903     if (mHp > getMaxHp())
904         mHp = getMaxHp();
905 
906     computeCreatureOverlayHealthValue();
907 
908     // Rogue creatures are not affected by wakefulness/hunger
909     if(!getSeat()->isRogueSeat())
910     {
911         decreaseWakefulness(mDefinition->getWakefulnessLostPerTurn());
912 
913         increaseHunger(mDefinition->getHungerGrowthPerTurn());
914     }
915 
916     mVisibleEnemyObjects         = getVisibleEnemyObjects();
917     mVisibleAlliedObjects        = getVisibleAlliedObjects();
918     mReachableAlliedObjects      = getReachableAttackableObjects(mVisibleAlliedObjects);
919 
920     // Check if we should compute mood
921     if(mMoodCooldownTurns > 0)
922     {
923         --mMoodCooldownTurns;
924     }
925     // Rogue creatures do not have mood
926     else if(!getSeat()->isRogueSeat())
927     {
928         computeMood();
929         computeCreatureOverlayMoodValue();
930         mMoodCooldownTurns = Random::Int(0, 5);
931     }
932 
933     if(mMoodValue < CreatureMoodLevel::Furious)
934     {
935         mNbTurnFurious = -1;
936     }
937     else
938     {
939         // If the creature is furious for too long, it will become rogue
940         if(mNbTurnFurious < 0)
941             mNbTurnFurious = 0;
942         else
943             ++mNbTurnFurious;
944 
945         if(mNbTurnFurious >= ConfigManager::getSingleton().getNbTurnsFuriousMax())
946         {
947             // We couldn't leave the dungeon in time, we become rogue
948             fireChatMsgBecameRogue();
949 
950             Seat* rogueSeat = getGameMap()->getSeatRogue();
951             changeSeat(rogueSeat);
952         }
953     }
954 
955     ++mNbTurnsWithoutBattle;
956 
957     bool isWarmUp = false;
958     // We use creature skills if we can
959     for(CreatureSkillData& skillData : mSkillData)
960     {
961         if(skillData.mWarmup > 0)
962         {
963             --skillData.mWarmup;
964             isWarmUp = true;
965         }
966 
967         if(skillData.mCooldown > 0)
968         {
969             --skillData.mCooldown;
970             continue;
971         }
972 
973         if(!skillData.mSkill->canBeUsedBy(this))
974             continue;
975 
976         if(!skillData.mSkill->tryUseSupport(*getGameMap(), this))
977             continue;
978 
979         skillData.mCooldown = skillData.mSkill->getCooldownNbTurns();
980         skillData.mWarmup = skillData.mSkill->getWarmupNbTurns();
981 
982         if(skillData.mWarmup > 0)
983             isWarmUp = true;
984     }
985 
986     // If a warmup is active, we do nothing
987     if(isWarmUp)
988         return;
989 
990     decidePrioritaryAction();
991 
992     // The loopback variable allows creatures to begin processing a new
993     // action immediately after some other action happens.
994     bool loopBack = false;
995     unsigned int loops = 0;
996 
997     mActionTry.clear();
998 
999     do
1000     {
1001         ++loops;
1002         loopBack = false;
1003 
1004         if (mActions.empty())
1005             loopBack = handleIdleAction();
1006         else
1007         {
1008             std::function<bool()> func = mActions.back().get()->action();
1009             loopBack = func();
1010         }
1011     } while (loopBack && loops < 20);
1012 
1013     if(!mActions.empty())
1014         mActions.back().get()->increaseNbTurnActive();
1015 
1016     for(std::unique_ptr<CreatureAction>& creatureAction : mActions)
1017         creatureAction.get()->increaseNbTurn();
1018 
1019     if(loops >= 20)
1020     {
1021         OD_LOG_INF("> 20 loops in Creature::doUpkeep name:" + getName() +
1022                 " seat id: " + Helper::toString(getSeat()->getId()) + ". Breaking out..");
1023     }
1024 }
1025 
decidePrioritaryAction()1026 void Creature::decidePrioritaryAction()
1027 {
1028     for(const CreatureBehaviour* behaviour : getDefinition()->getCreatureBehaviours())
1029     {
1030         if(!behaviour->processBehaviour(*this))
1031             return;
1032     }
1033 }
1034 
handleIdleAction()1035 bool Creature::handleIdleAction()
1036 {
1037     setAnimationState(EntityAnimation::idle_anim);
1038 
1039     if (mDefinition->isWorker())
1040     {
1041         // Decide what to do
1042         std::vector<CreatureActionType> workerActions = getSeat()->getPlayer()->getWorkerPreferredActions(*this);
1043         for(CreatureActionType actionType : workerActions)
1044         {
1045             if(hasActionBeenTried(actionType))
1046                 continue;
1047 
1048             switch(actionType)
1049             {
1050                 case CreatureActionType::searchEntityToCarry:
1051                     pushAction(Utils::make_unique<CreatureActionSearchEntityToCarry>(*this, false));
1052                     return true;
1053                 case CreatureActionType::searchGroundTileToClaim:
1054                     pushAction(Utils::make_unique<CreatureActionSearchGroundTileToClaim>(*this, false));
1055                     return true;
1056                 case CreatureActionType::searchTileToDig:
1057                     pushAction(Utils::make_unique<CreatureActionSearchTileToDig>(*this, false));
1058                     return true;
1059                 case CreatureActionType::searchWallTileToClaim:
1060                     pushAction(Utils::make_unique<CreatureActionSearchWallTileToClaim>(*this, false));
1061                     return true;
1062                 default:
1063                     OD_LOG_ERR("name=" + getName() + ", unexpected worker action=" + CreatureAction::toString(actionType));
1064                     break;
1065             }
1066         }
1067     }
1068 
1069     // We check if we are looking for our fee
1070     if(!mDefinition->isWorker() &&
1071        !hasActionBeenTried(CreatureActionType::getFee) &&
1072        (mGoldFee > 0))
1073     {
1074         pushAction(Utils::make_unique<CreatureActionGetFee>(*this));
1075         return true;
1076     }
1077 
1078     // We check if there is a go to war spell reachable
1079     std::vector<Spell*> callToWars = getGameMap()->getSpellsBySeatAndType(getSeat(), SpellType::callToWar);
1080     if(!callToWars.empty())
1081     {
1082         std::vector<Spell*> reachableCallToWars;
1083         for(Spell* callToWar : callToWars)
1084         {
1085             if(!callToWar->getIsOnMap())
1086                 continue;
1087 
1088             Tile* callToWarTile = callToWar->getPositionTile();
1089             if(callToWarTile == nullptr)
1090                 continue;
1091 
1092             if (!getGameMap()->pathExists(this, getPositionTile(), callToWarTile))
1093                 continue;
1094 
1095             reachableCallToWars.push_back(callToWar);
1096         }
1097 
1098         if(!reachableCallToWars.empty())
1099         {
1100             // We go there
1101             uint32_t index = Random::Uint(0,reachableCallToWars.size()-1);
1102             Spell* callToWar = reachableCallToWars[index];
1103             Tile* callToWarTile = callToWar->getPositionTile();
1104             std::list<Tile*> tempPath = getGameMap()->path(this, callToWarTile);
1105             // If we are 5 tiles from the call to war, we don't go there
1106             if(tempPath.size() >= 5)
1107             {
1108                 std::vector<Ogre::Vector3> path;
1109                 tileToVector3(tempPath, path, true, 0.0);
1110                 setWalkPath(EntityAnimation::walk_anim, EntityAnimation::idle_anim, true, true, path);
1111                 pushAction(Utils::make_unique<CreatureActionWalkToTile>(*this));
1112                 return false;
1113             }
1114         }
1115     }
1116 
1117     // Check to see if we have found a "home" tile where we can sleep. Even if we are not sleepy,
1118     // we want to have a bed
1119     if (!mDefinition->isWorker() &&
1120         !hasActionBeenTried(CreatureActionType::findHome) &&
1121         (mHomeTile == nullptr) &&
1122         (Random::Double(0.0, 1.0) < 0.5))
1123     {
1124         pushAction(Utils::make_unique<CreatureActionFindHome>(*this, false));
1125         return true;
1126     }
1127 
1128     // If we are sleepy, we go to sleep
1129     if (!mDefinition->isWorker() &&
1130         !hasActionBeenTried(CreatureActionType::sleep) &&
1131         (mHomeTile != nullptr) &&
1132         (Random::Double(20.0, 30.0) > mWakefulness))
1133     {
1134         pushAction(Utils::make_unique<CreatureActionSleep>(*this));
1135         return true;
1136     }
1137 
1138     // If we are hungry, we go to eat
1139     if (!mDefinition->isWorker() &&
1140         !hasActionBeenTried(CreatureActionType::searchFood) &&
1141         (Random::Double(70.0, 80.0) < mHunger))
1142     {
1143         pushAction(Utils::make_unique<CreatureActionSearchFood>(*this, false));
1144         return true;
1145     }
1146 
1147     // We try to steal some gold if there is some on the ground
1148     // Later, we might want to add a creature definition parameter to make some
1149     // creatures more likely to steal gold than others
1150     if (!mDefinition->isWorker() &&
1151         !hasActionBeenTried(CreatureActionType::stealFreeGold) &&
1152         (Random::Uint(0, 10) > 8))
1153     {
1154         pushAction(Utils::make_unique<CreatureActionStealFreeGold>(*this));
1155         return true;
1156     }
1157 
1158     // Otherwise, we try to work
1159     if (!mDefinition->isWorker() &&
1160         !hasActionBeenTried(CreatureActionType::searchJob) &&
1161         (Random::Double(0.0, 1.0) < 0.4))
1162     {
1163         pushAction(Utils::make_unique<CreatureActionSearchJob>(*this, false));
1164         return true;
1165     }
1166 
1167     // Any creature.
1168 
1169     // Workers should move around randomly at large jumps.  Non-workers either wander short distances or follow workers.
1170     Tile* tileDest = nullptr;
1171     // Define reachable tiles from the tiles within radius
1172     std::vector<Tile*> reachableTiles;
1173     for (Tile* tile: mTilesWithinSightRadius)
1174     {
1175         if (getGameMap()->pathExists(this, getPositionTile(), tile))
1176             reachableTiles.push_back(tile);
1177     }
1178 
1179     if (!mDefinition->isWorker())
1180     {
1181         // Non-workers only.
1182 
1183         // Check to see if we want to try to follow a worker around or if we want to try to explore.
1184         double r = Random::Double(0.0, 1.0);
1185         if (r < 0.7)
1186         {
1187             bool workerFound = false;
1188             // Try to find a worker to follow around.
1189             for (unsigned int i = 0; !workerFound && i < mReachableAlliedObjects.size(); ++i)
1190             {
1191                 // Check to see if we found a worker.
1192                 if (mReachableAlliedObjects[i]->getObjectType() == GameEntityType::creature
1193                     && static_cast<Creature*>(mReachableAlliedObjects[i])->mDefinition->isWorker())
1194                 {
1195                     // We found a worker so find a tile near the worker to walk to.  See if the worker is digging.
1196                     Tile* tempTile = mReachableAlliedObjects[i]->getCoveredTile(0);
1197                     if (static_cast<Creature*>(mReachableAlliedObjects[i])->isActionInList(CreatureActionType::digTile))
1198                     {
1199                         // Worker is digging, get near it since it could expose enemies.
1200                         int x = static_cast<int>(static_cast<double>(tempTile->getX()) + 3.0
1201                                 * Random::gaussianRandomDouble());
1202                         int y = static_cast<int>(static_cast<double>(tempTile->getY()) + 3.0
1203                                 * Random::gaussianRandomDouble());
1204                         tileDest = getGameMap()->getTile(x, y);
1205                     }
1206                     else
1207                     {
1208                         // Worker is not digging, wander a bit farther around the worker.
1209                         int x = static_cast<int>(static_cast<double>(tempTile->getX()) + 8.0
1210                                 * Random::gaussianRandomDouble());
1211                         int y = static_cast<int>(static_cast<double>(tempTile->getY()) + 8.0
1212                                 * Random::gaussianRandomDouble());
1213                         tileDest = getGameMap()->getTile(x, y);
1214                     }
1215                     workerFound = true;
1216                 }
1217 
1218                 // If there are no workers around, choose tiles far away to "roam" the dungeon.
1219                 if (!workerFound)
1220                 {
1221                     if (!reachableTiles.empty())
1222                     {
1223                         tileDest = reachableTiles[static_cast<unsigned int>(Random::Double(0.6, 0.8)
1224                                                                            * (reachableTiles.size() - 1))];
1225                     }
1226                 }
1227             }
1228         }
1229         else
1230         {
1231             // Randomly choose a tile near where we are standing to walk to.
1232             if (!reachableTiles.empty())
1233             {
1234                 unsigned int tileIndex = static_cast<unsigned int>(reachableTiles.size()
1235                                                                    * Random::Double(0.1, 0.3));
1236                 tileDest = reachableTiles[tileIndex];
1237             }
1238         }
1239     }
1240     else
1241     {
1242         // Workers only.
1243 
1244         // Choose a tile far away from our current position to wander to.
1245         if (!reachableTiles.empty())
1246         {
1247             tileDest = reachableTiles[Random::Uint(reachableTiles.size() / 2,
1248                                                    reachableTiles.size() - 1)];
1249         }
1250     }
1251 
1252     if(setDestination(tileDest))
1253         return false;
1254 
1255     return true;
1256 }
1257 
searchBestTargetInList(const std::vector<GameEntity * > & listObjects,const std::vector<Tile * > & tilesFilter,GameEntity * & attackedEntity,Tile * & attackedTile,Tile * & positionTile,CreatureSkillData * & creatureSkillData)1258 bool Creature::searchBestTargetInList(const std::vector<GameEntity*>& listObjects, const std::vector<Tile*>& tilesFilter, GameEntity*& attackedEntity,
1259         Tile*& attackedTile, Tile*& positionTile, CreatureSkillData*& creatureSkillData)
1260 {
1261     Tile* myTile = getPositionTile();
1262     if(myTile == nullptr)
1263     {
1264         OD_LOG_ERR("name=" + getName() + ", position=" + Helper::toString(getPosition()));
1265         return false;
1266     }
1267 
1268     GameEntity* entityFlee = nullptr;
1269     // Closest creature
1270     GameEntity* entityAttack = nullptr;
1271     Tile* tileAttack = nullptr;
1272     CreatureSkillData* skillData = nullptr;
1273     Tile* tilePosition = nullptr;
1274     int closestDist = -1;
1275     // We try to attack creatures first
1276     for(GameEntity* entity : listObjects)
1277     {
1278         GameEntity* entityAttackCheck = nullptr;
1279         Tile* tileAttackCheck = nullptr;
1280         CreatureSkillData* skillDataCheck = nullptr;
1281         int closestDistCheck = closestDist;
1282         // We check if this creature is closer than the other one (if any)
1283         std::vector<Tile*> coveredTiles = entity->getCoveredTiles();
1284         for(Tile* tile : coveredTiles)
1285         {
1286             if(std::find(mVisibleTiles.begin(), mVisibleTiles.end(), tile) == mVisibleTiles.end())
1287                 continue;
1288 
1289             int dist = Pathfinding::squaredDistanceTile(*tile, *myTile);
1290             if((closestDistCheck != -1) && (dist >= closestDistCheck))
1291                 continue;
1292 
1293             // We found a tile closer
1294             // Note that we don't break because if this entity is on more than 1 tile,
1295             // we want to attack the closest tile
1296             closestDistCheck = dist;
1297             entityAttackCheck = entity;
1298             tileAttackCheck = tile;
1299         }
1300 
1301         if((entityAttackCheck == nullptr) || (tileAttackCheck == nullptr))
1302             continue;
1303 
1304         // We check if we are supposed to flee from this entity
1305         if((entityFlee == nullptr) && entityAttackCheck->isDangerous(this, closestDistCheck))
1306             entityFlee = entityAttackCheck;
1307 
1308         // If we found a suitable enemy, we check if we can attack it
1309         double skillRangeMax = 0.0;
1310         for(CreatureSkillData& skillDataTmp : mSkillData)
1311         {
1312             if(skillDataTmp.mCooldown > 0)
1313                 continue;
1314 
1315             if(!skillDataTmp.mSkill->canBeUsedBy(this))
1316                 continue;
1317 
1318             double skillRange = skillDataTmp.mSkill->getRangeMax(this, entityAttackCheck);
1319             if(skillRange <= 0)
1320                 continue;
1321             if(skillRange < skillRangeMax)
1322                 continue;
1323 
1324             skillRangeMax = skillRange;
1325             skillDataCheck = &skillDataTmp;
1326         }
1327         if(skillRangeMax <= 0)
1328             continue;
1329 
1330         // Check if we can attack from our position
1331         int rangeTarget = Pathfinding::squaredDistanceTile(*tileAttackCheck, *myTile);
1332         if(rangeTarget <= (skillRangeMax * skillRangeMax))
1333         {
1334              // We can attack
1335              if((closestDist == -1) || (rangeTarget < closestDist))
1336              {
1337                 tilePosition = myTile;
1338                 entityAttack = entityAttackCheck;
1339                 tileAttack = tileAttackCheck;
1340                 skillData = skillDataCheck;
1341                 closestDist = rangeTarget;
1342              }
1343              continue;
1344         }
1345 
1346         // We check if we can attack from somewhere. To do that, we check
1347         // from the target point of view if there is a tile with visibility within range
1348         int skillRangeMaxInt = static_cast<int>(skillRangeMax);
1349         int skillRangeMaxIntSquared = skillRangeMaxInt * skillRangeMaxInt;
1350         int bestScoreAttack = -1;
1351         std::vector<Tile*> tiles;
1352         if(tilesFilter.empty())
1353             tiles = getGameMap()->visibleTiles(tileAttackCheck->getX(), tileAttackCheck->getY(), skillRangeMaxInt);
1354         else
1355         {
1356             float radiusSquared = skillRangeMaxInt * skillRangeMaxInt;
1357             for(Tile* tile : tilesFilter)
1358             {
1359                 float dist = Pathfinding::squaredDistanceTile(*tileAttackCheck, *tile);
1360                 if(dist > radiusSquared)
1361                     continue;
1362 
1363                 tiles.push_back(tile);
1364             }
1365         }
1366         for(Tile* tile : tiles)
1367         {
1368             if(tile->isFullTile())
1369                 continue;
1370 
1371             if(!getGameMap()->pathExists(this, myTile, tile))
1372                 continue;
1373 
1374             int distFoeTmp = Pathfinding::squaredDistanceTile(*tile, *tileAttackCheck);
1375             int distAttackTmp = Pathfinding::squaredDistanceTile(*tile, *myTile);
1376             // We compute a score for each tile. We will choose the best one. Note that we try to be as close as possible
1377             // from the fightIdleDist but by walking the less possible. We need to find a compromise
1378             int scoreAttack = std::abs(skillRangeMaxIntSquared - distFoeTmp) * 2 + distAttackTmp;
1379             if((bestScoreAttack != -1) && (bestScoreAttack <= scoreAttack))
1380                 continue;
1381 
1382             // We found a better target
1383             bestScoreAttack = scoreAttack;
1384             tilePosition = tile;
1385             entityAttack = entityAttackCheck;
1386             tileAttack = tileAttackCheck;
1387             skillData = skillDataCheck;
1388             closestDist = closestDistCheck;
1389             // We don't break because there might be a better spot
1390         }
1391     }
1392 
1393     // If there is a dangerous entity and we cannot attack, we should try to get away
1394     if((entityFlee != nullptr) && (skillData == nullptr))
1395     {
1396         // Let's try to run to the closest spot at the distance closest to the fight idle distance
1397         Tile* tileEntityFlee = entityFlee->getPositionTile();
1398         if(tileEntityFlee == nullptr)
1399         {
1400             OD_LOG_ERR("entity=" + entityFlee->getName() + ", position=" + Helper::toString(entityFlee->getPosition()));
1401             return false;
1402         }
1403 
1404         int bestScoreFlee = -1;
1405         int32_t fightIdleDist = getDefinition()->getFightIdleDist();
1406         Tile* fleeTile = nullptr;
1407         std::vector<Tile*> tiles;
1408         if(tilesFilter.empty())
1409             tiles = getGameMap()->visibleTiles(tileEntityFlee->getX(), tileEntityFlee->getY(), fightIdleDist);
1410         else
1411         {
1412             float radiusSquared = fightIdleDist * fightIdleDist;
1413             for(Tile* tile : tilesFilter)
1414             {
1415                 float dist = Pathfinding::squaredDistanceTile(*tileEntityFlee, *tile);
1416                 if(dist > radiusSquared)
1417                     continue;
1418 
1419                 tiles.push_back(tile);
1420             }
1421         }
1422         int32_t fightIdleDistSquared = fightIdleDist * fightIdleDist;
1423         for(Tile* tile : tiles)
1424         {
1425             if(tile->isFullTile())
1426                 continue;
1427 
1428             if(!getGameMap()->pathExists(this, myTile, tile))
1429                 continue;
1430 
1431             int distFoeTmp = Pathfinding::squaredDistanceTile(*tile, *tileEntityFlee);
1432             int fleeDistTmp = Pathfinding::squaredDistanceTile(*tile, *myTile);
1433             // We compute a score for each tile. We will choose the best one. Note that we try to be as close as possible
1434             // from the fightIdleDist but by walking the less possible. We need to find a compromise
1435             int scoreFlee = std::abs(fightIdleDistSquared - distFoeTmp) * 2 + fleeDistTmp;
1436             if((bestScoreFlee != -1) && (bestScoreFlee <= scoreFlee))
1437                 continue;
1438 
1439             bestScoreFlee = scoreFlee;
1440             fleeTile = tile;
1441         }
1442 
1443         if(fleeTile == nullptr)
1444             return false;
1445 
1446         attackedEntity = nullptr;
1447         attackedTile = nullptr;
1448         positionTile = fleeTile;
1449     }
1450     else if ((entityAttack == nullptr) ||
1451         (tileAttack == nullptr) ||
1452         (tilePosition == nullptr))
1453     {
1454         // We couldn't find an entity to attack
1455         return false;
1456     }
1457     else
1458     {
1459         attackedEntity = entityAttack;
1460         attackedTile = tileAttack;
1461         creatureSkillData = skillData;
1462         positionTile = tilePosition;
1463     }
1464 
1465     return true;
1466 }
1467 
engageAlliedNaturalEnemy(Creature & attackerCreature)1468 void Creature::engageAlliedNaturalEnemy(Creature& attackerCreature)
1469 {
1470     Tile* myTile = getPositionTile();
1471     if(myTile == nullptr)
1472     {
1473         OD_LOG_ERR("name=" + getName() + ", pos=" + Helper::toString(getPosition()));
1474         return;
1475     }
1476 
1477     // If we are already fighting, do nothing
1478     if(isActionInList(CreatureActionType::fight))
1479         return;
1480 
1481     // When fighting a natural enemy, we always fight to death
1482     // We want to notify the player that his creatures are fighting
1483     fightCreature(attackerCreature, false, true);
1484 }
1485 
getMoveSpeed() const1486 double Creature::getMoveSpeed() const
1487 {
1488     return getMoveSpeed(getPositionTile());
1489 }
1490 
getMoveSpeed(Tile * tile) const1491 double Creature::getMoveSpeed(Tile* tile) const
1492 {
1493     if(tile == nullptr)
1494     {
1495         OD_LOG_ERR("creature=" + getName());
1496         return 1.0;
1497     }
1498 
1499     if(getIsOnServerMap())
1500     {
1501         // Check if the covering building allows this creature to go through
1502         if(tile->getCoveringBuilding() != nullptr)
1503             return tile->getCoveringBuilding()->getCreatureSpeed(this, tile);
1504         else
1505             return tile->getCreatureSpeedDefault(this);
1506     }
1507     else
1508     {
1509         if(tile->getHasBridge())
1510             return getMoveSpeedGround();
1511         else
1512             return tile->getCreatureSpeedDefault(this);
1513     }
1514 }
1515 
getPhysicalDefense() const1516 double Creature::getPhysicalDefense() const
1517 {
1518     double defense = mPhysicalDefense;
1519     if (mWeaponL != nullptr)
1520         defense += mWeaponL->getPhysicalDefense();
1521     if (mWeaponR != nullptr)
1522         defense += mWeaponR->getPhysicalDefense();
1523 
1524     return defense;
1525 }
1526 
getMagicalDefense() const1527 double Creature::getMagicalDefense() const
1528 {
1529     double defense = mMagicalDefense;
1530     if (mWeaponL != nullptr)
1531         defense += mWeaponL->getMagicalDefense();
1532     if (mWeaponR != nullptr)
1533         defense += mWeaponR->getMagicalDefense();
1534 
1535     return defense;
1536 }
1537 
getElementDefense() const1538 double Creature::getElementDefense() const
1539 {
1540     double defense = mElementDefense;
1541     if (mWeaponL != nullptr)
1542         defense += mWeaponL->getElementDefense();
1543     if (mWeaponR != nullptr)
1544         defense += mWeaponR->getElementDefense();
1545 
1546     return defense;
1547 }
1548 
checkLevelUp()1549 void Creature::checkLevelUp()
1550 {
1551     if (getLevel() >= MAX_LEVEL)
1552         return;
1553 
1554     // Check the returned value.
1555     double newXP = mDefinition->getXPNeededWhenLevel(getLevel());
1556 
1557     // An error occurred
1558     if (newXP <= 0.0)
1559     {
1560         OD_LOG_ERR("creature=" + getName() + ", newXP=" + Helper::toString(newXP));
1561         return;
1562     }
1563 
1564     if (mExp < newXP)
1565         return;
1566 
1567     setLevel(mLevel + 1);
1568 }
1569 
exportToPacketForUpdate(ODPacket & os,const Seat * seat) const1570 void Creature::exportToPacketForUpdate(ODPacket& os, const Seat* seat) const
1571 {
1572     MovableGameEntity::exportToPacketForUpdate(os, seat);
1573 
1574     int seatId = getSeat()->getId();
1575     os << mLevel;
1576     os << seatId;
1577     os << mOverlayHealthValue;
1578 
1579     // Only allied players should see creature mood (except some states)
1580     uint32_t moodValue = 0;
1581     if(seat->isAlliedSeat(getSeat()))
1582         moodValue = mOverlayMoodValue;
1583     else if(mSeatPrison != nullptr)
1584     {
1585         if(mSeatPrison->isAlliedSeat(seat))
1586             moodValue = mOverlayMoodValue & CreatureMoodEnum::MoodPrisonFiltersPrisonAllies;
1587         else
1588             moodValue = mOverlayMoodValue & CreatureMoodEnum::MoodPrisonFiltersAllPlayers;
1589     }
1590 
1591     os << moodValue;
1592     os << mGroundSpeed;
1593     os << mWaterSpeed;
1594     os << mLavaSpeed;
1595     os << mSpeedModifier;
1596 
1597     int seatPrisonId = -1;
1598     if(mSeatPrison != nullptr)
1599         seatPrisonId = mSeatPrison->getId();
1600 
1601     os << seatPrisonId;
1602 }
1603 
updateFromPacket(ODPacket & is)1604 void Creature::updateFromPacket(ODPacket& is)
1605 {
1606     MovableGameEntity::updateFromPacket(is);
1607 
1608     int seatId;
1609     OD_ASSERT_TRUE(is >> mLevel);
1610     OD_ASSERT_TRUE(is >> seatId);
1611     OD_ASSERT_TRUE(is >> mOverlayHealthValue);
1612     OD_ASSERT_TRUE(is >> mOverlayMoodValue);
1613     OD_ASSERT_TRUE(is >> mGroundSpeed);
1614     OD_ASSERT_TRUE(is >> mWaterSpeed);
1615     OD_ASSERT_TRUE(is >> mLavaSpeed);
1616     OD_ASSERT_TRUE(is >> mSpeedModifier);
1617 
1618     // We do not scale the creature if it is picked up (because it is already not at its normal size). It will be
1619     // resized anyway when dropped
1620     if(getIsOnMap())
1621         RenderManager::getSingleton().rrScaleCreature(*this);
1622 
1623     if(getSeat()->getId() != seatId)
1624     {
1625         Seat* seat = getGameMap()->getSeatById(seatId);
1626         if(seat == nullptr)
1627         {
1628             OD_LOG_ERR("Creature " + getName() + ", wrong seatId=" + Helper::toString(seatId));
1629         }
1630         else
1631         {
1632             setSeat(seat);
1633         }
1634     }
1635 
1636     OD_ASSERT_TRUE(is >> seatId);
1637     if(seatId == -1)
1638         mSeatPrison = nullptr;
1639     else
1640     {
1641         mSeatPrison = getGameMap()->getSeatById(seatId);
1642         if(mSeatPrison == nullptr)
1643         {
1644             OD_LOG_ERR("Creature " + getName() + ", wrong seatId=" + Helper::toString(seatId));
1645         }
1646     }
1647 }
1648 
updateTilesInSight()1649 void Creature::updateTilesInSight()
1650 {
1651     Tile* posTile = getPositionTile();
1652     if (posTile == nullptr)
1653         return;
1654 
1655     // The tiles with sight radius without constraints
1656     mTilesWithinSightRadius = getGameMap()->circularRegion(posTile->getX(), posTile->getY(), mDefinition->getSightRadius());
1657 
1658     // Only the tiles the creature can "see".
1659     mVisibleTiles = getGameMap()->visibleTiles(posTile->getX(), posTile->getY(), mDefinition->getSightRadius());
1660 }
1661 
getVisibleEnemyObjects()1662 std::vector<GameEntity*> Creature::getVisibleEnemyObjects()
1663 {
1664     return getVisibleForce(getSeat(), true);
1665 }
1666 
getReachableAttackableObjects(const std::vector<GameEntity * > & objectsToCheck)1667 std::vector<GameEntity*> Creature::getReachableAttackableObjects(const std::vector<GameEntity*>& objectsToCheck)
1668 {
1669     std::vector<GameEntity*> tempVector;
1670     Tile* myTile = getPositionTile();
1671 
1672     // Loop over the vector of objects we are supposed to check.
1673     for (unsigned int i = 0; i < objectsToCheck.size(); ++i)
1674     {
1675         // Try to find a valid path from the tile this creature is in to the nearest tile where the current target object is.
1676         GameEntity* entity = objectsToCheck[i];
1677         // We only consider alive objects
1678         if(entity->getHP(nullptr) <= 0)
1679             continue;
1680 
1681         Tile* objectTile = entity->getCoveredTile(0);
1682         if (getGameMap()->pathExists(this, myTile, objectTile))
1683             tempVector.push_back(objectsToCheck[i]);
1684     }
1685 
1686     return tempVector;
1687 }
1688 
getCreaturesFromList(const std::vector<GameEntity * > & objectsToCheck,bool workersOnly)1689 std::vector<GameEntity*> Creature::getCreaturesFromList(const std::vector<GameEntity*> &objectsToCheck, bool workersOnly)
1690 {
1691     std::vector<GameEntity*> tempVector;
1692 
1693     // Loop over the vector of objects we are supposed to check.
1694     for (std::vector<GameEntity*>::const_iterator it = objectsToCheck.begin(); it != objectsToCheck.end(); ++it)
1695     {
1696         // Try to find a valid path from the tile this creature is in to the nearest tile where the current target object is.
1697         GameEntity* entity = *it;
1698         // We only consider alive objects
1699         if(entity->getObjectType() != GameEntityType::creature)
1700             continue;
1701 
1702         if(workersOnly && !static_cast<Creature*>(entity)->getDefinition()->isWorker())
1703             continue;
1704 
1705         tempVector.push_back(entity);
1706     }
1707 
1708     return tempVector;
1709 }
1710 
getVisibleAlliedObjects()1711 std::vector<GameEntity*> Creature::getVisibleAlliedObjects()
1712 {
1713     return getVisibleForce(getSeat(), false);
1714 }
1715 
getVisibleForce(Seat * seat,bool invert)1716 std::vector<GameEntity*> Creature::getVisibleForce(Seat* seat, bool invert)
1717 {
1718     return getGameMap()->getVisibleForce(mVisibleTiles, seat, invert);
1719 }
1720 
computeVisualDebugEntities()1721 void Creature::computeVisualDebugEntities()
1722 {
1723     if(!getIsOnServerMap())
1724         return;
1725 
1726     mHasVisualDebuggingEntities = true;
1727 
1728     updateTilesInSight();
1729 
1730     ServerNotification *serverNotification = new ServerNotification(
1731         ServerNotificationType::refreshCreatureVisDebug, nullptr);
1732 
1733     const std::string& name = getName();
1734     serverNotification->mPacket << name;
1735     serverNotification->mPacket << true;
1736     if(getIsOnMap())
1737     {
1738         uint32_t nbTiles = mVisibleTiles.size();
1739         serverNotification->mPacket << nbTiles;
1740 
1741         for (Tile* tile : mVisibleTiles)
1742             getGameMap()->tileToPacket(serverNotification->mPacket, tile);
1743     }
1744     else
1745     {
1746         uint32_t nbTiles = 0;
1747         serverNotification->mPacket << nbTiles;
1748     }
1749 
1750     ODServer::getSingleton().queueServerNotification(serverNotification);
1751 }
1752 
refreshVisualDebugEntities(const std::vector<Tile * > & tiles)1753 void Creature::refreshVisualDebugEntities(const std::vector<Tile*>& tiles)
1754 {
1755     if(getIsOnServerMap())
1756         return;
1757 
1758     mHasVisualDebuggingEntities = true;
1759 
1760     for (Tile* tile : tiles)
1761     {
1762         // We check if the visual debug is already on this tile
1763         if(std::find(mVisualDebugEntityTiles.begin(), mVisualDebugEntityTiles.end(), tile) != mVisualDebugEntityTiles.end())
1764             continue;
1765 
1766         RenderManager::getSingleton().rrCreateCreatureVisualDebug(this, tile);
1767 
1768         mVisualDebugEntityTiles.push_back(tile);
1769     }
1770 
1771     // now, we check if visual debug should be removed from a tile
1772     for (std::vector<Tile*>::iterator it = mVisualDebugEntityTiles.begin(); it != mVisualDebugEntityTiles.end();)
1773     {
1774         Tile* tile = *it;
1775         if(std::find(tiles.begin(), tiles.end(), tile) != tiles.end())
1776         {
1777             ++it;
1778             continue;
1779         }
1780 
1781         it = mVisualDebugEntityTiles.erase(it);
1782 
1783         RenderManager::getSingleton().rrDestroyCreatureVisualDebug(this, tile);
1784     }
1785 }
1786 
stopComputeVisualDebugEntities()1787 void Creature::stopComputeVisualDebugEntities()
1788 {
1789     if(!getIsOnServerMap())
1790         return;
1791 
1792     mHasVisualDebuggingEntities = false;
1793 
1794     ServerNotification *serverNotification = new ServerNotification(
1795         ServerNotificationType::refreshCreatureVisDebug, nullptr);
1796     const std::string& name = getName();
1797     serverNotification->mPacket << name;
1798     serverNotification->mPacket << false;
1799     ODServer::getSingleton().queueServerNotification(serverNotification);
1800 }
1801 
destroyVisualDebugEntities()1802 void Creature::destroyVisualDebugEntities()
1803 {
1804     if(getIsOnServerMap())
1805         return;
1806 
1807     mHasVisualDebuggingEntities = false;
1808 
1809     for (Tile* tile : mVisualDebugEntityTiles)
1810     {
1811         if (tile == nullptr)
1812             continue;
1813 
1814         RenderManager::getSingleton().rrDestroyCreatureVisualDebug(this, tile);
1815     }
1816     mVisualDebugEntityTiles.clear();
1817 }
1818 
getCoveredTiles()1819 std::vector<Tile*> Creature::getCoveredTiles()
1820 {
1821     std::vector<Tile*> tempVector;
1822     tempVector.push_back(getPositionTile());
1823     return tempVector;
1824 }
1825 
getCoveredTile(int index)1826 Tile* Creature::getCoveredTile(int index)
1827 {
1828     if(index > 0)
1829     {
1830         OD_LOG_ERR("name=" + getName() + ", index=" + Helper::toString(index));
1831         return nullptr;
1832     }
1833 
1834     return getPositionTile();
1835 }
1836 
numCoveredTiles() const1837 uint32_t Creature::numCoveredTiles() const
1838 {
1839     if(getPositionTile() == nullptr)
1840         return 0;
1841 
1842     return 1;
1843 }
1844 
CloseStatsWindow(const CEGUI::EventArgs &)1845 bool Creature::CloseStatsWindow(const CEGUI::EventArgs& /*e*/)
1846 {
1847     destroyStatsWindow();
1848     return true;
1849 }
1850 
createStatsWindow()1851 void Creature::createStatsWindow()
1852 {
1853     if (mStatsWindow != nullptr)
1854         return;
1855 
1856     ClientNotification *clientNotification = new ClientNotification(
1857         ClientNotificationType::askCreatureInfos);
1858     std::string name = getName();
1859     clientNotification->mPacket << name << true;
1860     ODClient::getSingleton().queueClientNotification(clientNotification);
1861 
1862     CEGUI::WindowManager* wmgr = CEGUI::WindowManager::getSingletonPtr();
1863     CEGUI::Window* rootWindow = CEGUI::System::getSingleton().getDefaultGUIContext().getRootWindow();
1864 
1865     mStatsWindow = wmgr->createWindow("OD/FrameWindow", std::string("CreatureStatsWindows_") + getName());
1866     mStatsWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.3, 0), CEGUI::UDim(0.3, 0)));
1867     mStatsWindow->setSize(CEGUI::USize(CEGUI::UDim(0, 380), CEGUI::UDim(0, 400)));
1868 
1869     CEGUI::Window* textWindow = wmgr->createWindow("OD/StaticText", "TextDisplay");
1870     textWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.05, 0), CEGUI::UDim(0.1, 0)));
1871     textWindow->setSize(CEGUI::USize(CEGUI::UDim(0.9, 0), CEGUI::UDim(0.85, 0)));
1872     textWindow->setProperty("FrameEnabled", "False");
1873     textWindow->setProperty("BackgroundEnabled", "False");
1874 
1875     // We want to close the window when the cross is clicked
1876     mStatsWindow->subscribeEvent(CEGUI::FrameWindow::EventCloseClicked,
1877         CEGUI::Event::Subscriber(&Creature::CloseStatsWindow, this));
1878 
1879     // Set the window title
1880     mStatsWindow->setText(getName() + " (" + getDefinition()->getClassName() + ")");
1881 
1882     mStatsWindow->addChild(textWindow);
1883     rootWindow->addChild(mStatsWindow);
1884     mStatsWindow->show();
1885 
1886     updateStatsWindow("Loading...");
1887 }
1888 
destroyStatsWindow()1889 void Creature::destroyStatsWindow()
1890 {
1891     if (mStatsWindow != nullptr)
1892     {
1893         ClientNotification *clientNotification = new ClientNotification(
1894             ClientNotificationType::askCreatureInfos);
1895         std::string name = getName();
1896         clientNotification->mPacket << name << false;
1897         ODClient::getSingleton().queueClientNotification(clientNotification);
1898 
1899         mStatsWindow->destroy();
1900         mStatsWindow = nullptr;
1901     }
1902 }
1903 
updateStatsWindow(const std::string & txt)1904 void Creature::updateStatsWindow(const std::string& txt)
1905 {
1906     if (mStatsWindow == nullptr)
1907         return;
1908 
1909     CEGUI::Window* textWindow = mStatsWindow->getChild("TextDisplay");
1910     textWindow->setText(txt);
1911 }
1912 
getStatsText()1913 std::string Creature::getStatsText()
1914 {
1915     // The creatures are not refreshed at each turn so this information is relevant in the server
1916     // GameMap only
1917     const std::string formatTitleOn = "[font='MedievalSharp-12'][colour='CCBBBBFF']";
1918     const std::string formatTitleOff = "[font='MedievalSharp-10'][colour='FFFFFFFF']";
1919 
1920     std::stringstream tempSS;
1921     tempSS << formatTitleOn << "Characteristics" << formatTitleOff << std::endl;
1922     tempSS << "Level: " << getLevel() << std::endl;
1923     tempSS << "Experience: " << mExp << std::endl;
1924     tempSS << "HP: " << getHP() << " / " << mMaxHP << std::endl;
1925     tempSS << "Gold: " << mGoldCarried << std::endl;
1926     if (!getDefinition()->isWorker())
1927     {
1928         tempSS << "Wakefulness: " << mWakefulness << std::endl;
1929         tempSS << "Hunger: " << mHunger << std::endl;
1930     }
1931     tempSS << "Move speed (G/W/L): " << getMoveSpeedGround() << " / "
1932         << getMoveSpeedWater() << " / " << getMoveSpeedLava() << std::endl;
1933     tempSS << "Weapons:" << std::endl;
1934     if(mWeaponL == nullptr)
1935         tempSS << " - Left: none" << std::endl;
1936     else
1937         tempSS << " - Left: " << mWeaponL->getName() << " | Damage (P/M/E): " << mWeaponL->getPhysicalDamage()
1938                << " / " << mWeaponL->getMagicalDamage() << " / " << mWeaponL->getElementDamage() << std::endl;
1939     if(mWeaponR == nullptr)
1940         tempSS << " - Right: none" << std::endl;
1941     else
1942         tempSS << " - Right: " << mWeaponR->getName() << " | Damage (P/M/E): " << mWeaponR->getPhysicalDamage()
1943                << " / " << mWeaponR->getMagicalDamage() << " / " << mWeaponR->getElementDamage() << std::endl;
1944     tempSS << "Defense (P/M/E): " << getPhysicalDefense() << " / " << getMagicalDefense() << " / " << getElementDefense() << std::endl;
1945     if (getDefinition()->isWorker())
1946     {
1947         tempSS << "Dig rate: " << getDigRate() << std::endl;
1948         tempSS << "Dance rate: " << mClaimRate << std::endl;
1949     }
1950 
1951     tempSS << formatTitleOn << "\nDebugging information" << formatTitleOff << std::endl;
1952     tempSS << "Seat and team IDs: " << getSeat()->getId() << " / " << getSeat()->getTeamId() << std::endl;
1953     tempSS << "Position: " << Helper::toString(getPosition()) << std::endl;
1954     tempSS << "Actions:";
1955     for(const std::unique_ptr<CreatureAction>& ca : mActions)
1956     {
1957         tempSS << " " << CreatureAction::toString(ca.get()->getType());
1958     }
1959     tempSS << std::endl;
1960     tempSS << "Destinations:";
1961     for(const Ogre::Vector3& dest : mWalkQueue)
1962     {
1963         tempSS << " " << Helper::toStringWithoutZ(dest);
1964     }
1965     tempSS << std::endl;
1966     tempSS << "Mood: " << CreatureMood::toString(mMoodValue) << std::endl;
1967     tempSS << "Mood points: " << Helper::toString(mMoodPoints) << std::endl;
1968     return tempSS.str();
1969 }
1970 
takeDamage(GameEntity * attacker,double absoluteDamage,double physicalDamage,double magicalDamage,double elementDamage,Tile * tileTakingDamage,bool ko)1971 double Creature::takeDamage(GameEntity* attacker, double absoluteDamage, double physicalDamage, double magicalDamage, double elementDamage,
1972         Tile *tileTakingDamage, bool ko)
1973 {
1974     mNbTurnsWithoutBattle = 0;
1975     physicalDamage = std::max(physicalDamage - getPhysicalDefense(), 0.0);
1976     magicalDamage = std::max(magicalDamage - getMagicalDefense(), 0.0);
1977     elementDamage = std::max(elementDamage - getElementDefense(), 0.0);
1978     double damageDone = std::min(mHp, absoluteDamage + physicalDamage + magicalDamage + elementDamage);
1979     mHp -= damageDone;
1980     if(mHp <= 0)
1981     {
1982         // If the attacking entity is a creature and its seat is configured to KO creatures
1983         // instead of killing, we KO
1984         if(ko)
1985         {
1986             mHp = 1.0;
1987             mKoTurnCounter = -ConfigManager::getSingleton().getNbTurnsKoCreatureAttacked();
1988             OD_LOG_INF("creature=" + getName() + " has been KO by " + attacker->getName());
1989             dropCarriedEquipment();
1990         }
1991     }
1992 
1993     computeCreatureOverlayHealthValue();
1994     computeCreatureOverlayMoodValue();
1995 
1996     if(!isAlive())
1997         fireEntityDead();
1998 
1999     if(!getIsOnServerMap())
2000         return damageDone;
2001 
2002     Player* player = getGameMap()->getPlayerBySeat(getSeat());
2003     if (player == nullptr)
2004         return damageDone;
2005 
2006     // If we are a worker attacked by a worker, we fight. Otherwise, we flee (if it is a fighter, a trap,
2007     // or whatever)
2008     if(!getDefinition()->isWorker())
2009         return damageDone;
2010 
2011     bool shouldFlee = true;
2012     if((attacker != nullptr) &&
2013        (attacker->getObjectType() == GameEntityType::creature))
2014     {
2015         Creature* creatureAttacking = static_cast<Creature*>(attacker);
2016         if(creatureAttacking->getDefinition()->isWorker())
2017         {
2018             // We do not flee because of this attack
2019             shouldFlee = false;
2020         }
2021     }
2022 
2023     if(shouldFlee)
2024     {
2025         flee();
2026         return damageDone;
2027     }
2028     return damageDone;
2029 }
2030 
receiveExp(double experience)2031 void Creature::receiveExp(double experience)
2032 {
2033     if (experience < 0)
2034         return;
2035 
2036     mExp += experience;
2037 }
2038 
useAttack(CreatureSkillData & skillData,GameEntity & entityAttack,Tile & tileAttack,bool ko,bool notifyPlayerIfHit)2039 void Creature::useAttack(CreatureSkillData& skillData, GameEntity& entityAttack,
2040         Tile& tileAttack, bool ko, bool notifyPlayerIfHit)
2041 {
2042     // Turn to face the entity we are attacking and set the animation state to Attack.
2043     const Ogre::Vector3& pos = getPosition();
2044     Ogre::Vector3 walkDirection(tileAttack.getX() - pos.x, tileAttack.getY() - pos.y, 0);
2045     walkDirection.normalise();
2046     setAnimationState(EntityAnimation::attack_anim, false, walkDirection, true);
2047     fireCreatureSound(CreatureSound::Attack);
2048     setNbTurnsWithoutBattle(0);
2049 
2050     // Calculate how much damage we do.
2051     Tile* myTile = getPositionTile();
2052     float range = Pathfinding::distanceTile(*myTile, tileAttack);
2053 
2054     // We use the skill
2055     skillData.mSkill->tryUseFight(*getGameMap(), this, range,
2056         &entityAttack, &tileAttack, ko, notifyPlayerIfHit);
2057     skillData.mWarmup = skillData.mSkill->getWarmupNbTurns();
2058     skillData.mCooldown = skillData.mSkill->getCooldownNbTurns();
2059 
2060     // Fighting is tiring
2061     decreaseWakefulness(0.5);
2062     // but gives experience
2063     receiveExp(1.5);
2064 }
2065 
isActionInList(CreatureActionType action) const2066 bool Creature::isActionInList(CreatureActionType action) const
2067 {
2068     for (const std::unique_ptr<CreatureAction>& ca : mActions)
2069     {
2070         if (ca.get()->getType() == action)
2071             return true;
2072     }
2073     return false;
2074 }
2075 
clearActionQueue()2076 void Creature::clearActionQueue()
2077 {
2078     mActions.clear();
2079 }
2080 
hasActionBeenTried(CreatureActionType actionType) const2081 bool Creature::hasActionBeenTried(CreatureActionType actionType) const
2082 {
2083     if(std::find(mActionTry.begin(), mActionTry.end(), actionType) == mActionTry.end())
2084         return false;
2085 
2086     return true;
2087 }
2088 
pushAction(std::unique_ptr<CreatureAction> && action)2089 void Creature::pushAction(std::unique_ptr<CreatureAction>&& action)
2090 {
2091     CreatureActionType actionType = action.get()->getType();
2092     if(std::find(mActionTry.begin(), mActionTry.end(), actionType) == mActionTry.end())
2093     {
2094         mActionTry.push_back(actionType);
2095     }
2096 
2097     mActions.emplace_back(std::move(action));
2098 }
2099 
popAction()2100 void Creature::popAction()
2101 {
2102     if(mActions.empty())
2103     {
2104         OD_LOG_ERR("name=" + getName() + ", trying to pop empty action list");
2105         return;
2106     }
2107 
2108     mActions.pop_back();
2109 }
2110 
tryPickup(Seat * seat)2111 bool Creature::tryPickup(Seat* seat)
2112 {
2113     if(!getIsOnMap())
2114         return false;
2115 
2116     // Cannot pick up dead creatures
2117     if (!getGameMap()->isInEditorMode() && !isAlive())
2118         return false;
2119 
2120     if(!getGameMap()->isInEditorMode() && (mSeatPrison == nullptr) && !getSeat()->canOwnedCreatureBePickedUpBy(seat))
2121         return false;
2122 
2123     if(!getGameMap()->isInEditorMode() && (mSeatPrison != nullptr) && !mSeatPrison->canOwnedCreatureBePickedUpBy(seat))
2124         return false;
2125 
2126     // KO creatures cannot be picked up
2127     if(isKo())
2128         return false;
2129 
2130     return true;
2131 }
2132 
pickup()2133 void Creature::pickup()
2134 {
2135     // Stop the creature walking and set it off the map to prevent the AI from running on it.
2136     removeEntityFromPositionTile();
2137     clearDestinations(EntityAnimation::idle_anim, true, true);
2138     clearActionQueue();
2139 
2140     if(!getIsOnServerMap())
2141         return;
2142 
2143     if(getHasVisualDebuggingEntities())
2144         computeVisualDebugEntities();
2145 
2146     fireCreatureSound(CreatureSound::Pickup);
2147 }
2148 
canGoThroughTile(Tile * tile) const2149 bool Creature::canGoThroughTile(Tile* tile) const
2150 {
2151     if(tile == nullptr)
2152         return false;
2153 
2154     return getMoveSpeed(tile) > 0.0;
2155 }
2156 
tryDrop(Seat * seat,Tile * tile)2157 bool Creature::tryDrop(Seat* seat, Tile* tile)
2158 {
2159     // check whether the tile is a ground tile ...
2160     if(tile->isFullTile())
2161         return false;
2162 
2163     // In editor mode, we allow creatures to be dropped anywhere they can walk
2164     if(getGameMap()->isInEditorMode() && canGoThroughTile(tile))
2165         return true;
2166 
2167     // we cannot drop a creature on a tile we don't see
2168     if(!seat->hasVisionOnTile(tile))
2169         return false;
2170 
2171     // If it is a worker, he can be dropped on dirt
2172     if (getDefinition()->isWorker() && (tile->getTileVisual() == TileVisual::dirtGround || tile->getTileVisual() == TileVisual::goldGround))
2173         return true;
2174 
2175     // Every creature can be dropped on allied claimed tiles
2176     if(tile->isClaimedForSeat(seat))
2177         return true;
2178 
2179     return false;
2180 }
2181 
drop(const Ogre::Vector3 & v)2182 void Creature::drop(const Ogre::Vector3& v)
2183 {
2184     setPosition(v);
2185     if(!getIsOnServerMap())
2186     {
2187         mDropCooldown = 2;
2188         return;
2189     }
2190 
2191     if(getHasVisualDebuggingEntities())
2192         computeVisualDebugEntities();
2193 
2194     fireCreatureSound(CreatureSound::Drop);
2195 
2196     // The creature is temporary KO
2197     mKoTurnCounter = mDefinition->getTurnsStunDropped();
2198     computeCreatureOverlayMoodValue();
2199 
2200     // Action queue should be empty but it shouldn't hurt
2201     clearActionQueue();
2202 
2203     // In editor mode, we do not check for forced actions
2204     if(getGameMap()->isInEditorMode())
2205         return;
2206 
2207     if(mDefinition->isWorker())
2208     {
2209         // If a worker is dropped, he will search in the tile he is and in the 4 neighboor tiles.
2210         // 1 - If the tile he is in a treasury and he is carrying gold, he should deposit it
2211         // 2 - if one of the 4 neighboor tiles is marked, he will dig
2212         // 3 - if there is a carryable entity where it is dropped, it should try to carry it
2213         // 4 - if the the tile he is in is not claimed and one of the neigbboor tiles is claimed, he will claim
2214         // 5 - if the the tile he is in is claimed and one of the neigbboor tiles is not claimed, he will claim
2215         // 6 - If the tile he is in is claimed and one of the neigbboor tiles is a not claimed wall, he will claim
2216         Tile* position = getPositionTile();
2217         Seat* seat = getSeat();
2218         Tile* tileMarkedDig = nullptr;
2219         Tile* tileMarkedDigPos = nullptr;
2220         Tile* tileToClaim = nullptr;
2221         Tile* tileWallNotClaimed = nullptr;
2222         for (Tile* tile : position->getAllNeighbors())
2223         {
2224             if(tileMarkedDig == nullptr &&
2225                 tile->getMarkedForDigging(getGameMap()->getPlayerBySeat(seat))
2226                 )
2227             {
2228                 // Check if there is room for digging
2229                 std::vector<Tile*> tiles;
2230                 tile->canWorkerDig(*this, tiles);
2231                 // We search for the closest neighbor tile (may be not the position
2232                 // tile if the player drops several workers at the same tile)
2233                 float distBest = -1;
2234                 for (Tile* neigh : tiles)
2235                 {
2236                     float dist = Pathfinding::squaredDistanceTile(*position, *neigh);
2237                     if((distBest != -1) && (distBest <= dist))
2238                         continue;
2239 
2240                     distBest = dist;
2241                     tileMarkedDig = tile;
2242                     tileMarkedDigPos = neigh;
2243                 }
2244             }
2245             else if(tileToClaim == nullptr &&
2246                 tile->isClaimedForSeat(seat) &&
2247                 position->isGroundClaimable(seat)
2248                 )
2249             {
2250                 tileToClaim = position;
2251             }
2252             else if(tileToClaim == nullptr &&
2253                 position->isClaimedForSeat(seat) &&
2254                 tile->isGroundClaimable(seat)
2255                 )
2256             {
2257                 tileToClaim = tile;
2258             }
2259             else if(tileWallNotClaimed == nullptr &&
2260                 position->isClaimedForSeat(seat) &&
2261                 tile->isWallClaimable(seat)
2262                 )
2263             {
2264                 tileWallNotClaimed = tile;
2265             }
2266         }
2267 
2268         // We try to deposit gold if we are on a room while carrying gold
2269         if((mGoldCarried > 0) && (mDigRate > 0.0) &&
2270            (position->getCoveringRoom() != nullptr))
2271         {
2272             int deposited = position->getCoveringRoom()->depositGold(mGoldCarried, position);
2273             if(deposited > 0)
2274             {
2275                 mGoldCarried -= deposited;
2276                 return;
2277             }
2278         }
2279 
2280         std::vector<GameEntity*> carryable;
2281         position->fillWithCarryableEntities(this, carryable);
2282 
2283         // Now, we can decide
2284         if((tileMarkedDig != nullptr) && (tileMarkedDigPos != nullptr) && (mDigRate > 0.0))
2285         {
2286             pushAction(Utils::make_unique<CreatureActionSearchTileToDig>(*this, true));
2287             pushAction(Utils::make_unique<CreatureActionDigTile>(*this, *tileMarkedDig, *tileMarkedDigPos));
2288             return;
2289         }
2290 
2291         if(!carryable.empty())
2292         {
2293             // We look for the most important entity to carry
2294             GameEntity* entityToCarry = carryable[0];
2295             for(GameEntity* entity : carryable)
2296             {
2297                 if(entity->getEntityCarryType(this) <= entityToCarry->getEntityCarryType(this))
2298                     continue;
2299 
2300                 entityToCarry = entity;
2301             }
2302 
2303             pushAction(Utils::make_unique<CreatureActionGrabEntity>(*this, *entityToCarry));
2304             return;
2305         }
2306 
2307         if((tileToClaim != nullptr) && (mClaimRate > 0.0))
2308         {
2309             pushAction(Utils::make_unique<CreatureActionSearchGroundTileToClaim>(*this, true));
2310             pushAction(Utils::make_unique<CreatureActionClaimGroundTile>(*this, *tileToClaim));
2311             return;
2312         }
2313 
2314         if((tileWallNotClaimed != nullptr) && (mClaimRate > 0.0))
2315         {
2316             pushAction(Utils::make_unique<CreatureActionSearchWallTileToClaim>(*this, true));
2317             pushAction(Utils::make_unique<CreatureActionClaimWallTile>(*this, *tileWallNotClaimed));
2318             return;
2319         }
2320 
2321         // We couldn't find why we were dropped here. Let's behave as usual
2322         return;
2323     }
2324 
2325     // Fighters
2326     // If we are dropped on a tile with a building, we notify it so that it
2327     // can do some special actions
2328     Tile* tile = getPositionTile();
2329     if((tile != nullptr) &&
2330        (tile->getCoveringBuilding() != nullptr))
2331     {
2332         Building* building = tile->getCoveringBuilding();
2333         building->creatureDropped(*this);
2334         return;
2335     }
2336 }
2337 
resizeMeshAfterDrop()2338 bool Creature::resizeMeshAfterDrop()
2339 {
2340     RenderManager::getSingleton().rrScaleCreature(*this);
2341     return false;
2342 }
2343 
setDestination(Tile * tile)2344 bool Creature::setDestination(Tile* tile)
2345 {
2346     if(tile == nullptr)
2347         return false;
2348 
2349     Tile *posTile = getPositionTile();
2350     if(posTile == nullptr)
2351         return false;
2352 
2353     std::list<Tile*> result = getGameMap()->path(this, tile);
2354 
2355     std::vector<Ogre::Vector3> path;
2356     tileToVector3(result, path, true, 0.0);
2357     setWalkPath(EntityAnimation::walk_anim, EntityAnimation::idle_anim, true, true, path);
2358     pushAction(Utils::make_unique<CreatureActionWalkToTile>(*this));
2359     return true;
2360 }
2361 
wanderRandomly(const std::string & animationState)2362 bool Creature::wanderRandomly(const std::string& animationState)
2363 {
2364     // We pick randomly a visible tile far away (at the end of visible tiles)
2365     if(mTilesWithinSightRadius.empty())
2366         return false;
2367 
2368     // Add reachable tiles only before searching for one of them
2369     std::vector<Tile*> reachableTiles;
2370     for (Tile* tile: mTilesWithinSightRadius)
2371     {
2372         if (getGameMap()->pathExists(this, getPositionTile(), tile))
2373             reachableTiles.push_back(tile);
2374     }
2375 
2376     if (reachableTiles.empty())
2377         return false;
2378 
2379     Tile* tileDestination = reachableTiles[Random::Uint(0, reachableTiles.size() - 1)];
2380     setDestination(tileDestination);
2381     return false;
2382 }
2383 
isAttackable(Tile * tile,Seat * seat) const2384 bool Creature::isAttackable(Tile* tile, Seat* seat) const
2385 {
2386     if(mHp <= 0.0)
2387         return false;
2388 
2389     // KO Creature to death creatures are not a threat and cannot be attacked. However, temporary KO can be
2390     if(mKoTurnCounter < 0)
2391         return false;
2392 
2393     // Creatures in prison are not a treat and cannot be attacked
2394     if(mSeatPrison != nullptr)
2395         return false;
2396 
2397     return true;
2398 }
2399 
getEntityCarryType(Creature * carrier)2400 EntityCarryType Creature::getEntityCarryType(Creature* carrier)
2401 {
2402     // Workers cannot be carried to crypt/prison
2403     if(getDefinition()->isWorker())
2404         return EntityCarryType::notCarryable;
2405 
2406     // KO to death entities can be carried
2407     if(mKoTurnCounter < 0)
2408         return EntityCarryType::koCreature;
2409 
2410     // Dead creatures are carryable
2411     if(getHP() <= 0.0)
2412         return EntityCarryType::corpse;
2413 
2414     return EntityCarryType::notCarryable;
2415 }
2416 
notifyEntityCarryOn(Creature * carrier)2417 void Creature::notifyEntityCarryOn(Creature* carrier)
2418 {
2419     removeEntityFromPositionTile();
2420 }
2421 
notifyEntityCarryOff(const Ogre::Vector3 & position)2422 void Creature::notifyEntityCarryOff(const Ogre::Vector3& position)
2423 {
2424     mPosition = position;
2425     addEntityToPositionTile();
2426 }
2427 
carryEntity(GameEntity * carriedEntity)2428 void Creature::carryEntity(GameEntity* carriedEntity)
2429 {
2430     if(!getIsOnServerMap())
2431         return;
2432 
2433     OD_ASSERT_TRUE(carriedEntity != nullptr);
2434     OD_ASSERT_TRUE(mCarriedEntity == nullptr);
2435     mCarriedEntity = nullptr;
2436     if(carriedEntity == nullptr)
2437         return;
2438 
2439     // We remove the carried entity from the clients gamemaps as well as the carrier
2440     // and we send the carrier creation message (that will embed the carried)
2441     carriedEntity->fireRemoveEntityToSeatsWithVision();
2442     // We only notify seats that already had vision. We copy the seats with vision list
2443     // because fireRemoveEntityToSeatsWithVision will empty it.
2444     std::vector<Seat*> seatsWithVision = mSeatsWithVisionNotified;
2445     // We remove ourself and send the creation
2446     fireRemoveEntityToSeatsWithVision();
2447     mCarriedEntity = carriedEntity;
2448     notifySeatsWithVision(seatsWithVision);
2449 }
2450 
releaseCarriedEntity()2451 void Creature::releaseCarriedEntity()
2452 {
2453     if(!getIsOnServerMap())
2454         return;
2455 
2456     GameEntity* carriedEntity = mCarriedEntity;
2457     mCarriedEntity = nullptr;
2458     if(carriedEntity == nullptr)
2459     {
2460         OD_LOG_ERR("name=" + getName());
2461         return;
2462     }
2463 
2464     for(Seat* seat : mSeatsWithVisionNotified)
2465     {
2466         if(seat->getPlayer() == nullptr)
2467             continue;
2468         if(!seat->getPlayer()->getIsHuman())
2469             continue;
2470 
2471         ServerNotification* serverNotification = new ServerNotification(
2472             ServerNotificationType::releaseCarriedEntity, seat->getPlayer());
2473         serverNotification->mPacket << getName() << carriedEntity->getObjectType();
2474         serverNotification->mPacket << carriedEntity->getName();
2475         serverNotification->mPacket << mPosition;
2476         ODServer::getSingleton().queueServerNotification(serverNotification);
2477     }
2478 }
2479 
canSlap(Seat * seat)2480 bool Creature::canSlap(Seat* seat)
2481 {
2482     Tile* tile = getPositionTile();
2483     if(tile == nullptr)
2484     {
2485         OD_LOG_ERR("entityName=" + getName());
2486         return false;
2487     }
2488 
2489     if(mDropCooldown > 0)
2490         return false;
2491 
2492     if(getGameMap()->isInEditorMode())
2493         return true;
2494 
2495     if(getHP() <= 0.0)
2496         return false;
2497 
2498     // If the creature is in prison, it can be slapped by the jail owner only
2499     if(mSeatPrison != nullptr)
2500         return (mSeatPrison == seat);
2501 
2502     // Only the owning player can slap a creature
2503     if(getSeat() != seat)
2504         return false;
2505 
2506     return true;
2507 }
2508 
slap()2509 void Creature::slap()
2510 {
2511     if(!getIsOnServerMap())
2512         return;
2513 
2514     fireCreatureSound(CreatureSound::Slap);
2515 
2516     // In editor mode, we remove the creature
2517     if(getGameMap()->isInEditorMode())
2518     {
2519         removeFromGameMap();
2520         deleteYourself();
2521         return;
2522     }
2523 
2524     CreatureEffectSlap* effect = new CreatureEffectSlap(
2525         ConfigManager::getSingleton().getSlapEffectDuration(), "");
2526     addCreatureEffect(effect);
2527     mHp -= mMaxHP * ConfigManager::getSingleton().getSlapDamagePercent() / 100.0;
2528     computeCreatureOverlayHealthValue();
2529 }
2530 
fireAddEntity(Seat * seat,bool async)2531 void Creature::fireAddEntity(Seat* seat, bool async)
2532 {
2533     if(async)
2534     {
2535         ServerNotification serverNotification(
2536             ServerNotificationType::addEntity, seat->getPlayer());
2537         exportHeadersToPacket(serverNotification.mPacket);
2538         exportToPacket(serverNotification.mPacket, seat);
2539         ODServer::getSingleton().sendAsyncMsg(serverNotification);
2540 
2541         if(mCarriedEntity != nullptr)
2542         {
2543             OD_LOG_ERR("Trying to fire add creature in async mode name=" + getName() + " while carrying " + mCarriedEntity->getName());
2544         }
2545         return;
2546     }
2547 
2548     ServerNotification* serverNotification = new ServerNotification(
2549         ServerNotificationType::addEntity, seat->getPlayer());
2550     exportHeadersToPacket(serverNotification->mPacket);
2551     exportToPacket(serverNotification->mPacket, seat);
2552     ODServer::getSingleton().queueServerNotification(serverNotification);
2553 
2554     if(mCarriedEntity != nullptr)
2555     {
2556         mCarriedEntity->addSeatWithVision(seat, false);
2557 
2558         serverNotification = new ServerNotification(
2559             ServerNotificationType::carryEntity, seat->getPlayer());
2560         serverNotification->mPacket << getName() << mCarriedEntity->getObjectType();
2561         serverNotification->mPacket << mCarriedEntity->getName();
2562         ODServer::getSingleton().queueServerNotification(serverNotification);
2563     }
2564 }
2565 
fireRemoveEntity(Seat * seat)2566 void Creature::fireRemoveEntity(Seat* seat)
2567 {
2568     // If we are carrying an entity, we release it first, then we can remove it and us
2569     if(mCarriedEntity != nullptr)
2570     {
2571         ServerNotification* serverNotification = new ServerNotification(
2572             ServerNotificationType::releaseCarriedEntity, seat->getPlayer());
2573         serverNotification->mPacket << getName() << mCarriedEntity->getObjectType();
2574         serverNotification->mPacket << mCarriedEntity->getName();
2575         serverNotification->mPacket << mPosition;
2576         ODServer::getSingleton().queueServerNotification(serverNotification);
2577 
2578         mCarriedEntity->removeSeatWithVision(seat);
2579     }
2580 
2581     const std::string& name = getName();
2582     ServerNotification *serverNotification = new ServerNotification(
2583         ServerNotificationType::removeEntity, seat->getPlayer());
2584     GameEntityType type = getObjectType();
2585     serverNotification->mPacket << type;
2586     serverNotification->mPacket << name;
2587     ODServer::getSingleton().queueServerNotification(serverNotification);
2588 }
2589 
fireCreatureRefreshIfNeeded()2590 void Creature::fireCreatureRefreshIfNeeded()
2591 {
2592     if(!mNeedFireRefresh)
2593         return;
2594 
2595     mNeedFireRefresh = false;
2596     for(Seat* seat : mSeatsWithVisionNotified)
2597     {
2598         if(seat->getPlayer() == nullptr)
2599             continue;
2600         if(!seat->getPlayer()->getIsHuman())
2601             continue;
2602 
2603         const std::string& name = getName();
2604         ServerNotification *serverNotification = new ServerNotification(
2605             ServerNotificationType::entitiesRefresh, seat->getPlayer());
2606         uint32_t nbCreature = 1;
2607         serverNotification->mPacket << nbCreature;
2608         serverNotification->mPacket << GameEntityType::creature;
2609         serverNotification->mPacket << name;
2610         exportToPacketForUpdate(serverNotification->mPacket, seat);
2611         ODServer::getSingleton().queueServerNotification(serverNotification);
2612     }
2613 }
2614 
fireChatMsgTookFee(int goldTaken)2615 void Creature::fireChatMsgTookFee(int goldTaken)
2616 {
2617     if(getSeat()->getPlayer() == nullptr)
2618         return;
2619     if(!getSeat()->getPlayer()->getIsHuman())
2620         return;
2621     if(getSeat()->getPlayer()->getHasLost())
2622         return;
2623 
2624     ServerNotification *serverNotification = new ServerNotification(
2625         ServerNotificationType::chatServer, getSeat()->getPlayer());
2626     std::string msg;
2627     // We don't display the same message if we have taken all our fee or only a part of it
2628     if(getGoldFee() <= 0)
2629         msg = getName() + " took its fee: " + Helper::toString(goldTaken);
2630     else
2631         msg = getName() + " took " + Helper::toString(goldTaken) + " from its fee";
2632 
2633     serverNotification->mPacket << msg << EventShortNoticeType::aboutCreatures;
2634     ODServer::getSingleton().queueServerNotification(serverNotification);
2635 }
2636 
fireChatMsgLeftDungeon()2637 void Creature::fireChatMsgLeftDungeon()
2638 {
2639     if(getSeat()->getPlayer() == nullptr)
2640         return;
2641     if(!getSeat()->getPlayer()->getIsHuman())
2642         return;
2643     if(getSeat()->getPlayer()->getHasLost())
2644         return;
2645 
2646     ServerNotification *serverNotification = new ServerNotification(
2647         ServerNotificationType::chatServer, getSeat()->getPlayer());
2648     std::string msg = getName() + " left your dungeon";
2649     serverNotification->mPacket << msg << EventShortNoticeType::aboutCreatures;
2650     ODServer::getSingleton().queueServerNotification(serverNotification);
2651 }
2652 
fireChatMsgLeavingDungeon()2653 void Creature::fireChatMsgLeavingDungeon()
2654 {
2655     if(getSeat()->getPlayer() == nullptr)
2656         return;
2657     if(!getSeat()->getPlayer()->getIsHuman())
2658         return;
2659     if(getSeat()->getPlayer()->getHasLost())
2660         return;
2661 
2662     ServerNotification *serverNotification = new ServerNotification(
2663         ServerNotificationType::chatServer, getSeat()->getPlayer());
2664     std::string msg = getName() + " is leaving your dungeon";
2665     serverNotification->mPacket << msg << EventShortNoticeType::aboutCreatures;
2666     ODServer::getSingleton().queueServerNotification(serverNotification);
2667 }
2668 
fireChatMsgBecameRogue()2669 void Creature::fireChatMsgBecameRogue()
2670 {
2671     if(getSeat()->getPlayer() == nullptr)
2672         return;
2673     if(!getSeat()->getPlayer()->getIsHuman())
2674         return;
2675     if(getSeat()->getPlayer()->getHasLost())
2676         return;
2677 
2678     ServerNotification *serverNotification = new ServerNotification(
2679         ServerNotificationType::chatServer, getSeat()->getPlayer());
2680     std::string msg = getName() + " is not under your control anymore !";
2681     serverNotification->mPacket << msg << EventShortNoticeType::aboutCreatures;
2682     ODServer::getSingleton().queueServerNotification(serverNotification);
2683 }
2684 
fireChatMsgUnhappy()2685 void Creature::fireChatMsgUnhappy()
2686 {
2687     if(getSeat()->getPlayer() == nullptr)
2688         return;
2689     if(!getSeat()->getPlayer()->getIsHuman())
2690         return;
2691     if(getSeat()->getPlayer()->getHasLost())
2692         return;
2693 
2694     ServerNotification *serverNotification = new ServerNotification(
2695         ServerNotificationType::chatServer, getSeat()->getPlayer());
2696     std::string msg = getName() + " is unhappy !";
2697     serverNotification->mPacket << msg << EventShortNoticeType::aboutCreatures;
2698     ODServer::getSingleton().queueServerNotification(serverNotification);
2699 }
2700 
fireChatMsgFurious()2701 void Creature::fireChatMsgFurious()
2702 {
2703     if(getSeat()->getPlayer() == nullptr)
2704         return;
2705     if(!getSeat()->getPlayer()->getIsHuman())
2706         return;
2707     if(getSeat()->getPlayer()->getHasLost())
2708         return;
2709 
2710     ServerNotification *serverNotification = new ServerNotification(
2711         ServerNotificationType::chatServer, getSeat()->getPlayer());
2712     std::string msg = getName() + " is furious !";
2713     serverNotification->mPacket << msg << EventShortNoticeType::aboutCreatures;
2714     ODServer::getSingleton().queueServerNotification(serverNotification);
2715 }
2716 
setupDefinition(GameMap & gameMap,const CreatureDefinition & defaultWorkerCreatureDefinition)2717 void Creature::setupDefinition(GameMap& gameMap, const CreatureDefinition& defaultWorkerCreatureDefinition)
2718 {
2719     bool setHpToStrHp = false;
2720     if(mDefinition == nullptr)
2721     {
2722         // If the classname corresponds to the default worker CreatureDefinition, we use
2723         // the dedicated class. The correct one will be set after the seat is initialized
2724         if(!mDefinitionString.empty() &&  mDefinitionString.compare(ConfigManager::DefaultWorkerCreatureDefinition) != 0)
2725         {
2726             mDefinition = gameMap.getClassDescription(mDefinitionString);
2727         }
2728         else
2729         {
2730             // If we are in editor mode, we take the default worker class. Otherwise, we take
2731             // the default worker from the seat faction
2732             if(gameMap.isInEditorMode() || !mSeat)
2733                 mDefinition = &defaultWorkerCreatureDefinition;
2734             else
2735                 mDefinition = getSeat()->getWorkerClassToSpawn();
2736         }
2737 
2738         if(mDefinition == nullptr)
2739         {
2740             OD_LOG_ERR("Definition=" + mDefinitionString);
2741             return;
2742         }
2743 
2744         if(getIsOnServerMap())
2745         {
2746             setHpToStrHp = true;
2747 
2748             // name
2749             if (getName().compare("autoname") == 0)
2750             {
2751                 std::string name = getGameMap()->nextUniqueNameCreature(mDefinition->getClassName());
2752                 setName(name);
2753             }
2754         }
2755     }
2756 
2757     if(getIsOnServerMap())
2758     {
2759         for(const CreatureSkill* skill : mDefinition->getCreatureSkills())
2760         {
2761             CreatureSkillData skillData(skill, skill->getCooldownNbTurns(), 0);
2762             mSkillData.push_back(skillData);
2763         }
2764     }
2765 
2766     buildStats();
2767 
2768     // Now, the max hp is known. If needed, we set it
2769     if(setHpToStrHp)
2770     {
2771         if(mHpString.compare("max") == 0)
2772             mHp = mMaxHP;
2773         else
2774             mHp = Helper::toDouble(mHpString);
2775 
2776         computeCreatureOverlayHealthValue();
2777     }
2778 }
2779 
fireCreatureSound(CreatureSound sound)2780 void Creature::fireCreatureSound(CreatureSound sound)
2781 {
2782     Tile* posTile = getPositionTile();
2783     if(posTile == nullptr)
2784         return;
2785 
2786     std::string soundFamily;
2787     switch(sound)
2788     {
2789         case CreatureSound::Pickup:
2790             soundFamily = getDefinition()->getSoundFamilyPickup();
2791             break;
2792         case CreatureSound::Drop:
2793             soundFamily = getDefinition()->getSoundFamilyDrop();
2794             break;
2795         case CreatureSound::Attack:
2796             soundFamily = getDefinition()->getSoundFamilyAttack();
2797             break;
2798         case CreatureSound::Die:
2799             soundFamily = getDefinition()->getSoundFamilyDie();
2800             break;
2801         case CreatureSound::Slap:
2802             soundFamily = getDefinition()->getSoundFamilySlap();
2803             break;
2804         case CreatureSound::Dig:
2805             soundFamily = "Default/Dig";
2806             break;
2807         default:
2808             OD_LOG_ERR("Wrong CreatureSound value=" + Helper::toString(static_cast<uint32_t>(sound)));
2809             return;
2810     }
2811 
2812     std::string soundComplete = "Creatures/" + soundFamily;
2813     for(Seat* seat : mSeatsWithVisionNotified)
2814     {
2815         if(seat->getPlayer() == nullptr)
2816             continue;
2817         if(!seat->getPlayer()->getIsHuman())
2818             continue;
2819 
2820         ServerNotification *serverNotification = new ServerNotification(
2821             ServerNotificationType::playSpatialSound, seat->getPlayer());
2822         serverNotification->mPacket << soundComplete << posTile->getX() << posTile->getY();
2823         ODServer::getSingleton().queueServerNotification(serverNotification);
2824     }
2825 }
2826 
itsPayDay()2827 void Creature::itsPayDay()
2828 {
2829     // Rogue creatures do not have to be paid
2830     if(getSeat()->isRogueSeat())
2831         return;
2832 
2833     mGoldFee += mDefinition->getFee(getLevel());
2834 }
2835 
increaseHunger(double value)2836 void Creature::increaseHunger(double value)
2837 {
2838     if(getSeat()->isRogueSeat())
2839         return;
2840 
2841     mHunger = std::min(100.0, mHunger + value);
2842 }
2843 
decreaseWakefulness(double value)2844 void Creature::decreaseWakefulness(double value)
2845 {
2846     if(getSeat()->isRogueSeat())
2847         return;
2848 
2849     mWakefulness = std::max(0.0, mWakefulness - value);
2850 }
2851 
computeMood()2852 void Creature::computeMood()
2853 {
2854     mMoodPoints = CreatureMoodManager::computeCreatureMoodModifiers(*this);
2855 
2856     CreatureMoodLevel oldMoodValue = mMoodValue;
2857     mMoodValue = CreatureMoodManager::getCreatureMoodLevel(mMoodPoints);
2858     if(mMoodValue == oldMoodValue)
2859         return;
2860 
2861     if((mMoodValue >= CreatureMoodLevel::Furious) &&
2862        (oldMoodValue < CreatureMoodLevel::Furious))
2863     {
2864         // We became unhappy
2865         fireChatMsgFurious();
2866     }
2867     else if((mMoodValue > CreatureMoodLevel::Neutral) &&
2868        (oldMoodValue <= CreatureMoodLevel::Neutral))
2869     {
2870         // We became unhappy
2871         fireChatMsgUnhappy();
2872     }
2873 }
2874 
computeCreatureOverlayHealthValue()2875 void Creature::computeCreatureOverlayHealthValue()
2876 {
2877     if(!getIsOnServerMap())
2878         return;
2879 
2880     uint32_t value = 0;
2881     double hp = getHP();
2882     // Note that we make a special case for hp = 0 to avoid errors due to roundness
2883     if(hp <= 0)
2884     {
2885         value = NB_OVERLAY_HEALTH_VALUES - 1;
2886     }
2887     else
2888     {
2889         uint32_t nbSteps = NB_OVERLAY_HEALTH_VALUES - 2;
2890         double healthStep = getMaxHp() / static_cast<double>(nbSteps);
2891         double tmpHealth = getMaxHp();
2892         for(value = 0; value < nbSteps; ++value)
2893         {
2894             if(hp >= tmpHealth)
2895                 break;
2896 
2897             tmpHealth -= healthStep;
2898         }
2899     }
2900 
2901     if(mOverlayHealthValue != value)
2902     {
2903         mOverlayHealthValue = value;
2904         mNeedFireRefresh = true;
2905     }
2906 }
2907 
computeCreatureOverlayMoodValue()2908 void Creature::computeCreatureOverlayMoodValue()
2909 {
2910     if(!getIsOnServerMap())
2911         return;
2912 
2913     uint32_t value = 0;
2914     // The creature mood applies only if the creature is alive
2915     if(isAlive())
2916     {
2917         if(mMoodValue == CreatureMoodLevel::Angry)
2918             value |= CreatureMoodEnum::Angry;
2919         else if(mMoodValue == CreatureMoodLevel::Furious)
2920             value |= CreatureMoodEnum::Furious;
2921 
2922         if(isActionInList(CreatureActionType::getFee))
2923             value |= CreatureMoodEnum::GetFee;
2924 
2925         if(isActionInList(CreatureActionType::leaveDungeon))
2926             value |= CreatureMoodEnum::LeaveDungeon;
2927 
2928         if(mKoTurnCounter < 0)
2929             value |= CreatureMoodEnum::KoDeath;
2930         else if(mKoTurnCounter > 0)
2931             value |= CreatureMoodEnum::KoTemp;
2932 
2933         if(isHungry())
2934             value |= CreatureMoodEnum::Hungry;
2935 
2936         if(isTired())
2937             value |= CreatureMoodEnum::Tired;
2938 
2939         if(mSeatPrison != nullptr)
2940             value |= CreatureMoodEnum::InJail;
2941     }
2942 
2943     if(mOverlayMoodValue != value)
2944     {
2945         mOverlayMoodValue = value;
2946         mNeedFireRefresh = true;
2947     }
2948 }
2949 
addCreatureEffect(CreatureEffect * effect)2950 void Creature::addCreatureEffect(CreatureEffect* effect)
2951 {
2952     std::string effectName = nextParticleSystemsName();
2953 
2954     OD_LOG_INF("Added CreatureEffect name=" + effectName + " on creature=" + getName());
2955 
2956     CreatureParticuleEffect* particleEffect = new CreatureParticuleEffect(*this, effectName, effect->getParticleEffectScript(),
2957         effect->getNbTurnsEffect(), effect);
2958     mEntityParticleEffects.push_back(particleEffect);
2959 
2960     mNeedFireRefresh = true;
2961 }
2962 
isHurt() const2963 bool Creature::isHurt() const
2964 {
2965     //On server side, we test HP
2966     if(getIsOnServerMap())
2967         return getHP() < getMaxHp();
2968 
2969     // On client side, we test overlay value. 0 represents full health
2970     return mOverlayHealthValue > 0;
2971 }
2972 
isKo() const2973 bool Creature::isKo() const
2974 {
2975     if(getIsOnServerMap())
2976         return mKoTurnCounter != 0;
2977 
2978     // On client side, we test mood overlay value
2979     return (mOverlayMoodValue & KoDeathOrTemp) != 0;
2980 }
2981 
isKoDeath() const2982 bool Creature::isKoDeath() const
2983 {
2984     if(getIsOnServerMap())
2985         return mKoTurnCounter < 0;
2986 
2987     // On client side, we test mood overlay value
2988     return (mOverlayMoodValue & CreatureMoodEnum::KoDeath) != 0;
2989 }
2990 
isKoTemp() const2991 bool Creature::isKoTemp() const
2992 {
2993     if(getIsOnServerMap())
2994         return mKoTurnCounter > 0;
2995 
2996     // On client side, we test mood overlay value
2997     return (mOverlayMoodValue & CreatureMoodEnum::KoTemp) != 0;
2998 }
2999 
isInPrison() const3000 bool Creature::isInPrison() const
3001 {
3002     return mSeatPrison != nullptr;
3003 }
3004 
correctEntityMovePosition(Ogre::Vector3 & position)3005 void Creature::correctEntityMovePosition(Ogre::Vector3& position)
3006 {
3007     static const double offset = 0.3;
3008     if(position.x > 0)
3009         position.x += Random::Double(-offset, offset);
3010 
3011     if(position.y > 0)
3012         position.y += Random::Double(-offset, offset);
3013 
3014     if(position.z > 0)
3015         position.z += Random::Double(-offset, offset);
3016 }
3017 
checkWalkPathValid()3018 void Creature::checkWalkPathValid()
3019 {
3020     bool stop = false;
3021     for(const Ogre::Vector3& dest : mWalkQueue)
3022     {
3023         Tile* tile = getGameMap()->getTile(Helper::round(dest.x), Helper::round(dest.y));
3024         if(tile == nullptr)
3025         {
3026             stop = true;
3027             break;
3028         }
3029 
3030         if(!canGoThroughTile(tile))
3031         {
3032             stop = true;
3033             break;
3034         }
3035     }
3036 
3037     if(!stop)
3038         return;
3039 
3040     // There is an unpassable tile in our way. We stop what we are doing
3041     clearDestinations(EntityAnimation::idle_anim, true, true);
3042 }
3043 
setJobCooldown(int val)3044 void Creature::setJobCooldown(int val)
3045 {
3046     // If the creature has been slapped, its cooldown is decreased
3047     if(hasSlapEffect())
3048         val = Helper::round(static_cast<float>(val) * 0.8f);
3049 
3050     mJobCooldown = val;
3051 }
3052 
isTired() const3053 bool Creature::isTired() const
3054 {
3055     if(getIsOnServerMap())
3056         return mWakefulness <= 20.0;
3057 
3058     return (mOverlayMoodValue & CreatureMoodEnum::Tired) != 0;
3059 }
3060 
isHungry() const3061 bool Creature::isHungry() const
3062 {
3063     if(getIsOnServerMap())
3064         return mHunger >= 80.0;
3065 
3066     return (mOverlayMoodValue & CreatureMoodEnum::Hungry) != 0;
3067 }
3068 
resetKoTurns()3069 void Creature::resetKoTurns()
3070 {
3071     mKoTurnCounter = 0;
3072     mNeedFireRefresh = true;
3073 }
3074 
setInJail(Room * prison)3075 void Creature::setInJail(Room* prison)
3076 {
3077     if(prison == nullptr)
3078     {
3079         if(mSeatPrison == nullptr)
3080             return;
3081 
3082         mSeatPrison = nullptr;
3083         mNeedFireRefresh = true;
3084         return;
3085     }
3086 
3087     // Creature is set in prison
3088     if(mSeatPrison == prison->getSeat())
3089         return;
3090 
3091     mSeatPrison = prison->getSeat();
3092     mNeedFireRefresh = true;
3093 }
3094 
isDangerous(const Creature * creature,int distance) const3095 bool Creature::isDangerous(const Creature* creature, int distance) const
3096 {
3097     if(getDefinition()->isWorker())
3098         return false;
3099 
3100     return true;
3101 }
3102 
clientUpkeep()3103 void Creature::clientUpkeep()
3104 {
3105     MovableGameEntity::clientUpkeep();
3106     if(mDropCooldown > 0)
3107         --mDropCooldown;
3108 }
3109 
setMoveSpeedModifier(double modifier)3110 void Creature::setMoveSpeedModifier(double modifier)
3111 {
3112     mSpeedModifier = modifier;
3113 
3114     mGroundSpeed = mDefinition->getMoveSpeedGround();
3115     mWaterSpeed = mDefinition->getMoveSpeedWater();
3116     mLavaSpeed  = mDefinition->getMoveSpeedLava();
3117 
3118     double multiplier = mLevel - 1;
3119     if (multiplier > 0.0)
3120     {
3121         mGroundSpeed += mDefinition->getGroundSpeedPerLevel() * multiplier;
3122         mWaterSpeed += mDefinition->getWaterSpeedPerLevel() * multiplier;
3123         mLavaSpeed += mDefinition->getLavaSpeedPerLevel() * multiplier;
3124     }
3125 
3126     mGroundSpeed *= mSpeedModifier;
3127     mWaterSpeed *= mSpeedModifier;
3128     mLavaSpeed *= mSpeedModifier;
3129     mNeedFireRefresh = true;
3130 }
3131 
clearMoveSpeedModifier()3132 void Creature::clearMoveSpeedModifier()
3133 {
3134     setMoveSpeedModifier(1.0);
3135 }
3136 
setDefenseModifier(double phy,double mag,double ele)3137 void Creature::setDefenseModifier(double phy, double mag, double ele)
3138 {
3139     mPhysicalDefense = mDefinition->getPhysicalDefense();
3140     mMagicalDefense = mDefinition->getMagicalDefense();
3141     mElementDefense = mDefinition->getElementDefense();
3142 
3143     mPhysicalDefense += phy;
3144     mMagicalDefense += mag;
3145     mElementDefense += ele;
3146 
3147     // Improve the stats to the current level
3148     double multiplier = mLevel - 1;
3149     if (multiplier <= 0.0)
3150         return;
3151 
3152     mPhysicalDefense += mDefinition->getPhysicalDefPerLevel() * multiplier;
3153     mMagicalDefense += mDefinition->getMagicalDefPerLevel() * multiplier;
3154     mElementDefense += mDefinition->getElementDefPerLevel() * multiplier;
3155 
3156     mNeedFireRefresh = true;
3157 }
3158 
clearDefenseModifier()3159 void Creature::clearDefenseModifier()
3160 {
3161     setDefenseModifier(0.0, 0.0, 0.0);
3162 }
3163 
setStrengthModifier(double modifier)3164 void Creature::setStrengthModifier(double modifier)
3165 {
3166     mModifierStrength = modifier;
3167     // Since strength is not used on client side, no need to send it
3168 }
3169 
clearStrengthModifier()3170 void Creature::clearStrengthModifier()
3171 {
3172     setStrengthModifier(1.0);
3173 }
3174 
isWarmup() const3175 bool Creature::isWarmup() const
3176 {
3177     for(const CreatureSkillData& skillData : mSkillData)
3178     {
3179         if(skillData.mWarmup > 0)
3180             return true;
3181     }
3182 
3183     return false;
3184 }
3185 
fight()3186 void Creature::fight()
3187 {
3188     clearDestinations(EntityAnimation::idle_anim, true, true);
3189     clearActionQueue();
3190     bool ko = getSeat()->getKoCreatures();
3191     pushAction(Utils::make_unique<CreatureActionFight>(*this, nullptr, ko, true));
3192 }
3193 
fightCreature(Creature & creature,bool ko,bool notifyPlayerIfHit)3194 void Creature::fightCreature(Creature& creature, bool ko, bool notifyPlayerIfHit)
3195 {
3196     clearDestinations(EntityAnimation::idle_anim, true, true);
3197     clearActionQueue();
3198     pushAction(Utils::make_unique<CreatureActionFight>(*this, &creature, ko, notifyPlayerIfHit));
3199 }
3200 
flee()3201 void Creature::flee()
3202 {
3203     clearDestinations(EntityAnimation::idle_anim, true, true);
3204     clearActionQueue();
3205     pushAction(Utils::make_unique<CreatureActionFlee>(*this));
3206 }
3207 
sleep()3208 void Creature::sleep()
3209 {
3210     clearDestinations(EntityAnimation::idle_anim, true, true);
3211     clearActionQueue();
3212     pushAction(Utils::make_unique<CreatureActionSleep>(*this));
3213 }
3214 
leaveDungeon()3215 void Creature::leaveDungeon()
3216 {
3217     clearDestinations(EntityAnimation::idle_anim, true, true);
3218     clearActionQueue();
3219     pushAction(Utils::make_unique<CreatureActionLeaveDungeon>(*this));
3220 }
3221 
needsToEat(bool forced) const3222 bool Creature::needsToEat(bool forced) const
3223 {
3224     if (forced && getHunger() < 5.0)
3225         return false;
3226 
3227     if (!forced && (getHunger() <= Random::Double(0.0, 15.0)))
3228         return false;
3229 
3230     return true;
3231 }
3232 
changeSeat(Seat * newSeat)3233 void Creature::changeSeat(Seat* newSeat)
3234 {
3235     OD_LOG_INF("creature=" + getName() + " changes side from seatId=" + Helper::toString(getSeat()->getId()) + " to seatId=" + Helper::toString(newSeat->getId()));
3236     OD_ASSERT_TRUE_MSG(getSeat() != newSeat, "creature=" + getName() + ", seatId=" + Helper::toString(newSeat->getId()));
3237     setSeat(newSeat);
3238     mMoodValue = CreatureMoodLevel::Neutral;
3239     mMoodPoints = 0;
3240     mWakefulness = 100;
3241     mHunger = 0;
3242     mNbTurnsTorture = 0;
3243     mNbTurnsPrison = 0;
3244     mActiveSlapsCount = 0;
3245     clearDestinations(EntityAnimation::idle_anim, true, true);
3246     clearActionQueue();
3247     mNeedFireRefresh = true;
3248     if (getHomeTile() != nullptr)
3249     {
3250         RoomDormitory* home = static_cast<RoomDormitory*>(getHomeTile()->getCoveringBuilding());
3251         home->releaseTileForSleeping(getHomeTile(), this);
3252     }
3253 }
3254