mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-14 02:33:51 +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)
|
if(!spell->combatSpell)
|
||||||
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
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)
|
if(specificProblem != ESpellCastProblem::OK)
|
||||||
return specificProblem;
|
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
|
if(battleMaxSpellLevel(side) < spell->level) //effect like Recanter's Cloak or Orb of Inhibition
|
||||||
return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
|
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;
|
return ESpellCastProblem::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,6 +361,11 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattl
|
|||||||
return ESpellCastProblem::OK;
|
return ESpellCastProblem::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EarthquakeMechanics::requiresCreatureTarget() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
///HypnotizeMechanics
|
///HypnotizeMechanics
|
||||||
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const
|
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
|
///WallMechanics
|
||||||
std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes) const
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RemoveObstacleMechanics::requiresCreatureTarget() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
///RisingSpellMechanics
|
///RisingSpellMechanics
|
||||||
HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const
|
HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const
|
||||||
{
|
{
|
||||||
@ -700,6 +727,11 @@ int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SacrificeMechanics::requiresCreatureTarget() const
|
||||||
|
{
|
||||||
|
return false;//canBeCast do all target existence checks
|
||||||
|
}
|
||||||
|
|
||||||
///SpecialRisingSpellMechanics
|
///SpecialRisingSpellMechanics
|
||||||
ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const
|
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!");
|
env->complain("Summoning didn't summon any!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SummonMechanics::requiresCreatureTarget() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
///TeleportMechanics
|
///TeleportMechanics
|
||||||
void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||||
{
|
{
|
||||||
|
@ -81,6 +81,7 @@ class DLL_LINKAGE EarthquakeMechanics : public DefaultSpellMechanics
|
|||||||
public:
|
public:
|
||||||
EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
EarthquakeMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
||||||
|
bool requiresCreatureTarget() const override;
|
||||||
protected:
|
protected:
|
||||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||||
};
|
};
|
||||||
@ -97,6 +98,7 @@ class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics
|
|||||||
public:
|
public:
|
||||||
ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override;
|
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override;
|
||||||
|
bool requiresCreatureTarget() const override;
|
||||||
protected:
|
protected:
|
||||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||||
};
|
};
|
||||||
@ -114,6 +116,7 @@ public:
|
|||||||
RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
||||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override;
|
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override;
|
||||||
|
bool requiresCreatureTarget() const override;
|
||||||
protected:
|
protected:
|
||||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||||
private:
|
private:
|
||||||
@ -135,6 +138,7 @@ public:
|
|||||||
SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};
|
SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};
|
||||||
|
|
||||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
||||||
|
bool requiresCreatureTarget() const override;
|
||||||
protected:
|
protected:
|
||||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||||
int calculateHealedHP(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){};
|
SummonMechanics(CSpell * s, CreatureID cre): DefaultSpellMechanics(s), creatureToSummon(cre){};
|
||||||
|
|
||||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const override;
|
||||||
|
bool requiresCreatureTarget() const override;
|
||||||
protected:
|
protected:
|
||||||
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||||
private:
|
private:
|
||||||
|
@ -784,3 +784,10 @@ void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& p
|
|||||||
sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
|
sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
|
||||||
sc.manaGained = 0;
|
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,
|
void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||||
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override;
|
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override;
|
||||||
|
|
||||||
|
bool requiresCreatureTarget() const override;
|
||||||
protected:
|
protected:
|
||||||
virtual void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
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));
|
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
|
std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
||||||
|
@ -266,8 +266,8 @@ public:
|
|||||||
public:
|
public:
|
||||||
///internal interface (for callbacks)
|
///internal interface (for callbacks)
|
||||||
|
|
||||||
///Checks general but spell-specific problems for all casting modes. Use only during battle.
|
///Checks general but spell-specific problems. Use only during battle.
|
||||||
ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ISpellCaster * caster) const;
|
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.
|
///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;
|
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,
|
virtual void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||||
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0;
|
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);
|
static std::unique_ptr<ISpellMechanics> createMechanics(CSpell * s);
|
||||||
protected:
|
protected:
|
||||||
CSpell * owner;
|
CSpell * owner;
|
||||||
|
Loading…
Reference in New Issue
Block a user