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