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"
2017-07-20 06:08:49 +02:00
# include "../JsonNode.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"
2017-07-20 06:08:49 +02:00
namespace spells
{
class ISpellMechanicsFactory ;
class IBattleCast ;
}
2015-02-26 19:59:18 +02:00
class CGObjectInstance ;
2015-02-02 10:25:26 +02:00
class CSpell ;
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 ;
2017-07-20 06:08:49 +02:00
class BattleInfo ;
2015-02-02 10:25:26 +02:00
struct CPackForClient ;
struct BattleSpellCast ;
class CGameInfoCallback ;
class CRandomGenerator ;
class CMap ;
2017-07-20 06:08:49 +02:00
class AdventureSpellCastParameters ;
2015-09-16 09:50:33 +02:00
class SpellCastEnvironment ;
2015-02-02 10:25:26 +02:00
2017-07-20 06:08:49 +02:00
namespace spells
{
struct SchoolInfo
2015-02-02 10:25:26 +02:00
{
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
} ;
2017-07-20 06:08:49 +02:00
}
2015-02-02 10:25:26 +02:00
enum class VerticalPosition : ui8 { TOP , CENTER , BOTTOM } ;
2017-07-20 06:08:49 +02:00
class DLL_LINKAGE CSpell : public spells : : Spell
2015-02-02 10:25:26 +02:00
{
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 ;
2017-07-20 06:08:49 +02:00
if ( version > = 754 )
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 ;
2017-07-20 06:08:49 +02:00
//TODO: remove these two when AI will understand special effects
std : : vector < std : : shared_ptr < Bonus > > effects ; //deprecated
std : : vector < std : : shared_ptr < Bonus > > cumulativeEffects ; //deprecated
JsonNode battleEffects ;
2015-02-02 10:25:26 +02:00
LevelInfo ( ) ;
~ LevelInfo ( ) ;
2017-07-20 06:08:49 +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 & 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 ;
2017-07-20 06:08:49 +02:00
if ( version > = 780 )
h & battleEffects ;
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
*
*/
2017-07-20 06:08:49 +02:00
const CSpell : : LevelInfo & getLevelInfo ( const int level ) const ;
2015-02-02 10:25:26 +02:00
public :
2017-07-20 06:08:49 +02:00
enum ESpellPositiveness
{
NEGATIVE = - 1 ,
NEUTRAL = 0 ,
POSITIVE = 1
} ;
2015-02-02 10:25:26 +02:00
2015-09-19 15:51:06 +02:00
struct DLL_LINKAGE TargetInfo
2015-02-02 10:25:26 +02:00
{
2017-07-20 06:08:49 +02:00
spells : : AimType type ;
2015-02-02 10:25:26 +02:00
bool smart ;
bool massive ;
bool clearAffected ;
2017-07-20 06:08:49 +02:00
bool clearTarget ;
2015-02-26 19:59:18 +02:00
2017-07-20 06:08:49 +02:00
TargetInfo ( const CSpell * spell , const int level , spells : : Mode mode ) ;
2015-02-02 10:25:26 +02:00
} ;
2017-07-20 06:08:49 +02:00
using BTVector = std : : vector < Bonus : : BonusType > ;
2015-02-02 10:25:26 +02:00
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)
2017-07-20 06:08:49 +02:00
JsonNode targetCondition ; //custom condition on what spell can affect
2015-02-02 10:25:26 +02:00
CSpell ( ) ;
~ CSpell ( ) ;
2015-02-26 19:59:18 +02:00
2017-07-20 06:08:49 +02:00
std : : vector < BattleHex > rangeInHexes ( const CBattleInfoCallback * cb , spells : : Mode mode , const spells : : Caster * caster , BattleHex centralHex ) const ;
spells : : AimType getTargetType ( ) const ;
2015-02-02 10:25:26 +02:00
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
2017-07-20 06:08:49 +02:00
bool hasBattleEffects ( ) const ;
2015-02-02 10:25:26 +02:00
///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
2017-07-20 06:08:49 +02:00
int64_t calculateDamage ( const spells : : Caster * caster ) const ;
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
///selects from allStacks actually affected stacks
2017-07-20 06:08:49 +02:00
std : : vector < const CStack * > getAffectedStacks ( const CBattleInfoCallback * cb , spells : : Mode mode , const spells : : Caster * caster , int spellLvl , const spells : : Target & target ) 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
*/
2017-07-20 06:08:49 +02:00
void forEachSchool ( const std : : function < void ( const spells : : SchoolInfo & , bool & ) > & cb ) const override ;
int32_t getIndex ( ) const override ;
2018-03-02 12:22:51 +02:00
int32_t getLevel ( ) const override ;
2015-02-02 10:25:26 +02:00
/**
* Returns resource name of icon for SPELL_IMMUNITY bonus
*/
const std : : string & getIconImmune ( ) const ;
const std : : string & getCastSound ( ) const ;
2017-07-20 06:08:49 +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 & 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-20 06:08:49 +02:00
if ( version > = 780 )
{
h & targetCondition ;
}
else
{
BTVector immunities ;
BTVector absoluteImmunities ;
BTVector limiters ;
BTVector absoluteLimiters ;
h & immunities ;
h & limiters ;
h & absoluteImmunities ;
h & absoluteLimiters ;
if ( ! h . saving )
targetCondition = convertTargetCondition ( immunities , absoluteImmunities , limiters , 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 ;
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
}
2017-07-20 06:08:49 +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.
2017-07-20 06:08:49 +02:00
bool canBeCast ( const CBattleInfoCallback * cb , spells : : Mode mode , const spells : : Caster * caster ) const ;
bool canBeCast ( spells : : Problem & problem , const CBattleInfoCallback * cb , spells : : Mode mode , const spells : : Caster * 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*
2017-07-20 06:08:49 +02:00
bool canBeCastAt ( const CBattleInfoCallback * cb , spells : : Mode mode , const spells : : Caster * caster , BattleHex destination ) const ; //DEPREACTED
bool canBeCastAt ( const CBattleInfoCallback * cb , spells : : Mode mode , const spells : : Caster * caster , const spells : : Target & target ) 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 ;
2015-02-26 19:59:18 +02:00
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
2017-07-20 06:08:49 +02:00
int64_t adjustRawDamage ( const spells : : Caster * caster , const battle : : Unit * affectedCreature , int64_t rawDamage ) const ;
2015-09-16 14:43:39 +02:00
///returns raw damage or healed HP
2017-07-20 06:08:49 +02:00
int64_t calculateRawEffectValue ( int32_t effectLevel , int32_t basePowerMultiplier , int32_t levelPowerMultiplier ) const ;
2015-09-16 14:43:39 +02:00
2017-07-20 06:08:49 +02:00
std : : unique_ptr < spells : : Mechanics > battleMechanics ( const spells : : IBattleCast * event ) const ;
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
2017-07-20 06:08:49 +02:00
JsonNode convertTargetCondition ( const BTVector & immunity , const BTVector & absImmunity , const BTVector & limit , const BTVector & absLimit ) const ;
2015-02-02 10:25:26 +02:00
//call this after load or deserialization. cant be done in constructor.
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
2017-07-20 06:08:49 +02:00
spells : : AimType targetType ;
2015-02-02 10:25:26 +02:00
///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
2017-07-20 06:08:49 +02:00
std : : unique_ptr < spells : : ISpellMechanicsFactory > mechanics ; //(!) do not serialize
2016-09-04 07:19:28 +02:00
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 .
*
*/
std : : vector < bool > getDefaultAllowed ( ) const override ;
2018-03-31 07:56:40 +02:00
const std : : vector < std : : string > & getTypeNames ( ) const override ;
2015-02-02 10:25:26 +02:00
2017-07-20 06:08:49 +02:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 10:25:26 +02:00
{
2017-07-20 06:08:49 +02:00
h & objects ;
if ( ! h . saving & & version < 780 )
{
update780 ( ) ;
}
if ( ! h . saving )
{
afterLoadFinalization ( ) ;
}
2015-02-02 10:25:26 +02:00
}
2015-02-26 19:59:18 +02:00
2015-02-02 10:25:26 +02:00
protected :
2017-07-20 06:08:49 +02:00
CSpell * loadFromJson ( const JsonNode & json , const std : : string & identifier , size_t index ) override ;
private :
void update780 ( ) ;
2015-02-02 10:25:26 +02:00
} ;