1 /***************************************************************************
2  *   Copyright (C) 2009 by Andrey Afletdinov <fheroes2@gmail.com>          *
3  *                                                                         *
4  *   Part of the Free Heroes2 Engine:                                      *
5  *   http://sourceforge.net/projects/fheroes2                              *
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU General Public License as published by  *
9  *   the Free Software Foundation; either version 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  *                                                                         *
12  *   This program is distributed in the hope that it will be useful,       *
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15  *   GNU General Public License for more details.                          *
16  *                                                                         *
17  *   You should have received a copy of the GNU General Public License     *
18  *   along with this program; if not, write to the                         *
19  *   Free Software Foundation, Inc.,                                       *
20  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
21  ***************************************************************************/
22 
23 #include <cmath>
24 
25 #include "castle.h"
26 #include "difficulty.h"
27 #include "game.h"
28 #include "icn.h"
29 #include "luck.h"
30 #include "monster.h"
31 #include "morale.h"
32 #include "race.h"
33 #include "rand.h"
34 #include "save_format_version.h"
35 #include "serialize.h"
36 #include "settings.h"
37 #include "speed.h"
38 #include "translations.h"
39 
GetMissileICN(uint32_t monsterID)40 uint32_t Monster::GetMissileICN( uint32_t monsterID )
41 {
42     switch ( monsterID ) {
43     case Monster::ARCHER:
44     case Monster::RANGER:
45         return ICN::ARCH_MSL;
46     case Monster::ORC:
47     case Monster::ORC_CHIEF:
48         return ICN::ORC__MSL;
49     case Monster::TROLL:
50         return ICN::TROLLMSL;
51     case Monster::WAR_TROLL:
52         return ICN::TROLL2MSL;
53     case Monster::ELF:
54     case Monster::GRAND_ELF:
55         return ICN::ELF__MSL;
56     case Monster::DRUID:
57     case Monster::GREATER_DRUID:
58         return ICN::DRUIDMSL;
59     case Monster::CENTAUR:
60         // Doesn't have own missile file, game falls back to ELF__MSL
61         return ICN::ELF__MSL;
62     case Monster::HALFLING:
63         return ICN::HALFLMSL;
64     case Monster::TITAN:
65         return ICN::TITANMSL;
66     case Monster::LICH:
67     case Monster::POWER_LICH:
68         return ICN::LICH_MSL;
69 
70     default:
71         break;
72     }
73 
74     return ICN::UNKNOWN;
75 }
76 
Monster(const int m)77 Monster::Monster( const int m )
78     : id( UNKNOWN )
79 {
80     if ( m <= WATER_ELEMENT ) {
81         id = m;
82     }
83     else if ( MONSTER_RND1 == m )
84         id = Rand( LevelType::LEVEL_1 ).GetID();
85     else if ( MONSTER_RND2 == m )
86         id = Rand( LevelType::LEVEL_2 ).GetID();
87     else if ( MONSTER_RND3 == m )
88         id = Rand( LevelType::LEVEL_3 ).GetID();
89     else if ( MONSTER_RND4 == m )
90         id = Rand( LevelType::LEVEL_4 ).GetID();
91     else if ( MONSTER_RND == m )
92         id = Rand( LevelType::LEVEL_ANY ).GetID();
93 }
94 
Monster(const Spell & sp)95 Monster::Monster( const Spell & sp )
96     : id( UNKNOWN )
97 {
98     switch ( sp.GetID() ) {
99     case Spell::SETEGUARDIAN:
100     case Spell::SUMMONEELEMENT:
101         id = EARTH_ELEMENT;
102         break;
103 
104     case Spell::SETAGUARDIAN:
105     case Spell::SUMMONAELEMENT:
106         id = AIR_ELEMENT;
107         break;
108 
109     case Spell::SETFGUARDIAN:
110     case Spell::SUMMONFELEMENT:
111         id = FIRE_ELEMENT;
112         break;
113 
114     case Spell::SETWGUARDIAN:
115     case Spell::SUMMONWELEMENT:
116         id = WATER_ELEMENT;
117         break;
118 
119     case Spell::HAUNT:
120         id = GHOST;
121         break;
122     default:
123         break;
124     }
125 }
126 
Monster(int race,u32 dw)127 Monster::Monster( int race, u32 dw )
128     : id( UNKNOWN )
129 {
130     id = FromDwelling( race, dw ).id;
131 }
132 
isValid(void) const133 bool Monster::isValid( void ) const
134 {
135     return id != UNKNOWN;
136 }
137 
operator ==(const Monster & m) const138 bool Monster::operator==( const Monster & m ) const
139 {
140     return id == m.id;
141 }
142 
operator !=(const Monster & m) const143 bool Monster::operator!=( const Monster & m ) const
144 {
145     return id != m.id;
146 }
147 
Upgrade(void)148 void Monster::Upgrade( void )
149 {
150     id = GetUpgrade().id;
151 }
152 
GetAttack() const153 u32 Monster::GetAttack() const
154 {
155     return fheroes2::getMonsterData( id ).battleStats.attack;
156 }
157 
GetDefense() const158 u32 Monster::GetDefense() const
159 {
160     return fheroes2::getMonsterData( id ).battleStats.defense;
161 }
162 
GetColor() const163 int Monster::GetColor() const
164 {
165     return Color::NONE;
166 }
167 
GetMorale() const168 int Monster::GetMorale() const
169 {
170     return Morale::NORMAL;
171 }
172 
GetLuck() const173 int Monster::GetLuck() const
174 {
175     return Luck::NORMAL;
176 }
177 
GetRace() const178 int Monster::GetRace() const
179 {
180     return fheroes2::getMonsterData( id ).generalStats.race;
181 }
182 
GetDamageMin() const183 u32 Monster::GetDamageMin() const
184 {
185     return fheroes2::getMonsterData( id ).battleStats.damageMin;
186 }
187 
GetDamageMax() const188 u32 Monster::GetDamageMax() const
189 {
190     return fheroes2::getMonsterData( id ).battleStats.damageMax;
191 }
192 
GetShots() const193 u32 Monster::GetShots() const
194 {
195     return fheroes2::getMonsterData( id ).battleStats.shots;
196 }
197 
GetHitPoints() const198 u32 Monster::GetHitPoints() const
199 {
200     return fheroes2::getMonsterData( id ).battleStats.hp;
201 }
202 
GetSpeed() const203 u32 Monster::GetSpeed() const
204 {
205     return fheroes2::getMonsterData( id ).battleStats.speed;
206 }
207 
GetGrown() const208 u32 Monster::GetGrown() const
209 {
210     return fheroes2::getMonsterData( id ).generalStats.baseGrowth;
211 }
212 
213 // Get universal heuristic of Monster type regardless of context; both combat and strategic value
214 // Doesn't account for situational special bonuses such as spell immunity
GetMonsterStrength(int attack,int defense) const215 double Monster::GetMonsterStrength( int attack, int defense ) const
216 {
217     const fheroes2::MonsterBattleStats & battleStats = fheroes2::getMonsterData( id ).battleStats;
218 
219     // If no modified values were provided then re-calculate
220     // GetAttack and GetDefense will call overloaded versions accounting for Hero bonuses
221     if ( attack == -1 )
222         attack = battleStats.attack;
223 
224     if ( defense == -1 )
225         defense = battleStats.defense;
226 
227     const double attackDefense = 1.0 + attack * 0.1 + defense * 0.05;
228     const double effectiveHP = battleStats.hp * ( ignoreRetaliation() ? 1.4 : 1 );
229 
230     double damagePotential = ( battleStats.damageMin + battleStats.damageMax ) / 2.0;
231 
232     if ( isTwiceAttack() ) {
233         // Melee attacker will lose potential on second attack after retaliation
234         damagePotential *= ( isArchers() || ignoreRetaliation() ) ? 2 : 1.75;
235     }
236     if ( isAbilityPresent( fheroes2::MonsterAbilityType::DOUBLE_DAMAGE_TO_UNDEAD ) )
237         damagePotential *= 1.15; // 15% of all Monsters are Undead, deals double dmg
238     if ( isDoubleCellAttack() )
239         damagePotential *= 1.2;
240     if ( isAbilityPresent( fheroes2::MonsterAbilityType::ALWAYS_RETALIATE ) )
241         damagePotential *= 1.25;
242     if ( isAbilityPresent( fheroes2::MonsterAbilityType::ALL_ADJACENT_CELL_MELEE_ATTACK ) || isAbilityPresent( fheroes2::MonsterAbilityType::AREA_SHOT ) )
243         damagePotential *= 1.3;
244 
245     double monsterSpecial = 1.0;
246     if ( isArchers() ) {
247         monsterSpecial += isAbilityPresent( fheroes2::MonsterAbilityType::NO_MELEE_PENALTY ) ? 0.5 : 0.4;
248     }
249     if ( isFlying() ) {
250         monsterSpecial += 0.3;
251     }
252 
253     switch ( id ) {
254     case Monster::UNICORN:
255     case Monster::CYCLOPS:
256     case Monster::MEDUSA:
257         // 20% to Blind, Paralyze and Petrify
258         monsterSpecial += 0.2;
259         break;
260     case Monster::VAMPIRE_LORD:
261         // Lifesteal
262         monsterSpecial += 0.3;
263         break;
264     case Monster::GENIE:
265         // Genie's ability to half enemy troops
266         monsterSpecial += 1;
267         break;
268     case Monster::GHOST:
269         // Ghost's ability to increase the numbers
270         monsterSpecial += 2;
271         break;
272     }
273 
274     // Higher speed gives initiative advantage/first attack. Remap speed value to -0.2...+0.15, AVERAGE is 0
275     // Punish slow speeds more as unit won't participate in first rounds and slows down strategic army
276     const int speedDiff = battleStats.speed - Speed::AVERAGE;
277     monsterSpecial += ( speedDiff < 0 ) ? speedDiff * 0.1 : speedDiff * 0.05;
278 
279     // Additonal HP and Damage effectiveness diminishes with every combat round; strictly x4 HP == x2 unit count
280     return sqrt( damagePotential * effectiveHP ) * attackDefense * monsterSpecial;
281 }
282 
GetRNDSize(bool skip_factor) const283 u32 Monster::GetRNDSize( bool skip_factor ) const
284 {
285     if ( !isValid() )
286         return 0;
287 
288     const uint32_t defaultArmySizePerLevel[7] = {0, 50, 30, 25, 25, 12, 8};
289     uint32_t result = 0;
290 
291     // Check for outliers
292     switch ( id ) {
293     case PEASANT:
294         result = 80;
295         break;
296     case ROGUE:
297         result = 40;
298         break;
299     case PIKEMAN:
300     case VETERAN_PIKEMAN:
301     case WOLF:
302     case ELF:
303     case GRAND_ELF:
304         result = 30;
305         break;
306     case GARGOYLE:
307         result = 25;
308         break;
309     case GHOST:
310     case MEDUSA:
311         result = 20;
312         break;
313     case MINOTAUR:
314     case MINOTAUR_KING:
315     case ROC:
316     case VAMPIRE:
317     case VAMPIRE_LORD:
318     case UNICORN:
319         result = 16;
320         break;
321     case CAVALRY:
322     case CHAMPION:
323         result = 18;
324         break;
325     case PALADIN:
326     case CRUSADER:
327     case CYCLOPS:
328     case PHOENIX:
329         result = 12;
330         break;
331     default:
332         // for most units default range is okay
333         result = defaultArmySizePerLevel[GetMonsterLevel()];
334         break;
335     }
336 
337     if ( !skip_factor && Settings::Get().ExtWorldNeutralArmyDifficultyScaling() ) {
338         uint32_t factor = 100;
339 
340         switch ( Game::getDifficulty() ) {
341         case Difficulty::EASY:
342             factor = 80;
343             break;
344         case Difficulty::NORMAL:
345             factor = 100;
346             break;
347         case Difficulty::HARD:
348             factor = 130;
349             break;
350         case Difficulty::EXPERT:
351             factor = 160;
352             break;
353         case Difficulty::IMPOSSIBLE:
354             factor = 190;
355             break;
356         default:
357             // Did you add a new difficulty mode? Add the corresponding case above!
358             assert( 0 );
359             break;
360         }
361 
362         result = ( result * factor / 100 );
363         // force minimal
364         if ( result == 0 )
365             result = 1;
366     }
367 
368     return ( result > 1 ) ? Rand::Get( result / 2, result ) : 1;
369 }
370 
isElemental() const371 bool Monster::isElemental() const
372 {
373     return isAbilityPresent( fheroes2::MonsterAbilityType::ELEMENTAL );
374 }
375 
isUndead() const376 bool Monster::isUndead() const
377 {
378     return isAbilityPresent( fheroes2::MonsterAbilityType::UNDEAD );
379 }
380 
isAbilityPresent(const fheroes2::MonsterAbilityType abilityType) const381 bool Monster::isAbilityPresent( const fheroes2::MonsterAbilityType abilityType ) const
382 {
383     const std::vector<fheroes2::MonsterAbility> & abilities = fheroes2::getMonsterData( id ).battleStats.abilities;
384 
385     return std::find( abilities.begin(), abilities.end(), fheroes2::MonsterAbility( abilityType ) ) != abilities.end();
386 }
387 
isFlying() const388 bool Monster::isFlying() const
389 {
390     return isAbilityPresent( fheroes2::MonsterAbilityType::FLYING );
391 }
392 
isWide() const393 bool Monster::isWide() const
394 {
395     return isAbilityPresent( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE );
396 }
397 
isArchers() const398 bool Monster::isArchers() const
399 {
400     return GetShots() > 0;
401 }
402 
isAllowUpgrade() const403 bool Monster::isAllowUpgrade() const
404 {
405     return id != GetUpgrade().id;
406 }
407 
ignoreRetaliation() const408 bool Monster::ignoreRetaliation() const
409 {
410     return isAbilityPresent( fheroes2::MonsterAbilityType::NO_ENEMY_RETALIATION );
411 }
412 
isDragons() const413 bool Monster::isDragons() const
414 {
415     return isAbilityPresent( fheroes2::MonsterAbilityType::DRAGON );
416 }
417 
isTwiceAttack() const418 bool Monster::isTwiceAttack() const
419 {
420     return isAbilityPresent( fheroes2::MonsterAbilityType::DOUBLE_MELEE_ATTACK ) || isAbilityPresent( fheroes2::MonsterAbilityType::DOUBLE_SHOOTING );
421 }
422 
isRegenerating() const423 bool Monster::isRegenerating() const
424 {
425     return isAbilityPresent( fheroes2::MonsterAbilityType::HP_REGENERATION );
426 }
427 
isDoubleCellAttack() const428 bool Monster::isDoubleCellAttack() const
429 {
430     return isAbilityPresent( fheroes2::MonsterAbilityType::TWO_CELL_MELEE_ATTACK );
431 }
432 
isAllAdjacentCellsAttack() const433 bool Monster::isAllAdjacentCellsAttack() const
434 {
435     return isAbilityPresent( fheroes2::MonsterAbilityType::ALL_ADJACENT_CELL_MELEE_ATTACK );
436 }
437 
isAffectedByMorale() const438 bool Monster::isAffectedByMorale() const
439 {
440     return !( isUndead() || isElemental() );
441 }
442 
GetDowngrade() const443 Monster Monster::GetDowngrade() const
444 {
445     switch ( id ) {
446     case RANGER:
447         return Monster( ARCHER );
448     case VETERAN_PIKEMAN:
449         return Monster( PIKEMAN );
450     case MASTER_SWORDSMAN:
451         return Monster( SWORDSMAN );
452     case CHAMPION:
453         return Monster( CAVALRY );
454     case CRUSADER:
455         return Monster( PALADIN );
456     case ORC_CHIEF:
457         return Monster( ORC );
458     case OGRE_LORD:
459         return Monster( OGRE );
460     case WAR_TROLL:
461         return Monster( TROLL );
462     case BATTLE_DWARF:
463         return Monster( DWARF );
464     case GRAND_ELF:
465         return Monster( ELF );
466     case GREATER_DRUID:
467         return Monster( DRUID );
468     case MUTANT_ZOMBIE:
469         return Monster( ZOMBIE );
470     case ROYAL_MUMMY:
471         return Monster( MUMMY );
472     case VAMPIRE_LORD:
473         return Monster( VAMPIRE );
474     case POWER_LICH:
475         return Monster( LICH );
476     case MINOTAUR_KING:
477         return Monster( MINOTAUR );
478     case RED_DRAGON:
479         return Monster( GREEN_DRAGON );
480     case BLACK_DRAGON:
481         return Monster( RED_DRAGON );
482     case STEEL_GOLEM:
483         return Monster( IRON_GOLEM );
484     case ARCHMAGE:
485         return Monster( MAGE );
486     case TITAN:
487         return Monster( GIANT );
488 
489     default:
490         break;
491     }
492 
493     return Monster( id );
494 }
495 
GetUpgrade(void) const496 Monster Monster::GetUpgrade( void ) const
497 {
498     switch ( id ) {
499     case ARCHER:
500         return Monster( RANGER );
501     case PIKEMAN:
502         return Monster( VETERAN_PIKEMAN );
503     case SWORDSMAN:
504         return Monster( MASTER_SWORDSMAN );
505     case CAVALRY:
506         return Monster( CHAMPION );
507     case PALADIN:
508         return Monster( CRUSADER );
509     case ORC:
510         return Monster( ORC_CHIEF );
511     case OGRE:
512         return Monster( OGRE_LORD );
513     case TROLL:
514         return Monster( WAR_TROLL );
515     case DWARF:
516         return Monster( BATTLE_DWARF );
517     case ELF:
518         return Monster( GRAND_ELF );
519     case DRUID:
520         return Monster( GREATER_DRUID );
521     case ZOMBIE:
522         return Monster( MUTANT_ZOMBIE );
523     case MUMMY:
524         return Monster( ROYAL_MUMMY );
525     case VAMPIRE:
526         return Monster( VAMPIRE_LORD );
527     case LICH:
528         return Monster( POWER_LICH );
529     case MINOTAUR:
530         return Monster( MINOTAUR_KING );
531     case GREEN_DRAGON:
532         return Monster( RED_DRAGON );
533     case RED_DRAGON:
534         return Monster( BLACK_DRAGON );
535     case IRON_GOLEM:
536         return Monster( STEEL_GOLEM );
537     case MAGE:
538         return Monster( ARCHMAGE );
539     case GIANT:
540         return Monster( TITAN );
541 
542     default:
543         break;
544     }
545 
546     return Monster( id );
547 }
548 
FromDwelling(int race,u32 dwelling)549 Monster Monster::FromDwelling( int race, u32 dwelling )
550 {
551     switch ( dwelling ) {
552     case DWELLING_MONSTER1:
553         switch ( race ) {
554         case Race::KNGT:
555             return Monster( PEASANT );
556         case Race::BARB:
557             return Monster( GOBLIN );
558         case Race::SORC:
559             return Monster( SPRITE );
560         case Race::WRLK:
561             return Monster( CENTAUR );
562         case Race::WZRD:
563             return Monster( HALFLING );
564         case Race::NECR:
565             return Monster( SKELETON );
566         default:
567             break;
568         }
569         break;
570 
571     case DWELLING_MONSTER2:
572         switch ( race ) {
573         case Race::KNGT:
574             return Monster( ARCHER );
575         case Race::BARB:
576             return Monster( ORC );
577         case Race::SORC:
578             return Monster( DWARF );
579         case Race::WRLK:
580             return Monster( GARGOYLE );
581         case Race::WZRD:
582             return Monster( BOAR );
583         case Race::NECR:
584             return Monster( ZOMBIE );
585         default:
586             break;
587         }
588         break;
589 
590     case DWELLING_UPGRADE2:
591         switch ( race ) {
592         case Race::KNGT:
593             return Monster( RANGER );
594         case Race::BARB:
595             return Monster( ORC_CHIEF );
596         case Race::SORC:
597             return Monster( BATTLE_DWARF );
598         case Race::WRLK:
599             return Monster( GARGOYLE );
600         case Race::WZRD:
601             return Monster( BOAR );
602         case Race::NECR:
603             return Monster( MUTANT_ZOMBIE );
604         default:
605             break;
606         }
607         break;
608 
609     case DWELLING_MONSTER3:
610         switch ( race ) {
611         case Race::KNGT:
612             return Monster( PIKEMAN );
613         case Race::BARB:
614             return Monster( WOLF );
615         case Race::SORC:
616             return Monster( ELF );
617         case Race::WRLK:
618             return Monster( GRIFFIN );
619         case Race::WZRD:
620             return Monster( IRON_GOLEM );
621         case Race::NECR:
622             return Monster( MUMMY );
623         default:
624             break;
625         }
626         break;
627 
628     case DWELLING_UPGRADE3:
629         switch ( race ) {
630         case Race::KNGT:
631             return Monster( VETERAN_PIKEMAN );
632         case Race::BARB:
633             return Monster( WOLF );
634         case Race::SORC:
635             return Monster( GRAND_ELF );
636         case Race::WRLK:
637             return Monster( GRIFFIN );
638         case Race::WZRD:
639             return Monster( STEEL_GOLEM );
640         case Race::NECR:
641             return Monster( ROYAL_MUMMY );
642         default:
643             break;
644         }
645         break;
646 
647     case DWELLING_MONSTER4:
648         switch ( race ) {
649         case Race::KNGT:
650             return Monster( SWORDSMAN );
651         case Race::BARB:
652             return Monster( OGRE );
653         case Race::SORC:
654             return Monster( DRUID );
655         case Race::WRLK:
656             return Monster( MINOTAUR );
657         case Race::WZRD:
658             return Monster( ROC );
659         case Race::NECR:
660             return Monster( VAMPIRE );
661         default:
662             break;
663         }
664         break;
665 
666     case DWELLING_UPGRADE4:
667         switch ( race ) {
668         case Race::KNGT:
669             return Monster( MASTER_SWORDSMAN );
670         case Race::BARB:
671             return Monster( OGRE_LORD );
672         case Race::SORC:
673             return Monster( GREATER_DRUID );
674         case Race::WRLK:
675             return Monster( MINOTAUR_KING );
676         case Race::WZRD:
677             return Monster( ROC );
678         case Race::NECR:
679             return Monster( VAMPIRE_LORD );
680         default:
681             break;
682         }
683         break;
684 
685     case DWELLING_MONSTER5:
686         switch ( race ) {
687         case Race::KNGT:
688             return Monster( CAVALRY );
689         case Race::BARB:
690             return Monster( TROLL );
691         case Race::SORC:
692             return Monster( UNICORN );
693         case Race::WRLK:
694             return Monster( HYDRA );
695         case Race::WZRD:
696             return Monster( MAGE );
697         case Race::NECR:
698             return Monster( LICH );
699         default:
700             break;
701         }
702         break;
703 
704     case DWELLING_UPGRADE5:
705         switch ( race ) {
706         case Race::KNGT:
707             return Monster( CHAMPION );
708         case Race::BARB:
709             return Monster( WAR_TROLL );
710         case Race::SORC:
711             return Monster( UNICORN );
712         case Race::WRLK:
713             return Monster( HYDRA );
714         case Race::WZRD:
715             return Monster( ARCHMAGE );
716         case Race::NECR:
717             return Monster( POWER_LICH );
718         default:
719             break;
720         }
721         break;
722 
723     case DWELLING_MONSTER6:
724         switch ( race ) {
725         case Race::KNGT:
726             return Monster( PALADIN );
727         case Race::BARB:
728             return Monster( CYCLOPS );
729         case Race::SORC:
730             return Monster( PHOENIX );
731         case Race::WRLK:
732             return Monster( GREEN_DRAGON );
733         case Race::WZRD:
734             return Monster( GIANT );
735         case Race::NECR:
736             return Monster( BONE_DRAGON );
737         default:
738             break;
739         }
740         break;
741 
742     case DWELLING_UPGRADE6:
743         switch ( race ) {
744         case Race::KNGT:
745             return Monster( CRUSADER );
746         case Race::BARB:
747             return Monster( CYCLOPS );
748         case Race::SORC:
749             return Monster( PHOENIX );
750         case Race::WRLK:
751             return Monster( RED_DRAGON );
752         case Race::WZRD:
753             return Monster( TITAN );
754         case Race::NECR:
755             return Monster( BONE_DRAGON );
756         default:
757             break;
758         }
759         break;
760 
761     case DWELLING_UPGRADE7:
762         switch ( race ) {
763         case Race::KNGT:
764             return Monster( CRUSADER );
765         case Race::BARB:
766             return Monster( CYCLOPS );
767         case Race::SORC:
768             return Monster( PHOENIX );
769         case Race::WRLK:
770             return Monster( BLACK_DRAGON );
771         case Race::WZRD:
772             return Monster( TITAN );
773         case Race::NECR:
774             return Monster( BONE_DRAGON );
775         default:
776             break;
777         }
778         break;
779 
780     default:
781         break;
782     }
783 
784     return Monster( UNKNOWN );
785 }
786 
Rand(const LevelType type)787 Monster Monster::Rand( const LevelType type )
788 {
789     if ( type == LevelType::LEVEL_ANY )
790         return Monster( Rand::Get( PEASANT, WATER_ELEMENT ) );
791     static std::vector<Monster> monstersVec[static_cast<int>( LevelType::LEVEL_4 )];
792     if ( monstersVec[0].empty() ) {
793         for ( uint32_t i = PEASANT; i <= WATER_ELEMENT; ++i ) {
794             const Monster monster( i );
795             if ( monster.GetRandomUnitLevel() > LevelType::LEVEL_ANY )
796                 monstersVec[static_cast<int>( monster.GetRandomUnitLevel() ) - 1].push_back( monster );
797         }
798     }
799     return Rand::Get( monstersVec[static_cast<int>( type ) - 1] );
800 }
801 
GetMonsterLevel() const802 int Monster::GetMonsterLevel() const
803 {
804     return fheroes2::getMonsterData( id ).generalStats.level;
805 }
806 
GetRandomUnitLevel() const807 Monster::LevelType Monster::GetRandomUnitLevel() const
808 {
809     switch ( id ) {
810     case PEASANT:
811     case ARCHER:
812     case GOBLIN:
813     case ORC:
814     case SPRITE:
815     case CENTAUR:
816     case HALFLING:
817     case SKELETON:
818     case ZOMBIE:
819     case ROGUE:
820     case MONSTER_RND1:
821         return LevelType::LEVEL_1;
822 
823     case RANGER:
824     case PIKEMAN:
825     case VETERAN_PIKEMAN:
826     case ORC_CHIEF:
827     case WOLF:
828     case DWARF:
829     case BATTLE_DWARF:
830     case ELF:
831     case GRAND_ELF:
832     case GARGOYLE:
833     case BOAR:
834     case IRON_GOLEM:
835     case MUTANT_ZOMBIE:
836     case MUMMY:
837     case NOMAD:
838     case MONSTER_RND2:
839         return LevelType::LEVEL_2;
840 
841     case SWORDSMAN:
842     case MASTER_SWORDSMAN:
843     case CAVALRY:
844     case CHAMPION:
845     case OGRE:
846     case OGRE_LORD:
847     case TROLL:
848     case WAR_TROLL:
849     case DRUID:
850     case GREATER_DRUID:
851     case GRIFFIN:
852     case MINOTAUR:
853     case MINOTAUR_KING:
854     case STEEL_GOLEM:
855     case ROC:
856     case MAGE:
857     case ARCHMAGE:
858     case ROYAL_MUMMY:
859     case VAMPIRE:
860     case VAMPIRE_LORD:
861     case LICH:
862     case GHOST:
863     case MEDUSA:
864     case EARTH_ELEMENT:
865     case AIR_ELEMENT:
866     case FIRE_ELEMENT:
867     case WATER_ELEMENT:
868     case MONSTER_RND3:
869         return LevelType::LEVEL_3;
870 
871     case PALADIN:
872     case CRUSADER:
873     case CYCLOPS:
874     case UNICORN:
875     case PHOENIX:
876     case HYDRA:
877     case GREEN_DRAGON:
878     case RED_DRAGON:
879     case BLACK_DRAGON:
880     case GIANT:
881     case TITAN:
882     case POWER_LICH:
883     case BONE_DRAGON:
884     case GENIE:
885     case MONSTER_RND4:
886         return LevelType::LEVEL_4;
887 
888     case MONSTER_RND:
889         switch ( Rand::Get( 0, 3 ) ) {
890         default:
891             return LevelType::LEVEL_1;
892         case 1:
893             return LevelType::LEVEL_2;
894         case 2:
895             return LevelType::LEVEL_3;
896         case 3:
897             return LevelType::LEVEL_4;
898         }
899         break;
900 
901     default:
902         break;
903     }
904 
905     return LevelType::LEVEL_ANY;
906 }
907 
GetDwelling(void) const908 u32 Monster::GetDwelling( void ) const
909 {
910     switch ( id ) {
911     case PEASANT:
912     case GOBLIN:
913     case SPRITE:
914     case CENTAUR:
915     case HALFLING:
916     case SKELETON:
917         return DWELLING_MONSTER1;
918 
919     case ARCHER:
920     case ORC:
921     case ZOMBIE:
922     case DWARF:
923     case GARGOYLE:
924     case BOAR:
925         return DWELLING_MONSTER2;
926 
927     case RANGER:
928     case ORC_CHIEF:
929     case BATTLE_DWARF:
930     case MUTANT_ZOMBIE:
931         return DWELLING_UPGRADE2;
932 
933     case PIKEMAN:
934     case WOLF:
935     case ELF:
936     case IRON_GOLEM:
937     case MUMMY:
938     case GRIFFIN:
939         return DWELLING_MONSTER3;
940 
941     case VETERAN_PIKEMAN:
942     case GRAND_ELF:
943     case STEEL_GOLEM:
944     case ROYAL_MUMMY:
945         return DWELLING_UPGRADE3;
946 
947     case SWORDSMAN:
948     case OGRE:
949     case DRUID:
950     case MINOTAUR:
951     case ROC:
952     case VAMPIRE:
953         return DWELLING_MONSTER4;
954 
955     case MASTER_SWORDSMAN:
956     case OGRE_LORD:
957     case GREATER_DRUID:
958     case MINOTAUR_KING:
959     case VAMPIRE_LORD:
960         return DWELLING_UPGRADE4;
961 
962     case CAVALRY:
963     case TROLL:
964     case MAGE:
965     case LICH:
966     case UNICORN:
967     case HYDRA:
968         return DWELLING_MONSTER5;
969 
970     case CHAMPION:
971     case WAR_TROLL:
972     case ARCHMAGE:
973     case POWER_LICH:
974         return DWELLING_UPGRADE5;
975 
976     case PALADIN:
977     case CYCLOPS:
978     case PHOENIX:
979     case GREEN_DRAGON:
980     case GIANT:
981     case BONE_DRAGON:
982         return DWELLING_MONSTER6;
983 
984     case CRUSADER:
985     case RED_DRAGON:
986     case TITAN:
987         return DWELLING_UPGRADE6;
988 
989     case BLACK_DRAGON:
990         return DWELLING_UPGRADE7;
991 
992     default:
993         break;
994     }
995 
996     return 0;
997 }
998 
GetName(void) const999 const char * Monster::GetName( void ) const
1000 {
1001     return _( fheroes2::getMonsterData( id ).generalStats.name );
1002 }
1003 
GetMultiName(void) const1004 const char * Monster::GetMultiName( void ) const
1005 {
1006     return _( fheroes2::getMonsterData( id ).generalStats.pluralName );
1007 }
1008 
GetPluralName(u32 count) const1009 const char * Monster::GetPluralName( u32 count ) const
1010 {
1011     const fheroes2::MonsterGeneralStats & generalStats = fheroes2::getMonsterData( id ).generalStats;
1012     return count == 1 ? _( generalStats.name ) : _( generalStats.pluralName );
1013 }
1014 
GetSpriteIndex(void) const1015 u32 Monster::GetSpriteIndex( void ) const
1016 {
1017     return UNKNOWN < id ? id - 1 : 0;
1018 }
1019 
ICNMonh(void) const1020 int Monster::ICNMonh( void ) const
1021 {
1022     return id >= PEASANT && id <= WATER_ELEMENT ? ICN::MONH0000 + id - PEASANT : ICN::UNKNOWN;
1023 }
1024 
GetCost(void) const1025 payment_t Monster::GetCost( void ) const
1026 {
1027     return payment_t( fheroes2::getMonsterData( id ).generalStats.cost );
1028 }
1029 
GetUpgradeCost(void) const1030 payment_t Monster::GetUpgradeCost( void ) const
1031 {
1032     const Monster upgr = GetUpgrade();
1033     const payment_t pay = id != upgr.id ? upgr.GetCost() - GetCost() : GetCost();
1034 
1035     return pay;
1036 }
1037 
GetCountFromHitPoints(const Monster & mons,u32 hp)1038 u32 Monster::GetCountFromHitPoints( const Monster & mons, u32 hp )
1039 {
1040     if ( hp ) {
1041         const u32 hp1 = mons.GetHitPoints();
1042         const u32 count = hp / hp1;
1043         return ( count * hp1 ) < hp ? count + 1 : count;
1044     }
1045 
1046     return 0;
1047 }
1048 
GetMonsterSprite() const1049 int Monster::GetMonsterSprite() const
1050 {
1051     return fheroes2::getMonsterData( id ).icnId;
1052 }
1053