瀏覽代碼

Reworked spell target existence check.
* related to 2269

AlexVinS 9 年之前
父節點
當前提交
f3b7fe947c

+ 1 - 66
lib/CBattleCallback.cpp

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

+ 37 - 0
lib/spells/BattleSpellMechanics.cpp

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

+ 5 - 0
lib/spells/BattleSpellMechanics.h

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

+ 7 - 0
lib/spells/CDefaultSpellMechanics.cpp

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

+ 2 - 0
lib/spells/CDefaultSpellMechanics.h

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

+ 54 - 2
lib/spells/CSpellHandler.cpp

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

+ 2 - 2
lib/spells/CSpellHandler.h

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

+ 3 - 0
lib/spells/ISpellMechanics.h

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