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