1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

Move getAffectedCreatures to CSpell. + more drafts

This commit is contained in:
AlexVinS 2014-11-13 04:53:25 +03:00
parent 5ba53da9bf
commit a06dae1f96
9 changed files with 275 additions and 201 deletions

View File

@ -457,12 +457,8 @@ void CBattleAI::attemptCastingSpell()
case OFFENSIVE_SPELL: case OFFENSIVE_SPELL:
{ {
int damageDealt = 0, damageReceived = 0; int damageDealt = 0, damageReceived = 0;
auto stacksSuffering = cb->getAffectedCreatures(ps.spell, skillLevel, playerID, ps.dest); auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, playerID, skillLevel, ps.dest, hero);
vstd::erase_if(stacksSuffering, [&](const CStack * s) -> bool
{
return ESpellCastProblem::OK != ps.spell->isImmuneByStack(hero, ECastingMode::HERO_CASTING, s);
});
if(stacksSuffering.empty()) if(stacksSuffering.empty())
return -1; return -1;

View File

@ -1582,7 +1582,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
for(auto s : stacks) for(auto s : stacks)
{ {
ESpellCastProblem::ESpellCastProblem res = spell->isImmuneByStack(caster,mode,s); ESpellCastProblem::ESpellCastProblem res = spell->isImmuneByStack(caster,s);
if(res == ESpellCastProblem::OK) if(res == ESpellCastProblem::OK)
{ {
@ -1661,7 +1661,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks(); auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
for(auto stack : stacks) for(auto stack : stacks)
{ {
if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, mode, stack)) if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, stack))
{ {
allStacksImmune = false; allStacksImmune = false;
break; break;
@ -1705,7 +1705,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway
{ {
bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, mode, stack); bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack);
bool casterStack = stack->owner == caster->getOwner(); bool casterStack = stack->owner == caster->getOwner();
if(spell->id == SpellID::SACRIFICE) if(spell->id == SpellID::SACRIFICE)
@ -1763,8 +1763,6 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
std::vector<BattleHex> ret; std::vector<BattleHex> ret;
RETURN_IF_NOT_BATTLE(ret); RETURN_IF_NOT_BATTLE(ret);
auto mode = ECastingMode::HERO_CASTING; //TODO get rid of this!
switch(spell->getTargetType()) switch(spell->getTargetType())
{ {
case CSpell::CREATURE: case CSpell::CREATURE:
@ -1774,7 +1772,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
for(const CStack * stack : battleAliveStacks()) for(const CStack * stack : battleAliveStacks())
{ {
bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, mode, stack); bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack);
bool casterStack = stack->owner == caster->getOwner(); bool casterStack = stack->owner == caster->getOwner();
if(!immune) if(!immune)
@ -1908,117 +1906,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
return battleIsImmune(nullptr, spell, mode, dest); return battleIsImmune(nullptr, spell, mode, dest);
} }
std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell * spell, int skillLevel, PlayerColor attackerOwner, BattleHex destinationTile)
{
std::set<const CStack*> attackedCres; //std::set to exclude multiple occurrences of two hex creatures
const ui8 attackerSide = playerToSide(attackerOwner) == 1;
const auto attackedHexes = spell->rangeInHexes(destinationTile, skillLevel, attackerSide);
const CSpell::TargetInfo ti = spell->getTargetInfo(skillLevel);
//TODO: more generic solution for mass spells
if (spell->id == SpellID::CHAIN_LIGHTNING)
{
std::set<BattleHex> possibleHexes;
for (auto stack : battleGetAllStacks())
{
if (stack->isValidTarget())
{
for (auto hex : stack->getHexes())
{
possibleHexes.insert (hex);
}
}
}
int targetsOnLevel[4] = {4, 4, 5, 5};
BattleHex lightningHex = destinationTile;
for (int i = 0; i < targetsOnLevel[skillLevel]; ++i)
{
auto stack = battleGetStackByPos (lightningHex, true);
if (!stack)
break;
attackedCres.insert (stack);
for (auto hex : stack->getHexes())
{
possibleHexes.erase (hex); //can't hit same place twice
}
if (possibleHexes.empty()) //not enough targets
break;
lightningHex = BattleHex::getClosestTile (stack->attackerOwned, destinationTile, possibleHexes);
}
}
else if (spell->getLevelInfo(skillLevel).range.size() > 1) //custom many-hex range
{
for(BattleHex hex : attackedHexes)
{
if(const CStack * st = battleGetStackByPos(hex, ti.onlyAlive))
{
if (spell->id == SpellID::DEATH_CLOUD) //Death Cloud //TODO: fireball and fire immunity
{
if (st->isLiving() || st->coversPos(destinationTile)) //directly hit or alive
{
attackedCres.insert(st);
}
}
else
attackedCres.insert(st);
}
}
}
else if(spell->getTargetType() == CSpell::CREATURE)
{
auto predicate = [=](const CStack * s){
const bool positiveToAlly = spell->isPositive() && s->owner == attackerOwner;
const bool negativeToEnemy = spell->isNegative() && s->owner != attackerOwner;
const bool validTarget = s->isValidTarget(!ti.onlyAlive); //todo: this should be handled by spell class
//for single target spells select stacks covering destination tile
const bool rangeCovers = ti.massive || s->coversPos(destinationTile);
//handle smart targeting
const bool positivenessFlag = !ti.smart || spell->isNeutral() || positiveToAlly || negativeToEnemy;
return rangeCovers && positivenessFlag && validTarget;
};
TStacks stacks = battleGetStacksIf(predicate);
if (ti.massive)
{
//for massive spells add all targets
for (auto stack : stacks)
attackedCres.insert(stack);
}
else
{
//for single target spells we must select one target. Alive stack is preferred (issue #1763)
for(auto stack : stacks)
{
if(stack->alive())
{
attackedCres.insert(stack);
break;
}
}
if(attackedCres.empty() && !stacks.empty())
{
attackedCres.insert(stacks.front());
}
}
}
else //custom range from attackedHexes
{
for(BattleHex hex : attackedHexes)
{
if(const CStack * st = battleGetStackByPos(hex, ti.onlyAlive))
attackedCres.insert(st);
}
}
return attackedCres;
}
const CStack * CBattleInfoCallback::getStackIf(std::function<bool(const CStack*)> pred) const const CStack * CBattleInfoCallback::getStackIf(std::function<bool(const CStack*)> pred) const
{ {
RETURN_IF_NOT_BATTLE(nullptr); RETURN_IF_NOT_BATTLE(nullptr);

View File

@ -283,7 +283,6 @@ public:
std::vector<BattleHex> battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const; std::vector<BattleHex> battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const;
ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel
ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //healing spells casted by stacks ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //healing spells casted by stacks
std::set<const CStack*> getAffectedCreatures(const CSpell * s, int skillLevel, PlayerColor attackerOwner, BattleHex destinationTile); //calculates stack affected by given spell
SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const; SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const;
SpellID getRandomBeneficialSpell(const CStack * subject) const; SpellID getRandomBeneficialSpell(const CStack * subject) const;

View File

@ -12,6 +12,7 @@
#include "mapObjects/CGHeroInstance.h" #include "mapObjects/CGHeroInstance.h"
#include "BattleState.h" #include "BattleState.h"
#include "CBattleCallback.h"
#include "SpellMechanics.h" #include "SpellMechanics.h"
@ -380,12 +381,130 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
return ret; return ret;
} }
std::set<const CStack* > CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster) const
{
std::set<const CStack* > attackedCres;//std::set to exclude multiple occurrences of two hex creatures
const ui8 attackerSide = cb->playerToSide(casterColor) == 1;
const auto attackedHexes = rangeInHexes(destination, spellLvl, attackerSide);
const CSpell::TargetInfo ti = getTargetInfoEx(spellLvl, mode);
//TODO: more generic solution for mass spells
if (id == SpellID::CHAIN_LIGHTNING)
{
std::set<BattleHex> possibleHexes;
for (auto stack : cb->battleGetAllStacks())
{
if (stack->isValidTarget())
{
for (auto hex : stack->getHexes())
{
possibleHexes.insert (hex);
}
}
}
int targetsOnLevel[4] = {4, 4, 5, 5};
BattleHex lightningHex = destination;
for (int i = 0; i < targetsOnLevel[spellLvl]; ++i)
{
auto stack = cb->battleGetStackByPos (lightningHex, true);
if (!stack)
break;
attackedCres.insert (stack);
for (auto hex : stack->getHexes())
{
possibleHexes.erase (hex); //can't hit same place twice
}
if (possibleHexes.empty()) //not enough targets
break;
lightningHex = BattleHex::getClosestTile (stack->attackerOwned, destination, possibleHexes);
}
}
else if (getLevelInfo(spellLvl).range.size() > 1) //custom many-hex range
{
for(BattleHex hex : attackedHexes)
{
if(const CStack * st = cb->battleGetStackByPos(hex, ti.onlyAlive))
{
attackedCres.insert(st);
}
}
}
else if(getTargetType() == CSpell::CREATURE)
{
auto predicate = [=](const CStack * s){
const bool positiveToAlly = isPositive() && s->owner == casterColor;
const bool negativeToEnemy = isNegative() && s->owner != casterColor;
const bool validTarget = s->isValidTarget(!ti.onlyAlive); //todo: this should be handled by spell class
//for single target spells select stacks covering destination tile
const bool rangeCovers = ti.massive || s->coversPos(destination);
//handle smart targeting
const bool positivenessFlag = !ti.smart || isNeutral() || positiveToAlly || negativeToEnemy;
return rangeCovers && positivenessFlag && validTarget;
};
TStacks stacks = cb->battleGetStacksIf(predicate);
if (ti.massive)
{
//for massive spells add all targets
for (auto stack : stacks)
attackedCres.insert(stack);
}
else
{
//for single target spells we must select one target. Alive stack is preferred (issue #1763)
for(auto stack : stacks)
{
if(stack->alive())
{
attackedCres.insert(stack);
break;
}
}
if(attackedCres.empty() && !stacks.empty())
{
attackedCres.insert(stacks.front());
}
}
}
else //custom range from attackedHexes
{
for(BattleHex hex : attackedHexes)
{
if(const CStack * st = cb->battleGetStackByPos(hex, ti.onlyAlive))
attackedCres.insert(st);
}
}
//now handle immunities
auto predicate = [&, this](const CStack * s)->bool
{
bool hitDirectly = ti.alwaysHitDirectly && s->coversPos(destination);
bool notImmune = (ESpellCastProblem::OK == isImmuneByStack(caster, s));
return !(hitDirectly || notImmune);
};
vstd::erase_if(attackedCres, predicate);
return attackedCres;
}
CSpell::ETargetType CSpell::getTargetType() const CSpell::ETargetType CSpell::getTargetType() const
{ {
return targetType; return targetType;
} }
const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
{ {
TargetInfo info; TargetInfo info;
@ -395,10 +514,28 @@ const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
info.smart = levelInfo.smartTarget; info.smart = levelInfo.smartTarget;
info.massive = levelInfo.range == "X"; info.massive = levelInfo.range == "X";
info.onlyAlive = !isRisingSpell(); info.onlyAlive = !isRisingSpell();
info.alwaysHitDirectly = false;
return info; return info;
} }
CSpell::TargetInfo CSpell::getTargetInfoEx(const int level, ECastingMode::ECastingMode mode) const
{
TargetInfo info = getTargetInfo(level);
if(mode == ECastingMode::ENCHANTER_CASTING)
{
info.smart = true; //FIXME: not sure about that, this makes all spells smart in this mode
info.massive = true;
}
else if(mode == ECastingMode::SPELL_LIKE_ATTACK)
{
info.alwaysHitDirectly = true;
}
return info;
}
bool CSpell::isCombatSpell() const bool CSpell::isCombatSpell() const
{ {
@ -595,9 +732,9 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj)
return ESpellCastProblem::NOT_DECIDED; return ESpellCastProblem::NOT_DECIDED;
} }
ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) const ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const
{ {
const auto immuneResult = mechanics->isImmuneByStack(caster,mode,obj); const auto immuneResult = mechanics->isImmuneByStack(caster,obj);
if (ESpellCastProblem::NOT_DECIDED != immuneResult) if (ESpellCastProblem::NOT_DECIDED != immuneResult)
return immuneResult; return immuneResult;
@ -649,7 +786,7 @@ void CSpell::setupMechanics()
switch (id) switch (id)
{ {
case SpellID::CLONE: case SpellID::CLONE:
mechanics = new CloneMechnics(this); mechanics = new CloneMechanics(this);
break; break;
case SpellID::DISPEL_HELPFUL_SPELLS: case SpellID::DISPEL_HELPFUL_SPELLS:
mechanics = new DispellHelpfulMechanics(this); mechanics = new DispellHelpfulMechanics(this);
@ -660,6 +797,8 @@ void CSpell::setupMechanics()
default: default:
if(isRisingSpell()) if(isRisingSpell())
mechanics = new SpecialRisingSpellMechanics(this); mechanics = new SpecialRisingSpellMechanics(this);
else if(isOffensiveSpell())
mechanics = new OffenciveSpellMechnics(this);
else else
mechanics = new DefaultSpellMechanics(this); mechanics = new DefaultSpellMechanics(this);
break; break;

View File

@ -4,6 +4,7 @@
#include "../lib/ConstTransitivePtr.h" #include "../lib/ConstTransitivePtr.h"
#include "int3.h" #include "int3.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "BattleHex.h"
#include "HeroBonus.h" #include "HeroBonus.h"
@ -17,12 +18,16 @@
* *
*/ */
class CLegacyConfigParser;
struct BattleHex;
class CSpell; class CSpell;
class ISpellMechanics;
class CLegacyConfigParser;
class CGHeroInstance; class CGHeroInstance;
class CStack; class CStack;
class CBattleInfoCallback;
struct CPackForClient; struct CPackForClient;
struct SpellSchoolInfo struct SpellSchoolInfo
@ -73,28 +78,19 @@ struct DLL_LINKAGE SpellCastContext
{ {
public: public:
SpellCastEnvironment * env; SpellCastEnvironment * env;
int spellLvl;
// BattleHex destination;
ui8 casterSide;
PlayerColor casterColor;
CGHeroInstance * caster;
CGHeroInstance * secHero;
int usedSpellPower;
ECastingMode::ECastingMode mode;
CStack * targetStack;
CStack * selectedStack;
}; };
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;
protected:
CSpell * owner;
};
class DLL_LINKAGE CSpell class DLL_LINKAGE CSpell
{ {
@ -137,6 +133,8 @@ public:
bool smart; bool smart;
bool massive; bool massive;
bool onlyAlive; bool onlyAlive;
///no immunity on primary target (mostly spell-like attack)
bool alwaysHitDirectly;
}; };
SpellID id; SpellID id;
@ -168,7 +166,8 @@ public:
si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none) si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none)
ETargetType getTargetType() const; //deprecated ETargetType getTargetType() const; //deprecated
const CSpell::TargetInfo getTargetInfo(const int level) const; CSpell::TargetInfo getTargetInfo(const int level) const;
CSpell::TargetInfo getTargetInfoEx(const int level, ECastingMode::ECastingMode mode) const;
bool isCombatSpell() const; bool isCombatSpell() const;
bool isAdventureSpell() const; bool isAdventureSpell() const;
@ -192,7 +191,7 @@ public:
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const; ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
//checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc. //checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc.
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) const; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const;
//internal, for use only by Mechanics classes. applying secondary skills //internal, for use only by Mechanics classes. applying secondary skills
ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const; ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
@ -202,7 +201,8 @@ public:
///calculate healed HP for all spells casted by hero ///calculate healed HP for all spells casted by hero
ui32 calculateHealedHP(const CGHeroInstance * caster, const CStack * stack, const CStack * sacrificedStack = nullptr) const; ui32 calculateHealedHP(const CGHeroInstance * caster, const CStack * stack, const CStack * sacrificedStack = nullptr) const;
///selects from allStacks actually affected stacks
std::set<const CStack *> getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster = nullptr) const;
si32 getCost(const int skillLevel) const; si32 getCost(const int skillLevel) const;
@ -289,6 +289,38 @@ private:
ISpellMechanics * mechanics;//(!) do not serialize ISpellMechanics * mechanics;//(!) do not serialize
}; };
class DLL_LINKAGE ISpellMechanics
{
public:
struct SpellTargetingContext
{
CBattleInfoCallback * cb;
CSpell::TargetInfo ti;
};
public:
ISpellMechanics(CSpell * s);
virtual ~ISpellMechanics(){};
virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0;
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0;
/** \brief
*
* \param
* \return true if no error
*
*/
virtual bool adventureCast(SpellCastContext & context) const = 0;
virtual bool battleCast(SpellCastContext & context) const = 0;
protected:
CSpell * owner;
};
bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door

View File

@ -402,8 +402,12 @@ namespace ESpellCastProblem
namespace ECastingMode namespace ECastingMode
{ {
enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack enum ECastingMode
MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING}; {
HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack
MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING,
SPELL_LIKE_ATTACK
};
} }
namespace EMarketMode namespace EMarketMode

View File

@ -14,15 +14,43 @@
#include "mapObjects/CGHeroInstance.h" #include "mapObjects/CGHeroInstance.h"
#include "BattleState.h" #include "BattleState.h"
#include "NetPacks.h"
///DefaultSpellMechanics ///DefaultSpellMechanics
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj)
std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargetingContext & ctx) const
{
}
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
{ {
//by default use general algorithm //by default use general algorithm
return owner->isImmuneBy(obj); return owner->isImmuneBy(obj);
} }
bool DefaultSpellMechanics::adventureCast(SpellCastContext& context) const
{
return false; //there is no general algorithm for castind adventure spells
}
bool DefaultSpellMechanics::battleCast(SpellCastContext& context) const
{
return false; //todo; DefaultSpellMechanics::battleCast
}
///OffenciveSpellMechnics
bool OffenciveSpellMechnics::battleCast(SpellCastContext& context) const
{
assert(owner->isOffensiveSpell());
//todo:OffenciveSpellMechnics::battleCast
}
///CloneMechanics ///CloneMechanics
ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack * obj) ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance* caster, const CStack * obj) const
{ {
//can't clone already cloned creature //can't clone already cloned creature
if (vstd::contains(obj->state, EBattleStackState::CLONED)) if (vstd::contains(obj->state, EBattleStackState::CLONED))
@ -47,11 +75,11 @@ ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHero
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
} }
//use default algorithm only if there is no mechanics-related problem //use default algorithm only if there is no mechanics-related problem
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj); return DefaultSpellMechanics::isImmuneByStack(caster,obj);
} }
///DispellHelpfulMechanics ///DispellHelpfulMechanics
ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const
{ {
TBonusListPtr spellBon = obj->getSpellBonuses(); TBonusListPtr spellBon = obj->getSpellBonuses();
bool hasPositiveSpell = false; bool hasPositiveSpell = false;
@ -69,11 +97,11 @@ ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(co
} }
//use default algorithm only if there is no mechanics-related problem //use default algorithm only if there is no mechanics-related problem
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj); return DefaultSpellMechanics::isImmuneByStack(caster,obj);
} }
///HypnotizeMechanics ///HypnotizeMechanics
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const
{ {
if(nullptr != caster) //do not resist hypnotize casted after attack, for example if(nullptr != caster) //do not resist hypnotize casted after attack, for example
{ {
@ -85,12 +113,12 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const C
if (subjectHealth > maxHealth) if (subjectHealth > maxHealth)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
} }
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj); return DefaultSpellMechanics::isImmuneByStack(caster,obj);
} }
///SpecialRisingSpellMechanics ///SpecialRisingSpellMechanics
ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const
{ {
// following does apply to resurrect and animate dead(?) only // following does apply to resurrect and animate dead(?) only
// for sacrifice health calculation and health limit check don't matter // for sacrifice health calculation and health limit check don't matter
@ -105,7 +133,7 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStac
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
} }
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj); return DefaultSpellMechanics::isImmuneByStack(caster,obj);
} }

View File

@ -16,32 +16,42 @@ class DefaultSpellMechanics: public ISpellMechanics
{ {
public: public:
DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
bool adventureCast(SpellCastContext & context) override; std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override;
bool battleCast(SpellCastContext & context) override;
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
bool adventureCast(SpellCastContext & context) const override;
bool battleCast(SpellCastContext & context) const override;
}; };
class CloneMechnics: public DefaultSpellMechanics class OffenciveSpellMechnics: public DefaultSpellMechanics
{ {
public: public:
CloneMechnics(CSpell * s): DefaultSpellMechanics(s){}; OffenciveSpellMechnics(CSpell * s): DefaultSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override; bool battleCast(SpellCastContext & context) const override;
};
class CloneMechanics: public DefaultSpellMechanics
{
public:
CloneMechanics(CSpell * s): DefaultSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
}; };
class DispellHelpfulMechanics: public DefaultSpellMechanics class DispellHelpfulMechanics: public DefaultSpellMechanics
{ {
public: public:
DispellHelpfulMechanics(CSpell * s): DefaultSpellMechanics(s){}; DispellHelpfulMechanics(CSpell * s): DefaultSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
}; };
class HypnotizeMechanics: public DefaultSpellMechanics class HypnotizeMechanics: public DefaultSpellMechanics
{ {
public: public:
HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){}; HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
}; };
///all rising spells ///all rising spells
class RisingSpellMechanics: public DefaultSpellMechanics class RisingSpellMechanics: public DefaultSpellMechanics
@ -56,7 +66,7 @@ class SpecialRisingSpellMechanics: public RisingSpellMechanics
{ {
public: public:
SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){}; SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
}; };
class SacrificeMechanics: public RisingSpellMechanics class SacrificeMechanics: public RisingSpellMechanics

View File

@ -787,7 +787,8 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
bat.bsa.front().flags |= BattleStackAttacked::EFFECT; bat.bsa.front().flags |= BattleStackAttacked::EFFECT;
bat.bsa.front().effect = VLC->spellh->objects.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other effect? bat.bsa.front().effect = VLC->spellh->objects.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other effect?
std::set<const CStack*> attackedCreatures = gs->curB->getAffectedCreatures(SpellID(bonus->subtype).toSpell(), bonus->val, att->owner, targetHex); std::set<const CStack*> attackedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att->owner, bonus->val, targetHex);
//TODO: get exact attacked hex for defender //TODO: get exact attacked hex for defender
for(const CStack * stack : attackedCreatures) for(const CStack * stack : attackedCreatures)
@ -4018,31 +4019,9 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
//must be vector, as in Chain Lightning order matters //must be vector, as in Chain Lightning order matters
std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
if (mode != ECastingMode::ENCHANTER_CASTING) auto creatures = spell->getAffectedStacks(gs->curB, mode, casterColor, spellLvl, destination, caster);
{ std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
auto creatures = gs->curB->getAffectedCreatures(spell, spellLvl, casterColor, destination);
std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
}
else //enchanter - hit all possible stacks
{
for (const CStack * stack : gs->curB->stacks)
{
/*if it's non negative spell and our unit or non positive spell and hostile unit */
if((!spell->isNegative() && stack->owner == casterColor)
|| (!spell->isPositive() && stack->owner != casterColor))
{
if(stack->isValidTarget()) //TODO: allow dead targets somewhere in the future
{
attackedCres.push_back(stack);
}
}
}
}
vstd::erase_if(attackedCres,[=](const CStack * s){
return ESpellCastProblem::OK != spell->isImmuneByStack(caster,mode,s);
});
for (auto cre : attackedCres) for (auto cre : attackedCres)
{ {
sc.affectedCres.insert (cre->ID); sc.affectedCres.insert (cre->ID);
@ -4453,7 +4432,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
{ {
if(battleStack->owner == gs->curB->sides.at(casterSide).color) //get enemy stacks which can be affected by this spell if(battleStack->owner == gs->curB->sides.at(casterSide).color) //get enemy stacks which can be affected by this spell
{ {
if (ESpellCastProblem::OK == spell->isImmuneByStack(nullptr, ECastingMode::MAGIC_MIRROR, battleStack)) if (ESpellCastProblem::OK == spell->isImmuneByStack(nullptr, battleStack))
mirrorTargets.push_back(battleStack); mirrorTargets.push_back(battleStack);
} }
} }