1 /*************************************************************************** 2 * Free Heroes of Might and Magic II: https://github.com/ihhub/fheroes2 * 3 * Copyright (C) 2021 * 4 * * 5 * This program is free software; you can redistribute it and/or modify * 6 * it under the terms of the GNU General Public License as published by * 7 * the Free Software Foundation; either version 2 of the License, or * 8 * (at your option) any later version. * 9 * * 10 * This program is distributed in the hope that it will be useful, * 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 * GNU General Public License for more details. * 14 * * 15 * You should have received a copy of the GNU General Public License * 16 * along with this program; if not, write to the * 17 * Free Software Foundation, Inc., * 18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 ***************************************************************************/ 20 21 #include "monster_info.h" 22 #include "icn.h" 23 #include "m82.h" 24 #include "monster.h" 25 #include "race.h" 26 #include "speed.h" 27 #include "spell.h" 28 #include "tools.h" 29 #include "translations.h" 30 31 #include <algorithm> 32 #include <cassert> 33 #include <map> 34 #include <set> 35 #include <sstream> 36 37 namespace 38 { 39 std::vector<fheroes2::MonsterData> monsterData; 40 populateMonsterData()41 void populateMonsterData() 42 { 43 const int monsterIcnIds[Monster::MONSTER_COUNT] 44 = { ICN::UNKNOWN, ICN::PEASANT, ICN::ARCHER, ICN::ARCHER2, ICN::PIKEMAN, ICN::PIKEMAN2, ICN::SWORDSMN, ICN::SWORDSM2, ICN::CAVALRYR, 45 ICN::CAVALRYB, ICN::PALADIN, ICN::PALADIN2, ICN::GOBLIN, ICN::ORC, ICN::ORC2, ICN::WOLF, ICN::OGRE, ICN::OGRE2, 46 ICN::TROLL, ICN::TROLL2, ICN::CYCLOPS, ICN::SPRITE, ICN::DWARF, ICN::DWARF2, ICN::ELF, ICN::ELF2, ICN::DRUID, 47 ICN::DRUID2, ICN::UNICORN, ICN::PHOENIX, ICN::CENTAUR, ICN::GARGOYLE, ICN::GRIFFIN, ICN::MINOTAUR, ICN::MINOTAU2, ICN::HYDRA, 48 ICN::DRAGGREE, ICN::DRAGRED, ICN::DRAGBLAK, ICN::HALFLING, ICN::BOAR, ICN::GOLEM, ICN::GOLEM2, ICN::ROC, ICN::MAGE1, 49 ICN::MAGE2, ICN::TITANBLU, ICN::TITANBLA, ICN::SKELETON, ICN::ZOMBIE, ICN::ZOMBIE2, ICN::MUMMYW, ICN::MUMMY2, ICN::VAMPIRE, 50 ICN::VAMPIRE2, ICN::LICH, ICN::LICH2, ICN::DRAGBONE, ICN::ROGUE, ICN::NOMAD, ICN::GHOST, ICN::GENIE, ICN::MEDUSA, 51 ICN::EELEM, ICN::AELEM, ICN::FELEM, ICN::WELEM, ICN::UNKNOWN, ICN::UNKNOWN, ICN::UNKNOWN, ICN::UNKNOWN, ICN::UNKNOWN }; 52 53 const char * binFileName[Monster::MONSTER_COUNT] 54 = { "UNKNOWN", "PEAS_FRM.BIN", "ARCHRFRM.BIN", "ARCHRFRM.BIN", "PIKMNFRM.BIN", "PIKMNFRM.BIN", "SWRDSFRM.BIN", "SWRDSFRM.BIN", "CVLRYFRM.BIN", 55 "CVLR2FRM.BIN", "PALADFRM.BIN", "PALADFRM.BIN", "GOBLNFRM.BIN", "ORC__FRM.BIN", "ORC__FRM.BIN", "WOLF_FRM.BIN", "OGRE_FRM.BIN", "OGRE_FRM.BIN", 56 "TROLLFRM.BIN", "TROLLFRM.BIN", "CYCLOFRM.BIN", "SPRITFRM.BIN", "DWARFFRM.BIN", "DWARFFRM.BIN", "ELF__FRM.BIN", "ELF__FRM.BIN", "DRUIDFRM.BIN", 57 "DRUIDFRM.BIN", "UNICOFRM.BIN", "PHOENFRM.BIN", "CENTRFRM.BIN", "GARGLFRM.BIN", "GRIFFFRM.BIN", "MINOTFRM.BIN", "MINOTFRM.BIN", "HYDRAFRM.BIN", 58 "DRAGGFRM.BIN", "DRAGRFRM.BIN", "DRAGBFRM.BIN", "HALFLFRM.BIN", "BOAR_FRM.BIN", "GOLEMFRM.BIN", "GOLEMFRM.BIN", "ROC__FRM.BIN", "MAGE1FRM.BIN", 59 "MAGE1FRM.BIN", "TITANFRM.BIN", "TITA2FRM.BIN", "SKEL_FRM.BIN", "ZOMB_FRM.BIN", "ZOMB_FRM.BIN", "MUMMYFRM.BIN", "MUMMYFRM.BIN", "VAMPIFRM.BIN", 60 "VAMPIFRM.BIN", "LICH_FRM.BIN", "LICH_FRM.BIN", "DRABNFRM.BIN", "ROGUEFRM.BIN", "NOMADFRM.BIN", "GHOSTFRM.BIN", "GENIEFRM.BIN", "MEDUSFRM.BIN", 61 "FELEMFRM.BIN", "FELEMFRM.BIN", "FELEMFRM.BIN", "FELEMFRM.BIN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN" }; 62 63 const fheroes2::MonsterSound monsterSounds[Monster::MONSTER_COUNT] = { 64 // melee attack | death | movement | wince |ranged attack 65 { M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN }, // Unknown Monster 66 { M82::PSNTATTK, M82::PSNTKILL, M82::PSNTMOVE, M82::PSNTWNCE, M82::UNKNOWN }, // Peasant 67 { M82::ARCHATTK, M82::ARCHKILL, M82::ARCHMOVE, M82::ARCHWNCE, M82::ARCHSHOT }, // Archer 68 { M82::ARCHATTK, M82::ARCHKILL, M82::ARCHMOVE, M82::ARCHWNCE, M82::ARCHSHOT }, // Ranger 69 { M82::PIKEATTK, M82::PIKEKILL, M82::PIKEMOVE, M82::PIKEWNCE, M82::UNKNOWN }, // Pikeman 70 { M82::PIKEATTK, M82::PIKEKILL, M82::PIKEMOVE, M82::PIKEWNCE, M82::UNKNOWN }, // Veteran Pikeman 71 { M82::SWDMATTK, M82::SWDMKILL, M82::SWDMMOVE, M82::SWDMWNCE, M82::UNKNOWN }, // Swordsman 72 { M82::SWDMATTK, M82::SWDMKILL, M82::SWDMMOVE, M82::SWDMWNCE, M82::UNKNOWN }, // Master Swordsman 73 { M82::CAVLATTK, M82::CAVLKILL, M82::CAVLMOVE, M82::CAVLWNCE, M82::UNKNOWN }, // Cavalry 74 { M82::CAVLATTK, M82::CAVLKILL, M82::CAVLMOVE, M82::CAVLWNCE, M82::UNKNOWN }, // Champion 75 { M82::PLDNATTK, M82::PLDNKILL, M82::PLDNMOVE, M82::PLDNWNCE, M82::UNKNOWN }, // Paladin 76 { M82::PLDNATTK, M82::PLDNKILL, M82::PLDNMOVE, M82::PLDNWNCE, M82::UNKNOWN }, // Crusader 77 { M82::GBLNATTK, M82::GBLNKILL, M82::GBLNMOVE, M82::GBLNWNCE, M82::UNKNOWN }, // Goblin 78 { M82::ORC_ATTK, M82::ORC_KILL, M82::ORC_MOVE, M82::ORC_WNCE, M82::ORC_SHOT }, // Orc 79 { M82::ORC_ATTK, M82::ORC_KILL, M82::ORC_MOVE, M82::ORC_WNCE, M82::ORC_SHOT }, // Orc Chief 80 { M82::WOLFATTK, M82::WOLFKILL, M82::WOLFMOVE, M82::WOLFWNCE, M82::UNKNOWN }, // Wolf 81 { M82::OGREATTK, M82::OGREKILL, M82::OGREMOVE, M82::OGREWNCE, M82::UNKNOWN }, // Ogre 82 { M82::OGREATTK, M82::OGREKILL, M82::OGREMOVE, M82::OGREWNCE, M82::UNKNOWN }, // Ogre Lord 83 { M82::TRLLATTK, M82::TRLLKILL, M82::TRLLMOVE, M82::TRLLWNCE, M82::TRLLSHOT }, // Troll 84 { M82::TRLLATTK, M82::TRLLKILL, M82::TRLLMOVE, M82::TRLLWNCE, M82::TRLLSHOT }, // War Troll 85 { M82::CYCLATTK, M82::CYCLKILL, M82::CYCLMOVE, M82::CYCLWNCE, M82::UNKNOWN }, // Cyclops 86 { M82::SPRTATTK, M82::SPRTKILL, M82::SPRTMOVE, M82::SPRTWNCE, M82::UNKNOWN }, // Sprite 87 { M82::DWRFATTK, M82::DWRFKILL, M82::DWRFMOVE, M82::DWRFWNCE, M82::UNKNOWN }, // Dwarf 88 { M82::DWRFATTK, M82::DWRFKILL, M82::DWRFMOVE, M82::DWRFWNCE, M82::UNKNOWN }, // Battle Dwarf 89 { M82::ELF_ATTK, M82::ELF_KILL, M82::ELF_MOVE, M82::ELF_WNCE, M82::ELF_SHOT }, // Elf 90 { M82::ELF_ATTK, M82::ELF_KILL, M82::ELF_MOVE, M82::ELF_WNCE, M82::ELF_SHOT }, // Grand Elf 91 { M82::DRUIATTK, M82::DRUIKILL, M82::DRUIMOVE, M82::DRUIWNCE, M82::DRUISHOT }, // Druid 92 { M82::DRUIATTK, M82::DRUIKILL, M82::DRUIMOVE, M82::DRUIWNCE, M82::DRUISHOT }, // Greater Druid 93 { M82::UNICATTK, M82::UNICKILL, M82::UNICMOVE, M82::UNICWNCE, M82::UNKNOWN }, // Unicorn 94 { M82::PHOEATTK, M82::PHOEKILL, M82::PHOEMOVE, M82::PHOEWNCE, M82::UNKNOWN }, // Phoenix 95 { M82::CNTRATTK, M82::CNTRKILL, M82::CNTRMOVE, M82::CNTRWNCE, M82::CNTRSHOT }, // Centaur 96 { M82::GARGATTK, M82::GARGKILL, M82::GARGMOVE, M82::GARGWNCE, M82::UNKNOWN }, // Gargoyle 97 { M82::GRIFATTK, M82::GRIFKILL, M82::GRIFMOVE, M82::GRIFWNCE, M82::UNKNOWN }, // Griffin 98 { M82::MINOATTK, M82::MINOKILL, M82::MINOMOVE, M82::MINOWNCE, M82::UNKNOWN }, // Minotaur 99 { M82::MINOATTK, M82::MINOKILL, M82::MINOMOVE, M82::MINOWNCE, M82::UNKNOWN }, // Minotaur King 100 { M82::HYDRATTK, M82::HYDRKILL, M82::HYDRMOVE, M82::HYDRWNCE, M82::UNKNOWN }, // Hydra 101 { M82::DRGNATTK, M82::DRGNKILL, M82::DRGNMOVE, M82::DRGNWNCE, M82::UNKNOWN }, // Green Dragon 102 { M82::DRGNATTK, M82::DRGNKILL, M82::DRGNMOVE, M82::DRGNWNCE, M82::UNKNOWN }, // Red Dragon 103 { M82::DRGNATTK, M82::DRGNKILL, M82::DRGNMOVE, M82::DRGNWNCE, M82::UNKNOWN }, // Black Dragon 104 { M82::HALFATTK, M82::HALFKILL, M82::HALFMOVE, M82::HALFWNCE, M82::HALFSHOT }, // Halfling 105 { M82::BOARATTK, M82::BOARKILL, M82::BOARMOVE, M82::BOARWNCE, M82::UNKNOWN }, // Boar 106 { M82::GOLMATTK, M82::GOLMKILL, M82::GOLMMOVE, M82::GOLMWNCE, M82::UNKNOWN }, // Iron Golem 107 { M82::GOLMATTK, M82::GOLMKILL, M82::GOLMMOVE, M82::GOLMWNCE, M82::UNKNOWN }, // Steel Golem 108 { M82::ROC_ATTK, M82::ROC_KILL, M82::ROC_MOVE, M82::ROC_WNCE, M82::UNKNOWN }, // Roc 109 { M82::MAGEATTK, M82::MAGEKILL, M82::MAGEMOVE, M82::MAGEWNCE, M82::MAGESHOT }, // Mage 110 { M82::MAGEATTK, M82::MAGEKILL, M82::MAGEMOVE, M82::MAGEWNCE, M82::MAGESHOT }, // Archmage 111 { M82::TITNATTK, M82::TITNKILL, M82::TITNMOVE, M82::TITNWNCE, M82::UNKNOWN }, // Giant 112 { M82::TITNATTK, M82::TITNKILL, M82::TITNMOVE, M82::TITNWNCE, M82::TITNSHOT }, // Titan 113 { M82::SKELATTK, M82::SKELKILL, M82::SKELMOVE, M82::SKELWNCE, M82::UNKNOWN }, // Skeleton 114 { M82::ZOMBATTK, M82::ZOMBKILL, M82::ZOMBMOVE, M82::ZOMBWNCE, M82::UNKNOWN }, // Zombie 115 { M82::ZOMBATTK, M82::ZOMBKILL, M82::ZOMBMOVE, M82::ZOMBWNCE, M82::UNKNOWN }, // Mutant Zombie 116 { M82::MUMYATTK, M82::MUMYKILL, M82::MUMYMOVE, M82::MUMYWNCE, M82::UNKNOWN }, // Mummy 117 { M82::MUMYATTK, M82::MUMYKILL, M82::MUMYMOVE, M82::MUMYWNCE, M82::UNKNOWN }, // Royal Mummy 118 { M82::VAMPATTK, M82::VAMPKILL, M82::VAMPMOVE, M82::VAMPWNCE, M82::UNKNOWN }, // Vampire 119 { M82::VAMPATTK, M82::VAMPKILL, M82::VAMPMOVE, M82::VAMPWNCE, M82::UNKNOWN }, // Vampire Lord 120 { M82::LICHATTK, M82::LICHKILL, M82::LICHMOVE, M82::LICHWNCE, M82::LICHSHOT }, // Lich 121 { M82::LICHATTK, M82::LICHKILL, M82::LICHMOVE, M82::LICHWNCE, M82::LICHSHOT }, // Power Lich 122 { M82::BONEATTK, M82::BONEKILL, M82::BONEMOVE, M82::BONEWNCE, M82::UNKNOWN }, // Bone Dragon 123 { M82::ROGUATTK, M82::ROGUKILL, M82::ROGUMOVE, M82::ROGUWNCE, M82::UNKNOWN }, // Rogue 124 { M82::NMADATTK, M82::NMADKILL, M82::NMADMOVE, M82::NMADWNCE, M82::UNKNOWN }, // Nomad 125 { M82::GHSTATTK, M82::GHSTKILL, M82::GHSTMOVE, M82::GHSTWNCE, M82::UNKNOWN }, // Ghost 126 { M82::GENIATTK, M82::GENIKILL, M82::GENIMOVE, M82::GENIWNCE, M82::UNKNOWN }, // Genie 127 { M82::MEDSATTK, M82::MEDSKILL, M82::MEDSMOVE, M82::MEDSWNCE, M82::UNKNOWN }, // Medusa 128 { M82::EELMATTK, M82::EELMKILL, M82::EELMMOVE, M82::EELMWNCE, M82::UNKNOWN }, // Earth Elemental 129 { M82::AELMATTK, M82::AELMKILL, M82::AELMMOVE, M82::AELMWNCE, M82::UNKNOWN }, // Air Elemental 130 { M82::FELMATTK, M82::FELMKILL, M82::FELMMOVE, M82::FELMWNCE, M82::UNKNOWN }, // Fire Elemental 131 { M82::WELMATTK, M82::WELMKILL, M82::WELMMOVE, M82::WELMWNCE, M82::UNKNOWN }, // Water Elemental 132 { M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN }, // Random Monster 133 { M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN }, // Random Monster 1 134 { M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN }, // Random Monster 2 135 { M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN }, // Random Monster 3 136 { M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN, M82::UNKNOWN }, // Random Monster 4 137 }; 138 139 // Monster abilities and weaknesses will be added later. 140 const fheroes2::MonsterBattleStats monsterBattleStats[Monster::MONSTER_COUNT] = { 141 // attack | defence | damageMin | damageMax | hp | speed | shots | abilities | weaknesses 142 { 0, 0, 0, 0, 0, Speed::VERYSLOW, 0, {}, {} }, // Unknown Monster 143 { 1, 1, 1, 1, 1, Speed::VERYSLOW, 0, {}, {} }, // Peasant 144 { 5, 3, 2, 3, 10, Speed::VERYSLOW, 12, {}, {} }, // Archer 145 { 5, 3, 2, 3, 10, Speed::AVERAGE, 24, {}, {} }, // Ranger 146 { 5, 9, 3, 4, 15, Speed::AVERAGE, 0, {}, {} }, // Pikeman 147 { 5, 9, 3, 4, 20, Speed::FAST, 0, {}, {} }, // Veteran Pikeman 148 { 7, 9, 4, 6, 25, Speed::AVERAGE, 0, {}, {} }, // Swordsman 149 { 7, 9, 4, 6, 30, Speed::FAST, 0, {}, {} }, // Master Swordsman 150 { 10, 9, 5, 10, 30, Speed::VERYFAST, 0, {}, {} }, // Cavalry 151 { 10, 9, 5, 10, 40, Speed::ULTRAFAST, 0, {}, {} }, // Champion 152 { 11, 12, 10, 20, 50, Speed::FAST, 0, {}, {} }, // Paladin 153 { 11, 12, 10, 20, 65, Speed::VERYFAST, 0, {}, {} }, // Crusader 154 { 3, 1, 1, 2, 3, Speed::AVERAGE, 0, {}, {} }, // Goblin 155 { 3, 4, 2, 3, 10, Speed::VERYSLOW, 8, {}, {} }, // Orc 156 { 3, 4, 3, 4, 15, Speed::SLOW, 16, {}, {} }, // Orc Chief 157 { 6, 2, 3, 5, 20, Speed::VERYFAST, 0, {}, {} }, // Wolf 158 { 9, 5, 4, 6, 40, Speed::VERYSLOW, 0, {}, {} }, // Ogre 159 { 9, 5, 5, 7, 60, Speed::AVERAGE, 0, {}, {} }, // Ogre Lord 160 { 10, 5, 5, 7, 40, Speed::AVERAGE, 8, {}, {} }, // Troll 161 { 10, 5, 7, 9, 40, Speed::FAST, 16, {}, {} }, // War Troll 162 { 12, 9, 12, 24, 80, Speed::FAST, 0, {}, {} }, // Cyclops 163 { 4, 2, 1, 2, 2, Speed::AVERAGE, 0, {}, {} }, // Sprite 164 { 6, 5, 2, 4, 20, Speed::VERYSLOW, 0, {}, {} }, // Dwarf 165 { 6, 6, 2, 4, 20, Speed::AVERAGE, 0, {}, {} }, // Battle Dwarf 166 { 4, 3, 2, 3, 15, Speed::AVERAGE, 24, {}, {} }, // Elf 167 { 5, 5, 2, 3, 15, Speed::VERYFAST, 24, {}, {} }, // Grand Elf 168 { 7, 5, 5, 8, 25, Speed::FAST, 8, {}, {} }, // Druid 169 { 7, 7, 5, 8, 25, Speed::VERYFAST, 16, {}, {} }, // Greater Druid 170 { 10, 9, 7, 14, 40, Speed::FAST, 0, {}, {} }, // Unicorn 171 { 12, 10, 20, 40, 100, Speed::ULTRAFAST, 0, {}, {} }, // Phoenix 172 { 3, 1, 1, 2, 5, Speed::AVERAGE, 8, {}, {} }, // Centaur 173 { 4, 7, 2, 3, 15, Speed::VERYFAST, 0, {}, {} }, // Gargoyle 174 { 6, 6, 3, 5, 25, Speed::AVERAGE, 0, {}, {} }, // Griffin 175 { 9, 8, 5, 10, 35, Speed::AVERAGE, 0, {}, {} }, // Minotaur 176 { 9, 8, 5, 10, 45, Speed::VERYFAST, 0, {}, {} }, // Minotaur King 177 { 8, 9, 6, 12, 75, Speed::VERYSLOW, 0, {}, {} }, // Hydra 178 { 12, 12, 25, 50, 200, Speed::AVERAGE, 0, {}, {} }, // Green Dragon 179 { 13, 13, 25, 50, 250, Speed::FAST, 0, {}, {} }, // Red Dragon 180 { 14, 14, 25, 50, 300, Speed::VERYFAST, 0, {}, {} }, // Black Dragon 181 { 2, 1, 1, 3, 3, Speed::SLOW, 12, {}, {} }, // Halfling 182 { 5, 4, 2, 3, 15, Speed::VERYFAST, 0, {}, {} }, // Boar 183 { 5, 10, 4, 5, 30, Speed::VERYSLOW, 0, {}, {} }, // Iron Golem 184 { 7, 10, 4, 5, 35, Speed::SLOW, 0, {}, {} }, // Steel Golem 185 { 7, 7, 4, 8, 40, Speed::AVERAGE, 0, {}, {} }, // Roc 186 { 11, 7, 7, 9, 30, Speed::FAST, 12, {}, {} }, // Mage 187 { 12, 8, 7, 9, 35, Speed::VERYFAST, 24, {}, {} }, // Archmage 188 { 13, 10, 20, 30, 150, Speed::AVERAGE, 0, {}, {} }, // Giant 189 { 15, 15, 20, 30, 300, Speed::VERYFAST, 24, {}, {} }, // Titan 190 { 4, 3, 2, 3, 4, Speed::AVERAGE, 0, {}, {} }, // Skeleton 191 { 5, 2, 2, 3, 15, Speed::VERYSLOW, 0, {}, {} }, // Zombie 192 { 5, 2, 2, 3, 20, Speed::AVERAGE, 0, {}, {} }, // Mutant Zombie 193 { 6, 6, 3, 4, 25, Speed::AVERAGE, 0, {}, {} }, // Mummy 194 { 6, 6, 3, 4, 30, Speed::FAST, 0, {}, {} }, // Royal Mummy 195 { 8, 6, 5, 7, 30, Speed::AVERAGE, 0, {}, {} }, // Vampire 196 { 8, 6, 5, 7, 40, Speed::FAST, 0, {}, {} }, // Vampire Lord 197 { 7, 12, 8, 10, 25, Speed::FAST, 12, {}, {} }, // Lich 198 { 7, 13, 8, 10, 35, Speed::VERYFAST, 24, {}, {} }, // Power Lich 199 { 11, 9, 25, 45, 150, Speed::AVERAGE, 0, {}, {} }, // Bone Dragon 200 { 6, 1, 1, 2, 4, Speed::FAST, 0, {}, {} }, // Rogue 201 { 7, 6, 2, 5, 20, Speed::VERYFAST, 0, {}, {} }, // Nomad 202 { 8, 7, 4, 6, 20, Speed::FAST, 0, {}, {} }, // Ghost 203 { 10, 9, 20, 30, 50, Speed::VERYFAST, 0, {}, {} }, // Genie 204 { 8, 9, 6, 10, 35, Speed::AVERAGE, 0, {}, {} }, // Medusa 205 { 8, 8, 4, 5, 50, Speed::SLOW, 0, {}, {} }, // Earth Elemental 206 { 7, 7, 2, 8, 35, Speed::VERYFAST, 0, {}, {} }, // Air Elemental 207 { 8, 6, 4, 6, 40, Speed::FAST, 0, {}, {} }, // Fire Elemental 208 { 6, 8, 3, 7, 45, Speed::AVERAGE, 0, {}, {} }, // Water Elemental 209 { 0, 0, 0, 0, 0, Speed::VERYSLOW, 0, {}, {} }, // Random Monster 210 { 0, 0, 0, 0, 0, Speed::VERYSLOW, 0, {}, {} }, // Random Monster 1 211 { 0, 0, 0, 0, 0, Speed::VERYSLOW, 0, {}, {} }, // Random Monster 2 212 { 0, 0, 0, 0, 0, Speed::VERYSLOW, 0, {}, {} }, // Random Monster 3 213 { 0, 0, 0, 0, 0, Speed::VERYSLOW, 0, {}, {} }, // Random Monster 4 214 }; 215 216 const fheroes2::MonsterGeneralStats monsterGeneralStats[Monster::MONSTER_COUNT] 217 = { // name | plural name | growth | race | level | cost 218 { gettext_noop( "Unknown Monster" ), gettext_noop( "Unknown Monsters" ), 0, Race::NONE, 0, { 0, 0, 0, 0, 0, 0, 0 } }, 219 { gettext_noop( "Peasant" ), gettext_noop( "Peasants" ), 12, Race::KNGT, 1, { 20, 0, 0, 0, 0, 0, 0 } }, 220 { gettext_noop( "Archer" ), gettext_noop( "Archers" ), 8, Race::KNGT, 2, { 150, 0, 0, 0, 0, 0, 0 } }, 221 { gettext_noop( "Ranger" ), gettext_noop( "Rangers" ), 8, Race::KNGT, 2, { 200, 0, 0, 0, 0, 0, 0 } }, 222 { gettext_noop( "Pikeman" ), gettext_noop( "Pikemen" ), 5, Race::KNGT, 3, { 200, 0, 0, 0, 0, 0, 0 } }, 223 { gettext_noop( "Veteran Pikeman" ), gettext_noop( "Veteran Pikemen" ), 5, Race::KNGT, 3, { 250, 0, 0, 0, 0, 0, 0 } }, 224 { gettext_noop( "Swordsman" ), gettext_noop( "Swordsmen" ), 4, Race::KNGT, 4, { 250, 0, 0, 0, 0, 0, 0 } }, 225 { gettext_noop( "Master Swordsman" ), gettext_noop( "Master Swordsmen" ), 4, Race::KNGT, 4, { 300, 0, 0, 0, 0, 0, 0 } }, 226 { gettext_noop( "Cavalry" ), gettext_noop( "Cavalries" ), 3, Race::KNGT, 5, { 300, 0, 0, 0, 0, 0, 0 } }, 227 { gettext_noop( "Champion" ), gettext_noop( "Champions" ), 3, Race::KNGT, 5, { 375, 0, 0, 0, 0, 0, 0 } }, 228 { gettext_noop( "Paladin" ), gettext_noop( "Paladins" ), 2, Race::KNGT, 6, { 600, 0, 0, 0, 0, 0, 0 } }, 229 { gettext_noop( "Crusader" ), gettext_noop( "Crusaders" ), 2, Race::KNGT, 6, { 1000, 0, 0, 0, 0, 0, 0 } }, 230 { gettext_noop( "Goblin" ), gettext_noop( "Goblins" ), 10, Race::BARB, 1, { 40, 0, 0, 0, 0, 0, 0 } }, 231 { gettext_noop( "Orc" ), gettext_noop( "Orcs" ), 8, Race::BARB, 2, { 140, 0, 0, 0, 0, 0, 0 } }, 232 { gettext_noop( "Orc Chief" ), gettext_noop( "Orc Chiefs" ), 8, Race::BARB, 2, { 175, 0, 0, 0, 0, 0, 0 } }, 233 { gettext_noop( "Wolf" ), gettext_noop( "Wolves" ), 5, Race::BARB, 3, { 200, 0, 0, 0, 0, 0, 0 } }, 234 { gettext_noop( "Ogre" ), gettext_noop( "Ogres" ), 4, Race::BARB, 4, { 300, 0, 0, 0, 0, 0, 0 } }, 235 { gettext_noop( "Ogre Lord" ), gettext_noop( "Ogre Lords" ), 4, Race::BARB, 4, { 500, 0, 0, 0, 0, 0, 0 } }, 236 { gettext_noop( "Troll" ), gettext_noop( "Trolls" ), 3, Race::BARB, 5, { 600, 0, 0, 0, 0, 0, 0 } }, 237 { gettext_noop( "War Troll" ), gettext_noop( "War Trolls" ), 3, Race::BARB, 5, { 700, 0, 0, 0, 0, 0, 0 } }, 238 { gettext_noop( "Cyclops" ), gettext_noop( "Cyclopes" ), 2, Race::BARB, 6, { 750, 0, 0, 0, 0, 1, 0 } }, 239 { gettext_noop( "Sprite" ), gettext_noop( "Sprites" ), 8, Race::SORC, 1, { 50, 0, 0, 0, 0, 0, 0 } }, 240 { gettext_noop( "Dwarf" ), gettext_noop( "Dwarves" ), 6, Race::SORC, 2, { 200, 0, 0, 0, 0, 0, 0 } }, 241 { gettext_noop( "Battle Dwarf" ), gettext_noop( "Battle Dwarves" ), 6, Race::SORC, 2, { 250, 0, 0, 0, 0, 0, 0 } }, 242 { gettext_noop( "Elf" ), gettext_noop( "Elves" ), 4, Race::SORC, 3, { 250, 0, 0, 0, 0, 0, 0 } }, 243 { gettext_noop( "Grand Elf" ), gettext_noop( "Grand Elves" ), 4, Race::SORC, 3, { 300, 0, 0, 0, 0, 0, 0 } }, 244 { gettext_noop( "Druid" ), gettext_noop( "Druids" ), 3, Race::SORC, 4, { 350, 0, 0, 0, 0, 0, 0 } }, 245 { gettext_noop( "Greater Druid" ), gettext_noop( "Greater Druids" ), 3, Race::SORC, 4, { 400, 0, 0, 0, 0, 0, 0 } }, 246 { gettext_noop( "Unicorn" ), gettext_noop( "Unicorns" ), 2, Race::SORC, 5, { 500, 0, 0, 0, 0, 0, 0 } }, 247 { gettext_noop( "Phoenix" ), gettext_noop( "Phoenixes" ), 1, Race::SORC, 6, { 1500, 0, 1, 0, 0, 0, 0 } }, 248 { gettext_noop( "Centaur" ), gettext_noop( "Centaurs" ), 8, Race::WRLK, 1, { 60, 0, 0, 0, 0, 0, 0 } }, 249 { gettext_noop( "Gargoyle" ), gettext_noop( "Gargoyles" ), 6, Race::WRLK, 2, { 200, 0, 0, 0, 0, 0, 0 } }, 250 { gettext_noop( "Griffin" ), gettext_noop( "Griffins" ), 4, Race::WRLK, 3, { 300, 0, 0, 0, 0, 0, 0 } }, 251 { gettext_noop( "Minotaur" ), gettext_noop( "Minotaurs" ), 3, Race::WRLK, 4, { 400, 0, 0, 0, 0, 0, 0 } }, 252 { gettext_noop( "Minotaur King" ), gettext_noop( "Minotaur Kings" ), 3, Race::WRLK, 4, { 500, 0, 0, 0, 0, 0, 0 } }, 253 { gettext_noop( "Hydra" ), gettext_noop( "Hydras" ), 2, Race::WRLK, 5, { 800, 0, 0, 0, 0, 0, 0 } }, 254 { gettext_noop( "Green Dragon" ), gettext_noop( "Green Dragons" ), 1, Race::WRLK, 6, { 3000, 0, 0, 0, 1, 0, 0 } }, 255 { gettext_noop( "Red Dragon" ), gettext_noop( "Red Dragons" ), 1, Race::WRLK, 6, { 3500, 0, 0, 0, 1, 0, 0 } }, 256 { gettext_noop( "Black Dragon" ), gettext_noop( "Black Dragons" ), 1, Race::WRLK, 6, { 4000, 0, 0, 0, 2, 0, 0 } }, 257 { gettext_noop( "Halfling" ), gettext_noop( "Halflings" ), 8, Race::WZRD, 1, { 50, 0, 0, 0, 0, 0, 0 } }, 258 { gettext_noop( "Boar" ), gettext_noop( "Boars" ), 6, Race::WZRD, 2, { 150, 0, 0, 0, 0, 0, 0 } }, 259 { gettext_noop( "Iron Golem" ), gettext_noop( "Iron Golems" ), 4, Race::WZRD, 3, { 300, 0, 0, 0, 0, 0, 0 } }, 260 { gettext_noop( "Steel Golem" ), gettext_noop( "Steel Golems" ), 4, Race::WZRD, 3, { 350, 0, 0, 0, 0, 0, 0 } }, 261 { gettext_noop( "Roc" ), gettext_noop( "Rocs" ), 3, Race::WZRD, 4, { 400, 0, 0, 0, 0, 0, 0 } }, 262 { gettext_noop( "Mage" ), gettext_noop( "Magi" ), 2, Race::WZRD, 5, { 600, 0, 0, 0, 0, 0, 0 } }, 263 { gettext_noop( "Archmage" ), gettext_noop( "Archmagi" ), 2, Race::WZRD, 5, { 700, 0, 0, 0, 0, 0, 0 } }, 264 { gettext_noop( "Giant" ), gettext_noop( "Giants" ), 1, Race::WZRD, 6, { 2000, 0, 0, 0, 0, 0, 1 } }, 265 { gettext_noop( "Titan" ), gettext_noop( "Titans" ), 1, Race::WZRD, 6, { 5000, 0, 0, 0, 0, 0, 2 } }, 266 { gettext_noop( "Skeleton" ), gettext_noop( "Skeletons" ), 8, Race::NECR, 1, { 75, 0, 0, 0, 0, 0, 0 } }, 267 { gettext_noop( "Zombie" ), gettext_noop( "Zombies" ), 6, Race::NECR, 2, { 150, 0, 0, 0, 0, 0, 0 } }, 268 { gettext_noop( "Mutant Zombie" ), gettext_noop( "Mutant Zombies" ), 6, Race::NECR, 2, { 200, 0, 0, 0, 0, 0, 0 } }, 269 { gettext_noop( "Mummy" ), gettext_noop( "Mummies" ), 4, Race::NECR, 3, { 250, 0, 0, 0, 0, 0, 0 } }, 270 { gettext_noop( "Royal Mummy" ), gettext_noop( "Royal Mummies" ), 4, Race::NECR, 3, { 300, 0, 0, 0, 0, 0, 0 } }, 271 { gettext_noop( "Vampire" ), gettext_noop( "Vampires" ), 3, Race::NECR, 4, { 500, 0, 0, 0, 0, 0, 0 } }, 272 { gettext_noop( "Vampire Lord" ), gettext_noop( "Vampire Lords" ), 3, Race::NECR, 4, { 650, 0, 0, 0, 0, 0, 0 } }, 273 { gettext_noop( "Lich" ), gettext_noop( "Liches" ), 2, Race::NECR, 5, { 750, 0, 0, 0, 0, 0, 0 } }, 274 { gettext_noop( "Power Lich" ), gettext_noop( "Power Liches" ), 2, Race::NECR, 5, { 900, 0, 0, 0, 0, 0, 0 } }, 275 { gettext_noop( "Bone Dragon" ), gettext_noop( "Bone Dragons" ), 1, Race::NECR, 6, { 1500, 0, 0, 0, 0, 0, 0 } }, 276 { gettext_noop( "Rogue" ), gettext_noop( "Rogues" ), 8, Race::NONE, 1, { 50, 0, 0, 0, 0, 0, 0 } }, 277 { gettext_noop( "Nomad" ), gettext_noop( "Nomads" ), 4, Race::NONE, 2, { 200, 0, 0, 0, 0, 0, 0 } }, 278 { gettext_noop( "Ghost" ), gettext_noop( "Ghosts" ), 3, Race::NONE, 3, { 1000, 0, 0, 0, 0, 0, 0 } }, 279 { gettext_noop( "Genie" ), gettext_noop( "Genies" ), 2, Race::NONE, 6, { 650, 0, 0, 0, 0, 0, 1 } }, 280 { gettext_noop( "Medusa" ), gettext_noop( "Medusas" ), 5, Race::NONE, 4, { 500, 0, 0, 0, 0, 0, 0 } }, 281 { gettext_noop( "Earth Elemental" ), gettext_noop( "Earth Elementals" ), 4, Race::NONE, 4, { 500, 0, 0, 0, 0, 0, 0 } }, 282 { gettext_noop( "Air Elemental" ), gettext_noop( "Air Elementals" ), 4, Race::NONE, 4, { 500, 0, 0, 0, 0, 0, 0 } }, 283 { gettext_noop( "Fire Elemental" ), gettext_noop( "Fire Elementals" ), 4, Race::NONE, 4, { 500, 0, 0, 0, 0, 0, 0 } }, 284 { gettext_noop( "Water Elemental" ), gettext_noop( "Water Elementals" ), 4, Race::NONE, 4, { 500, 0, 0, 0, 0, 0, 0 } }, 285 { gettext_noop( "Random Monster" ), gettext_noop( "Random Monsters" ), 0, Race::NONE, 0, { 0, 0, 0, 0, 0, 0, 0 } }, 286 { gettext_noop( "Random Monster 1" ), gettext_noop( "Random Monsters 1" ), 0, Race::NONE, 1, { 0, 0, 0, 0, 0, 0, 0 } }, 287 { gettext_noop( "Random Monster 2" ), gettext_noop( "Random Monsters 2" ), 0, Race::NONE, 2, { 0, 0, 0, 0, 0, 0, 0 } }, 288 { gettext_noop( "Random Monster 3" ), gettext_noop( "Random Monsters 3" ), 0, Race::NONE, 3, { 0, 0, 0, 0, 0, 0, 0 } }, 289 { gettext_noop( "Random Monster 4" ), gettext_noop( "Random Monsters 4" ), 0, Race::NONE, 4, { 0, 0, 0, 0, 0, 0, 0 } } }; 290 291 monsterData.reserve( Monster::MONSTER_COUNT ); 292 293 for ( int i = 0; i < Monster::MONSTER_COUNT; ++i ) { 294 monsterData.emplace_back( monsterIcnIds[i], binFileName[i], monsterSounds[i], monsterBattleStats[i], monsterGeneralStats[i] ); 295 } 296 297 // Add monster abilities and weaknesses. 298 monsterData[Monster::RANGER].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_SHOOTING ); 299 300 monsterData[Monster::CAVALRY].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 301 302 monsterData[Monster::CHAMPION].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 303 304 monsterData[Monster::PALADIN].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_MELEE_ATTACK ); 305 306 monsterData[Monster::CRUSADER].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_MELEE_ATTACK ); 307 monsterData[Monster::CRUSADER].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_DAMAGE_TO_UNDEAD ); 308 monsterData[Monster::CRUSADER].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL, 100, Spell::CURSE ); 309 monsterData[Monster::CRUSADER].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL, 100, Spell::MASSCURSE ); 310 311 monsterData[Monster::WOLF].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 312 monsterData[Monster::WOLF].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_MELEE_ATTACK ); 313 314 monsterData[Monster::TROLL].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::HP_REGENERATION ); 315 316 monsterData[Monster::WAR_TROLL].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::HP_REGENERATION ); 317 318 monsterData[Monster::CYCLOPS].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::TWO_CELL_MELEE_ATTACK ); 319 monsterData[Monster::CYCLOPS].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::SPELL_CASTER, 20, Spell::PARALYZE ); 320 321 monsterData[Monster::SPRITE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::NO_ENEMY_RETALIATION ); 322 monsterData[Monster::SPRITE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 323 324 monsterData[Monster::DWARF].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::MAGIC_RESISTANCE, 25, 0 ); 325 326 monsterData[Monster::BATTLE_DWARF].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::MAGIC_RESISTANCE, 25, 0 ); 327 328 monsterData[Monster::ELF].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_SHOOTING ); 329 330 monsterData[Monster::GRAND_ELF].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_SHOOTING ); 331 332 monsterData[Monster::UNICORN].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 333 monsterData[Monster::UNICORN].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::SPELL_CASTER, 20, Spell::BLIND ); 334 335 monsterData[Monster::PHOENIX].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 336 monsterData[Monster::PHOENIX].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::TWO_CELL_MELEE_ATTACK ); 337 monsterData[Monster::PHOENIX].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 338 monsterData[Monster::PHOENIX].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ELEMENTAL_SPELL_IMMUNITY ); 339 340 monsterData[Monster::CENTAUR].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 341 342 monsterData[Monster::GARGOYLE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 343 344 monsterData[Monster::GRIFFIN].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 345 monsterData[Monster::GRIFFIN].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 346 monsterData[Monster::GRIFFIN].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ALWAYS_RETALIATE ); 347 348 monsterData[Monster::HYDRA].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 349 monsterData[Monster::HYDRA].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ALL_ADJACENT_CELL_MELEE_ATTACK ); 350 monsterData[Monster::HYDRA].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::NO_ENEMY_RETALIATION ); 351 352 monsterData[Monster::GREEN_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DRAGON ); 353 monsterData[Monster::GREEN_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 354 monsterData[Monster::GREEN_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 355 monsterData[Monster::GREEN_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::MAGIC_RESISTANCE, 100, 0 ); 356 monsterData[Monster::GREEN_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::TWO_CELL_MELEE_ATTACK ); 357 358 monsterData[Monster::RED_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DRAGON ); 359 monsterData[Monster::RED_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 360 monsterData[Monster::RED_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 361 monsterData[Monster::RED_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::MAGIC_RESISTANCE, 100, 0 ); 362 monsterData[Monster::RED_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::TWO_CELL_MELEE_ATTACK ); 363 364 monsterData[Monster::BLACK_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DRAGON ); 365 monsterData[Monster::BLACK_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 366 monsterData[Monster::BLACK_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 367 monsterData[Monster::BLACK_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::MAGIC_RESISTANCE, 100, 0 ); 368 monsterData[Monster::BLACK_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::TWO_CELL_MELEE_ATTACK ); 369 370 monsterData[Monster::BOAR].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 371 372 monsterData[Monster::IRON_GOLEM].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ELEMENTAL_SPELL_DAMAGE_REDUCTION, 50, 0 ); 373 374 monsterData[Monster::STEEL_GOLEM].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ELEMENTAL_SPELL_DAMAGE_REDUCTION, 50, 0 ); 375 376 monsterData[Monster::ROC].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 377 monsterData[Monster::ROC].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 378 379 monsterData[Monster::MAGE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::NO_MELEE_PENALTY ); 380 381 monsterData[Monster::ARCHMAGE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::NO_MELEE_PENALTY ); 382 monsterData[Monster::ARCHMAGE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::SPELL_CASTER, 20, Spell::DISPEL ); 383 384 monsterData[Monster::GIANT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::MIND_SPELL_IMMUNITY ); 385 386 monsterData[Monster::TITAN].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::NO_MELEE_PENALTY ); 387 monsterData[Monster::TITAN].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::MIND_SPELL_IMMUNITY ); 388 389 monsterData[Monster::SKELETON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 390 391 monsterData[Monster::ZOMBIE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 392 393 monsterData[Monster::MUTANT_ZOMBIE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 394 395 monsterData[Monster::MUMMY].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 396 monsterData[Monster::MUMMY].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::SPELL_CASTER, 20, Spell::CURSE ); 397 398 monsterData[Monster::ROYAL_MUMMY].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 399 monsterData[Monster::ROYAL_MUMMY].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::SPELL_CASTER, 30, Spell::CURSE ); 400 401 monsterData[Monster::VAMPIRE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 402 monsterData[Monster::VAMPIRE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 403 monsterData[Monster::VAMPIRE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::NO_ENEMY_RETALIATION ); 404 405 monsterData[Monster::VAMPIRE_LORD].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 406 monsterData[Monster::VAMPIRE_LORD].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 407 monsterData[Monster::VAMPIRE_LORD].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::NO_ENEMY_RETALIATION ); 408 monsterData[Monster::VAMPIRE_LORD].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::HP_DRAIN ); 409 410 monsterData[Monster::LICH].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 411 monsterData[Monster::LICH].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::AREA_SHOT ); 412 413 monsterData[Monster::POWER_LICH].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 414 monsterData[Monster::POWER_LICH].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::AREA_SHOT ); 415 416 monsterData[Monster::BONE_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 417 monsterData[Monster::BONE_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DRAGON ); 418 monsterData[Monster::BONE_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 419 monsterData[Monster::BONE_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 420 monsterData[Monster::BONE_DRAGON].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::MORAL_DECREMENT, 100, 1 ); 421 422 monsterData[Monster::ROGUE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::NO_ENEMY_RETALIATION ); 423 424 monsterData[Monster::GHOST].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::SOUL_EATER ); 425 monsterData[Monster::GHOST].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 426 monsterData[Monster::GHOST].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::UNDEAD ); 427 428 monsterData[Monster::GENIE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ENEMY_HALFING, 10, 0 ); 429 monsterData[Monster::GENIE].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FLYING ); 430 431 monsterData[Monster::MEDUSA].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 432 monsterData[Monster::MEDUSA].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::SPELL_CASTER, 20, Spell::STONE ); 433 434 monsterData[Monster::NOMAD].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::DOUBLE_HEX_SIZE ); 435 436 monsterData[Monster::AIR_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ELEMENTAL ); 437 monsterData[Monster::AIR_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL, 100, Spell::METEORSHOWER ); 438 monsterData[Monster::AIR_ELEMENT].battleStats.weaknesses.emplace_back( fheroes2::MonsterWeaknessType::EXTRA_DAMAGE_FROM_CERTAIN_SPELL, 100, 439 Spell::LIGHTNINGBOLT ); 440 monsterData[Monster::AIR_ELEMENT].battleStats.weaknesses.emplace_back( fheroes2::MonsterWeaknessType::EXTRA_DAMAGE_FROM_CERTAIN_SPELL, 100, 441 Spell::CHAINLIGHTNING ); 442 monsterData[Monster::AIR_ELEMENT].battleStats.weaknesses.emplace_back( fheroes2::MonsterWeaknessType::EXTRA_DAMAGE_FROM_CERTAIN_SPELL, 100, 443 Spell::ELEMENTALSTORM ); 444 445 monsterData[Monster::EARTH_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ELEMENTAL ); 446 monsterData[Monster::EARTH_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL, 100, Spell::LIGHTNINGBOLT ); 447 monsterData[Monster::EARTH_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL, 100, Spell::CHAINLIGHTNING ); 448 monsterData[Monster::EARTH_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL, 100, Spell::ELEMENTALSTORM ); 449 monsterData[Monster::EARTH_ELEMENT].battleStats.weaknesses.emplace_back( fheroes2::MonsterWeaknessType::EXTRA_DAMAGE_FROM_CERTAIN_SPELL, 100, 450 Spell::METEORSHOWER ); 451 452 monsterData[Monster::FIRE_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ELEMENTAL ); 453 monsterData[Monster::FIRE_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::FIRE_SPELL_IMMUNITY ); 454 monsterData[Monster::FIRE_ELEMENT].battleStats.weaknesses.emplace_back( fheroes2::MonsterWeaknessType::EXTRA_DAMAGE_FROM_COLD_SPELL ); 455 456 monsterData[Monster::WATER_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::ELEMENTAL ); 457 monsterData[Monster::WATER_ELEMENT].battleStats.abilities.emplace_back( fheroes2::MonsterAbilityType::COLD_SPELL_IMMUNITY ); 458 monsterData[Monster::WATER_ELEMENT].battleStats.weaknesses.emplace_back( fheroes2::MonsterWeaknessType::EXTRA_DAMAGE_FROM_FIRE_SPELL ); 459 460 // TODO: verify that no duplicates of abilities and weaknesses exist. 461 } 462 removeDuplicateSpell(std::set<int> & sortedSpellIds,const int massSpellId,const int spellId)463 void removeDuplicateSpell( std::set<int> & sortedSpellIds, const int massSpellId, const int spellId ) 464 { 465 if ( sortedSpellIds.count( massSpellId ) > 0 && sortedSpellIds.count( spellId ) > 0 ) { 466 sortedSpellIds.erase( massSpellId ); 467 } 468 } 469 replaceMassSpells(const std::vector<int> & spellIds)470 std::vector<int> replaceMassSpells( const std::vector<int> & spellIds ) 471 { 472 std::set<int> sortedSpellIds( spellIds.begin(), spellIds.end() ); 473 474 removeDuplicateSpell( sortedSpellIds, Spell::CHAINLIGHTNING, Spell::LIGHTNINGBOLT ); 475 removeDuplicateSpell( sortedSpellIds, Spell::MASSCURE, Spell::CURE ); 476 removeDuplicateSpell( sortedSpellIds, Spell::MASSCURSE, Spell::CURSE ); 477 removeDuplicateSpell( sortedSpellIds, Spell::MASSBLESS, Spell::BLESS ); 478 removeDuplicateSpell( sortedSpellIds, Spell::MASSHASTE, Spell::HASTE ); 479 removeDuplicateSpell( sortedSpellIds, Spell::MASSSHIELD, Spell::SHIELD ); 480 removeDuplicateSpell( sortedSpellIds, Spell::MASSDISPEL, Spell::DISPEL ); 481 removeDuplicateSpell( sortedSpellIds, Spell::MASSSLOW, Spell::SLOW ); 482 483 return std::vector<int>( sortedSpellIds.begin(), sortedSpellIds.end() ); 484 } 485 } 486 487 namespace fheroes2 488 { getMonsterData(const int monsterId)489 const MonsterData & getMonsterData( const int monsterId ) 490 { 491 if ( monsterData.empty() ) { 492 populateMonsterData(); 493 } 494 495 assert( monsterId >= 0 && static_cast<size_t>( monsterId ) < monsterData.size() ); 496 if ( monsterId < 0 || static_cast<size_t>( monsterId ) >= monsterData.size() ) { 497 return monsterData.front(); 498 } 499 500 return monsterData[monsterId]; 501 } 502 getMonsterAbilityDescription(const MonsterAbility & ability,const bool ignoreBasicAbility)503 std::string getMonsterAbilityDescription( const MonsterAbility & ability, const bool ignoreBasicAbility ) 504 { 505 switch ( ability.type ) { 506 case MonsterAbilityType::NONE: 507 return ignoreBasicAbility ? "" : _( "None" ); 508 case MonsterAbilityType::DOUBLE_SHOOTING: 509 return _( "Double shot" ); 510 case MonsterAbilityType::DOUBLE_HEX_SIZE: 511 return ignoreBasicAbility ? "" : _( "2-hex monster" ); 512 case MonsterAbilityType::DOUBLE_MELEE_ATTACK: 513 return _( "Double strike" ); 514 case MonsterAbilityType::DOUBLE_DAMAGE_TO_UNDEAD: 515 return _( "Double damage to Undead" ); 516 case MonsterAbilityType::MAGIC_RESISTANCE: 517 return std::to_string( ability.percentage ) + _( "% magic resistance" ); 518 case MonsterAbilityType::MIND_SPELL_IMMUNITY: 519 return _( "Immune to Mind spells" ); 520 case MonsterAbilityType::ELEMENTAL_SPELL_IMMUNITY: 521 return _( "Immune to Elemental spells" ); 522 case MonsterAbilityType::FIRE_SPELL_IMMUNITY: 523 return _( "Immune to Fire spells" ); 524 case MonsterAbilityType::COLD_SPELL_IMMUNITY: 525 return _( "Immune to Cold spells" ); 526 case MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL: 527 if ( ability.percentage == 100 ) { 528 return std::string( _( "Immune to " ) ) + Spell( ability.value ).GetName(); 529 } 530 else { 531 std::string str = _( "% immunity to %{spell} spell" ); 532 StringReplace( str, "%{spell}", Spell( ability.value ).GetName() ); 533 return std::to_string( ability.percentage ) + str; 534 } 535 case MonsterAbilityType::ELEMENTAL_SPELL_DAMAGE_REDUCTION: 536 return std::to_string( ability.percentage ) + _( "% damage from Elemental spells" ); 537 case MonsterAbilityType::SPELL_CASTER: 538 if ( ability.value == Spell::DISPEL ) { 539 return std::to_string( ability.percentage ) + _( "% chance to Dispel beneficial spells" ); 540 } 541 else if ( ability.value == Spell::PARALYZE ) { 542 return std::to_string( ability.percentage ) + _( "% chance to Paralyze" ); 543 } 544 else if ( ability.value == Spell::STONE ) { 545 return std::to_string( ability.percentage ) + _( "% chance to Petrify" ); 546 } 547 else if ( ability.value == Spell::BLIND ) { 548 return std::to_string( ability.percentage ) + _( "% chance to Blind" ); 549 } 550 else if ( ability.value == Spell::CURSE ) { 551 return std::to_string( ability.percentage ) + _( "% chance to Curse" ); 552 } 553 else { 554 std::string str = _( "% chance to cast %{spell} spell" ); 555 StringReplace( str, "%{spell}", Spell( ability.value ).GetName() ); 556 return std::to_string( ability.percentage ) + str; 557 } 558 case MonsterAbilityType::HP_REGENERATION: 559 return _( "HP regeneration" ); 560 case MonsterAbilityType::TWO_CELL_MELEE_ATTACK: 561 return _( "Two hexes attack" ); 562 case MonsterAbilityType::FLYING: 563 return ignoreBasicAbility ? "" : _( "Flyer" ); 564 case MonsterAbilityType::ALWAYS_RETALIATE: 565 return _( "Always retaliates" ); 566 case MonsterAbilityType::ALL_ADJACENT_CELL_MELEE_ATTACK: 567 return _( "Attacks all adjacent enemies" ); 568 case MonsterAbilityType::NO_MELEE_PENALTY: 569 return _( "No melee penalty" ); 570 case MonsterAbilityType::DRAGON: 571 return ignoreBasicAbility ? "" : _( "Dragon" ); 572 case MonsterAbilityType::UNDEAD: 573 return _( "Undead" ); 574 case MonsterAbilityType::NO_ENEMY_RETALIATION: 575 return _( "No enemy retaliation" ); 576 case MonsterAbilityType::HP_DRAIN: 577 return _( "HP drain" ); 578 case MonsterAbilityType::AREA_SHOT: 579 return _( "Cloud attack" ); 580 case MonsterAbilityType::MORAL_DECREMENT: 581 return _( "Decreases enemy's morale by " ) + std::to_string( ability.value ); 582 case MonsterAbilityType::ENEMY_HALFING: 583 return std::to_string( ability.percentage ) + _( "% chance to halve enemy" ); 584 case MonsterAbilityType::SOUL_EATER: 585 return _( "Soul Eater" ); 586 case MonsterAbilityType::ELEMENTAL: 587 return ignoreBasicAbility ? _( "No Morale" ) : _( "Elemental" ); 588 default: 589 break; 590 } 591 592 assert( 0 ); // Did you add a new ability? Please add the implementation! 593 return ""; 594 } 595 getMonsterWeaknessDescription(const MonsterWeakness & weakness,const bool ignoreBasicAbility)596 std::string getMonsterWeaknessDescription( const MonsterWeakness & weakness, const bool ignoreBasicAbility ) 597 { 598 switch ( weakness.type ) { 599 case MonsterWeaknessType::NONE: 600 return ignoreBasicAbility ? "" : _( "None" ); 601 case MonsterWeaknessType::EXTRA_DAMAGE_FROM_FIRE_SPELL: 602 return _( "200% damage from Fire spells" ); 603 case MonsterWeaknessType::EXTRA_DAMAGE_FROM_COLD_SPELL: 604 return _( "200% damage from Cold spells" ); 605 case MonsterWeaknessType::EXTRA_DAMAGE_FROM_CERTAIN_SPELL: { 606 std::string str = _( "% damage from %{spell} spell" ); 607 StringReplace( str, "%{spell}", Spell( weakness.value ).GetName() ); 608 return std::to_string( weakness.percentage + 100 ) + str; 609 } 610 default: 611 break; 612 } 613 614 assert( 0 ); // Did you add a new weakness? Please add the implementation! 615 return ""; 616 } 617 getMonsterDescription(const int monsterId)618 std::string getMonsterDescription( const int monsterId ) 619 { 620 if ( monsterData.empty() ) { 621 populateMonsterData(); 622 } 623 624 assert( monsterId >= 0 && static_cast<size_t>( monsterId ) < monsterData.size() ); 625 if ( monsterId < 0 || static_cast<size_t>( monsterId ) >= monsterData.size() ) { 626 return ""; 627 } 628 629 const MonsterData & monster = monsterData[monsterId]; 630 631 std::ostringstream os; 632 os << "----------" << std::endl; 633 os << "Name: " << monster.generalStats.name << std::endl; 634 os << "Plural name: " << monster.generalStats.pluralName << std::endl; 635 os << "Base growth: " << monster.generalStats.baseGrowth << std::endl; 636 os << "Race: " << Race::String( monster.generalStats.race ) << std::endl; 637 os << "Level: " << monster.generalStats.level << std::endl; 638 os << "Cost: " << Funds( monster.generalStats.cost ).String() << std::endl; 639 os << std::endl; 640 os << "Attack: " << monster.battleStats.attack << std::endl; 641 os << "Defense: " << monster.battleStats.defense << std::endl; 642 os << "Min damage: " << monster.battleStats.damageMin << std::endl; 643 os << "Max damage: " << monster.battleStats.damageMax << std::endl; 644 os << "Hit Points: " << monster.battleStats.hp << std::endl; 645 os << "Speed: " << Speed::String( monster.battleStats.speed ) << std::endl; 646 os << "Number of shots: " << monster.battleStats.shots << std::endl; 647 if ( !monster.battleStats.abilities.empty() ) { 648 os << std::endl; 649 os << "Abilities:" << std::endl; 650 for ( const MonsterAbility & ability : monster.battleStats.abilities ) { 651 os << " " << getMonsterAbilityDescription( ability, false ) << std::endl; 652 } 653 } 654 655 if ( !monster.battleStats.weaknesses.empty() ) { 656 os << std::endl; 657 os << "Weaknesses:" << std::endl; 658 for ( const MonsterWeakness & weakness : monster.battleStats.weaknesses ) { 659 os << " " << getMonsterWeaknessDescription( weakness, false ) << std::endl; 660 } 661 } 662 663 return os.str(); 664 } 665 getMonsterPropertiesDescription(const int monsterId)666 std::vector<std::string> getMonsterPropertiesDescription( const int monsterId ) 667 { 668 std::vector<std::string> output; 669 670 const MonsterBattleStats & battleStats = getMonsterData( monsterId ).battleStats; 671 672 const std::vector<MonsterAbility> & abilities = battleStats.abilities; 673 674 std::map<uint32_t, std::vector<int>> immuneToSpells; 675 for ( const MonsterAbility & ability : abilities ) { 676 if ( ability.type == MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL ) { 677 immuneToSpells[ability.percentage].emplace_back( ability.value ); 678 continue; 679 } 680 const std::string abilityDescription = getMonsterAbilityDescription( ability, true ); 681 if ( !abilityDescription.empty() ) { 682 output.emplace_back( abilityDescription + '.' ); 683 } 684 } 685 686 for ( auto spellInfoIter = immuneToSpells.begin(); spellInfoIter != immuneToSpells.end(); ++spellInfoIter ) { 687 assert( !spellInfoIter->second.empty() ); 688 689 std::string temp; 690 691 if ( spellInfoIter->first == 100 ) { 692 temp += _( "Immune to " ); 693 } 694 else { 695 temp += std::to_string( spellInfoIter->first ) + _( "% immunity to " ); 696 } 697 698 const std::vector<int> sortedSpells = replaceMassSpells( spellInfoIter->second ); 699 700 for ( size_t i = 0; i < sortedSpells.size(); ++i ) { 701 if ( i > 0 ) { 702 temp += ", "; 703 } 704 705 if ( sortedSpells[i] == Spell::LIGHTNINGBOLT ) { 706 temp += _( "Lightning" ); 707 } 708 else { 709 temp += Spell( sortedSpells[i] ).GetName(); 710 } 711 } 712 713 temp += '.'; 714 output.emplace_back( std::move( temp ) ); 715 } 716 717 std::map<uint32_t, std::vector<int>> extraDamageSpells; 718 const std::vector<MonsterWeakness> & weaknesses = battleStats.weaknesses; 719 for ( const MonsterWeakness & weakness : weaknesses ) { 720 if ( weakness.type == MonsterWeaknessType::EXTRA_DAMAGE_FROM_CERTAIN_SPELL ) { 721 extraDamageSpells[weakness.percentage].emplace_back( weakness.value ); 722 continue; 723 } 724 725 const std::string weaknessDescription = getMonsterWeaknessDescription( weakness, true ); 726 if ( !weaknessDescription.empty() ) { 727 output.emplace_back( weaknessDescription + '.' ); 728 } 729 } 730 731 for ( auto spellInfoIter = extraDamageSpells.begin(); spellInfoIter != extraDamageSpells.end(); ++spellInfoIter ) { 732 assert( !spellInfoIter->second.empty() ); 733 734 std::string temp; 735 736 temp += std::to_string( spellInfoIter->first + 100 ) + _( "% damage from " ); 737 738 const std::vector<int> sortedSpells = replaceMassSpells( spellInfoIter->second ); 739 740 for ( size_t i = 0; i < sortedSpells.size(); ++i ) { 741 if ( i > 0 ) { 742 temp += ", "; 743 } 744 745 if ( sortedSpells[i] == Spell::LIGHTNINGBOLT ) { 746 temp += _( "Lightning" ); 747 } 748 else { 749 temp += Spell( sortedSpells[i] ).GetName(); 750 } 751 } 752 753 temp += '.'; 754 output.emplace_back( std::move( temp ) ); 755 } 756 757 return output; 758 } 759 getSpellResistance(const int monsterId,const int spellId)760 uint32_t getSpellResistance( const int monsterId, const int spellId ) 761 { 762 const std::vector<MonsterAbility> & abilities = getMonsterData( monsterId ).battleStats.abilities; 763 764 Spell spell( spellId ); 765 766 std::vector<MonsterAbility>::const_iterator foundAbility; 767 768 if ( spell.isMindInfluence() ) { 769 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::MIND_SPELL_IMMUNITY ) ); 770 if ( foundAbility != abilities.end() ) { 771 return 100; 772 } 773 774 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::UNDEAD ) ); 775 if ( foundAbility != abilities.end() ) { 776 return 100; 777 } 778 779 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::ELEMENTAL ) ); 780 if ( foundAbility != abilities.end() ) { 781 return 100; 782 } 783 } 784 785 if ( spell.isALiveOnly() ) { 786 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::UNDEAD ) ); 787 if ( foundAbility != abilities.end() ) { 788 return 100; 789 } 790 } 791 792 if ( spell.isUndeadOnly() ) { 793 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::UNDEAD ) ); 794 if ( foundAbility == abilities.end() ) { 795 return 100; 796 } 797 } 798 799 if ( spell == Spell::RESURRECT || spell == Spell::RESURRECTTRUE || spell == Spell::ANIMATEDEAD ) { 800 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::ELEMENTAL ) ); 801 if ( foundAbility != abilities.end() ) { 802 return 100; 803 } 804 } 805 806 if ( spell.isCold() ) { 807 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::COLD_SPELL_IMMUNITY ) ); 808 if ( foundAbility != abilities.end() ) { 809 return 100; 810 } 811 } 812 813 if ( spell.isFire() ) { 814 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::FIRE_SPELL_IMMUNITY ) ); 815 if ( foundAbility != abilities.end() ) { 816 return 100; 817 } 818 } 819 820 if ( spell == Spell::COLDRAY || spell == Spell::COLDRING || spell == Spell::FIREBALL || spell == Spell::FIREBLAST || spell == Spell::LIGHTNINGBOLT 821 || spell == Spell::CHAINLIGHTNING || spell == Spell::ELEMENTALSTORM ) { 822 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::ELEMENTAL_SPELL_IMMUNITY ) ); 823 if ( foundAbility != abilities.end() ) { 824 return 100; 825 } 826 } 827 828 uint32_t spellResistance = 0; 829 830 // Find magic immunity for every spell. 831 foundAbility = std::find( abilities.begin(), abilities.end(), MonsterAbility( MonsterAbilityType::MAGIC_RESISTANCE ) ); 832 if ( foundAbility != abilities.end() ) { 833 if ( foundAbility->percentage == 100 ) { 834 // Immune to everything. 835 return 100; 836 } 837 if ( spell.isDamage() || spell.isApplyToEnemies() ) { 838 spellResistance = foundAbility->percentage; 839 } 840 } 841 842 for ( const MonsterAbility & ability : abilities ) { 843 if ( ability.type == MonsterAbilityType::IMMUNE_TO_CERTAIN_SPELL && static_cast<int>( ability.value ) == spellId ) { 844 spellResistance = std::max( spellResistance, ability.percentage ); 845 } 846 } 847 848 return spellResistance; 849 } 850 } 851