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:
parent
5ba53da9bf
commit
a06dae1f96
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 ¢er, const int3 &pos); //for spells like Dimension Door
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user