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 "spell_info.h" 22 #include "heroes.h" 23 #include "kingdom.h" 24 #include "spell.h" 25 #include "tools.h" 26 #include "translations.h" 27 28 #include <cassert> 29 30 namespace fheroes2 31 { getSpellDamage(const Spell & spell,const uint32_t spellPower,const HeroBase * hero)32 uint32_t getSpellDamage( const Spell & spell, const uint32_t spellPower, const HeroBase * hero ) 33 { 34 assert( spellPower > 0 ); 35 36 uint32_t damage = spell.Damage() * spellPower; 37 38 if ( hero == nullptr ) { 39 return damage; 40 } 41 42 switch ( spell.GetID() ) { 43 case Spell::COLDRAY: 44 case Spell::COLDRING: 45 if ( hero->hasArtifact( Artifact::EVERCOLD_ICICLE ) ) { 46 damage += damage * Artifact( Artifact::EVERCOLD_ICICLE ).ExtraValue() / 100; 47 } 48 break; 49 50 case Spell::FIREBALL: 51 case Spell::FIREBLAST: 52 if ( hero->hasArtifact( Artifact::EVERHOT_LAVA_ROCK ) ) { 53 damage += damage * Artifact( Artifact::EVERHOT_LAVA_ROCK ).ExtraValue() / 100; 54 } 55 break; 56 57 case Spell::LIGHTNINGBOLT: 58 case Spell::CHAINLIGHTNING: 59 if ( hero->hasArtifact( Artifact::LIGHTNING_ROD ) ) { 60 damage += damage * Artifact( Artifact::LIGHTNING_ROD ).ExtraValue() / 100; 61 } 62 break; 63 default: 64 break; 65 } 66 67 return damage; 68 } 69 getSummonMonsterCount(const Spell & spell,const uint32_t spellPower,const HeroBase * hero)70 uint32_t getSummonMonsterCount( const Spell & spell, const uint32_t spellPower, const HeroBase * hero ) 71 { 72 assert( spellPower > 0 ); 73 74 uint32_t monsterCount = spell.ExtraValue() * spellPower; 75 if ( hero == nullptr ) { 76 return monsterCount; 77 } 78 79 uint32_t artifactCount = hero->artifactCount( Artifact::BOOK_ELEMENTS ); 80 if ( artifactCount > 0 ) { 81 monsterCount *= artifactCount * 2; 82 } 83 84 return monsterCount; 85 } 86 getHPRestorePoints(const Spell & spell,const uint32_t spellPower,const HeroBase * hero)87 uint32_t getHPRestorePoints( const Spell & spell, const uint32_t spellPower, const HeroBase * hero ) 88 { 89 (void)hero; 90 91 assert( spellPower > 0 ); 92 93 return spell.Restore() * spellPower; 94 } 95 getResurrectPoints(const Spell & spell,const uint32_t spellPower,const HeroBase * hero)96 uint32_t getResurrectPoints( const Spell & spell, const uint32_t spellPower, const HeroBase * hero ) 97 { 98 assert( spellPower > 0 ); 99 100 uint32_t resurrectionPoints = spell.Resurrect() * spellPower; 101 if ( hero == nullptr ) { 102 return resurrectionPoints; 103 } 104 105 uint32_t artifactCount = hero ? hero->artifactCount( Artifact::ANKH ) : 0; 106 if ( artifactCount ) { 107 resurrectionPoints *= artifactCount * 2; 108 } 109 110 return resurrectionPoints; 111 } 112 getGuardianMonsterCount(const Spell & spell,const uint32_t spellPower,const HeroBase * hero)113 uint32_t getGuardianMonsterCount( const Spell & spell, const uint32_t spellPower, const HeroBase * hero ) 114 { 115 (void)hero; 116 117 assert( spellPower > 0 ); 118 119 return spell.ExtraValue() * spellPower; 120 } 121 getHypnorizeMonsterHPPoints(const Spell & spell,const uint32_t spellPower,const HeroBase * hero)122 uint32_t getHypnorizeMonsterHPPoints( const Spell & spell, const uint32_t spellPower, const HeroBase * hero ) 123 { 124 (void)hero; 125 126 assert( spell == Spell::HYPNOTIZE ); 127 assert( spellPower > 0 ); 128 129 return spell.ExtraValue() * spellPower; 130 } 131 getNearestCastleTownGate(const Heroes & hero)132 const Castle * getNearestCastleTownGate( const Heroes & hero ) 133 { 134 const Kingdom & kingdom = hero.GetKingdom(); 135 const KingdomCastles & castles = kingdom.GetCastles(); 136 137 const fheroes2::Point & heroPosition = hero.GetCenter(); 138 int32_t minDistance = -1; 139 140 const Castle * nearestCastle = nullptr; 141 142 for ( const Castle * castle : castles ) { 143 if ( castle == nullptr ) { 144 continue; 145 } 146 147 const fheroes2::Point & castlePosition = castle->GetCenter(); 148 const int32_t offsetX = heroPosition.x - castlePosition.x; 149 const int32_t offsetY = heroPosition.y - castlePosition.y; 150 const int32_t distance = offsetX * offsetX + offsetY * offsetY; 151 if ( minDistance < 0 || distance < minDistance ) { 152 minDistance = distance; 153 nearestCastle = castle; 154 } 155 } 156 157 return nearestCastle; 158 } 159 getSpellDescription(const Spell & spell,const HeroBase * hero)160 std::string getSpellDescription( const Spell & spell, const HeroBase * hero ) 161 { 162 if ( hero == nullptr ) { 163 return spell.GetDescription(); 164 } 165 166 std::string description = spell.GetDescription(); 167 168 if ( spell.isDamage() ) { 169 description += "\n \n"; 170 description += _( "This spell does %{damage} points of damage." ); 171 StringReplace( description, "%{damage}", getSpellDamage( spell, hero->GetPower(), hero ) ); 172 173 return description; 174 } 175 176 if ( spell.isSummon() ) { 177 const Monster monster( spell ); 178 if ( !monster.isValid() ) { 179 // Did you add a new summoning spell but forgot to add corresponding monster? 180 assert( 0 ); 181 return spell.GetDescription(); 182 } 183 184 const uint32_t summonCount = getSummonMonsterCount( spell, hero->GetPower(), hero ); 185 186 description += "\n \n"; 187 description += _( "This spell summons\n%{count} %{monster}." ); 188 StringReplace( description, "%{count}", summonCount ); 189 StringReplace( description, "%{monster}", monster.GetPluralName( summonCount ) ); 190 191 return description; 192 } 193 194 if ( spell.isRestore() ) { 195 description += "\n \n"; 196 description += _( "This spell restores %{hp} HP." ); 197 StringReplace( description, "%{hp}", getHPRestorePoints( spell, hero->GetPower(), hero ) ); 198 199 return description; 200 } 201 202 if ( spell.isResurrect() ) { 203 description += "\n \n"; 204 description += _( "This spell restores %{hp} HP." ); 205 StringReplace( description, "%{hp}", getResurrectPoints( spell, hero->GetPower(), hero ) ); 206 207 return description; 208 } 209 210 if ( spell.isGuardianType() ) { 211 const Monster monster( spell ); 212 if ( !monster.isValid() ) { 213 // Did you add a new guardian spell but forgot to add corresponding monster? 214 assert( 0 ); 215 return spell.GetDescription(); 216 } 217 218 const uint32_t guardianCount = getGuardianMonsterCount( spell, hero->GetPower(), hero ); 219 220 description += "\n \n"; 221 description += _( "This spell summons %{count} %{monster} to guard the mine." ); 222 StringReplace( description, "%{count}", guardianCount ); 223 StringReplace( description, "%{monster}", monster.GetPluralName( guardianCount ) ); 224 225 return description; 226 } 227 228 if ( spell == Spell::TOWNGATE ) { 229 const Heroes * realHero = dynamic_cast<const Heroes *>( hero ); 230 if ( realHero == nullptr ) { 231 return description; 232 } 233 234 const Castle * castle = getNearestCastleTownGate( *realHero ); 235 if ( castle == nullptr ) { 236 return description; 237 } 238 239 description += "\n \n"; 240 241 description += _( "The nearest town is %{town}." ); 242 StringReplace( description, "%{town}", castle->GetName() ); 243 244 return description; 245 } 246 247 if ( spell == Spell::HYPNOTIZE ) { 248 description += "\n \n"; 249 description += _( "This spell controls up to\n%{hp} HP." ); 250 StringReplace( description, "%{hp}", getHypnorizeMonsterHPPoints( spell, hero->GetPower(), hero ) ); 251 252 return description; 253 } 254 255 return description; 256 } 257 } 258