mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Move getAffectedStacks to mechanics classes
This commit is contained in:
parent
22c251b57d
commit
c7480e7fe5
@ -31,7 +31,6 @@
|
||||
namespace SpellConfig
|
||||
{
|
||||
static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
|
||||
|
||||
}
|
||||
|
||||
///CSpell::LevelInfo
|
||||
@ -176,116 +175,18 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl,
|
||||
|
||||
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(this, 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};
|
||||
ISpellMechanics::SpellTargetingContext ctx(this, cb,mode,casterColor,spellLvl,destination);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
std::set<const CStack* > attackedCres = mechanics->getAffectedStacks(ctx);
|
||||
|
||||
//now handle immunities
|
||||
auto predicate = [&, this](const CStack * s)->bool
|
||||
{
|
||||
bool hitDirectly = ti.alwaysHitDirectly && s->coversPos(destination);
|
||||
bool hitDirectly = ctx.ti.alwaysHitDirectly && s->coversPos(destination);
|
||||
bool notImmune = (ESpellCastProblem::OK == isImmuneByStack(caster, s));
|
||||
|
||||
return !(hitDirectly || notImmune);
|
||||
};
|
||||
|
||||
};
|
||||
vstd::erase_if(attackedCres, predicate);
|
||||
|
||||
return attackedCres;
|
||||
|
@ -208,7 +208,76 @@ std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex,
|
||||
|
||||
std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargetingContext & ctx) const
|
||||
{
|
||||
std::set<const CStack* > attackedCres;//std::set to exclude multiple occurrences of two hex creatures
|
||||
|
||||
const ui8 attackerSide = ctx.cb->playerToSide(ctx.casterColor) == 1;
|
||||
const auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide);
|
||||
|
||||
const CSpell::TargetInfo ti(owner, ctx.schoolLvl, ctx.mode);
|
||||
|
||||
//TODO: more generic solution for mass spells
|
||||
if (owner->getLevelInfo(ctx.schoolLvl).range.size() > 1) //custom many-hex range
|
||||
{
|
||||
for(BattleHex hex : attackedHexes)
|
||||
{
|
||||
if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ti.onlyAlive))
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(ti.type == CSpell::CREATURE)
|
||||
{
|
||||
auto predicate = [=](const CStack * s){
|
||||
const bool positiveToAlly = owner->isPositive() && s->owner == ctx.casterColor;
|
||||
const bool negativeToEnemy = owner->isNegative() && s->owner != ctx.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(ctx.destination);
|
||||
//handle smart targeting
|
||||
const bool positivenessFlag = !ti.smart || owner->isNeutral() || positiveToAlly || negativeToEnemy;
|
||||
|
||||
return rangeCovers && positivenessFlag && validTarget;
|
||||
};
|
||||
|
||||
TStacks stacks = ctx.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 = ctx.cb->battleGetStackByPos(hex, ti.onlyAlive))
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
|
||||
return attackedCres;
|
||||
}
|
||||
|
||||
|
||||
@ -218,6 +287,7 @@ ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(cons
|
||||
return owner->isImmuneBy(obj);
|
||||
}
|
||||
|
||||
///WallMechanics
|
||||
std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool* outDroppedHexes) const
|
||||
{
|
||||
using namespace SRSLPraserHelpers;
|
||||
@ -256,7 +326,42 @@ std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 sch
|
||||
return ret;
|
||||
}
|
||||
|
||||
///ChainLightningMechanics
|
||||
std::set<const CStack *> ChainLightningMechanics::getAffectedStacks(SpellTargetingContext & ctx) const
|
||||
{
|
||||
std::set<const CStack* > attackedCres;
|
||||
|
||||
std::set<BattleHex> possibleHexes;
|
||||
for(auto stack : ctx.cb->battleGetAllStacks())
|
||||
{
|
||||
if(stack->isValidTarget())
|
||||
{
|
||||
for(auto hex : stack->getHexes())
|
||||
{
|
||||
possibleHexes.insert (hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
int targetsOnLevel[4] = {4, 4, 5, 5};
|
||||
|
||||
BattleHex lightningHex = ctx.destination;
|
||||
for(int i = 0; i < targetsOnLevel[ctx.schoolLvl]; ++i)
|
||||
{
|
||||
auto stack = ctx.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, ctx.destination, possibleHexes);
|
||||
}
|
||||
|
||||
return attackedCres;
|
||||
}
|
||||
|
||||
///CloneMechanics
|
||||
ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance* caster, const CStack * obj) const
|
||||
|
@ -11,18 +11,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "CSpellHandler.h"
|
||||
#include "BattleHex.h"
|
||||
|
||||
class DLL_LINKAGE ISpellMechanics
|
||||
{
|
||||
public:
|
||||
|
||||
struct SpellTargetingContext
|
||||
struct DLL_LINKAGE SpellTargetingContext
|
||||
{
|
||||
CBattleInfoCallback * cb;
|
||||
|
||||
const CBattleInfoCallback * cb;
|
||||
CSpell::TargetInfo ti;
|
||||
|
||||
ECastingMode::ECastingMode mode;
|
||||
BattleHex destination;
|
||||
PlayerColor casterColor;
|
||||
int schoolLvl;
|
||||
|
||||
SpellTargetingContext(const CSpell * s, const CBattleInfoCallback * c, ECastingMode::ECastingMode m, PlayerColor cc, int lvl, BattleHex dest)
|
||||
: cb(c), ti(s,lvl, m), mode(m), destination(dest)
|
||||
{};
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
@ -77,7 +84,7 @@ class ChainLightningMechanics: public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
ChainLightningMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
|
||||
std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override;
|
||||
};
|
||||
|
||||
class CloneMechanics: public DefaultSpellMechanics
|
||||
|
Loading…
Reference in New Issue
Block a user