mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-27 00:41:08 +02:00
Move getAffectedCreatures to CSpell. + more drafts
This commit is contained in:
@ -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;
|
||||
|
Reference in New Issue
Block a user