mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Reworked spell target existence check.
* related to 2269
This commit is contained in:
parent
4cd264ef86
commit
f3b7fe947c
@ -1661,79 +1661,14 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
||||
if(!spell->combatSpell)
|
||||
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
||||
|
||||
const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCast(this, caster);
|
||||
const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCast(this, mode, caster);
|
||||
|
||||
if(specificProblem != ESpellCastProblem::OK)
|
||||
return specificProblem;
|
||||
|
||||
if(spell->isNegative() || spell->hasEffects())
|
||||
{
|
||||
bool allStacksImmune = true;
|
||||
//we are interested only in enemy stacks when casting offensive spells
|
||||
//TODO: should hero be able to cast non-smart negative spell if all enemy stacks are immune?
|
||||
auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
|
||||
for(auto stack : stacks)
|
||||
{
|
||||
if(ESpellCastProblem::OK == spell->isImmuneByStack(caster, stack))
|
||||
{
|
||||
allStacksImmune = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allStacksImmune)
|
||||
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
||||
}
|
||||
|
||||
if(battleMaxSpellLevel(side) < spell->level) //effect like Recanter's Cloak or Orb of Inhibition
|
||||
return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
|
||||
|
||||
//checking if there exists an appropriate target
|
||||
switch(spell->getTargetType())
|
||||
{
|
||||
case CSpell::CREATURE:
|
||||
if(mode == ECastingMode::HERO_CASTING)
|
||||
{
|
||||
const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell));
|
||||
bool targetExists = false;
|
||||
|
||||
for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway
|
||||
{
|
||||
bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack);
|
||||
bool casterStack = stack->owner == caster->getOwner();
|
||||
|
||||
if(!immune)
|
||||
{
|
||||
switch (spell->positiveness)
|
||||
{
|
||||
case CSpell::POSITIVE:
|
||||
if(casterStack || !ti.smart)
|
||||
{
|
||||
targetExists = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CSpell::NEUTRAL:
|
||||
targetExists = true;
|
||||
break;
|
||||
case CSpell::NEGATIVE:
|
||||
if(!casterStack || !ti.smart)
|
||||
{
|
||||
targetExists = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!targetExists)
|
||||
{
|
||||
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
|
@ -361,6 +361,11 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattl
|
||||
return ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
bool EarthquakeMechanics::requiresCreatureTarget() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
///HypnotizeMechanics
|
||||
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
|
||||
{
|
||||
@ -504,6 +509,23 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, con
|
||||
}
|
||||
}
|
||||
|
||||
bool ObstacleMechanics::requiresCreatureTarget() const
|
||||
{
|
||||
switch(owner->id)
|
||||
{
|
||||
case SpellID::QUICKSAND:
|
||||
return false;
|
||||
case SpellID::LAND_MINE:
|
||||
return true;
|
||||
case SpellID::FORCE_FIELD:
|
||||
return false;
|
||||
case SpellID::FIRE_WALL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
///WallMechanics
|
||||
std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes) const
|
||||
{
|
||||
@ -606,6 +628,11 @@ bool RemoveObstacleMechanics::canRemove(const CObstacleInstance * obstacle, cons
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RemoveObstacleMechanics::requiresCreatureTarget() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
///RisingSpellMechanics
|
||||
HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const
|
||||
{
|
||||
@ -700,6 +727,11 @@ int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SacrificeMechanics::requiresCreatureTarget() const
|
||||
{
|
||||
return false;//canBeCast do all target existence checks
|
||||
}
|
||||
|
||||
///SpecialRisingSpellMechanics
|
||||
ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const
|
||||
{
|
||||
@ -774,6 +806,11 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, const
|
||||
env->complain("Summoning didn't summon any!");
|
||||
}
|
||||
|
||||
bool SummonMechanics::requiresCreatureTarget() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
///TeleportMechanics
|
||||
void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
|
@ -81,6 +81,7 @@ class DLL_LINKAGE EarthquakeMechanics : public DefaultSpellMechanics
|
||||
public:
|
||||
EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
||||
bool requiresCreatureTarget() const override;
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
@ -97,6 +98,7 @@ class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics
|
||||
public:
|
||||
ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override;
|
||||
bool requiresCreatureTarget() const override;
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
@ -114,6 +116,7 @@ public:
|
||||
RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override;
|
||||
bool requiresCreatureTarget() const override;
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
private:
|
||||
@ -135,6 +138,7 @@ public:
|
||||
SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
||||
bool requiresCreatureTarget() const override;
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
@ -155,6 +159,7 @@ public:
|
||||
SummonMechanics(CSpell * s, CreatureID cre): DefaultSpellMechanics(s), creatureToSummon(cre){};
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
||||
bool requiresCreatureTarget() const override;
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
private:
|
||||
|
@ -784,3 +784,10 @@ void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& p
|
||||
sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
|
||||
sc.manaGained = 0;
|
||||
}
|
||||
|
||||
bool DefaultSpellMechanics::requiresCreatureTarget() const
|
||||
{
|
||||
//most spells affects creatures somehow regardless of Target Type
|
||||
//for few exceptions see overrides
|
||||
return true;
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ public:
|
||||
|
||||
void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override;
|
||||
|
||||
bool requiresCreatureTarget() const override;
|
||||
protected:
|
||||
virtual void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
||||
|
||||
|
@ -149,9 +149,61 @@ ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affecte
|
||||
return adjustRawDamage(caster, affectedCreature, calculateRawEffectValue(spellSchoolLevel, usedSpellPower));
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const
|
||||
ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster) const
|
||||
{
|
||||
return mechanics->canBeCast(cb, caster);
|
||||
const ESpellCastProblem::ESpellCastProblem generalProblem = mechanics->canBeCast(cb, caster);
|
||||
|
||||
if(generalProblem != ESpellCastProblem::OK)
|
||||
return generalProblem;
|
||||
|
||||
//check for creature target existence
|
||||
if(mechanics->requiresCreatureTarget())
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
case ECastingMode::HERO_CASTING:
|
||||
case ECastingMode::CREATURE_ACTIVE_CASTING:
|
||||
case ECastingMode::ENCHANTER_CASTING:
|
||||
case ECastingMode::PASSIVE_CASTING:
|
||||
{
|
||||
TargetInfo tinfo(this, caster->getSpellSchoolLevel(this), mode);
|
||||
|
||||
bool targetExists = false;
|
||||
|
||||
for(const CStack * stack : cb->battleGetAllStacks())
|
||||
{
|
||||
bool immune = !(stack->isValidTarget(!tinfo.onlyAlive) && ESpellCastProblem::OK == isImmuneByStack(caster, stack));
|
||||
bool casterStack = stack->owner == caster->getOwner();
|
||||
|
||||
if(!immune)
|
||||
{
|
||||
switch (positiveness)
|
||||
{
|
||||
case CSpell::POSITIVE:
|
||||
if(casterStack || !tinfo.smart)
|
||||
targetExists = true;
|
||||
break;
|
||||
case CSpell::NEUTRAL:
|
||||
targetExists = true;
|
||||
break;
|
||||
case CSpell::NEGATIVE:
|
||||
if(!casterStack || !tinfo.smart)
|
||||
targetExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(targetExists)
|
||||
break;
|
||||
}
|
||||
if(!targetExists)
|
||||
{
|
||||
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
||||
|
@ -266,8 +266,8 @@ public:
|
||||
public:
|
||||
///internal interface (for callbacks)
|
||||
|
||||
///Checks general but spell-specific problems for all casting modes. Use only during battle.
|
||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const;
|
||||
///Checks general but spell-specific problems. Use only during battle.
|
||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster) const;
|
||||
|
||||
///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc.
|
||||
ESpellCastProblem::ESpellCastProblem canBeCastAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const;
|
||||
|
@ -111,6 +111,9 @@ public:
|
||||
virtual void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0;
|
||||
|
||||
//if true use generic algorithm for target existence check, see CSpell::canBeCast
|
||||
virtual bool requiresCreatureTarget() const = 0;
|
||||
|
||||
static std::unique_ptr<ISpellMechanics> createMechanics(CSpell * s);
|
||||
protected:
|
||||
CSpell * owner;
|
||||
|
Loading…
Reference in New Issue
Block a user