2015-02-02 10:25:26 +02:00
/*
* CSpellHandler . h , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2015-02-26 19:59:18 +02:00
# pragma once
2015-03-18 21:22:52 +02:00
# include "Magic.h"
2015-02-26 19:59:18 +02:00
# include "../IHandlerBase.h"
# include "../ConstTransitivePtr.h"
# include "../int3.h"
# include "../GameConstants.h"
2017-06-24 16:42:05 +02:00
# include "../battle/BattleHex.h"
2015-02-26 19:59:18 +02:00
# include "../HeroBonus.h"
class CGObjectInstance ;
2015-02-02 10:25:26 +02:00
class CSpell ;
class ISpellMechanics ;
2016-09-04 07:19:28 +02:00
class IAdventureSpellMechanics ;
2015-02-02 10:25:26 +02:00
class CLegacyConfigParser ;
class CGHeroInstance ;
class CStack ;
class CBattleInfoCallback ;
2016-08-30 04:13:45 +02:00
struct BattleInfo ;
2015-02-02 10:25:26 +02:00
struct CPackForClient ;
struct BattleSpellCast ;
class CGameInfoCallback ;
class CRandomGenerator ;
class CMap ;
2015-09-16 09:50:33 +02:00
struct AdventureSpellCastParameters ;
struct BattleSpellCastParameters ;
class SpellCastEnvironment ;
2015-02-02 10:25:26 +02:00
struct SpellSchoolInfo
{
ESpellSchool id ; //backlink
Bonus : : BonusType damagePremyBonus ;
2015-02-26 19:59:18 +02:00
Bonus : : BonusType immunityBonus ;
2015-02-02 10:25:26 +02:00
std : : string jsonName ;
SecondarySkill : : ESecondarySkill skill ;
2015-02-26 19:59:18 +02:00
Bonus : : BonusType knoledgeBonus ;
2015-02-02 10:25:26 +02:00
} ;
enum class VerticalPosition : ui8 { TOP , CENTER , BOTTOM } ;
class DLL_LINKAGE CSpell
{
public :
struct ProjectileInfo
{
///in radians. Only positive value. Negative angle is handled by vertical flip
2015-02-26 19:59:18 +02:00
double minimumAngle ;
2015-02-02 10:25:26 +02:00
///resource name
std : : string resourceName ;
2015-02-26 19:59:18 +02:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 10:25:26 +02:00
{
2017-07-31 15:35:42 +02:00
h & minimumAngle ;
h & resourceName ;
2015-02-26 19:59:18 +02:00
}
2015-02-02 10:25:26 +02:00
} ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
struct AnimationItem
{
std : : string resourceName ;
VerticalPosition verticalPosition ;
2016-02-15 12:34:37 +02:00
int pause ;
2015-02-26 19:59:18 +02:00
2015-09-12 22:52:04 +02:00
AnimationItem ( ) ;
2015-02-26 19:59:18 +02:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 10:25:26 +02:00
{
2017-07-31 15:35:42 +02:00
h & resourceName ;
h & verticalPosition ;
2015-12-06 01:51:54 +02:00
if ( version > = 754 ) //save format backward compatibility
2015-03-22 12:49:14 +02:00
{
h & pause ;
2016-02-15 12:34:37 +02:00
}
2015-03-22 12:49:14 +02:00
else if ( ! h . saving )
{
pause = 0 ;
}
2015-02-26 19:59:18 +02:00
}
2015-02-02 10:25:26 +02:00
} ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
typedef AnimationItem TAnimation ;
2015-02-26 19:59:18 +02:00
typedef std : : vector < TAnimation > TAnimationQueue ;
2015-02-02 10:25:26 +02:00
struct DLL_LINKAGE AnimationInfo
{
AnimationInfo ( ) ;
~ AnimationInfo ( ) ;
2015-02-26 19:59:18 +02:00
///displayed on all affected targets.
2015-02-02 10:25:26 +02:00
TAnimationQueue affect ;
///displayed on caster.
TAnimationQueue cast ;
2015-09-21 11:19:35 +02:00
///displayed on target hex. If spell was cast with no target selection displayed on entire battlefield (f.e. ARMAGEDDON)
2015-02-02 10:25:26 +02:00
TAnimationQueue hit ;
2015-09-21 11:19:35 +02:00
///displayed "between" caster and (first) target. Ignored if spell was cast with no target selection.
2015-02-02 10:25:26 +02:00
///use selectProjectile to access
std : : vector < ProjectileInfo > projectile ;
2015-02-26 19:59:18 +02:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 10:25:26 +02:00
{
2017-07-31 15:35:42 +02:00
h & projectile ;
h & hit ;
h & cast ;
2017-01-26 09:44:30 +02:00
if ( version > = 762 )
2017-07-31 15:35:42 +02:00
{
2017-01-26 09:44:30 +02:00
h & affect ;
2017-07-31 15:35:42 +02:00
}
2015-02-02 10:25:26 +02:00
}
std : : string selectProjectile ( const double angle ) const ;
} animationInfo ;
public :
struct LevelInfo
{
std : : string description ; //descriptions of spell for skill level
si32 cost ;
si32 power ;
si32 AIValue ;
bool smartTarget ;
bool clearTarget ;
bool clearAffected ;
std : : string range ;
2016-11-02 19:11:01 +02:00
std : : vector < std : : shared_ptr < Bonus > > effects ;
std : : vector < std : : shared_ptr < Bonus > > cumulativeEffects ;
2015-02-02 10:25:26 +02:00
LevelInfo ( ) ;
~ LevelInfo ( ) ;
template < typename Handler > void serialize ( Handler & h , const int version )
{
2017-07-31 15:35:42 +02:00
h & description ;
h & cost ;
h & power ;
h & AIValue ;
h & smartTarget ;
h & range ;
2016-11-02 19:11:01 +02:00
if ( version > = 773 )
{
2017-07-31 15:35:42 +02:00
h & effects ;
h & cumulativeEffects ;
2016-11-02 19:11:01 +02:00
}
else
{
//all old effects treated as not cumulative, special cases handled by CSpell::serialize
std : : vector < Bonus > old ;
h & old ;
if ( ! h . saving )
{
effects . clear ( ) ;
cumulativeEffects . clear ( ) ;
for ( const Bonus & oldBonus : old )
effects . push_back ( std : : make_shared < Bonus > ( oldBonus ) ) ;
}
}
2017-07-31 15:35:42 +02:00
h & clearTarget ;
h & clearAffected ;
2015-02-02 10:25:26 +02:00
}
} ;
/** \brief Low level accessor. Don`t use it if absolutely necessary
*
* \ param level . spell school level
* \ return Spell level info structure
*
*/
const CSpell : : LevelInfo & getLevelInfo ( const int level ) const ;
public :
enum ETargetType { NO_TARGET , CREATURE , OBSTACLE , LOCATION } ;
enum ESpellPositiveness { NEGATIVE = - 1 , NEUTRAL = 0 , POSITIVE = 1 } ;
2015-09-19 15:51:06 +02:00
struct DLL_LINKAGE TargetInfo
2015-02-02 10:25:26 +02:00
{
ETargetType type ;
bool smart ;
bool massive ;
bool onlyAlive ;
///no immunity on primary target (mostly spell-like attack)
bool alwaysHitDirectly ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
bool clearTarget ;
bool clearAffected ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
TargetInfo ( const CSpell * spell , const int level ) ;
TargetInfo ( const CSpell * spell , const int level , ECastingMode : : ECastingMode mode ) ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
private :
void init ( const CSpell * spell , const int level ) ;
} ;
SpellID id ;
2015-08-24 10:55:45 +02:00
std : : string identifier ;
2015-02-02 10:25:26 +02:00
std : : string name ;
si32 level ;
2016-11-02 19:11:01 +02:00
std : : map < ESpellSchool , bool > school ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
si32 power ; //spell's power
std : : map < TFaction , si32 > probabilities ; //% chance to gain for castles
bool combatSpell ; //is this spell combat (true) or adventure (false)
bool creatureAbility ; //if true, only creatures can use this spell
si8 positiveness ; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative
std : : vector < SpellID > counteredSpells ; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs)
CSpell ( ) ;
~ CSpell ( ) ;
2015-02-26 19:59:18 +02:00
std : : vector < BattleHex > rangeInHexes ( BattleHex centralHex , ui8 schoolLvl , ui8 side , bool * outDroppedHexes = nullptr ) const ; //convert range to specific hexes; last optional out parameter is set to true, if spell would cover unavailable hexes (that are not included in ret)
2015-02-02 10:25:26 +02:00
ETargetType getTargetType ( ) const ; //deprecated
bool isCombatSpell ( ) const ;
bool isAdventureSpell ( ) const ;
bool isCreatureAbility ( ) const ;
bool isPositive ( ) const ;
bool isNegative ( ) const ;
bool isNeutral ( ) const ;
2016-09-29 22:14:22 +02:00
boost : : logic : : tribool getPositiveness ( ) const ;
2015-02-02 10:25:26 +02:00
bool isDamageSpell ( ) const ;
2015-02-26 19:59:18 +02:00
bool isRisingSpell ( ) const ;
2015-02-02 10:25:26 +02:00
bool isOffensiveSpell ( ) const ;
bool isSpecialSpell ( ) const ;
bool hasEffects ( ) const ;
2016-11-02 19:11:01 +02:00
void getEffects ( std : : vector < Bonus > & lst , const int level , const bool cumulative , const si32 duration , boost : : optional < si32 * > maxDuration = boost : : none ) const ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
2015-03-18 21:22:52 +02:00
ui32 calculateDamage ( const ISpellCaster * caster , const CStack * affectedCreature , int spellSchoolLevel , int usedSpellPower ) const ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
///selects from allStacks actually affected stacks
2016-09-06 05:40:23 +02:00
std : : vector < const CStack * > getAffectedStacks ( const CBattleInfoCallback * cb , ECastingMode : : ECastingMode mode , const ISpellCaster * caster , int spellLvl , BattleHex destination ) const ;
2015-02-02 10:25:26 +02:00
si32 getCost ( const int skillLevel ) const ;
/**
* Returns spell level power , base power ignored
*/
si32 getPower ( const int skillLevel ) const ;
si32 getProbability ( const TFaction factionId ) const ;
/**
* Calls cb for each school this spell belongs to
*
* Set stop to true to abort looping
2015-02-26 19:59:18 +02:00
*/
2015-02-02 10:25:26 +02:00
void forEachSchool ( const std : : function < void ( const SpellSchoolInfo & , bool & ) > & cb ) const ;
/**
* Returns resource name of icon for SPELL_IMMUNITY bonus
*/
const std : : string & getIconImmune ( ) const ;
const std : : string & getCastSound ( ) const ;
template < typename Handler > void serialize ( Handler & h , const int version )
{
2017-07-31 15:35:42 +02:00
h & identifier ;
h & id ;
h & name ;
h & level ;
h & power ;
h & probabilities ;
h & attributes ;
h & combatSpell ;
h & creatureAbility ;
h & positiveness ;
h & counteredSpells ;
h & isRising ;
h & isDamage ;
h & isOffensive ;
2015-02-02 10:25:26 +02:00
h & targetType ;
2017-07-31 15:35:42 +02:00
h & immunities ;
h & limiters ;
h & absoluteImmunities ;
h & absoluteLimiters ;
2015-02-02 10:25:26 +02:00
h & iconImmune ;
h & defaultProbability ;
h & isSpecial ;
2017-07-31 15:35:42 +02:00
h & castSound ;
h & iconBook ;
h & iconEffect ;
h & iconScenarioBonus ;
h & iconScroll ;
2015-02-26 19:59:18 +02:00
h & levels ;
h & school ;
2015-02-02 10:25:26 +02:00
h & animationInfo ;
if ( ! h . saving )
2017-07-31 15:35:42 +02:00
{
2015-02-02 10:25:26 +02:00
setup ( ) ;
2017-07-31 15:35:42 +02:00
}
2016-11-02 19:11:01 +02:00
//backward compatibility
//can not be added to level structure as level structure does not know spell id
if ( ! h . saving & & version < 773 )
2017-07-31 15:35:42 +02:00
{
2016-11-02 19:11:01 +02:00
if ( id = = SpellID : : DISRUPTING_RAY | | id = = SpellID : : ACID_BREATH_DEFENSE )
for ( auto & level : levels )
std : : swap ( level . effects , level . cumulativeEffects ) ;
2017-07-31 15:35:42 +02:00
}
2015-02-02 10:25:26 +02:00
}
friend class CSpellHandler ;
friend class Graphics ;
2015-03-18 15:39:07 +02:00
public :
///internal interface (for callbacks)
2016-02-15 12:34:37 +02:00
2016-09-05 10:36:25 +02:00
///Checks general but spell-specific problems. Use only during battle.
ESpellCastProblem : : ESpellCastProblem canBeCast ( const CBattleInfoCallback * cb , ECastingMode : : ECastingMode mode , const ISpellCaster * caster ) const ;
2015-03-18 15:39:07 +02:00
2017-07-15 23:01:33 +02:00
///checks for creature immunity / anything that prevent casting *at given hex*
ESpellCastProblem : : ESpellCastProblem canBeCastAt ( const CBattleInfoCallback * cb , ECastingMode : : ECastingMode mode , const ISpellCaster * caster , BattleHex destination ) const ;
2016-03-01 08:50:04 +02:00
2015-03-18 15:39:07 +02:00
///checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc.
2015-09-26 19:09:54 +02:00
ESpellCastProblem : : ESpellCastProblem isImmuneByStack ( const ISpellCaster * caster , const CStack * obj ) const ;
2015-02-02 10:25:26 +02:00
public :
///Server logic. Has write access to GameState via packets.
///May be executed on client side by (future) non-cheat-proof scripts.
2015-02-26 19:59:18 +02:00
2017-07-03 20:09:27 +02:00
bool adventureCast ( const SpellCastEnvironment * env , const AdventureSpellCastParameters & parameters ) const ;
2016-09-06 05:40:23 +02:00
void battleCast ( const SpellCastEnvironment * env , const BattleSpellCastParameters & parameters ) const ;
2015-02-26 19:59:18 +02:00
public :
2015-02-02 10:25:26 +02:00
///Client-server logic. Has direct write access to GameState.
///Shall be called (only) when applying packets on BOTH SIDES
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
///implementation of BattleSpellCast applying
void applyBattle ( BattleInfo * battle , const BattleSpellCast * packet ) const ;
2015-09-16 14:43:39 +02:00
public : //internal, for use only by Mechanics classes
///applies caster`s secondary skills and affectedCreature`s to raw damage
int adjustRawDamage ( const ISpellCaster * caster , const CStack * affectedCreature , int rawDamage ) const ;
///returns raw damage or healed HP
2016-02-15 12:34:37 +02:00
int calculateRawEffectValue ( int effectLevel , int effectPower ) const ;
2015-09-16 14:43:39 +02:00
///generic immunity calculation
2015-09-17 12:23:13 +02:00
ESpellCastProblem : : ESpellCastProblem internalIsImmune ( const ISpellCaster * caster , const CStack * obj ) const ;
2015-09-16 14:43:39 +02:00
2015-02-02 10:25:26 +02:00
private :
void setIsOffensive ( const bool val ) ;
void setIsRising ( const bool val ) ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
//call this after load or deserialization. cant be done in constructor.
void setup ( ) ;
void setupMechanics ( ) ;
private :
si32 defaultProbability ;
bool isRising ;
bool isDamage ;
bool isOffensive ;
bool isSpecial ;
std : : string attributes ; //reference only attributes //todo: remove or include in configuration format, currently unused
ETargetType targetType ;
std : : vector < Bonus : : BonusType > immunities ; //any of these grants immunity
std : : vector < Bonus : : BonusType > absoluteImmunities ; //any of these grants immunity, can't be negated
std : : vector < Bonus : : BonusType > limiters ; //all of them are required to be affected
std : : vector < Bonus : : BonusType > absoluteLimiters ; //all of them are required to be affected, can't be negated
///graphics related stuff
std : : string iconImmune ;
std : : string iconBook ;
std : : string iconEffect ;
std : : string iconScenarioBonus ;
std : : string iconScroll ;
///sound related stuff
std : : string castSound ;
std : : vector < LevelInfo > levels ;
2015-02-26 19:59:18 +02:00
2016-09-04 07:19:28 +02:00
std : : unique_ptr < ISpellMechanics > mechanics ; //(!) do not serialize
std : : unique_ptr < IAdventureSpellMechanics > adventureMechanics ; //(!) do not serialize
2015-02-02 10:25:26 +02:00
} ;
bool DLL_LINKAGE isInScreenRange ( const int3 & center , const int3 & pos ) ; //for spells like Dimension Door
class DLL_LINKAGE CSpellHandler : public CHandlerBase < SpellID , CSpell >
{
public :
CSpellHandler ( ) ;
virtual ~ CSpellHandler ( ) ;
///IHandler base
std : : vector < JsonNode > loadLegacyData ( size_t dataSize ) override ;
void afterLoadFinalization ( ) override ;
void beforeValidate ( JsonNode & object ) override ;
/**
* Gets a list of default allowed spells . OH3 spells are all allowed by default .
*
* @ return a list of allowed spells , the index is the spell id and the value either 0 for not allowed or 1 for allowed
*/
std : : vector < bool > getDefaultAllowed ( ) const override ;
const std : : string getTypeName ( ) const override ;
2016-02-21 19:58:09 +02:00
///json serialization helper
static si32 decodeSpell ( const std : : string & identifier ) ;
///json serialization helper
static std : : string encodeSpell ( const si32 index ) ;
2015-02-02 10:25:26 +02:00
template < typename Handler > void serialize ( Handler & h , const int version )
{
h & objects ;
}
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
protected :
2015-08-24 10:55:45 +02:00
CSpell * loadFromJson ( const JsonNode & json , const std : : string & identifier ) override ;
2015-02-02 10:25:26 +02:00
} ;