diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 8417e0310..b43d73a6d 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1599,6 +1599,11 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell if(!spell->combatSpell) return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL; + const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCasted(this); + + if(specificProblem != ESpellCastProblem::OK) + return specificProblem; + if(spell->isNegative() || spell->hasEffects()) { bool allStacksImmune = true; diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index f09819c84..fe91cf8b9 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -236,6 +236,21 @@ void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, B env->sendAndApply(&ca); } +ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCasted(const CBattleInfoCallback * cb) const +{ + if(nullptr == cb->battleGetDefendedTown()) + { + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + } + + if(CGTownInstance::NONE == cb->battleGetDefendedTown()->fortLevel()) + { + return ESpellCastProblem::NO_APPROPRIATE_TARGET; + } + + return ESpellCastProblem::OK; +} + ///HypnotizeMechanics ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const { diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 6cc4ee5b5..c415d3837 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -48,6 +48,7 @@ class DLL_LINKAGE EarthquakeMechanics : public DefaultSpellMechanics { public: EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 2442d4404..41018f2eb 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -712,6 +712,13 @@ std::set DefaultSpellMechanics::getAffectedStacks(SpellTargeting return attackedCres; } +ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCasted(const CBattleInfoCallback * cb) const +{ + //no problems by default, this method is for spell-specific problems + return ESpellCastProblem::OK; +} + + ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const { //by default use general algorithm diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index 48a83116e..5a100fc5e 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -33,6 +33,8 @@ public: std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override; std::set getAffectedStacks(SpellTargetingContext & ctx) const override; + ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb) const override; + ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 5638d331a..476632654 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -234,6 +234,11 @@ ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affec return ret; } +ESpellCastProblem::ESpellCastProblem CSpell::canBeCasted(const CBattleInfoCallback * cb) const +{ + return mechanics->canBeCasted(cb); +} + std::vector CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const { return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes); diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 20396bfc0..0779e9e87 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -246,9 +246,6 @@ public: //internal, for use only by Mechanics classes ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const; - //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. - ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const; - //internal, for use only by Mechanics classes. applying secondary skills ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const; @@ -301,6 +298,14 @@ public: } friend class CSpellHandler; friend class Graphics; +public: + ///internal interface (for callbacks) + + ///Checks general but spell-specific problems for all casting modes. Use only during battle. + ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb) const; + + ///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. + ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const; public: ///Server logic. Has write access to GameState via packets. ///May be executed on client side by (future) non-cheat-proof scripts. diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 7a91d3f35..8e884f6c5 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -38,9 +38,11 @@ public: virtual std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0; virtual std::set getAffectedStacks(SpellTargetingContext & ctx) const = 0; - + + virtual ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb) const = 0; + virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0; - + virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0; virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0; virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;