|
|
@@ -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};
|
|
|
-
|
|
|
- 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);
|
|
|
+ ISpellMechanics::SpellTargetingContext ctx(this, cb,mode,casterColor,spellLvl,destination);
|
|
|
|
|
|
- }
|
|
|
- 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;
|