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 "spell.h"
24 #include "artifact.h"
25 #include "heroes_base.h"
26 #include "race.h"
27 #include "rand.h"
28 #include "resource.h"
29 #include "serialize.h"
30 #include "translations.h"
31 
32 struct spellstats_t
33 {
34     const char * name;
35     uint8_t spellPoints;
36     uint16_t movePoints;
37     uint32_t imageId;
38     uint8_t extraValue;
39     const char * description;
40 };
41 
42 // The original resources don't have most of sprites for Mass Spells
43 // so we made some tricks in AGG source file. All modified sprite IDs start from 60
44 
45 spellstats_t spells[] = {
46     //  name | sp | mp | spr | value| bits | description
47     { "Unknown", 0, 0, 0, 0, "Unknown spell." },
48     { gettext_noop( "Fireball" ), 9, 0, 8, 10, gettext_noop( "Causes a giant fireball to strike the selected area, damaging all nearby creatures." ) },
49     { gettext_noop( "Fireblast" ), 15, 0, 9, 10,
50       gettext_noop( "An improved version of fireball, fireblast affects two hexes around the center point of the spell, rather than one." ) },
51     { gettext_noop( "Lightning Bolt" ), 7, 0, 4, 25, gettext_noop( "Causes a bolt of electrical energy to strike the selected creature." ) },
52     { gettext_noop( "Chain Lightning" ), 15, 0, 5, 40,
53       gettext_noop(
54           "Causes a bolt of electrical energy to strike a selected creature, then strike the nearest creature with half damage, then strike the NEXT nearest creature with half again damage, and so on, until it becomes too weak to be harmful.  Warning:  This spell can hit your own creatures!" ) },
55     { gettext_noop( "Teleport" ), 9, 0, 10, 0, gettext_noop( "Teleports the creature you select to any open position on the battlefield." ) },
56     { gettext_noop( "Cure" ), 6, 0, 6, 5,
57       gettext_noop( "Removes all negative spells cast upon one of your units, and restores up to %{count} HP per level of spell power." ) },
58     { gettext_noop( "Mass Cure" ), 15, 0, 60, 5,
59       gettext_noop( "Removes all negative spells cast upon your forces, and restores up to %{count} HP per level of spell power, per creature." ) },
60     { gettext_noop( "Resurrect" ), 12, 0, 13, 50, gettext_noop( "Resurrects creatures from a damaged or dead unit until end of combat." ) },
61     { gettext_noop( "Resurrect True" ), 15, 0, 12, 50, gettext_noop( "Resurrects creatures from a damaged or dead unit permanently." ) },
62     { gettext_noop( "Haste" ), 3, 0, 14, 0, gettext_noop( "Increases the speed of any creature by %{count}." ) },
63     { gettext_noop( "Mass Haste" ), 10, 0, 61, 0, gettext_noop( "Increases the speed of all of your creatures by %{count}." ) },
64     { gettext_noop( "spell|Slow" ), 3, 0, 1, 0, gettext_noop( "Slows target to half movement rate." ) },
65     { gettext_noop( "Mass Slow" ), 15, 0, 62, 0, gettext_noop( "Slows all enemies to half movement rate." ) },
66     { gettext_noop( "spell|Blind" ), 6, 0, 21, 0, gettext_noop( "Clouds the affected creatures' eyes, preventing them from moving." ) },
67     { gettext_noop( "Bless" ), 3, 0, 7, 0, gettext_noop( "Causes the selected creatures to inflict maximum damage." ) },
68     { gettext_noop( "Mass Bless" ), 12, 0, 63, 0, gettext_noop( "Causes all of your units to inflict maximum damage." ) },
69     { gettext_noop( "Stoneskin" ), 3, 0, 31, 3, gettext_noop( "Magically increases the defense skill of the selected creatures." ) },
70     { gettext_noop( "Steelskin" ), 6, 0, 30, 5, gettext_noop( "Increases the defense skill of the targeted creatures.  This is an improved version of Stoneskin." ) },
71     { gettext_noop( "Curse" ), 3, 0, 3, 0, gettext_noop( "Causes the selected creatures to inflict minimum damage." ) },
72     { gettext_noop( "Mass Curse" ), 12, 0, 64, 0, gettext_noop( "Causes all enemy troops to inflict minimum damage." ) },
73     { gettext_noop( "Holy Word" ), 9, 0, 22, 10, gettext_noop( "Damages all undead in the battle." ) },
74     { gettext_noop( "Holy Shout" ), 12, 0, 23, 20, gettext_noop( "Damages all undead in the battle.  This is an improved version of Holy Word." ) },
75     { gettext_noop( "Anti-Magic" ), 7, 0, 17, 0, gettext_noop( "Prevents harmful magic against the selected creatures." ) },
76     { gettext_noop( "Dispel Magic" ), 5, 0, 18, 0, gettext_noop( "Removes all magic spells from a single target." ) },
77     { gettext_noop( "Mass Dispel" ), 12, 0, 18, 0, gettext_noop( "Removes all magic spells from all creatures." ) },
78     { gettext_noop( "Magic Arrow" ), 3, 0, 38, 10, gettext_noop( "Causes a magic arrow to strike the selected target." ) },
79     { gettext_noop( "Berserker" ), 12, 0, 19, 0, gettext_noop( "Causes a creature to attack its nearest neighbor." ) },
80     { gettext_noop( "Armageddon" ), 20, 0, 16, 50, gettext_noop( "Holy terror strikes the battlefield, causing severe damage to all creatures." ) },
81     { gettext_noop( "Elemental Storm" ), 15, 0, 11, 25, gettext_noop( "Magical elements pour down on the battlefield, damaging all creatures." ) },
82     { gettext_noop( "Meteor Shower" ), 15, 0, 24, 25, gettext_noop( "A rain of rocks strikes an area of the battlefield, damaging all nearby creatures." ) },
83     { gettext_noop( "Paralyze" ), 9, 0, 20, 0, gettext_noop( "The targeted creatures are paralyzed, unable to move or retaliate." ) },
84     { gettext_noop( "Hypnotize" ), 15, 0, 37, 25,
85       gettext_noop( "Brings a single enemy unit under your control if its hits are less than %{count} times the caster's spell power." ) },
86     { gettext_noop( "Cold Ray" ), 6, 0, 36, 20, gettext_noop( "Drains body heat from a single enemy unit." ) },
87     { gettext_noop( "Cold Ring" ), 9, 0, 35, 10, gettext_noop( "Drains body heat from all units surrounding the center point, but not including the center point." ) },
88     { gettext_noop( "Disrupting Ray" ), 7, 0, 34, 3, gettext_noop( "Reduces the defense rating of an enemy unit by three." ) },
89     { gettext_noop( "Death Ripple" ), 6, 0, 28, 5, gettext_noop( "Damages all living (non-undead) units in the battle." ) },
90     { gettext_noop( "Death Wave" ), 10, 0, 29, 10,
91       gettext_noop( "Damages all living (non-undead) units in the battle.  This spell is an improved version of Death Ripple." ) },
92     { gettext_noop( "Dragon Slayer" ), 6, 0, 32, 5, gettext_noop( "Greatly increases a unit's attack skill vs. Dragons." ) },
93     { gettext_noop( "Blood Lust" ), 3, 0, 27, 3, gettext_noop( "Increases a unit's attack skill." ) },
94     { gettext_noop( "Animate Dead" ), 10, 0, 25, 50, gettext_noop( "Resurrects creatures from a damaged or dead undead unit permanently." ) },
95     { gettext_noop( "Mirror Image" ), 25, 0, 26, 0,
96       gettext_noop(
97           "Creates an illusionary unit that duplicates one of your existing units.  This illusionary unit does the same damages as the original, but will vanish if it takes any damage." ) },
98     { gettext_noop( "Shield" ), 3, 0, 15, 2, gettext_noop( "Halves damage received from ranged attacks for a single unit." ) },
99     { gettext_noop( "Mass Shield" ), 7, 0, 65, 0, gettext_noop( "Halves damage received from ranged attacks for all of your units." ) },
100     { gettext_noop( "Summon Earth Elemental" ), 30, 0, 56, 3, gettext_noop( "Summons Earth Elementals to fight for your army." ) },
101     { gettext_noop( "Summon Air Elemental" ), 30, 0, 57, 3, gettext_noop( "Summons Air Elementals to fight for your army." ) },
102     { gettext_noop( "Summon Fire Elemental" ), 30, 0, 58, 3, gettext_noop( "Summons Fire Elementals to fight for your army." ) },
103     { gettext_noop( "Summon Water Elemental" ), 30, 0, 59, 3, gettext_noop( "Summons Water Elementals to fight for your army." ) },
104     { gettext_noop( "Earthquake" ), 15, 0, 33, 0, gettext_noop( "Damages castle walls." ) },
105     { gettext_noop( "View Mines" ), 1, 0, 39, 0, gettext_noop( "Causes all mines across the land to become visible." ) },
106     { gettext_noop( "View Resources" ), 1, 0, 40, 0, gettext_noop( "Causes all resources across the land to become visible." ) },
107     { gettext_noop( "View Artifacts" ), 2, 0, 41, 0, gettext_noop( "Causes all artifacts across the land to become visible." ) },
108     { gettext_noop( "View Towns" ), 2, 0, 42, 0, gettext_noop( "Causes all towns and castles across the land to become visible." ) },
109     { gettext_noop( "View Heroes" ), 2, 0, 43, 0, gettext_noop( "Causes all Heroes across the land to become visible." ) },
110     { gettext_noop( "View All" ), 3, 0, 44, 0, gettext_noop( "Causes the entire land to become visible." ) },
111     { gettext_noop( "Identify Hero" ), 3, 0, 45, 0, gettext_noop( "Allows the caster to view detailed information on enemy Heroes." ) },
112     { gettext_noop( "Summon Boat" ), 5, 0, 46, 0,
113       gettext_noop(
114           "Summons the nearest unoccupied, friendly boat to an adjacent shore location.  A friendly boat is one which you just built or were the most recent player to occupy." ) },
115     { gettext_noop( "Dimension Door" ), 10, 0, 47, 0, gettext_noop( "Allows the caster to magically transport to a nearby location." ) },
116     { gettext_noop( "Town Gate" ), 10, 0, 48, 0, gettext_noop( "Returns the caster to any town or castle currently owned." ) },
117     { gettext_noop( "Town Portal" ), 20, 0, 49, 0, gettext_noop( "Returns the hero to the town or castle of choice, provided it is controlled by you." ) },
118     { gettext_noop( "Visions" ), 6, 0, 50, 3, gettext_noop( "Visions predicts the likely outcome of an encounter with a neutral army camp." ) },
119     { gettext_noop( "Haunt" ), 8, 0, 51, 4,
120       gettext_noop( "Haunts a mine you control with Ghosts.  This mine stops producing resources.  (If I can't keep it, nobody will!)" ) },
121     { gettext_noop( "Set Earth Guardian" ), 15, 0, 52, 4, gettext_noop( "Sets Earth Elementals to guard a mine against enemy armies." ) },
122     { gettext_noop( "Set Air Guardian" ), 15, 0, 53, 4, gettext_noop( "Sets Air Elementals to guard a mine against enemy armies." ) },
123     { gettext_noop( "Set Fire Guardian" ), 15, 0, 54, 4, gettext_noop( "Sets Fire Elementals to guard a mine against enemy armies." ) },
124     { gettext_noop( "Set Water Guardian" ), 15, 0, 55, 4, gettext_noop( "Sets Water Elementals to guard a mine against enemy armies." ) },
125     { "Random", 1, 0, 0, 0, "Random" },
126     { "Random 1", 1, 0, 0, 0, "Random 1" },
127     { "Random 2", 1, 0, 0, 0, "Random 2" },
128     { "Random 3", 1, 0, 0, 0, "Random 3" },
129     { "Random 4", 1, 0, 0, 0, "Random 4" },
130     { "Random 5", 1, 0, 0, 0, "Random 5" },
131     { gettext_noop( "Petrification" ), 1, 0, 0, 0,
132       gettext_noop( "Turns the affected creature into stone.  A petrified creature receives half damage from a direct attack." ) },
133 };
134 
Spell(int s)135 Spell::Spell( int s )
136     : id( s > STONE ? NONE : s )
137 {}
138 
operator <(const Spell & s) const139 bool Spell::operator<( const Spell & s ) const
140 {
141     return id < s.id;
142 }
143 
operator ==(const Spell & s) const144 bool Spell::operator==( const Spell & s ) const
145 {
146     return s.id == id;
147 }
148 
operator !=(const Spell & s) const149 bool Spell::operator!=( const Spell & s ) const
150 {
151     return s.id != id;
152 }
153 
isValid(void) const154 bool Spell::isValid( void ) const
155 {
156     return id != Spell::NONE;
157 }
158 
GetID(void) const159 int Spell::GetID( void ) const
160 {
161     return id;
162 }
163 
GetName(void) const164 const char * Spell::GetName( void ) const
165 {
166     return _( spells[id].name );
167 }
168 
GetDescription(void) const169 const char * Spell::GetDescription( void ) const
170 {
171     return _( spells[id].description );
172 }
173 
MovePoint(void) const174 u32 Spell::MovePoint( void ) const
175 {
176     return spells[id].movePoints;
177 }
178 
SpellPoint(const HeroBase * hero) const179 u32 Spell::SpellPoint( const HeroBase * hero ) const
180 {
181     u32 res = spells[id].spellPoints;
182     u32 acount = 0;
183 
184     if ( hero ) {
185         switch ( id ) {
186         case BLESS:
187         case MASSBLESS:
188             acount = hero->artifactCount( Artifact::SNAKE_RING );
189             if ( acount )
190                 res = spells[id].spellPoints / ( acount * 2 );
191             break;
192 
193         case SUMMONEELEMENT:
194         case SUMMONAELEMENT:
195         case SUMMONFELEMENT:
196         case SUMMONWELEMENT:
197             acount = hero->artifactCount( Artifact::ELEMENTAL_RING );
198             if ( acount )
199                 res = spells[id].spellPoints / ( acount * 2 );
200             break;
201 
202         case CURSE:
203         case MASSCURSE:
204             acount = hero->artifactCount( Artifact::EVIL_EYE );
205             if ( acount )
206                 res = spells[id].spellPoints / ( acount * 2 );
207             break;
208 
209         default:
210             break;
211         }
212 
213         if ( isMindInfluence() ) {
214             acount = hero->artifactCount( Artifact::SKULLCAP );
215             if ( acount )
216                 res = spells[id].spellPoints / ( acount * 2 );
217         }
218     }
219 
220     return res ? res : 1;
221 }
222 
isLevel(int lvl) const223 bool Spell::isLevel( int lvl ) const
224 {
225     return Level() == lvl;
226 }
227 
Level(void) const228 int Spell::Level( void ) const
229 {
230     switch ( id ) {
231     case BLESS:
232     case BLOODLUST:
233     case CURE:
234     case CURSE:
235     case DISPEL:
236     case HASTE:
237     case ARROW:
238     case SHIELD:
239     case SLOW:
240     case STONESKIN:
241 
242     case VIEWMINES:
243     case VIEWRESOURCES:
244         return 1;
245 
246     case BLIND:
247     case COLDRAY:
248     case DEATHRIPPLE:
249     case DISRUPTINGRAY:
250     case DRAGONSLAYER:
251     case LIGHTNINGBOLT:
252     case STEELSKIN:
253 
254     case HAUNT:
255     case SUMMONBOAT:
256     case VIEWARTIFACTS:
257     case VISIONS:
258         return 2;
259 
260     case ANIMATEDEAD:
261     case ANTIMAGIC:
262     case COLDRING:
263     case DEATHWAVE:
264     case EARTHQUAKE:
265     case FIREBALL:
266     case HOLYWORD:
267     case MASSBLESS:
268     case MASSCURSE:
269     case MASSDISPEL:
270     case MASSHASTE:
271     case PARALYZE:
272     case TELEPORT:
273 
274     case IDENTIFYHERO:
275     case VIEWHEROES:
276     case VIEWTOWNS:
277         return 3;
278 
279     case BERSERKER:
280     case CHAINLIGHTNING:
281     case ELEMENTALSTORM:
282     case FIREBLAST:
283     case HOLYSHOUT:
284     case MASSCURE:
285     case MASSSHIELD:
286     case MASSSLOW:
287     case METEORSHOWER:
288     case RESURRECT:
289 
290     case SETEGUARDIAN:
291     case SETAGUARDIAN:
292     case SETFGUARDIAN:
293     case SETWGUARDIAN:
294     case TOWNGATE:
295     case VIEWALL:
296         return 4;
297 
298     case ARMAGEDDON:
299     case HYPNOTIZE:
300     case MIRRORIMAGE:
301     case RESURRECTTRUE:
302     case SUMMONEELEMENT:
303     case SUMMONAELEMENT:
304     case SUMMONFELEMENT:
305     case SUMMONWELEMENT:
306 
307     case DIMENSIONDOOR:
308     case TOWNPORTAL:
309         return 5;
310 
311     default:
312         break;
313     }
314 
315     return 0;
316 }
317 
isCombat(void) const318 bool Spell::isCombat( void ) const
319 {
320     switch ( id ) {
321     case NONE:
322     case VIEWMINES:
323     case VIEWRESOURCES:
324     case VIEWARTIFACTS:
325     case VIEWTOWNS:
326     case VIEWHEROES:
327     case VIEWALL:
328     case IDENTIFYHERO:
329     case SUMMONBOAT:
330     case DIMENSIONDOOR:
331     case TOWNGATE:
332     case TOWNPORTAL:
333     case VISIONS:
334     case HAUNT:
335     case SETEGUARDIAN:
336     case SETAGUARDIAN:
337     case SETFGUARDIAN:
338     case SETWGUARDIAN:
339         return false;
340     default:
341         break;
342     }
343     return true;
344 }
345 
isFire() const346 bool Spell::isFire() const
347 {
348     return id == FIREBALL || id == FIREBLAST;
349 }
350 
isCold() const351 bool Spell::isCold() const
352 {
353     return id == COLDRAY || id == COLDRING;
354 }
355 
isGuardianType() const356 bool Spell::isGuardianType() const
357 {
358     switch ( id ) {
359     case HAUNT:
360     case SETEGUARDIAN:
361     case SETAGUARDIAN:
362     case SETFGUARDIAN:
363     case SETWGUARDIAN:
364         return true;
365     default:
366         break;
367     }
368 
369     return false;
370 }
371 
isAdventure(void) const372 bool Spell::isAdventure( void ) const
373 {
374     return !isCombat();
375 }
376 
isDamage(void) const377 bool Spell::isDamage( void ) const
378 {
379     return Damage() != 0;
380 }
381 
Damage(void) const382 u32 Spell::Damage( void ) const
383 {
384     switch ( id ) {
385     case ARROW:
386     case FIREBALL:
387     case FIREBLAST:
388     case LIGHTNINGBOLT:
389     case COLDRING:
390     case DEATHWAVE:
391     case HOLYWORD:
392     case CHAINLIGHTNING:
393     case ARMAGEDDON:
394     case ELEMENTALSTORM:
395     case METEORSHOWER:
396     case COLDRAY:
397     case HOLYSHOUT:
398     case DEATHRIPPLE:
399         return spells[id].extraValue;
400 
401     default:
402         break;
403     }
404 
405     return 0;
406 }
407 
isMindInfluence(void) const408 bool Spell::isMindInfluence( void ) const
409 {
410     switch ( id ) {
411     case BLIND:
412     case PARALYZE:
413     case BERSERKER:
414     case HYPNOTIZE:
415         return true;
416 
417     default:
418         break;
419     }
420 
421     return false;
422 }
423 
IndexSprite(void) const424 u32 Spell::IndexSprite( void ) const
425 {
426     return spells[id].imageId;
427 }
428 
Restore(void) const429 u32 Spell::Restore( void ) const
430 {
431     switch ( id ) {
432     case Spell::CURE:
433     case Spell::MASSCURE:
434         return spells[id].extraValue;
435 
436     default:
437         break;
438     }
439 
440     return Resurrect();
441 }
442 
Resurrect(void) const443 u32 Spell::Resurrect( void ) const
444 {
445     switch ( id ) {
446     case Spell::ANIMATEDEAD:
447     case Spell::RESURRECT:
448     case Spell::RESURRECTTRUE:
449         return spells[id].extraValue;
450 
451     default:
452         break;
453     }
454 
455     return 0;
456 }
457 
isRestore(void) const458 bool Spell::isRestore( void ) const
459 {
460     return Restore() != 0;
461 }
462 
isResurrect(void) const463 bool Spell::isResurrect( void ) const
464 {
465     return Resurrect() != 0;
466 }
467 
ExtraValue(void) const468 u32 Spell::ExtraValue( void ) const
469 {
470     return spells[id].extraValue;
471 }
472 
Rand(int lvl,bool adv)473 Spell Spell::Rand( int lvl, bool adv )
474 {
475     std::vector<Spell> v;
476     v.reserve( 15 );
477 
478     for ( u32 sp = NONE; sp < STONE; ++sp ) {
479         const Spell spell( sp );
480         if ( ( ( adv && !spell.isCombat() ) || ( !adv && spell.isCombat() ) ) && lvl == spell.Level() )
481             v.push_back( spell );
482     }
483     return !v.empty() ? Rand::Get( v ) : Spell( Spell::NONE );
484 }
485 
RandCombat(int lvl)486 Spell Spell::RandCombat( int lvl )
487 {
488     return Rand( lvl, false );
489 }
490 
RandAdventure(int lvl)491 Spell Spell::RandAdventure( int lvl )
492 {
493     Spell res = Rand( lvl, true );
494     return res.isValid() ? res : RandCombat( lvl );
495 }
496 
isUndeadOnly(void) const497 bool Spell::isUndeadOnly( void ) const
498 {
499     switch ( id ) {
500     case ANIMATEDEAD:
501     case HOLYWORD:
502     case HOLYSHOUT:
503         return true;
504 
505     default:
506         break;
507     }
508 
509     return false;
510 }
511 
isALiveOnly(void) const512 bool Spell::isALiveOnly( void ) const
513 {
514     switch ( id ) {
515     case BLESS:
516     case MASSBLESS:
517     case CURSE:
518     case MASSCURSE:
519     case DEATHRIPPLE:
520     case DEATHWAVE:
521     case RESURRECT:
522     case RESURRECTTRUE:
523         return true;
524 
525     default:
526         break;
527     }
528 
529     return false;
530 }
531 
isSingleTarget() const532 bool Spell::isSingleTarget() const
533 {
534     switch ( id ) {
535     case LIGHTNINGBOLT:
536     case TELEPORT:
537     case CURE:
538     case RESURRECT:
539     case RESURRECTTRUE:
540     case HASTE:
541     case SLOW:
542     case BLIND:
543     case BLESS:
544     case STONESKIN:
545     case STEELSKIN:
546     case CURSE:
547     case ANTIMAGIC:
548     case DISPEL:
549     case ARROW:
550     case BERSERKER:
551     case PARALYZE:
552     case HYPNOTIZE:
553     case COLDRAY:
554     case DISRUPTINGRAY:
555     case DRAGONSLAYER:
556     case BLOODLUST:
557     case ANIMATEDEAD:
558     case MIRRORIMAGE:
559     case SHIELD:
560         return true;
561     }
562 
563     return false;
564 }
565 
isApplyWithoutFocusObject(void) const566 bool Spell::isApplyWithoutFocusObject( void ) const
567 {
568     if ( isMassActions() || isSummon() )
569         return true;
570     else
571         switch ( id ) {
572         case DEATHRIPPLE:
573         case DEATHWAVE:
574         case EARTHQUAKE:
575         case HOLYWORD:
576         case HOLYSHOUT:
577         case ARMAGEDDON:
578         case ELEMENTALSTORM:
579             return true;
580 
581         default:
582             break;
583         }
584 
585     return false;
586 }
587 
isSummon(void) const588 bool Spell::isSummon( void ) const
589 {
590     switch ( id ) {
591     case SUMMONEELEMENT:
592     case SUMMONAELEMENT:
593     case SUMMONFELEMENT:
594     case SUMMONWELEMENT:
595         return true;
596 
597     default:
598         break;
599     }
600 
601     return false;
602 }
603 
isEffectDispel() const604 bool Spell::isEffectDispel() const
605 {
606     switch ( id ) {
607     case CURE:
608     case MASSCURE:
609     case DISPEL:
610     case MASSDISPEL:
611         return true;
612 
613     default:
614         break;
615     }
616 
617     return false;
618 }
619 
isApplyToAnyTroops(void) const620 bool Spell::isApplyToAnyTroops( void ) const
621 {
622     switch ( id ) {
623     case DISPEL:
624     case MASSDISPEL:
625         return true;
626 
627     default:
628         break;
629     }
630 
631     return false;
632 }
633 
isApplyToFriends(void) const634 bool Spell::isApplyToFriends( void ) const
635 {
636     switch ( id ) {
637     case BLESS:
638     case BLOODLUST:
639     case CURE:
640     case HASTE:
641     case SHIELD:
642     case STONESKIN:
643     case DRAGONSLAYER:
644     case STEELSKIN:
645     case ANIMATEDEAD:
646     case ANTIMAGIC:
647     case TELEPORT:
648     case RESURRECT:
649     case MIRRORIMAGE:
650     case RESURRECTTRUE:
651 
652     case MASSBLESS:
653     case MASSCURE:
654     case MASSHASTE:
655     case MASSSHIELD:
656         return true;
657 
658     default:
659         break;
660     }
661 
662     return false;
663 }
664 
isMassActions(void) const665 bool Spell::isMassActions( void ) const
666 {
667     switch ( id ) {
668     case MASSCURE:
669     case MASSHASTE:
670     case MASSSLOW:
671     case MASSBLESS:
672     case MASSCURSE:
673     case MASSDISPEL:
674     case MASSSHIELD:
675         return true;
676 
677     default:
678         break;
679     }
680 
681     return false;
682 }
683 
isApplyToEnemies(void) const684 bool Spell::isApplyToEnemies( void ) const
685 {
686     switch ( id ) {
687     case MASSSLOW:
688     case MASSCURSE:
689 
690     case CURSE:
691     case ARROW:
692     case SLOW:
693     case BLIND:
694     case COLDRAY:
695     case DISRUPTINGRAY:
696     case LIGHTNINGBOLT:
697     case CHAINLIGHTNING:
698     case PARALYZE:
699     case BERSERKER:
700     case HYPNOTIZE:
701         return true;
702 
703     default:
704         break;
705     }
706 
707     return false;
708 }
709 
isRaceCompatible(int race) const710 bool Spell::isRaceCompatible( int race ) const
711 {
712     switch ( id ) {
713     case MASSCURE:
714     case MASSBLESS:
715     case HOLYSHOUT:
716     case HOLYWORD:
717     case BLESS:
718     case CURE:
719         if ( Race::NECR == race )
720             return false;
721         break;
722 
723     case DEATHWAVE:
724     case DEATHRIPPLE:
725     case ANIMATEDEAD:
726         if ( Race::NECR != race )
727             return false;
728         break;
729 
730     default:
731         break;
732     }
733 
734     return true;
735 }
736 
CalculateDimensionDoorDistance()737 int32_t Spell::CalculateDimensionDoorDistance()
738 {
739     // original h2 variant
740     return 14;
741 }
742 
operator <<(StreamBase & msg,const Spell & spell)743 StreamBase & operator<<( StreamBase & msg, const Spell & spell )
744 {
745     return msg << spell.id;
746 }
747 
operator >>(StreamBase & msg,Spell & spell)748 StreamBase & operator>>( StreamBase & msg, Spell & spell )
749 {
750     return msg >> spell.id;
751 }
752