1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-09-16 09:26:28 +02:00

Extracted adventure spell mechanics to distinct class hierarchy.

This commit is contained in:
AlexVinS
2016-09-04 08:19:28 +03:00
parent eb128a0207
commit a23144b361
8 changed files with 193 additions and 159 deletions

View File

@@ -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<Bonus> 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
{

View File

@@ -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;

View File

@@ -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<Bonus> 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: "<<owner->name<<"; mode:"<<parameters.mode;

View File

@@ -25,13 +25,6 @@ struct SpellCastContext
StacksInjured & si;
};
enum class ESpellCastResult
{
OK,
CANCEL,//cast failed but it is not an error
ERROR//internal error occurred
};
class DLL_LINKAGE DefaultSpellMechanics : public ISpellMechanics
{
public:
@@ -46,7 +39,7 @@ public:
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override;
virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;
bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final;
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override final;
void battleLogSingleTarget(std::vector<std::string> & 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;

View File

@@ -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

View File

@@ -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<LevelInfo> levels;
ISpellMechanics * mechanics;//(!) do not serialize
std::unique_ptr<ISpellMechanics> mechanics;//(!) do not serialize
std::unique_ptr<IAdventureSpellMechanics> adventureMechanics;//(!) do not serialize
};
bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door

View File

@@ -83,72 +83,87 @@ ISpellMechanics::ISpellMechanics(CSpell * s):
}
ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s)
std::unique_ptr<ISpellMechanics> ISpellMechanics::createMechanics(CSpell * s)
{
switch (s->id)
{
case SpellID::ANTI_MAGIC:
return new AntimagicMechanics(s);
return make_unique<AntimagicMechanics>(s);
case SpellID::ACID_BREATH_DAMAGE:
return new AcidBreathDamageMechanics(s);
return make_unique<AcidBreathDamageMechanics>(s);
case SpellID::CHAIN_LIGHTNING:
return new ChainLightningMechanics(s);
return make_unique<ChainLightningMechanics>(s);
case SpellID::CLONE:
return new CloneMechanics(s);
return make_unique<CloneMechanics>(s);
case SpellID::CURE:
return new CureMechanics(s);
return make_unique<CureMechanics>(s);
case SpellID::DEATH_STARE:
return new DeathStareMechanics(s);
return make_unique<DeathStareMechanics>(s);
case SpellID::DISPEL:
return new DispellMechanics(s);
return make_unique<DispellMechanics>(s);
case SpellID::DISPEL_HELPFUL_SPELLS:
return new DispellHelpfulMechanics(s);
return make_unique<DispellHelpfulMechanics>(s);
case SpellID::EARTHQUAKE:
return new EarthquakeMechanics(s);
return make_unique<EarthquakeMechanics>(s);
case SpellID::FIRE_WALL:
case SpellID::FORCE_FIELD:
return new WallMechanics(s);
return make_unique<WallMechanics>(s);
case SpellID::HYPNOTIZE:
return new HypnotizeMechanics(s);
return make_unique<HypnotizeMechanics>(s);
case SpellID::LAND_MINE:
case SpellID::QUICKSAND:
return new ObstacleMechanics(s);
return make_unique<ObstacleMechanics>(s);
case SpellID::REMOVE_OBSTACLE:
return new RemoveObstacleMechanics(s);
return make_unique<RemoveObstacleMechanics>(s);
case SpellID::SACRIFICE:
return new SacrificeMechanics(s);
return make_unique<SacrificeMechanics>(s);
case SpellID::SUMMON_FIRE_ELEMENTAL:
return new SummonMechanics(s, CreatureID::FIRE_ELEMENTAL);
return make_unique<SummonMechanics>(s, CreatureID::FIRE_ELEMENTAL);
case SpellID::SUMMON_EARTH_ELEMENTAL:
return new SummonMechanics(s, CreatureID::EARTH_ELEMENTAL);
return make_unique<SummonMechanics>(s, CreatureID::EARTH_ELEMENTAL);
case SpellID::SUMMON_WATER_ELEMENTAL:
return new SummonMechanics(s, CreatureID::WATER_ELEMENTAL);
return make_unique<SummonMechanics>(s, CreatureID::WATER_ELEMENTAL);
case SpellID::SUMMON_AIR_ELEMENTAL:
return new SummonMechanics(s, CreatureID::AIR_ELEMENTAL);
return make_unique<SummonMechanics>(s, CreatureID::AIR_ELEMENTAL);
case SpellID::TELEPORT:
return new TeleportMechanics(s);
return make_unique<TeleportMechanics>(s);
default:
if(s->isRisingSpell())
return make_unique<SpecialRisingSpellMechanics>(s);
else
return make_unique<DefaultSpellMechanics>(s);
}
}
//IAdventureSpellMechanics
IAdventureSpellMechanics::IAdventureSpellMechanics(CSpell * s):
owner(s)
{
}
std::unique_ptr<IAdventureSpellMechanics> IAdventureSpellMechanics::createMechanics(CSpell * s)
{
switch (s->id)
{
case SpellID::SUMMON_BOAT:
return new SummonBoatMechanics(s);
return make_unique<SummonBoatMechanics>(s);
case SpellID::SCUTTLE_BOAT:
return new ScuttleBoatMechanics(s);
return make_unique<ScuttleBoatMechanics>(s);
case SpellID::DIMENSION_DOOR:
return new DimensionDoorMechanics(s);
return make_unique<DimensionDoorMechanics>(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<AdventureSpellMechanics>(s); //implemented using bonus system
case SpellID::TOWN_PORTAL:
return new TownPortalMechanics(s);
return make_unique<TownPortalMechanics>(s);
case SpellID::VIEW_EARTH:
return new ViewEarthMechanics(s);
return make_unique<ViewEarthMechanics>(s);
case SpellID::VIEW_AIR:
return new ViewAirMechanics(s);
return make_unique<ViewAirMechanics>(s);
default:
if(s->isRisingSpell())
return new SpecialRisingSpellMechanics(s);
else
return new DefaultSpellMechanics(s);
return std::unique_ptr<IAdventureSpellMechanics>();
}
}

View File

@@ -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<std::string> & logLines, const BattleSpellCast * packet,
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0;
static ISpellMechanics * createMechanics(CSpell * s);
static std::unique_ptr<ISpellMechanics> 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<IAdventureSpellMechanics> createMechanics(CSpell * s);
protected:
CSpell * owner;
};