2015-02-02 11:25:26 +03: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 20:59:18 +03:00
#pragma once
2018-03-17 17:58:30 +03:00
#include <vcmi/spells/Spell.h>
#include <vcmi/spells/Service.h>
#include <vcmi/spells/Magic.h>
2017-07-20 07:08:49 +03:00
#include "../JsonNode.h"
2015-02-26 20:59:18 +03: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"
2023-05-01 20:29:53 +03:00
#include "../bonuses/Bonus.h"
2015-02-26 20:59:18 +03:00
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_BEGIN
2015-02-02 11:25:26 +03:00
class CSpell ;
2016-09-04 08:19:28 +03:00
class IAdventureSpellMechanics ;
2015-02-02 11:25:26 +03:00
class CBattleInfoCallback ;
2017-07-20 07:08:49 +03:00
class AdventureSpellCastParameters ;
2015-09-16 10:50:33 +03:00
class SpellCastEnvironment ;
2018-03-17 17:58:30 +03:00
class JsonSerializeFormat ;
2015-02-02 11:25:26 +03:00
2018-03-17 17:58:30 +03:00
namespace test
{
class CSpellTest ;
}
2017-07-20 07:08:49 +03:00
namespace spells
{
2018-03-17 17:58:30 +03:00
class ISpellMechanicsFactory ;
class IBattleCast ;
2017-07-20 07:08:49 +03:00
struct SchoolInfo
2015-02-02 11:25:26 +03:00
{
2023-05-05 21:28:07 +03:00
SpellSchool id ; //backlink
2023-05-01 01:20:01 +03:00
BonusType immunityBonus ;
2015-02-02 11:25:26 +03:00
std :: string jsonName ;
};
2017-07-20 07:08:49 +03:00
}
2023-05-05 21:28:07 +03:00
namespace SpellConfig
{
extern const spells :: SchoolInfo SCHOOL [ 4 ];
}
2015-02-02 11:25:26 +03:00
enum class VerticalPosition : ui8 { TOP , CENTER , BOTTOM };
2017-07-20 07:08:49 +03:00
class DLL_LINKAGE CSpell : public spells :: Spell
2015-02-02 11:25:26 +03:00
{
public :
struct ProjectileInfo
{
///in radians. Only positive value. Negative angle is handled by vertical flip
2015-02-26 20:59:18 +03:00
double minimumAngle ;
2015-02-02 11:25:26 +03:00
///resource name
std :: string resourceName ;
2015-02-26 20:59:18 +03:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 11:25:26 +03:00
{
2017-07-31 16:35:42 +03:00
h & minimumAngle ;
h & resourceName ;
2015-02-26 20:59:18 +03:00
}
2015-02-02 11:25:26 +03:00
};
2015-02-26 20:59:18 +03:00
2015-02-02 11:25:26 +03:00
struct AnimationItem
{
std :: string resourceName ;
2022-12-22 00:25:35 +02:00
std :: string effectName ;
2015-02-02 11:25:26 +03:00
VerticalPosition verticalPosition ;
2016-02-15 13:34:37 +03:00
int pause ;
2015-02-26 20:59:18 +03:00
2015-09-12 23:52:04 +03:00
AnimationItem ();
2015-02-26 20:59:18 +03:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 11:25:26 +03:00
{
2017-07-31 16:35:42 +03:00
h & resourceName ;
2022-12-30 00:36:10 +02:00
if ( version > 806 )
2022-12-25 18:07:08 +02:00
h & effectName ;
2017-07-31 16:35:42 +03:00
h & verticalPosition ;
2022-06-20 17:39:50 +03:00
h & pause ;
2015-02-26 20:59:18 +03:00
}
2015-02-02 11:25:26 +03:00
};
2015-02-26 20:59:18 +03:00
2023-04-18 00:11:16 +03:00
using TAnimation = AnimationItem ;
using TAnimationQueue = std :: vector < TAnimation > ;
2015-02-26 20:59:18 +03:00
2015-02-02 11:25:26 +03:00
struct DLL_LINKAGE AnimationInfo
{
2015-02-26 20:59:18 +03:00
///displayed on all affected targets.
2015-02-02 11:25:26 +03:00
TAnimationQueue affect ;
///displayed on caster.
TAnimationQueue cast ;
2015-09-21 12:19:35 +03:00
///displayed on target hex. If spell was cast with no target selection displayed on entire battlefield (f.e. ARMAGEDDON)
2015-02-02 11:25:26 +03:00
TAnimationQueue hit ;
2015-09-21 12:19:35 +03:00
///displayed "between" caster and (first) target. Ignored if spell was cast with no target selection.
2015-02-02 11:25:26 +03:00
///use selectProjectile to access
std :: vector < ProjectileInfo > projectile ;
2015-02-26 20:59:18 +03:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 11:25:26 +03:00
{
2017-07-31 16:35:42 +03:00
h & projectile ;
h & hit ;
h & cast ;
2022-06-20 17:39:50 +03:00
h & affect ;
2015-02-02 11:25:26 +03:00
}
std :: string selectProjectile ( const double angle ) const ;
} animationInfo ;
2023-02-11 18:18:05 +03:00
2015-02-02 11:25:26 +03:00
public :
struct LevelInfo
{
2023-02-11 18:18:05 +03:00
si32 cost = 0 ;
si32 power = 0 ;
si32 AIValue = 0 ;
2015-02-02 11:25:26 +03:00
2023-02-11 18:18:05 +03:00
bool smartTarget = true ;
bool clearTarget = false ;
bool clearAffected = false ;
std :: string range = "0" ;
2015-02-02 11:25:26 +03:00
2017-07-20 07:08:49 +03: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 11:25:26 +03:00
2017-07-20 07:08:49 +03:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 11:25:26 +03:00
{
2017-07-31 16:35:42 +03:00
h & cost ;
h & power ;
h & AIValue ;
h & smartTarget ;
h & range ;
2022-06-20 17:39:50 +03:00
h & effects ;
h & cumulativeEffects ;
2017-07-31 16:35:42 +03:00
h & clearTarget ;
h & clearAffected ;
2022-06-20 17:39:50 +03:00
h & battleEffects ;
2015-02-02 11:25:26 +03:00
}
};
/** \brief Low level accessor. Don`t use it if absolutely necessary
*
* \param level. spell school level
* \return Spell level info structure
*
*/
2018-03-17 17:58:30 +03:00
const CSpell :: LevelInfo & getLevelInfo ( const int32_t level ) const ;
2023-01-02 00:06:42 +02:00
SpellID id ;
std :: string identifier ;
std :: string modScope ;
2015-02-02 11:25:26 +03:00
public :
2017-07-20 07:08:49 +03:00
enum ESpellPositiveness
{
NEGATIVE = - 1 ,
NEUTRAL = 0 ,
POSITIVE = 1
};
2015-02-02 11:25:26 +03:00
2015-09-19 16:51:06 +03:00
struct DLL_LINKAGE TargetInfo
2015-02-02 11:25:26 +03:00
{
2017-07-20 07:08:49 +03:00
spells :: AimType type ;
2015-02-02 11:25:26 +03:00
bool smart ;
bool massive ;
bool clearAffected ;
2017-07-20 07:08:49 +03:00
bool clearTarget ;
2015-02-26 20:59:18 +03:00
2018-03-17 17:58:30 +03:00
TargetInfo ( const CSpell * spell , const int32_t level , spells :: Mode mode );
2015-02-02 11:25:26 +03:00
};
2023-05-01 01:20:01 +03:00
using BTVector = std :: vector < BonusType > ;
2017-07-20 07:08:49 +03:00
2015-02-02 11:25:26 +03:00
si32 level ;
2023-05-05 21:28:07 +03:00
std :: map < SpellSchool , bool > school ;
2015-02-26 20:59:18 +03:00
2015-02-02 11:25:26 +03:00
si32 power ; //spell's power
2023-04-09 18:26:32 +03:00
std :: map < FactionID , si32 > probabilities ; //% chance to gain for castles
2015-02-02 11:25:26 +03:00
2018-03-17 17:58:30 +03:00
bool combat ; //is this spell combat (true) or adventure (false)
2015-02-02 11:25:26 +03:00
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 07:08:49 +03:00
JsonNode targetCondition ; //custom condition on what spell can affect
2015-02-02 11:25:26 +03:00
CSpell ();
~ CSpell ();
2018-03-17 17:58:30 +03:00
int64_t calculateDamage ( const spells :: Caster * caster ) const override ;
2015-02-02 11:25:26 +03:00
2023-06-06 18:34:04 +03:00
bool hasSchool ( ESpellSchool school ) const override ;
2015-02-02 11:25:26 +03:00
/**
* Calls cb for each school this spell belongs to
*
* Set stop to true to abort looping
2015-02-26 20:59:18 +03:00
*/
2018-03-17 17:58:30 +03:00
void forEachSchool ( const std :: function < void ( const spells :: SchoolInfo & , bool & ) > & cb ) const override ;
spells :: AimType getTargetType () const ;
bool hasEffects () const ;
2023-04-16 20:42:56 +03:00
void getEffects ( std :: vector < Bonus > & lst , const int level , const bool cumulative , const si32 duration , std :: optional < si32 *> maxDuration = std :: nullopt ) const ;
2018-03-17 17:58:30 +03:00
bool hasBattleEffects () const ;
int32_t getCost ( const int32_t skillLevel ) const override ;
2023-04-09 18:26:32 +03:00
si32 getProbability ( const FactionID & factionId ) const ;
2018-03-17 17:58:30 +03:00
int32_t getBasePower () const override ;
int32_t getLevelPower ( const int32_t skillLevel ) const override ;
2017-07-20 07:08:49 +03:00
int32_t getIndex () const override ;
2018-03-17 17:58:30 +03:00
int32_t getIconIndex () const override ;
2023-01-18 23:56:01 +02:00
std :: string getJsonKey () const override ;
2018-03-17 17:58:30 +03:00
SpellID getId () const override ;
2023-01-02 00:06:42 +02:00
std :: string getNameTextID () const override ;
std :: string getNameTranslated () const override ;
2015-02-02 11:25:26 +03:00
2023-01-02 00:06:42 +02:00
std :: string getDescriptionTextID ( int32_t level ) const override ;
std :: string getDescriptionTranslated ( int32_t level ) const override ;
int32_t getLevel () const override ;
2015-02-02 11:25:26 +03:00
2018-03-17 17:58:30 +03:00
boost :: logic :: tribool getPositiveness () const override ;
bool isPositive () const override ;
bool isNegative () const override ;
bool isNeutral () const override ;
2023-03-21 12:18:31 +03:00
bool isMagical () const override ;
2018-03-17 17:58:30 +03:00
bool isDamage () const override ;
bool isOffensive () const override ;
bool isSpecial () const override ;
bool isAdventure () const override ;
bool isCombat () const override ;
bool isCreatureAbility () const override ;
void registerIcons ( const IconRegistar & cb ) const override ;
const std :: string & getIconImmune () const ; ///< Returns resource name of icon for SPELL_IMMUNITY bonus
const std :: string & getIconBook () const ;
const std :: string & getIconEffect () const ;
const std :: string & getIconScenarioBonus () const ;
const std :: string & getIconScroll () const ;
const std :: string & getCastSound () const override ;
void updateFrom ( const JsonNode & data );
void serializeJson ( JsonSerializeFormat & handler );
2015-02-02 11:25:26 +03:00
2017-07-20 07:08:49 +03:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 11:25:26 +03:00
{
2017-07-31 16:35:42 +03:00
h & identifier ;
2023-04-04 22:22:58 +03:00
if ( version > 820 )
h & modScope ;
2017-07-31 16:35:42 +03:00
h & id ;
h & level ;
h & power ;
h & probabilities ;
h & attributes ;
2018-03-17 17:58:30 +03:00
h & combat ;
2017-07-31 16:35:42 +03:00
h & creatureAbility ;
h & positiveness ;
h & counteredSpells ;
2018-03-17 17:58:30 +03:00
h & rising ;
h & damage ;
h & offensive ;
2015-02-02 11:25:26 +03:00
h & targetType ;
2022-06-20 17:39:50 +03:00
h & targetCondition ;
2015-02-02 11:25:26 +03:00
h & iconImmune ;
h & defaultProbability ;
2018-03-17 17:58:30 +03:00
h & special ;
2017-07-31 16:35:42 +03:00
h & castSound ;
h & iconBook ;
h & iconEffect ;
h & iconScenarioBonus ;
h & iconScroll ;
2015-02-26 20:59:18 +03:00
h & levels ;
h & school ;
2015-02-02 11:25:26 +03:00
h & animationInfo ;
2023-03-21 12:18:31 +03:00
h & nonMagical ;
2015-02-02 11:25:26 +03:00
}
friend class CSpellHandler ;
friend class Graphics ;
2018-03-17 17:58:30 +03:00
friend class test :: CSpellTest ;
2015-03-18 16:39:07 +03:00
public :
///internal interface (for callbacks)
2016-02-15 13:34:37 +03:00
2016-09-05 11:36:25 +03:00
///Checks general but spell-specific problems. Use only during battle.
2017-07-20 07:08:49 +03: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 16:39:07 +03:00
2015-02-02 11:25:26 +03: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 20:59:18 +03:00
2018-03-17 17:58:30 +03:00
bool adventureCast ( SpellCastEnvironment * env , const AdventureSpellCastParameters & parameters ) const ;
2015-02-26 20:59:18 +03:00
2015-09-16 15:43:39 +03:00
public : //internal, for use only by Mechanics classes
///applies caster`s secondary skills and affectedCreature`s to raw damage
2017-07-20 07:08:49 +03:00
int64_t adjustRawDamage ( const spells :: Caster * caster , const battle :: Unit * affectedCreature , int64_t rawDamage ) const ;
2015-09-16 15:43:39 +03:00
2017-07-20 07:08:49 +03:00
///returns raw damage or healed HP
int64_t calculateRawEffectValue ( int32_t effectLevel , int32_t basePowerMultiplier , int32_t levelPowerMultiplier ) const ;
std :: unique_ptr < spells :: Mechanics > battleMechanics ( const spells :: IBattleCast * event ) const ;
2015-02-02 11:25:26 +03:00
private :
void setIsOffensive ( const bool val );
void setIsRising ( const bool val );
2015-02-26 20:59:18 +03:00
2017-07-20 07:08:49 +03:00
JsonNode convertTargetCondition ( const BTVector & immunity , const BTVector & absImmunity , const BTVector & limit , const BTVector & absLimit ) const ;
2015-02-02 11:25:26 +03:00
//call this after load or deserialization. cant be done in constructor.
void setupMechanics ();
2018-03-17 17:58:30 +03:00
2015-02-02 11:25:26 +03:00
private :
si32 defaultProbability ;
2018-03-17 17:58:30 +03:00
bool rising ;
bool damage ;
bool offensive ;
bool special ;
2023-03-21 12:18:31 +03:00
bool nonMagical ; //For creature abilities like bind
2015-02-02 11:25:26 +03:00
std :: string attributes ; //reference only attributes //todo: remove or include in configuration format, currently unused
2017-07-20 07:08:49 +03:00
spells :: AimType targetType ;
2015-02-02 11:25:26 +03: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 20:59:18 +03:00
2017-07-20 07:08:49 +03:00
std :: unique_ptr < spells :: ISpellMechanicsFactory > mechanics ; //(!) do not serialize
2016-09-04 08:19:28 +03:00
std :: unique_ptr < IAdventureSpellMechanics > adventureMechanics ; //(!) do not serialize
2015-02-02 11:25:26 +03:00
};
bool DLL_LINKAGE isInScreenRange ( const int3 & center , const int3 & pos ); //for spells like Dimension Door
2018-03-17 17:58:30 +03:00
class DLL_LINKAGE CSpellHandler : public CHandlerBase < SpellID , spells :: Spell , CSpell , spells :: Service >
2015-02-02 11:25:26 +03:00
{
public :
///IHandler base
2023-03-15 21:34:29 +02:00
std :: vector < JsonNode > loadLegacyData () override ;
2015-02-02 11:25:26 +03:00
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 ;
2017-07-20 07:08:49 +03:00
template < typename Handler > void serialize ( Handler & h , const int version )
2015-02-02 11:25:26 +03:00
{
2017-07-20 07:08:49 +03:00
h & objects ;
if ( ! h . saving )
{
afterLoadFinalization ();
}
2015-02-02 11:25:26 +03:00
}
2015-02-26 20:59:18 +03:00
2015-02-02 11:25:26 +03:00
protected :
2018-03-17 17:58:30 +03:00
const std :: vector < std :: string > & getTypeNames () const override ;
CSpell * loadFromJson ( const std :: string & scope , const JsonNode & json , const std :: string & identifier , size_t index ) override ;
2015-02-02 11:25:26 +03:00
};
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_END