1
0
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:
AlexVinS 2016-09-05 11:36:25 +03:00
parent 4cd264ef86
commit f3b7fe947c
8 changed files with 111 additions and 70 deletions

View File

@ -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;
}

View File

@ -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
{

View File

@ -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:

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;