1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Extract SpellMechanics to separate file

This commit is contained in:
AlexVinS 2014-11-12 11:36:34 +03:00
parent 22178151aa
commit 5ba53da9bf
6 changed files with 219 additions and 157 deletions

View File

@ -83,6 +83,7 @@ set(lib_SRCS
VCMI_Lib.cpp
VCMIDirs.cpp
IHandlerBase.cpp
SpellMechanics.cpp
IGameCallback.cpp
CGameInfoCallback.cpp

View File

@ -13,6 +13,10 @@
#include "mapObjects/CGHeroInstance.h"
#include "BattleState.h"
#include "SpellMechanics.h"
/*
* CSpellHandler.cpp, part of VCMI engine
*
@ -132,160 +136,13 @@ namespace SRSLPraserHelpers
}
///CSpellMechanics
CSpellMechanics::CSpellMechanics(CSpell * s):
///ISpellMechanics
ISpellMechanics::ISpellMechanics(CSpell * s):
owner(s)
{
}
CSpellMechanics::~CSpellMechanics()
{
}
ESpellCastProblem::ESpellCastProblem CSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj)
{
//by default use general algorithm
return owner->isImmuneBy(obj);
}
namespace
{
class CloneMechnics: public CSpellMechanics
{
public:
CloneMechnics(CSpell * s): CSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
};
class DispellHelpfulMechanics: public CSpellMechanics
{
public:
DispellHelpfulMechanics(CSpell * s): CSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
};
class HypnotizeMechanics: public CSpellMechanics
{
public:
HypnotizeMechanics(CSpell * s): CSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
};
///all rising spells
class RisingSpellMechanics: public CSpellMechanics
{
public:
RisingSpellMechanics(CSpell * s): CSpellMechanics(s){};
};
///all rising spells but SACRIFICE
class SpecialRisingSpellMechanics: public RisingSpellMechanics
{
public:
SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
};
class SacrificeMechanics: public RisingSpellMechanics
{
public:
SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};
};
///CloneMechanics
ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack * obj)
{
//can't clone already cloned creature
if (vstd::contains(obj->state, EBattleStackState::CLONED))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
//TODO: how about stacks casting Clone?
//currently Clone casted by stack is assumed Expert level
ui8 schoolLevel;
if (caster)
{
schoolLevel = caster->getSpellSchoolLevel(owner);
}
else
{
schoolLevel = 3;
}
if (schoolLevel < 3)
{
int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
int creLevel = obj->getCreature()->level;
if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
//use default algorithm only if there is no mechanics-related problem
return CSpellMechanics::isImmuneByStack(caster,mode,obj);
}
///DispellHelpfulMechanics
ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj)
{
TBonusListPtr spellBon = obj->getSpellBonuses();
bool hasPositiveSpell = false;
for(const Bonus * b : *spellBon)
{
if(SpellID(b->sid).toSpell()->isPositive())
{
hasPositiveSpell = true;
break;
}
}
if(!hasPositiveSpell)
{
return ESpellCastProblem::NO_SPELLS_TO_DISPEL;
}
//use default algorithm only if there is no mechanics-related problem
return CSpellMechanics::isImmuneByStack(caster,mode,obj);
}
///HypnotizeMechanics
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj)
{
if(nullptr != caster) //do not resist hypnotize casted after attack, for example
{
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
ui64 subjectHealth = (obj->count - 1) * obj->MaxHealth() + obj->firstHPleft;
//apply 'damage' bonus for hypnotize, including hero specialty
ui64 maxHealth = owner->calculateBonus(caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), caster, obj);
if (subjectHealth > maxHealth)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
return CSpellMechanics::isImmuneByStack(caster,mode,obj);
}
///SpecialRisingSpellMechanics
ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj)
{
// following does apply to resurrect and animate dead(?) only
// for sacrifice health calculation and health limit check don't matter
if(obj->count >= obj->baseAmount)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
if (caster) //FIXME: Archangels can cast immune stack
{
auto maxHealth = owner->calculateHealedHP (caster, obj);
if (maxHealth < obj->MaxHealth()) //must be able to rise at least one full creature
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
return CSpellMechanics::isImmuneByStack(caster,mode,obj);
}
}
///CSpell::LevelInfo
CSpell::LevelInfo::LevelInfo()
:description(""),cost(0),power(0),AIValue(0),smartTarget(true),range("0")
@ -804,7 +661,7 @@ void CSpell::setupMechanics()
if(isRisingSpell())
mechanics = new SpecialRisingSpellMechanics(this);
else
mechanics = new CSpellMechanics(this);
mechanics = new DefaultSpellMechanics(this);
break;
}

View File

@ -23,6 +23,8 @@ class CSpell;
class CGHeroInstance;
class CStack;
struct CPackForClient;
struct SpellSchoolInfo
{
ESpellSchool id; //backlink
@ -59,15 +61,37 @@ static SpellSchoolInfo spellSchoolConfig[4] =
}
};
class CPackForClient;
class DLL_LINKAGE CSpellMechanics
///callback to be provided by server
class DLL_LINKAGE SpellCastEnvironment
{
public:
CSpellMechanics(CSpell * s);
virtual ~CSpellMechanics();
virtual void sendAndApply(CPackForClient * info) = 0;
};
///helper struct
struct DLL_LINKAGE SpellCastContext
{
public:
SpellCastEnvironment * env;
};
class DLL_LINKAGE ISpellMechanics
{
public:
ISpellMechanics(CSpell * s);
virtual ~ISpellMechanics(){};
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) = 0;
/** \brief
*
* \param
* \return true if no error
*
*/
virtual bool adventureCast(SpellCastContext & context) = 0;
virtual bool battleCast(SpellCastContext & context) = 0;
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj);
protected:
CSpell * owner;
};
@ -262,7 +286,7 @@ private:
std::vector<LevelInfo> levels;
CSpellMechanics * mechanics;//(!) do not serialize
ISpellMechanics * mechanics;//(!) do not serialize
};

112
lib/SpellMechanics.cpp Normal file
View File

@ -0,0 +1,112 @@
/*
* SpellMechanics.cpp, 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
*
*/
#include "StdInc.h"
#include "SpellMechanics.h"
#include "mapObjects/CGHeroInstance.h"
#include "BattleState.h"
///DefaultSpellMechanics
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj)
{
//by default use general algorithm
return owner->isImmuneBy(obj);
}
///CloneMechanics
ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack * obj)
{
//can't clone already cloned creature
if (vstd::contains(obj->state, EBattleStackState::CLONED))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
//TODO: how about stacks casting Clone?
//currently Clone casted by stack is assumed Expert level
ui8 schoolLevel;
if (caster)
{
schoolLevel = caster->getSpellSchoolLevel(owner);
}
else
{
schoolLevel = 3;
}
if (schoolLevel < 3)
{
int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
int creLevel = obj->getCreature()->level;
if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
//use default algorithm only if there is no mechanics-related problem
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);
}
///DispellHelpfulMechanics
ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj)
{
TBonusListPtr spellBon = obj->getSpellBonuses();
bool hasPositiveSpell = false;
for(const Bonus * b : *spellBon)
{
if(SpellID(b->sid).toSpell()->isPositive())
{
hasPositiveSpell = true;
break;
}
}
if(!hasPositiveSpell)
{
return ESpellCastProblem::NO_SPELLS_TO_DISPEL;
}
//use default algorithm only if there is no mechanics-related problem
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);
}
///HypnotizeMechanics
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj)
{
if(nullptr != caster) //do not resist hypnotize casted after attack, for example
{
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
ui64 subjectHealth = (obj->count - 1) * obj->MaxHealth() + obj->firstHPleft;
//apply 'damage' bonus for hypnotize, including hero specialty
ui64 maxHealth = owner->calculateBonus(caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), caster, obj);
if (subjectHealth > maxHealth)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);
}
///SpecialRisingSpellMechanics
ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj)
{
// following does apply to resurrect and animate dead(?) only
// for sacrifice health calculation and health limit check don't matter
if(obj->count >= obj->baseAmount)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
if (caster) //FIXME: Archangels can cast immune stack
{
auto maxHealth = owner->calculateHealedHP (caster, obj);
if (maxHealth < obj->MaxHealth()) //must be able to rise at least one full creature
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);
}

66
lib/SpellMechanics.h Normal file
View File

@ -0,0 +1,66 @@
/*
* SpellMechanics.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
*
*/
#pragma once
#include "CSpellHandler.h"
class DefaultSpellMechanics: public ISpellMechanics
{
public:
DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
bool adventureCast(SpellCastContext & context) override;
bool battleCast(SpellCastContext & context) override;
};
class CloneMechnics: public DefaultSpellMechanics
{
public:
CloneMechnics(CSpell * s): DefaultSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
};
class DispellHelpfulMechanics: public DefaultSpellMechanics
{
public:
DispellHelpfulMechanics(CSpell * s): DefaultSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
};
class HypnotizeMechanics: public DefaultSpellMechanics
{
public:
HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
};
///all rising spells
class RisingSpellMechanics: public DefaultSpellMechanics
{
public:
RisingSpellMechanics(CSpell * s): DefaultSpellMechanics(s){};
};
///all rising spells but SACRIFICE
class SpecialRisingSpellMechanics: public RisingSpellMechanics
{
public:
SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
};
class SacrificeMechanics: public RisingSpellMechanics
{
public:
SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};
};

View File

@ -185,6 +185,8 @@
<Unit filename="ResourceSet.cpp" />
<Unit filename="ResourceSet.h" />
<Unit filename="ScopeGuard.h" />
<Unit filename="SpellMechanics.cpp" />
<Unit filename="SpellMechanics.h" />
<Unit filename="StartInfo.h" />
<Unit filename="StdInc.h">
<Option weight="0" />