diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 3241fd25a..5fa1be1ad 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -15,12 +15,94 @@ #include "../CRandomGenerator.h" #include "../mapObjects/CGHeroInstance.h" #include "../NetPacks.h" -#include "../BattleState.h" -#include "../CGameState.h" #include "../CGameInfoCallback.h" #include "../mapping/CMap.h" #include "../CPlayerState.h" +///AdventureSpellMechanics +bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const +{ + if(!owner->isAdventureSpell()) + { + env->complain("Attempt to cast non adventure spell in adventure mode"); + return false; + } + + const CGHeroInstance * caster = parameters.caster; + + if(caster->inTownGarrison) + { + env->complain("Attempt to cast an adventure spell in town garrison"); + return false; + } + + const int cost = caster->getSpellCost(owner); + + if(!caster->canCastThisSpell(owner)) + { + env->complain("Hero cannot cast this spell!"); + return false; + } + + if(caster->mana < cost) + { + env->complain("Hero doesn't have enough spell points to cast this spell!"); + return false; + } + + { + AdvmapSpellCast asc; + asc.caster = caster; + asc.spellID = owner->id; + env->sendAndApply(&asc); + } + + switch(applyAdventureEffects(env, parameters)) + { + case ESpellCastResult::OK: + { + SetMana sm; + sm.hid = caster->id; + sm.absolute = false; + sm.val = -cost; + env->sendAndApply(&sm); + return true; + } + break; + case ESpellCastResult::CANCEL: + return true; + } + return false; +} + +ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const +{ + if(owner->hasEffects()) + { + const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + + std::vector bonuses; + + owner->getEffects(bonuses, schoolLevel); + + for(Bonus b : bonuses) + { + GiveBonus gb; + gb.id = parameters.caster->id.getNum(); + gb.bonus = b; + env->sendAndApply(&gb); + } + + return ESpellCastResult::OK; + } + else + { + //There is no generic algorithm of adventure cast + env->complain("Unimplemented adventure spell"); + return ESpellCastResult::ERROR; + } +} + ///SummonBoatMechanics ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const { diff --git a/lib/spells/AdventureSpellMechanics.h b/lib/spells/AdventureSpellMechanics.h index c198290a8..b2ee4bda4 100644 --- a/lib/spells/AdventureSpellMechanics.h +++ b/lib/spells/AdventureSpellMechanics.h @@ -10,47 +10,62 @@ #pragma once -#include "CDefaultSpellMechanics.h" +#include "ISpellMechanics.h" -class ISpellMechanics; -class DefaultSpellMechanics; +enum class ESpellCastResult +{ + OK, + CANCEL,//cast failed but it is not an error + ERROR//internal error occurred +}; -class DLL_LINKAGE SummonBoatMechanics : public DefaultSpellMechanics +class DLL_LINKAGE AdventureSpellMechanics: public IAdventureSpellMechanics { public: - SummonBoatMechanics(CSpell * s): DefaultSpellMechanics(s){}; + AdventureSpellMechanics(CSpell * s): IAdventureSpellMechanics(s){}; + + bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final; +protected: + ///actual adventure cast implementation + virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; +}; + +class DLL_LINKAGE SummonBoatMechanics : public AdventureSpellMechanics +{ +public: + SummonBoatMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; }; -class DLL_LINKAGE ScuttleBoatMechanics : public DefaultSpellMechanics +class DLL_LINKAGE ScuttleBoatMechanics : public AdventureSpellMechanics { public: - ScuttleBoatMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ScuttleBoatMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; }; -class DLL_LINKAGE DimensionDoorMechanics : public DefaultSpellMechanics +class DLL_LINKAGE DimensionDoorMechanics : public AdventureSpellMechanics { public: - DimensionDoorMechanics(CSpell * s): DefaultSpellMechanics(s){}; + DimensionDoorMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; }; -class DLL_LINKAGE TownPortalMechanics : public DefaultSpellMechanics +class DLL_LINKAGE TownPortalMechanics : public AdventureSpellMechanics { public: - TownPortalMechanics(CSpell * s): DefaultSpellMechanics(s){}; + TownPortalMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; }; -class DLL_LINKAGE ViewMechanics : public DefaultSpellMechanics +class DLL_LINKAGE ViewMechanics : public AdventureSpellMechanics { public: - ViewMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ViewMechanics(CSpell * s): AdventureSpellMechanics(s){}; protected: ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; virtual bool filterObject(const CGObjectInstance * obj, const int spellLevel) const = 0; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index c043e88e3..90d97fbdf 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -145,89 +145,6 @@ void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCa } } -bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const -{ - if(!owner->isAdventureSpell()) - { - env->complain("Attempt to cast non adventure spell in adventure mode"); - return false; - } - - const CGHeroInstance * caster = parameters.caster; - - if(caster->inTownGarrison) - { - env->complain("Attempt to cast an adventure spell in town garrison"); - return false; - } - - const int cost = caster->getSpellCost(owner); - - if(!caster->canCastThisSpell(owner)) - { - env->complain("Hero cannot cast this spell!"); - return false; - } - - if(caster->mana < cost) - { - env->complain("Hero doesn't have enough spell points to cast this spell!"); - return false; - } - - { - AdvmapSpellCast asc; - asc.caster = caster; - asc.spellID = owner->id; - env->sendAndApply(&asc); - } - - switch(applyAdventureEffects(env, parameters)) - { - case ESpellCastResult::OK: - { - SetMana sm; - sm.hid = caster->id; - sm.absolute = false; - sm.val = -cost; - env->sendAndApply(&sm); - return true; - } - break; - case ESpellCastResult::CANCEL: - return true; - } - return false; -} - -ESpellCastResult DefaultSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const -{ - if(owner->hasEffects()) - { - const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - - std::vector bonuses; - - owner->getEffects(bonuses, schoolLevel); - - for(Bonus b : bonuses) - { - GiveBonus gb; - gb.id = parameters.caster->id.getNum(); - gb.bonus = b; - env->sendAndApply(&gb); - } - - return ESpellCastResult::OK; - } - else - { - //There is no generic algorithm of adventure cast - env->complain("Unimplemented adventure spell"); - return ESpellCastResult::ERROR; - } -} - void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const { logGlobal->debugStream() << "Started spell cast. Spell: "<name<<"; mode:"< & logLines, const BattleSpellCast * packet, @@ -54,9 +47,6 @@ public: protected: virtual void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const; - ///actual adventure cast implementation - virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; - void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const; private: void castMagicMirror(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 9a758a978..8f0bb024d 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -91,14 +91,15 @@ CSpell::CSpell(): defaultProbability(0), isRising(false), isDamage(false), isOffensive(false), targetType(ETargetType::NO_TARGET), - mechanics(nullptr) + mechanics(), + adventureMechanics() { levels.resize(GameConstants::SPELL_SCHOOL_LEVELS); } CSpell::~CSpell() { - delete mechanics; + } void CSpell::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const @@ -110,7 +111,12 @@ bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastP { assert(env); - return mechanics->adventureCast(env, parameters); + if(!adventureMechanics.get()) + { + env->complain("Invalid adventure spell cast attempt!"); + return false; + } + return adventureMechanics->adventureCast(env, parameters); } void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const @@ -622,13 +628,8 @@ void CSpell::setup() void CSpell::setupMechanics() { - if(nullptr != mechanics) - { - logGlobal->errorStream() << "Spell " << this->name << ": mechanics already set"; - delete mechanics; - } - mechanics = ISpellMechanics::createMechanics(this); + adventureMechanics = IAdventureSpellMechanics::createMechanics(this); } ///CSpell::AnimationInfo diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 183113baf..f9684713c 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -20,6 +20,7 @@ class CGObjectInstance; class CSpell; class ISpellMechanics; +class IAdventureSpellMechanics; class CLegacyConfigParser; class CGHeroInstance; class CStack; @@ -338,7 +339,8 @@ private: std::vector levels; - ISpellMechanics * mechanics;//(!) do not serialize + std::unique_ptr mechanics;//(!) do not serialize + std::unique_ptr adventureMechanics;//(!) do not serialize }; bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spells like Dimension Door diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index f7055cd3e..ef0b7a0f1 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -83,72 +83,87 @@ ISpellMechanics::ISpellMechanics(CSpell * s): } -ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s) +std::unique_ptr ISpellMechanics::createMechanics(CSpell * s) { switch (s->id) { case SpellID::ANTI_MAGIC: - return new AntimagicMechanics(s); + return make_unique(s); case SpellID::ACID_BREATH_DAMAGE: - return new AcidBreathDamageMechanics(s); + return make_unique(s); case SpellID::CHAIN_LIGHTNING: - return new ChainLightningMechanics(s); + return make_unique(s); case SpellID::CLONE: - return new CloneMechanics(s); + return make_unique(s); case SpellID::CURE: - return new CureMechanics(s); + return make_unique(s); case SpellID::DEATH_STARE: - return new DeathStareMechanics(s); + return make_unique(s); case SpellID::DISPEL: - return new DispellMechanics(s); + return make_unique(s); case SpellID::DISPEL_HELPFUL_SPELLS: - return new DispellHelpfulMechanics(s); + return make_unique(s); case SpellID::EARTHQUAKE: - return new EarthquakeMechanics(s); + return make_unique(s); case SpellID::FIRE_WALL: case SpellID::FORCE_FIELD: - return new WallMechanics(s); + return make_unique(s); case SpellID::HYPNOTIZE: - return new HypnotizeMechanics(s); + return make_unique(s); case SpellID::LAND_MINE: case SpellID::QUICKSAND: - return new ObstacleMechanics(s); + return make_unique(s); case SpellID::REMOVE_OBSTACLE: - return new RemoveObstacleMechanics(s); + return make_unique(s); case SpellID::SACRIFICE: - return new SacrificeMechanics(s); + return make_unique(s); case SpellID::SUMMON_FIRE_ELEMENTAL: - return new SummonMechanics(s, CreatureID::FIRE_ELEMENTAL); + return make_unique(s, CreatureID::FIRE_ELEMENTAL); case SpellID::SUMMON_EARTH_ELEMENTAL: - return new SummonMechanics(s, CreatureID::EARTH_ELEMENTAL); + return make_unique(s, CreatureID::EARTH_ELEMENTAL); case SpellID::SUMMON_WATER_ELEMENTAL: - return new SummonMechanics(s, CreatureID::WATER_ELEMENTAL); + return make_unique(s, CreatureID::WATER_ELEMENTAL); case SpellID::SUMMON_AIR_ELEMENTAL: - return new SummonMechanics(s, CreatureID::AIR_ELEMENTAL); + return make_unique(s, CreatureID::AIR_ELEMENTAL); case SpellID::TELEPORT: - return new TeleportMechanics(s); + return make_unique(s); + default: + if(s->isRisingSpell()) + return make_unique(s); + else + return make_unique(s); + } +} + +//IAdventureSpellMechanics +IAdventureSpellMechanics::IAdventureSpellMechanics(CSpell * s): + owner(s) +{ + +} + +std::unique_ptr IAdventureSpellMechanics::createMechanics(CSpell * s) +{ + switch (s->id) + { case SpellID::SUMMON_BOAT: - return new SummonBoatMechanics(s); + return make_unique(s); case SpellID::SCUTTLE_BOAT: - return new ScuttleBoatMechanics(s); + return make_unique(s); case SpellID::DIMENSION_DOOR: - return new DimensionDoorMechanics(s); + return make_unique(s); case SpellID::FLY: case SpellID::WATER_WALK: case SpellID::VISIONS: case SpellID::DISGUISE: - return new DefaultSpellMechanics(s); //implemented using bonus system + return make_unique(s); //implemented using bonus system case SpellID::TOWN_PORTAL: - return new TownPortalMechanics(s); + return make_unique(s); case SpellID::VIEW_EARTH: - return new ViewEarthMechanics(s); + return make_unique(s); case SpellID::VIEW_AIR: - return new ViewAirMechanics(s); + return make_unique(s); default: - if(s->isRisingSpell()) - return new SpecialRisingSpellMechanics(s); - else - return new DefaultSpellMechanics(s); + return std::unique_ptr(); } } - diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 1fb343546..8bf0c91df 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -76,12 +76,6 @@ private: void prepare(const CSpell * spell); }; -struct DLL_LINKAGE AdventureSpellCastParameters -{ - const CGHeroInstance * caster; - int3 pos; -}; - struct DLL_LINKAGE SpellTargetingContext { CSpell::TargetInfo ti; @@ -112,13 +106,31 @@ public: virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * 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; virtual void battleLogSingleTarget(std::vector & logLines, const BattleSpellCast * packet, const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0; - static ISpellMechanics * createMechanics(CSpell * s); + static std::unique_ptr createMechanics(CSpell * s); +protected: + CSpell * owner; +}; + +struct DLL_LINKAGE AdventureSpellCastParameters +{ + const CGHeroInstance * caster; + int3 pos; +}; + +class DLL_LINKAGE IAdventureSpellMechanics +{ +public: + IAdventureSpellMechanics(CSpell * s); + virtual ~IAdventureSpellMechanics() = default; + + virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0; + + static std::unique_ptr createMechanics(CSpell * s); protected: CSpell * owner; };