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