1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +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:
{
int damageDealt = 0, damageReceived = 0;
auto stacksSuffering = cb->getAffectedCreatures(ps.spell, skillLevel, playerID, ps.dest);
vstd::erase_if(stacksSuffering, [&](const CStack * s) -> bool
{
return ESpellCastProblem::OK != ps.spell->isImmuneByStack(hero, ECastingMode::HERO_CASTING, s);
});
auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, playerID, skillLevel, ps.dest, hero);
if(stacksSuffering.empty())
return -1;

View File

@ -1582,7 +1582,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
for(auto s : stacks)
{
ESpellCastProblem::ESpellCastProblem res = spell->isImmuneByStack(caster,mode,s);
ESpellCastProblem::ESpellCastProblem res = spell->isImmuneByStack(caster,s);
if(res == ESpellCastProblem::OK)
{
@ -1661,7 +1661,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
for(auto stack : stacks)
{
if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, mode, stack))
if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, stack))
{
allStacksImmune = false;
break;
@ -1705,7 +1705,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
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();
if(spell->id == SpellID::SACRIFICE)
@ -1763,8 +1763,6 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
std::vector<BattleHex> ret;
RETURN_IF_NOT_BATTLE(ret);
auto mode = ECastingMode::HERO_CASTING; //TODO get rid of this!
switch(spell->getTargetType())
{
case CSpell::CREATURE:
@ -1774,7 +1772,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
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();
if(!immune)
@ -1908,117 +1906,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
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
{
RETURN_IF_NOT_BATTLE(nullptr);

View File

@ -283,7 +283,6 @@ public:
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(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 getRandomBeneficialSpell(const CStack * subject) const;

View File

@ -12,6 +12,7 @@
#include "mapObjects/CGHeroInstance.h"
#include "BattleState.h"
#include "CBattleCallback.h"
#include "SpellMechanics.h"
@ -380,12 +381,130 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
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
{
return targetType;
}
const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
{
TargetInfo info;
@ -395,10 +514,28 @@ const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
info.smart = levelInfo.smartTarget;
info.massive = levelInfo.range == "X";
info.onlyAlive = !isRisingSpell();
info.alwaysHitDirectly = false;
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
{
@ -595,9 +732,9 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj)
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)
return immuneResult;
@ -649,7 +786,7 @@ void CSpell::setupMechanics()
switch (id)
{
case SpellID::CLONE:
mechanics = new CloneMechnics(this);
mechanics = new CloneMechanics(this);
break;
case SpellID::DISPEL_HELPFUL_SPELLS:
mechanics = new DispellHelpfulMechanics(this);
@ -660,6 +797,8 @@ void CSpell::setupMechanics()
default:
if(isRisingSpell())
mechanics = new SpecialRisingSpellMechanics(this);
else if(isOffensiveSpell())
mechanics = new OffenciveSpellMechnics(this);
else
mechanics = new DefaultSpellMechanics(this);
break;

View File

@ -4,6 +4,7 @@
#include "../lib/ConstTransitivePtr.h"
#include "int3.h"
#include "GameConstants.h"
#include "BattleHex.h"
#include "HeroBonus.h"
@ -17,12 +18,16 @@
*
*/
class CLegacyConfigParser;
struct BattleHex;
class CSpell;
class ISpellMechanics;
class CLegacyConfigParser;
class CGHeroInstance;
class CStack;
class CBattleInfoCallback;
struct CPackForClient;
struct SpellSchoolInfo
@ -73,28 +78,19 @@ struct DLL_LINKAGE SpellCastContext
{
public:
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
{
@ -137,6 +133,8 @@ public:
bool smart;
bool massive;
bool onlyAlive;
///no immunity on primary target (mostly spell-like attack)
bool alwaysHitDirectly;
};
SpellID id;
@ -168,7 +166,8 @@ public:
si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none)
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 isAdventureSpell() const;
@ -192,7 +191,7 @@ public:
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.
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
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
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;
@ -289,6 +289,38 @@ private:
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

View File

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

View File

@ -14,15 +14,43 @@
#include "mapObjects/CGHeroInstance.h"
#include "BattleState.h"
#include "NetPacks.h"
///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
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
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
if (vstd::contains(obj->state, EBattleStackState::CLONED))
@ -47,11 +75,11 @@ ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHero
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
//use default algorithm only if there is no mechanics-related problem
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);
return DefaultSpellMechanics::isImmuneByStack(caster,obj);
}
///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();
bool hasPositiveSpell = false;
@ -69,11 +97,11 @@ ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(co
}
//use default algorithm only if there is no mechanics-related problem
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);
return DefaultSpellMechanics::isImmuneByStack(caster,obj);
}
///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
{
@ -85,12 +113,12 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const C
if (subjectHealth > maxHealth)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);
return DefaultSpellMechanics::isImmuneByStack(caster,obj);
}
///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
// 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 DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);
return DefaultSpellMechanics::isImmuneByStack(caster,obj);
}

View File

@ -16,32 +16,42 @@ 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;
std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const 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:
CloneMechnics(CSpell * s): DefaultSpellMechanics(s){};
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;
OffenciveSpellMechnics(CSpell * s): DefaultSpellMechanics(s){};
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
{
public:
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
{
public:
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
class RisingSpellMechanics: public DefaultSpellMechanics
@ -56,7 +66,7 @@ class SpecialRisingSpellMechanics: public RisingSpellMechanics
{
public:
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

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().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
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
std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
if (mode != ECastingMode::ENCHANTER_CASTING)
{
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);
});
auto creatures = spell->getAffectedStacks(gs->curB, mode, casterColor, spellLvl, destination, caster);
std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
for (auto cre : attackedCres)
{
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 (ESpellCastProblem::OK == spell->isImmuneByStack(nullptr, ECastingMode::MAGIC_MIRROR, battleStack))
if (ESpellCastProblem::OK == spell->isImmuneByStack(nullptr, battleStack))
mirrorTargets.push_back(battleStack);
}
}