mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Reworked spell target existence check.
* related to 2269
This commit is contained in:
		| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user