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/CreatureDefinition.h"
19 
20 #include "creaturebehaviour/CreatureBehaviour.h"
21 #include "creaturebehaviour/CreatureBehaviourManager.h"
22 #include "creatureskill/CreatureSkill.h"
23 #include "creatureskill/CreatureSkillManager.h"
24 #include "creaturemood/CreatureMood.h"
25 #include "creaturemood/CreatureMoodManager.h"
26 #include "network/ODPacket.h"
27 #include "rooms/RoomManager.h"
28 #include "rooms/RoomType.h"
29 #include "utils/Helper.h"
30 #include "utils/LogManager.h"
31 
32 static CreatureRoomAffinity EMPTY_AFFINITY(RoomType::nullRoomType, 0, 0);
33 
CreatureDefinition(const std::string & className,CreatureJob job,const std::string & meshName,const std::string & bedMeshName,int bedDim1,int bedDim2,int bedPosX,int bedPosY,double bedOrientX,double bedOrientY,double sightRadius,int maxGoldCarryable,double digRate,double digRatePerLevel,double claimRate,double claimRatePerLevel,double minHP,double hpPerLevel,double hpHealPerTurn,double wakefulnessLostPerTurn,double hungerGrowthPerTurn,double moveSpeedGround,double moveSpeedWater,double moveSpeedLava,double groundSpeedPerLevel,double waterSpeedPerLevel,double lavaSpeedPerLevel,double physicalDefense,double physicalDefPerLevel,double magicalDefense,double magicalDefPerLevel,double elementDefense,double elementDefPerLevel,int32_t fightIdleDist,int32_t feeBase,int32_t feePerLevel,int32_t sleepHeal,int32_t turnsStunDropped)34 CreatureDefinition::CreatureDefinition(
35             const std::string&      className,
36             CreatureJob             job,
37             const std::string&      meshName,
38             const std::string&      bedMeshName,
39             int                     bedDim1,
40             int                     bedDim2,
41             int                     bedPosX,
42             int                     bedPosY,
43             double                  bedOrientX,
44             double                  bedOrientY,
45             double                  sightRadius,
46             int                     maxGoldCarryable,
47             double                  digRate,
48             double                  digRatePerLevel,
49             double                  claimRate,
50             double                  claimRatePerLevel,
51             double                  minHP,
52             double                  hpPerLevel,
53             double                  hpHealPerTurn,
54             double                  wakefulnessLostPerTurn,
55             double                  hungerGrowthPerTurn,
56             double                  moveSpeedGround,
57             double                  moveSpeedWater,
58             double                  moveSpeedLava,
59             double                  groundSpeedPerLevel,
60             double                  waterSpeedPerLevel,
61             double                  lavaSpeedPerLevel,
62             double                  physicalDefense,
63             double                  physicalDefPerLevel,
64             double                  magicalDefense,
65             double                  magicalDefPerLevel,
66             double                  elementDefense,
67             double                  elementDefPerLevel,
68             int32_t                 fightIdleDist,
69             int32_t                 feeBase,
70             int32_t                 feePerLevel,
71             int32_t                 sleepHeal,
72             int32_t                 turnsStunDropped) :
73         mCreatureJob (job),
74         mClassName   (className),
75         mMeshName    (meshName),
76         mBedMeshName (bedMeshName),
77         mBedDim1     (bedDim1),
78         mBedDim2     (bedDim2),
79         mBedPosX     (bedPosX),
80         mBedPosY     (bedPosY),
81         mBedOrientX  (bedOrientX),
82         mBedOrientY  (bedOrientY),
83         mSightRadius (sightRadius),
84         mMaxGoldCarryable (maxGoldCarryable),
85         mDigRate     (digRate),
86         mDigRatePerLevel (digRatePerLevel),
87         mClaimRate   (claimRate),
88         mClaimRatePerLevel(claimRatePerLevel),
89         mMinHP       (minHP),
90         mHpPerLevel  (hpPerLevel),
91         mHpHealPerTurn      (hpHealPerTurn),
92         mWakefulnessLostPerTurn(wakefulnessLostPerTurn),
93         mHungerGrowthPerTurn(hungerGrowthPerTurn),
94         mMoveSpeedGround    (moveSpeedGround),
95         mMoveSpeedWater     (moveSpeedWater),
96         mMoveSpeedLava      (moveSpeedLava),
97         mGroundSpeedPerLevel(groundSpeedPerLevel),
98         mWaterSpeedPerLevel (waterSpeedPerLevel),
99         mLavaSpeedPerLevel  (lavaSpeedPerLevel),
100         mPhysicalDefense    (physicalDefense),
101         mPhysicalDefPerLevel(physicalDefPerLevel),
102         mMagicalDefense     (magicalDefense),
103         mMagicalDefPerLevel (magicalDefPerLevel),
104         mElementDefense     (elementDefense),
105         mElementDefPerLevel (elementDefPerLevel),
106         mFightIdleDist      (fightIdleDist),
107         mFeeBase            (feeBase),
108         mFeePerLevel        (feePerLevel),
109         mSleepHeal          (sleepHeal),
110         mTurnsStunDropped   (turnsStunDropped),
111         mWeaponSpawnL       ("none"),
112         mWeaponSpawnR       ("none"),
113         mSoundFamilyPickup  ("Default/Pickup"),
114         mSoundFamilyDrop    ("Default/Drop"),
115         mSoundFamilyAttack  ("Default/Attack"),
116         mSoundFamilyDie     ("Default/Die"),
117         mSoundFamilySlap    ("Default/Slap")
118 {
119     mXPTable.assign(MAX_LEVEL - 1, 100.0);
120 }
121 
CreatureDefinition(const CreatureDefinition & def)122 CreatureDefinition::CreatureDefinition(const CreatureDefinition& def) :
123         mCreatureJob(def.mCreatureJob),
124         mClassName(def.mClassName),
125         mMeshName(def.mMeshName),
126         mBedMeshName(def.mBedMeshName),
127         mBedDim1(def.mBedDim1),
128         mBedDim2(def.mBedDim2),
129         mBedPosX(def.mBedPosX),
130         mBedPosY(def.mBedPosY),
131         mBedOrientX(def.mBedOrientX),
132         mBedOrientY(def.mBedOrientY),
133         mSightRadius(def.mSightRadius),
134         mMaxGoldCarryable(def.mMaxGoldCarryable),
135         mDigRate(def.mDigRate),
136         mDigRatePerLevel(def.mDigRatePerLevel),
137         mClaimRate(def.mClaimRate),
138         mClaimRatePerLevel(def.mClaimRatePerLevel),
139         mMinHP(def.mMinHP),
140         mHpPerLevel(def.mHpPerLevel),
141         mHpHealPerTurn(def.mHpHealPerTurn),
142         mWakefulnessLostPerTurn(def.mWakefulnessLostPerTurn),
143         mHungerGrowthPerTurn(def.mHungerGrowthPerTurn),
144         mMoveSpeedGround(def.mMoveSpeedGround),
145         mMoveSpeedWater(def.mMoveSpeedWater),
146         mMoveSpeedLava(def.mMoveSpeedLava),
147         mGroundSpeedPerLevel(def.mGroundSpeedPerLevel),
148         mWaterSpeedPerLevel(def.mWaterSpeedPerLevel),
149         mLavaSpeedPerLevel(def.mLavaSpeedPerLevel),
150         mPhysicalDefense(def.mPhysicalDefense),
151         mPhysicalDefPerLevel(def.mPhysicalDefPerLevel),
152         mMagicalDefense(def.mMagicalDefense),
153         mMagicalDefPerLevel(def.mMagicalDefPerLevel),
154         mElementDefense(def.mElementDefense),
155         mElementDefPerLevel(def.mElementDefPerLevel),
156         mFightIdleDist(def.mFightIdleDist),
157         mFeeBase(def.mFeeBase),
158         mFeePerLevel(def.mFeePerLevel),
159         mSleepHeal(def.mSleepHeal),
160         mTurnsStunDropped(def.mTurnsStunDropped),
161         mWeaponSpawnL(def.mWeaponSpawnL),
162         mWeaponSpawnR(def.mWeaponSpawnR),
163         mMoodModifierName(def.mMoodModifierName),
164         mSoundFamilyPickup(def.mSoundFamilyPickup),
165         mSoundFamilyDrop(def.mSoundFamilyDrop),
166         mSoundFamilyAttack(def.mSoundFamilyAttack),
167         mSoundFamilyDie(def.mSoundFamilyDie),
168         mSoundFamilySlap(def.mSoundFamilySlap)
169 {
170     for(const double& xp : def.mXPTable)
171     {
172         mXPTable.push_back(xp);
173     }
174     for(const CreatureRoomAffinity& aff : def.mRoomAffinity)
175     {
176         mRoomAffinity.push_back(aff);
177     }
178     for(const CreatureSkill* skill : def.mCreatureSkills)
179     {
180         CreatureSkill* cloned = CreatureSkillManager::clone(skill);
181         if(cloned == nullptr)
182         {
183             OD_LOG_ERR("Error while cloning creature def=" + def.getClassName() + ", skill=" + skill->getSkillName());
184             continue;
185         }
186         mCreatureSkills.push_back(cloned);
187     }
188     for(const CreatureBehaviour* behaviour : def.mCreatureBehaviours)
189     {
190         CreatureBehaviour* cloned = CreatureBehaviourManager::clone(behaviour);
191         if(cloned == nullptr)
192         {
193             OD_LOG_ERR("Error while cloning creature def=" + def.getClassName() + ", behaviour=" + behaviour->getName());
194             continue;
195         }
196         mCreatureBehaviours.push_back(cloned);
197     }
198     for(const CreatureMood* mood : def.mCreatureMoods)
199     {
200         CreatureMood* cloned = CreatureMoodManager::clone(mood);
201         if(cloned == nullptr)
202         {
203             OD_LOG_ERR("Error while cloning creature def=" + def.getClassName() + ", mood=" + mood->getModifierName());
204             continue;
205         }
206         mCreatureMoods.push_back(cloned);
207     }
208 }
209 
~CreatureDefinition()210 CreatureDefinition::~CreatureDefinition()
211 {
212     for(const CreatureSkill* skill : mCreatureSkills)
213     {
214         CreatureSkillManager::dispose(skill);
215     }
216     mCreatureSkills.clear();
217     for(const CreatureBehaviour* behaviour : mCreatureBehaviours)
218     {
219         CreatureBehaviourManager::dispose(behaviour);
220     }
221     mCreatureBehaviours.clear();
222     for(const CreatureMood* mood : mCreatureMoods)
223     {
224         CreatureMoodManager::dispose(mood);
225     }
226     mCreatureMoods.clear();
227 }
228 
getXPNeededWhenLevel(unsigned int level) const229 double CreatureDefinition::getXPNeededWhenLevel(unsigned int level) const
230 {
231     // Return 0.0, meaning there is an error.
232     if (level < 1 || level > mXPTable.size())
233     {
234         // This should never happen
235         OD_LOG_ERR("class=" + getClassName() + ", level=" + Helper::toString(level) + ", max level=" + Helper::toString(mXPTable.size()));
236         return 0.0;
237     }
238 
239     return mXPTable[level - 1];
240 }
241 
creatureJobFromString(const std::string & s)242 CreatureDefinition::CreatureJob CreatureDefinition::creatureJobFromString(const std::string& s)
243 {
244     if (s.compare("Worker") == 0)
245         return Worker;
246 
247     // Use Fighter as a default value
248     return Fighter;
249 }
250 
creatureJobToString(CreatureJob c)251 std::string CreatureDefinition::creatureJobToString(CreatureJob c)
252 {
253     switch (c)
254     {
255     case Worker:
256         return "Worker";
257 
258     default:
259     case Fighter:
260         return "Fighter";
261     }
262 }
263 
getFee(unsigned int level) const264 int32_t CreatureDefinition::getFee(unsigned int level) const
265 {
266     return mFeeBase + (mFeePerLevel * level);
267 }
268 
operator <<(ODPacket & os,const CreatureDefinition * c)269 ODPacket& operator<<(ODPacket& os, const CreatureDefinition* c)
270 {
271     std::string creatureJob = CreatureDefinition::creatureJobToString(c->mCreatureJob);
272     os << c->mClassName
273        << creatureJob
274        << c->mMeshName;
275     os << c->mBedMeshName << c->mBedDim1 << c->mBedDim2 << c->mBedPosX << c->mBedPosY << c->mBedOrientX << c->mBedOrientY;
276     os << c->mMinHP;
277     os << c->mHpPerLevel;
278     os << c->mHpHealPerTurn;
279     os << c->mWakefulnessLostPerTurn;
280     os << c->mHungerGrowthPerTurn;
281     os << c->mSightRadius;
282     os << c->mMaxGoldCarryable;
283     os << c->mDigRate << c->mDigRatePerLevel;
284     os << c->mClaimRate << c->mClaimRatePerLevel;
285     os << c->mMoveSpeedGround << c->mMoveSpeedWater << c->mMoveSpeedLava;
286     os << c->mGroundSpeedPerLevel << c->mWaterSpeedPerLevel << c->mLavaSpeedPerLevel;
287     os << c->mPhysicalDefense << c->mPhysicalDefPerLevel;
288     os << c->mMagicalDefense << c->mMagicalDefPerLevel;
289     os << c->mElementDefense << c->mElementDefPerLevel;
290     os << c->mFightIdleDist;
291     os << c->mFeeBase;
292     os << c->mFeePerLevel;
293     os << c->mSleepHeal;
294     os << c->mTurnsStunDropped;
295     os << c->mMoodModifierName;
296     os << c->mWeaponSpawnL;
297     os << c->mWeaponSpawnR;
298     os << c->mSoundFamilyPickup;
299     os << c->mSoundFamilyDrop;
300     os << c->mSoundFamilyAttack;
301     os << c->mSoundFamilyDie;
302     os << c->mSoundFamilySlap;
303 
304     for (unsigned int i = 0; i < c->mXPTable.size(); ++i)
305         os << c->mXPTable[i];
306 
307     return os;
308 }
309 
operator >>(ODPacket & is,CreatureDefinition * c)310 ODPacket& operator>>(ODPacket& is, CreatureDefinition* c)
311 {
312     std::string tempString;
313     is >> c->mClassName >> tempString;
314     c->mCreatureJob = CreatureDefinition::creatureJobFromString(tempString);
315     is >> c->mMeshName;
316     is >> c->mBedMeshName >> c->mBedDim1 >> c->mBedDim2 >>c->mBedPosX >> c->mBedPosY >> c->mBedOrientX >> c->mBedOrientY;
317     is >> c->mMinHP >> c->mHpPerLevel >> c->mHpHealPerTurn;
318     is >> c->mWakefulnessLostPerTurn >> c->mHungerGrowthPerTurn;
319     is >> c->mSightRadius;
320     is >> c->mMaxGoldCarryable;
321     is >> c->mDigRate >> c->mDigRatePerLevel;
322     is >> c->mClaimRate >> c->mClaimRatePerLevel;
323     is >> c->mMoveSpeedGround >> c->mMoveSpeedWater >> c->mMoveSpeedLava;
324     is >> c->mGroundSpeedPerLevel >> c->mWaterSpeedPerLevel >> c->mLavaSpeedPerLevel;
325     is >> c->mPhysicalDefense >> c->mPhysicalDefPerLevel;
326     is >> c->mMagicalDefense >> c->mMagicalDefPerLevel;
327     is >> c->mElementDefense >> c->mElementDefPerLevel;
328     is >> c->mFightIdleDist;
329     is >> c->mFeeBase;
330     is >> c->mFeePerLevel;
331     is >> c->mSleepHeal;
332     is >> c->mTurnsStunDropped;
333     is >> c->mMoodModifierName;
334     is >> c->mWeaponSpawnL;
335     is >> c->mWeaponSpawnR;
336     is >> c->mSoundFamilyPickup;
337     is >> c->mSoundFamilyDrop;
338     is >> c->mSoundFamilyAttack;
339     is >> c->mSoundFamilyDie;
340     is >> c->mSoundFamilySlap;
341 
342     for (unsigned int i = 0; i < c->mXPTable.size(); ++i)
343     {
344         double xpValue;
345         is >> xpValue;
346         c->mXPTable[i] = xpValue;
347     }
348 
349     return is;
350 }
351 
load(std::stringstream & defFile,const std::map<std::string,CreatureDefinition * > & defMap)352 CreatureDefinition* CreatureDefinition::load(std::stringstream& defFile, const std::map<std::string, CreatureDefinition*>& defMap)
353 {
354     if (!defFile.good())
355         return nullptr;
356 
357     CreatureDefinition* creatureDef = new CreatureDefinition();
358     if(!update(creatureDef, defFile, defMap))
359     {
360         delete creatureDef;
361         creatureDef = nullptr;
362     }
363 
364     return creatureDef;
365 
366 }
367 
update(CreatureDefinition * creatureDef,std::stringstream & defFile,const std::map<std::string,CreatureDefinition * > & defMap)368 bool CreatureDefinition::update(CreatureDefinition* creatureDef, std::stringstream& defFile, const std::map<std::string, CreatureDefinition*>& defMap)
369 {
370     std::string nextParam;
371     bool exit = false;
372     // Parameters that should not be overriden if a Creature definition is extended. They will be set after
373     // the class is copied if there is a base class
374     std::string name = creatureDef->mClassName;
375     std::string baseDefinition;
376     while (defFile.good())
377     {
378         if (exit)
379             break;
380 
381         if(!(defFile >> nextParam))
382             break;
383 
384         if (nextParam == "[/Creature]" || nextParam == "[/CreatureDefinitions]")
385         {
386             exit = true;
387             break;
388         }
389 
390         if (nextParam == "Name")
391         {
392             defFile >> name;
393             continue;
394         }
395 
396         if (nextParam == "BaseDefinition")
397         {
398             defFile >> baseDefinition;
399             auto it = defMap.find(baseDefinition);
400             if(it == defMap.end())
401             {
402                 OD_LOG_ERR("Couldn't find base class " + baseDefinition);
403                 return false;
404             }
405             // TODO : this seems weird. Operator "=" is not defined
406             *creatureDef = *(it->second);
407             continue;
408         }
409 
410         if (nextParam == "[XP]")
411         {
412             loadXPTable(defFile, creatureDef);
413             continue;
414         }
415 
416         if (nextParam == "[CreatureSkills]")
417         {
418             loadCreatureSkills(defFile, creatureDef);
419             continue;
420         }
421 
422         if (nextParam == "[CreatureBehaviours]")
423         {
424             loadCreatureBehaviours(defFile, creatureDef);
425             continue;
426         }
427 
428         if (nextParam == "[MoodModifiers]")
429         {
430             loadCreatureMoods(defFile, creatureDef);
431             continue;
432         }
433 
434         if (nextParam == "[RoomAffinity]")
435         {
436             loadRoomAffinity(defFile, creatureDef);
437             continue;
438         }
439 
440         if (nextParam != "[Stats]")
441             continue;
442 
443         while (defFile.good())
444         {
445             if (exit)
446                 break;
447 
448             if(!(defFile >> nextParam))
449                 break;
450 
451             if (nextParam == "[/Stats]")
452                 break;
453 
454             // Handle ill-formed files.
455             if (nextParam == "[/Creature]" || nextParam == "[/CreatureDefinitions]")
456             {
457                 exit = true;
458                 break;
459             }
460 
461             if (nextParam == "CreatureJob")
462             {
463                 defFile >> nextParam;
464                 creatureDef->mCreatureJob = CreatureDefinition::creatureJobFromString(nextParam);
465                 continue;
466             }
467             else if (nextParam == "MeshName")
468             {
469                 defFile >> nextParam;
470                 creatureDef->mMeshName = nextParam;
471                 continue;
472             }
473             else if (nextParam == "BedMeshName")
474             {
475                 defFile >> nextParam;
476                 creatureDef->mBedMeshName = nextParam;
477                 continue;
478             }
479             else if (nextParam == "BedDim")
480             {
481                 defFile >> nextParam;
482                 creatureDef->mBedDim1 = Helper::toInt(nextParam);
483                 defFile >> nextParam;
484                 creatureDef->mBedDim2 = Helper::toInt(nextParam);
485                 continue;
486             }
487             else if (nextParam == "BedSleepPos")
488             {
489                 defFile >> nextParam;
490                 creatureDef->mBedPosX = Helper::toInt(nextParam);
491                 defFile >> nextParam;
492                 creatureDef->mBedPosY = Helper::toInt(nextParam);
493                 defFile >> nextParam;
494                 creatureDef->mBedOrientX = Helper::toDouble(nextParam);
495                 defFile >> nextParam;
496                 creatureDef->mBedOrientY = Helper::toDouble(nextParam);
497                 continue;
498             }
499             else if (nextParam == "MinHP")
500             {
501                 defFile >> nextParam;
502                 creatureDef->mMinHP = Helper::toDouble(nextParam);
503                 continue;
504             }
505             else if (nextParam == "HP/Level")
506             {
507                 defFile >> nextParam;
508                 creatureDef->mHpPerLevel = Helper::toDouble(nextParam);
509                 continue;
510             }
511             else if (nextParam == "Heal/Turn")
512             {
513                 defFile >> nextParam;
514                 creatureDef->mHpHealPerTurn = Helper::toDouble(nextParam);
515                 continue;
516             }
517             else if (nextParam == "WakefulnessLost/Turn")
518             {
519                 defFile >> nextParam;
520                 creatureDef->mWakefulnessLostPerTurn = Helper::toDouble(nextParam);
521                 continue;
522             }
523             else if (nextParam == "HungerGrowth/Turn")
524             {
525                 defFile >> nextParam;
526                 creatureDef->mHungerGrowthPerTurn = Helper::toDouble(nextParam);
527                 continue;
528             }
529             else if (nextParam == "TileSightRadius")
530             {
531                 defFile >> nextParam;
532                 creatureDef->mSightRadius = Helper::toInt(nextParam);
533                 continue;
534             }
535             else if (nextParam == "MaxGoldCarryable")
536             {
537                 defFile >> nextParam;
538                 creatureDef->mMaxGoldCarryable = Helper::toInt(nextParam);
539                 continue;
540             }
541             else if (nextParam == "DigRate")
542             {
543                 defFile >> nextParam;
544                 creatureDef->mDigRate = Helper::toDouble(nextParam);
545                 continue;
546             }
547             else if (nextParam == "DigRate/Level")
548             {
549                 defFile >> nextParam;
550                 creatureDef->mDigRatePerLevel = Helper::toDouble(nextParam);
551                 continue;
552             }
553             else if (nextParam == "ClaimRate")
554             {
555                 defFile >> nextParam;
556                 creatureDef->mClaimRate = Helper::toDouble(nextParam);
557                 continue;
558             }
559             else if (nextParam == "ClaimRate/Level")
560             {
561                 defFile >> nextParam;
562                 creatureDef->mClaimRatePerLevel = Helper::toDouble(nextParam);
563                 continue;
564             }
565             else if (nextParam == "GroundMoveSpeed")
566             {
567                 defFile >> nextParam;
568                 creatureDef->mMoveSpeedGround = Helper::toDouble(nextParam);
569                 continue;
570             }
571             else if (nextParam == "WaterMoveSpeed")
572             {
573                 defFile >> nextParam;
574                 creatureDef->mMoveSpeedWater = Helper::toDouble(nextParam);
575                 continue;
576             }
577             else if (nextParam == "LavaMoveSpeed")
578             {
579                 defFile >> nextParam;
580                 creatureDef->mMoveSpeedLava = Helper::toDouble(nextParam);
581                 continue;
582             }
583             else if (nextParam == "GroundSpeed/Level")
584             {
585                 defFile >> nextParam;
586                 creatureDef->mGroundSpeedPerLevel = Helper::toDouble(nextParam);
587                 continue;
588             }
589             else if (nextParam == "WaterSpeed/Level")
590             {
591                 defFile >> nextParam;
592                 creatureDef->mWaterSpeedPerLevel = Helper::toDouble(nextParam);
593                 continue;
594             }
595             else if (nextParam == "LavaSpeed/Level")
596             {
597                 defFile >> nextParam;
598                 creatureDef->mLavaSpeedPerLevel = Helper::toDouble(nextParam);
599                 continue;
600             }
601 
602             else if (nextParam == "PhysicalDefense")
603             {
604                 defFile >> nextParam;
605                 creatureDef->mPhysicalDefense = Helper::toDouble(nextParam);
606                 continue;
607             }
608             else if (nextParam == "PhysicalDef/Level")
609             {
610                 defFile >> nextParam;
611                 creatureDef->mPhysicalDefPerLevel = Helper::toDouble(nextParam);
612                 continue;
613             }
614             else if (nextParam == "MagicalDefense")
615             {
616                 defFile >> nextParam;
617                 creatureDef->mMagicalDefense = Helper::toDouble(nextParam);
618                 continue;
619             }
620             else if (nextParam == "MagicalDef/Level")
621             {
622                 defFile >> nextParam;
623                 creatureDef->mMagicalDefPerLevel = Helper::toDouble(nextParam);
624                 continue;
625             }
626             else if (nextParam == "ElementDefense")
627             {
628                 defFile >> nextParam;
629                 creatureDef->mElementDefense = Helper::toDouble(nextParam);
630                 continue;
631             }
632             else if (nextParam == "ElementDef/Level")
633             {
634                 defFile >> nextParam;
635                 creatureDef->mElementDefPerLevel = Helper::toDouble(nextParam);
636                 continue;
637             }
638             else if (nextParam == "FightIdleDist")
639             {
640                 defFile >> nextParam;
641                 creatureDef->mFightIdleDist = Helper::toInt(nextParam);
642                 continue;
643             }
644             else if (nextParam == "FeeBase")
645             {
646                 defFile >> nextParam;
647                 creatureDef->mFeeBase = Helper::toInt(nextParam);
648                 continue;
649             }
650             else if (nextParam == "FeePerLevel")
651             {
652                 defFile >> nextParam;
653                 creatureDef->mFeePerLevel = Helper::toInt(nextParam);
654                 continue;
655             }
656             else if (nextParam == "SleepHeal")
657             {
658                 defFile >> nextParam;
659                 creatureDef->mSleepHeal = Helper::toDouble(nextParam);
660                 continue;
661             }
662             else if (nextParam == "TurnsStunDropped")
663             {
664                 defFile >> nextParam;
665                 creatureDef->mTurnsStunDropped = Helper::toInt(nextParam);
666                 continue;
667             }
668             else if (nextParam == "CreatureMoodName")
669             {
670                 defFile >> nextParam;
671                 creatureDef->mMoodModifierName = nextParam;
672                 continue;
673             }
674             else if (nextParam == "WeaponSpawnL")
675             {
676                 defFile >> creatureDef->mWeaponSpawnL;
677                 continue;
678             }
679             else if (nextParam == "WeaponSpawnR")
680             {
681                 defFile >> creatureDef->mWeaponSpawnR;
682                 continue;
683             }
684             else if (nextParam == "SoundFamilyPickup")
685             {
686                 defFile >> creatureDef->mSoundFamilyPickup;
687                 continue;
688             }
689             else if (nextParam == "moundFamilyDrop")
690             {
691                 defFile >> creatureDef->mSoundFamilyDrop;
692                 continue;
693             }
694             else if (nextParam == "SoundFamilyAttack")
695             {
696                 defFile >> creatureDef->mSoundFamilyAttack;
697                 continue;
698             }
699             else if (nextParam == "SoundFamilyDie")
700             {
701                 defFile >> creatureDef->mSoundFamilyDie;
702                 continue;
703             }
704             else if (nextParam == "SoundFamilySlap")
705             {
706                 defFile >> creatureDef->mSoundFamilySlap;
707                 continue;
708             }
709         }
710     }
711 
712     if (name.empty())
713     {
714         OD_LOG_ERR("Cannot have empty creature def name");
715         return false;
716     }
717     creatureDef->mClassName = name;
718     creatureDef->mBaseDefinition = baseDefinition;
719 
720     return true;
721 }
722 
writeCreatureDefinitionDiff(const CreatureDefinition * def1,const CreatureDefinition * def2,std::ostream & file,const std::map<std::string,CreatureDefinition * > & defMap)723 void CreatureDefinition::writeCreatureDefinitionDiff(
724     const CreatureDefinition* def1, const CreatureDefinition* def2,
725     std::ostream& file, const std::map<std::string, CreatureDefinition*>& defMap)
726 {
727     file << "[Creature]" << std::endl;
728     file << "    Name\t" << def2->mClassName << std::endl;
729     if(!def2->mBaseDefinition.empty())
730     {
731         // If there is a base definition, we take it as the reference no matter what def1 is because
732         // we want to write only the differences between the reference and def2
733         auto it = defMap.find(def2->mBaseDefinition);
734         if(it != defMap.end())
735         {
736             def1 = it->second;
737         }
738         OD_ASSERT_TRUE_MSG(def1 != nullptr, "BaseDefinition=" + def2->mBaseDefinition);
739         file << "    BaseDefinition\t" << def2->mBaseDefinition << std::endl;
740     }
741     file << "    [Stats]" << std::endl;
742 
743     if(def1 == nullptr || (def1->mCreatureJob != def2->mCreatureJob))
744         file << "    CreatureJob\t" << creatureJobToString(def2->mCreatureJob) << std::endl;
745 
746     if(def1 == nullptr || (def1->mMeshName.compare(def2->mMeshName) != 0))
747         file << "    MeshName\t" << def2->mMeshName << std::endl;
748 
749     if(def1 == nullptr || (def1->mBedMeshName.compare(def2->mBedMeshName) != 0))
750         file << "    BedMeshName\t" << def2->mBedMeshName << std::endl;
751 
752     if(def1 == nullptr || (def1->mBedDim1 != def2->mBedDim1) || (def1->mBedDim2 != def2->mBedDim2))
753         file << "    BedDim\t" << def2->mBedDim1 << "\t" << def2->mBedDim2 << std::endl;
754 
755     if(def1 == nullptr || (def1->mBedPosX != def2->mBedPosX) || (def1->mBedPosY != def2->mBedPosY) || (def1->mBedOrientX != def2->mBedOrientX) || (def1->mBedOrientY != def2->mBedOrientY))
756         file << "    BedPos\t" << def2->mBedPosX << "\t" << def2->mBedPosY << "\t" << def2->mBedOrientX << "\t" << def2->mBedOrientY << std::endl;
757 
758     if(def1 == nullptr || (def1->mMinHP != def2->mMinHP))
759         file << "    MinHP\t" << def2->mMinHP << std::endl;
760 
761     if(def1 == nullptr || (def1->mHpPerLevel != def2->mHpPerLevel))
762         file << "    HP/Level\t" << def2->mHpPerLevel << std::endl;
763 
764     if(def1 == nullptr || (def1->mHpHealPerTurn != def2->mHpHealPerTurn))
765         file << "    Heal/Turn\t" << def2->mHpHealPerTurn << std::endl;
766 
767     if(def1 == nullptr || (def1->mWakefulnessLostPerTurn != def2->mWakefulnessLostPerTurn))
768         file << "    WakefulnessLost/Turn\t" << def2->mWakefulnessLostPerTurn << std::endl;
769 
770     if(def1 == nullptr || (def1->mHungerGrowthPerTurn != def2->mHungerGrowthPerTurn))
771         file << "    HungerGrowth/Turn\t" << def2->mHungerGrowthPerTurn << std::endl;
772 
773     if(def1 == nullptr || (def1->mSightRadius != def2->mSightRadius))
774         file << "    TileSightRadius\t" << def2->mSightRadius << std::endl;
775 
776     if(def1 == nullptr || (def1->mMaxGoldCarryable != def2->mMaxGoldCarryable))
777         file << "    MaxGoldCarryable\t" << def2->mMaxGoldCarryable << std::endl;
778 
779     if(def1 == nullptr || (def1->mDigRate != def2->mDigRate))
780         file << "    DigRate\t" << def2->mDigRate << std::endl;
781 
782     if(def1 == nullptr || (def1->mDigRatePerLevel != def2->mDigRatePerLevel))
783         file << "    DigRate/Level\t" << def2->mDigRatePerLevel << std::endl;
784 
785     if(def1 == nullptr || (def1->mClaimRate != def2->mClaimRate))
786         file << "    ClaimRate\t" << def2->mClaimRate << std::endl;
787 
788     if(def1 == nullptr || (def1->mClaimRatePerLevel != def2->mClaimRatePerLevel))
789         file << "    ClaimRate/Level\t" << def2->mClaimRatePerLevel << std::endl;
790 
791     if(def1 == nullptr || (def1->mMoveSpeedGround != def2->mMoveSpeedGround))
792         file << "    GroundMoveSpeed\t" << def2->mMoveSpeedGround << std::endl;
793 
794     if(def1 == nullptr || (def1->mMoveSpeedWater != def2->mMoveSpeedWater))
795         file << "    WaterMoveSpeed\t" << def2->mMoveSpeedWater << std::endl;
796 
797     if(def1 == nullptr || (def1->mMoveSpeedLava != def2->mMoveSpeedLava))
798         file << "    LavaMoveSpeed\t" << def2->mMoveSpeedLava << std::endl;
799 
800     if(def1 == nullptr || (def1->mGroundSpeedPerLevel != def2->mGroundSpeedPerLevel))
801         file << "    GroundSpeed/Level\t" << def2->mGroundSpeedPerLevel << std::endl;
802 
803     if(def1 == nullptr || (def1->mWaterSpeedPerLevel != def2->mWaterSpeedPerLevel))
804         file << "    WaterSpeed/Level\t" << def2->mWaterSpeedPerLevel << std::endl;
805 
806     if(def1 == nullptr || (def1->mLavaSpeedPerLevel != def2->mLavaSpeedPerLevel))
807         file << "    LavaSpeed/Level\t" << def2->mLavaSpeedPerLevel << std::endl;
808 
809     if(def1 == nullptr || (def1->mPhysicalDefense != def2->mPhysicalDefense))
810         file << "    PhysicalDefense\t" << def2->mPhysicalDefense << std::endl;
811 
812     if(def1 == nullptr || (def1->mPhysicalDefPerLevel != def2->mPhysicalDefPerLevel))
813         file << "    PhysicalDef/Level\t" << def2->mPhysicalDefPerLevel << std::endl;
814 
815     if(def1 == nullptr || (def1->mMagicalDefense != def2->mMagicalDefense))
816         file << "    MagicalDefense\t" << def2->mMagicalDefense << std::endl;
817 
818     if(def1 == nullptr || (def1->mMagicalDefPerLevel != def2->mMagicalDefPerLevel))
819         file << "    MagicalDef/Level\t" << def2->mMagicalDefPerLevel << std::endl;
820 
821     if(def1 == nullptr || (def1->mElementDefense != def2->mElementDefense))
822         file << "    ElementDefense\t" << def2->mElementDefense << std::endl;
823 
824     if(def1 == nullptr || (def1->mElementDefPerLevel != def2->mElementDefPerLevel))
825         file << "    ElementDef/Level\t" << def2->mElementDefPerLevel << std::endl;
826 
827     if(def1 == nullptr || (def1->mFightIdleDist != def2->mFightIdleDist))
828         file << "    FightIdleDist\t" << def2->mFightIdleDist << std::endl;
829 
830     if(def1 == nullptr || (def1->mFeeBase != def2->mFeeBase))
831         file << "    FeeBase\t" << def2->mFeeBase << std::endl;
832 
833     if(def1 == nullptr || (def1->mFeePerLevel != def2->mFeePerLevel))
834         file << "    FeePerLevel\t" << def2->mFeePerLevel << std::endl;
835 
836     if(def1 == nullptr || (def1->mSleepHeal != def2->mSleepHeal))
837         file << "    SleepHeal\t" << def2->mSleepHeal << std::endl;
838 
839     if(def1 == nullptr || (def1->mTurnsStunDropped != def2->mTurnsStunDropped))
840         file << "    TurnsStunDropped\t" << def2->mTurnsStunDropped << std::endl;
841 
842     if(!def2->mMoodModifierName.empty() && (def1 == nullptr || (def1->mMoodModifierName != def2->mMoodModifierName)))
843         file << "    CreatureMoodName\t" << def2->mMoodModifierName << std::endl;
844 
845     if(def1 == nullptr || (def1->mWeaponSpawnL.compare(def2->mWeaponSpawnL) != 0))
846         file << "    WeaponSpawnL\t" << def2->mWeaponSpawnL << std::endl;
847 
848     if(def1 == nullptr || (def1->mWeaponSpawnR.compare(def2->mWeaponSpawnR) != 0))
849         file << "    WeaponSpawnL\t" << def2->mWeaponSpawnR << std::endl;
850 
851     if(def1 == nullptr || (def1->mSoundFamilyPickup.compare(def2->mSoundFamilyPickup) != 0))
852         file << "    SoundFamilyPickup\t" << def2->mSoundFamilyPickup << std::endl;
853 
854     if(def1 == nullptr || (def1->mSoundFamilyDrop.compare(def2->mSoundFamilyDrop) != 0))
855         file << "    SoundFamilyDrop\t" << def2->mSoundFamilyDrop << std::endl;
856 
857     if(def1 == nullptr || (def1->mSoundFamilyAttack.compare(def2->mSoundFamilyAttack) != 0))
858         file << "    SoundFamilyAttack\t" << def2->mSoundFamilyAttack << std::endl;
859 
860     if(def1 == nullptr || (def1->mSoundFamilyDie.compare(def2->mSoundFamilyDie) != 0))
861         file << "    SoundFamilyDie\t" << def2->mSoundFamilyDie << std::endl;
862 
863     if(def1 == nullptr || (def1->mSoundFamilySlap.compare(def2->mSoundFamilySlap) != 0))
864         file << "    SoundFamilySlap\t" << def2->mSoundFamilySlap << std::endl;
865 
866     file << "    [/Stats]" << std::endl;
867 
868     bool isSame;
869 
870     isSame = (def1 != nullptr && def1->mRoomAffinity.size() == def2->mRoomAffinity.size());
871     uint32_t index = 0;
872     while(isSame &&
873           index < def2->mRoomAffinity.size())
874     {
875         isSame = def1->mRoomAffinity[index] == def2->mRoomAffinity[index];
876         ++index;
877     }
878     if(!isSame)
879     {
880         file << "    [RoomAffinity]" << std::endl;
881         for(const CreatureRoomAffinity& roomAffinity : def2->mRoomAffinity)
882         {
883             file << "    " << RoomManager::getRoomNameFromRoomType(roomAffinity.getRoomType());
884             file << "\t" << roomAffinity.getLikeness();
885             file << "\t" << roomAffinity.getEfficiency();
886             file << std::endl;
887         }
888         file << "    [/RoomAffinity]" << std::endl;
889     }
890 
891     isSame = true;
892     for(uint32_t i = 0; i < (MAX_LEVEL - 1); ++i)
893     {
894         if(def1 == nullptr || (def1->mXPTable[i] != def2->mXPTable[i]))
895         {
896             isSame = false;
897             break;
898         }
899     }
900     if(!isSame)
901     {
902         file << "    [XP]" << std::endl;
903         uint32_t i = 2;
904         uint32_t levelMax;
905         while(i <= MAX_LEVEL)
906         {
907             levelMax = std::min(i + 10U - (i % 10), MAX_LEVEL);
908             file << "    # " << i <<"-" << levelMax << std::endl;
909             file << "    ";
910 
911             while(i < levelMax)
912             {
913                 file << def2->mXPTable[i - 2] << "\t";
914                 ++i;
915             }
916             file << def2->mXPTable[i - 2] << std::endl;
917             ++i;
918         }
919         file << "    [/XP]" << std::endl;
920     }
921 
922     isSame = (def1 != nullptr && (def1->mCreatureMoods.size() == def2->mCreatureMoods.size()));
923     if(isSame)
924     {
925         for(uint32_t i = 0; i < def1->mCreatureMoods.size(); ++i)
926         {
927             if(!CreatureMoodManager::areEqual(*def1->mCreatureMoods[i], *def2->mCreatureMoods[i]))
928             {
929                 isSame = false;
930                 break;
931             }
932         }
933     }
934     if(!isSame && !def2->mCreatureMoods.empty())
935     {
936         file << "    [MoodModifiers]" << std::endl;
937         for(const CreatureMood* mood : def2->mCreatureMoods)
938         {
939             std::string format;
940             CreatureMoodManager::getFormatString(*mood, format);
941             file << "    " << format << std::endl;
942             file << "    ";
943             CreatureMoodManager::write(*mood, file);
944             file << std::endl;
945         }
946         file << "    [/MoodModifiers]" << std::endl;
947     }
948 
949     isSame = (def1 != nullptr && (def1->mCreatureSkills.size() == def2->mCreatureSkills.size()));
950     if(isSame)
951     {
952         for(uint32_t i = 0; i < def1->mCreatureSkills.size(); ++i)
953         {
954             if(!CreatureSkillManager::areEqual(*def1->mCreatureSkills[i], *def2->mCreatureSkills[i]))
955             {
956                 isSame = false;
957                 break;
958             }
959         }
960     }
961     if(!isSame && !def2->mCreatureSkills.empty())
962     {
963         file << "    [CreatureSkills]" << std::endl;
964         for(const CreatureSkill* skill : def2->mCreatureSkills)
965         {
966             std::string format;
967             CreatureSkillManager::getFormatString(*skill, format);
968             file << "    " << format << std::endl;
969             file << "    ";
970             CreatureSkillManager::write(*skill, file);
971             file << std::endl;
972         }
973         file << "    [/CreatureSkills]" << std::endl;
974     }
975 
976     isSame = (def1 != nullptr && (def1->mCreatureBehaviours.size() == def2->mCreatureBehaviours.size()));
977     if(isSame)
978     {
979         for(uint32_t i = 0; i < def1->mCreatureBehaviours.size(); ++i)
980         {
981             if(!CreatureBehaviourManager::areEqual(*def1->mCreatureBehaviours[i], *def2->mCreatureBehaviours[i]))
982             {
983                 isSame = false;
984                 break;
985             }
986         }
987     }
988     if(!isSame && !def2->mCreatureBehaviours.empty())
989     {
990         file << "    [CreatureBehaviours]" << std::endl;
991         for(const CreatureBehaviour* behaviour : def2->mCreatureBehaviours)
992         {
993             std::string format;
994             CreatureBehaviourManager::getFormatString(*behaviour, format);
995             file << "    " << format << std::endl;
996             file << "    ";
997             CreatureBehaviourManager::write(*behaviour, file);
998             file << std::endl;
999         }
1000         file << "    [/CreatureBehaviours]" << std::endl;
1001     }
1002     file << "[/Creature]" << std::endl;
1003 }
1004 
loadXPTable(std::stringstream & defFile,CreatureDefinition * creatureDef)1005 void CreatureDefinition::loadXPTable(std::stringstream& defFile, CreatureDefinition* creatureDef)
1006 {
1007     if (creatureDef == nullptr)
1008     {
1009         OD_LOG_ERR("Cannot load null creature def");
1010         return;
1011     }
1012 
1013     std::string nextParam;
1014     bool exit = false;
1015 
1016     // The XP index
1017     unsigned int i = 0;
1018 
1019     while (defFile.good())
1020     {
1021         if (exit)
1022             break;
1023 
1024         if(!(defFile >> nextParam))
1025             break;
1026 
1027         if (nextParam == "[/XP]" || nextParam == "[/Stats]" ||
1028             nextParam == "[/Creature]" || nextParam == "[/Creatures]")
1029         {
1030             exit = true;
1031             break;
1032         }
1033 
1034         // Ignore values after the max level
1035         if (i >= MAX_LEVEL - 1)
1036         {
1037             OD_LOG_ERR("creatureDef=" + creatureDef->getClassName() + ", i=" + Helper::toString(i < MAX_LEVEL - 1));
1038             continue;
1039         }
1040 
1041         creatureDef->mXPTable[i++] = Helper::toDouble(nextParam);
1042     }
1043 }
1044 
loadCreatureSkills(std::stringstream & defFile,CreatureDefinition * creatureDef)1045 void CreatureDefinition::loadCreatureSkills(std::stringstream& defFile, CreatureDefinition* creatureDef)
1046 {
1047     if (creatureDef == nullptr)
1048     {
1049         OD_LOG_ERR("Cannot load null creature def");
1050         return;
1051     }
1052 
1053     if(!defFile.good())
1054     {
1055         OD_LOG_ERR("input file invalid");
1056         return;
1057     }
1058 
1059     std::string nextParam;
1060     // We want to start on the next line
1061     std::getline(defFile, nextParam);
1062     while (defFile.good())
1063     {
1064         if(!Helper::readNextLineNotEmpty(defFile, nextParam))
1065             break;
1066 
1067         if (nextParam == "[/CreatureSkills]"||
1068             nextParam == "[/Creature]" || nextParam == "[/Creatures]")
1069         {
1070             break;
1071         }
1072 
1073         std::stringstream ss(nextParam);
1074         CreatureSkill* skill = CreatureSkillManager::load(ss);
1075         if (skill == nullptr)
1076         {
1077             OD_LOG_ERR("line=" + nextParam);
1078             continue;
1079         }
1080 
1081         creatureDef->mCreatureSkills.push_back(skill);
1082     }
1083 }
1084 
loadCreatureBehaviours(std::stringstream & defFile,CreatureDefinition * creatureDef)1085 void CreatureDefinition::loadCreatureBehaviours(std::stringstream& defFile, CreatureDefinition* creatureDef)
1086 {
1087     if (creatureDef == nullptr)
1088     {
1089         OD_LOG_ERR("Cannot load null creature def");
1090         return;
1091     }
1092 
1093     if(!defFile.good())
1094     {
1095         OD_LOG_ERR("input file invalid");
1096         return;
1097     }
1098 
1099     std::string nextParam;
1100     // We want to start on the next line
1101     std::getline(defFile, nextParam);
1102     while (defFile.good())
1103     {
1104         if(!Helper::readNextLineNotEmpty(defFile, nextParam))
1105             break;
1106 
1107         if (nextParam == "[/CreatureBehaviours]" ||
1108             nextParam == "[/Creature]" || nextParam == "[/Creatures]")
1109         {
1110             break;
1111         }
1112 
1113         std::stringstream ss(nextParam);
1114         CreatureBehaviour* behaviour = CreatureBehaviourManager::load(ss);
1115         if (behaviour == nullptr)
1116         {
1117             OD_LOG_ERR("line=" + nextParam);
1118             continue;
1119         }
1120 
1121         creatureDef->mCreatureBehaviours.push_back(behaviour);
1122     }
1123 }
1124 
loadCreatureMoods(std::stringstream & defFile,CreatureDefinition * creatureDef)1125 void CreatureDefinition::loadCreatureMoods(std::stringstream& defFile, CreatureDefinition* creatureDef)
1126 {
1127     if (creatureDef == nullptr)
1128     {
1129         OD_LOG_ERR("Cannot load null creature def");
1130         return;
1131     }
1132 
1133     if(!defFile.good())
1134     {
1135         OD_LOG_ERR("input file invalid");
1136         return;
1137     }
1138 
1139     std::string nextParam;
1140     // We want to start on the next line
1141     std::getline(defFile, nextParam);
1142     while (defFile.good())
1143     {
1144         if(!Helper::readNextLineNotEmpty(defFile, nextParam))
1145             break;
1146 
1147         if (nextParam == "[/MoodModifiers]" ||
1148             nextParam == "[/Creature]" || nextParam == "[/Creatures]")
1149         {
1150             break;
1151         }
1152 
1153         std::stringstream ss(nextParam);
1154         CreatureMood* mood = CreatureMoodManager::load(ss);
1155         if (mood == nullptr)
1156         {
1157             OD_LOG_ERR("line=" + nextParam);
1158             continue;
1159         }
1160 
1161         creatureDef->mCreatureMoods.push_back(mood);
1162     }
1163 }
1164 
loadRoomAffinity(std::stringstream & defFile,CreatureDefinition * creatureDef)1165 void CreatureDefinition::loadRoomAffinity(std::stringstream& defFile, CreatureDefinition* creatureDef)
1166 {
1167     OD_ASSERT_TRUE(creatureDef != nullptr);
1168     if (creatureDef == nullptr)
1169     {
1170         OD_LOG_ERR("Cannot load room affinity for null creatureDef");
1171         return;
1172     }
1173 
1174     std::string nextParam;
1175     bool exit = false;
1176 
1177     creatureDef->mRoomAffinity.clear();
1178     while (defFile.good())
1179     {
1180         if (exit)
1181             break;
1182 
1183         if(!(defFile >> nextParam))
1184             break;
1185 
1186         if (nextParam == "[/RoomAffinity]" ||
1187             nextParam == "[/Creature]" || nextParam == "[/Creatures]")
1188         {
1189             exit = true;
1190             break;
1191         }
1192 
1193         std::string roomName = nextParam;
1194 
1195         if(!(defFile >> nextParam))
1196             break;
1197 
1198         if (nextParam == "[/RoomAffinity]" ||
1199             nextParam == "[/Creature]" || nextParam == "[/Creatures]")
1200         {
1201             exit = true;
1202             break;
1203         }
1204         int32_t likeness = Helper::toInt(nextParam);
1205 
1206         if(!(defFile >> nextParam))
1207             break;
1208 
1209         if (nextParam == "[/RoomAffinity]" ||
1210             nextParam == "[/Creature]" || nextParam == "[/Creatures]")
1211         {
1212             exit = true;
1213             break;
1214         }
1215         double efficiency = Helper::toDouble(nextParam);
1216 
1217         RoomType roomType = RoomManager::getRoomTypeFromRoomName(roomName);
1218         if(roomType == RoomType::nullRoomType)
1219         {
1220             OD_LOG_ERR("Unknown room name=" + roomName);
1221             continue;
1222         }
1223 
1224         // We sort the CreatureRoomAffinity from the most liked to the less
1225         std::vector<CreatureRoomAffinity>::iterator it = creatureDef->mRoomAffinity.begin();
1226         while(it != creatureDef->mRoomAffinity.end())
1227         {
1228             CreatureRoomAffinity& roomAffinity = *it;
1229             if(roomAffinity.getLikeness() <= likeness)
1230                 break;
1231 
1232             ++it;
1233         }
1234         creatureDef->mRoomAffinity.insert(it, CreatureRoomAffinity(roomType, likeness, efficiency));
1235     }
1236 }
1237 
getRoomAffinity(RoomType roomType) const1238 const CreatureRoomAffinity& CreatureDefinition::getRoomAffinity(RoomType roomType) const
1239 {
1240     for(const CreatureRoomAffinity& roomAffinity : mRoomAffinity)
1241     {
1242         if(roomAffinity.getRoomType() != roomType)
1243             continue;
1244 
1245         return roomAffinity;
1246     }
1247 
1248     return EMPTY_AFFINITY;
1249 }
1250