mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Move getAffectedCreatures to CSpell. + more drafts
This commit is contained in:
		| @@ -458,11 +458,7 @@ void CBattleAI::attemptCastingSpell() | ||||
| 			{ | ||||
| 				int damageDealt = 0, damageReceived = 0; | ||||
| 				 | ||||
| 				auto stacksSuffering = cb->getAffectedCreatures(ps.spell, skillLevel, playerID, ps.dest); | ||||
| 				vstd::erase_if(stacksSuffering, [&](const CStack * s) -> bool | ||||
| 				{ | ||||
| 					return  ESpellCastProblem::OK !=  ps.spell->isImmuneByStack(hero, ECastingMode::HERO_CASTING, s); | ||||
| 				}); | ||||
| 				auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, playerID, skillLevel, ps.dest, hero); | ||||
|  | ||||
| 				if(stacksSuffering.empty()) | ||||
| 					return -1; | ||||
|   | ||||
| @@ -1582,7 +1582,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C | ||||
| 		 | ||||
| 		for(auto s : stacks) | ||||
| 		{ | ||||
| 			ESpellCastProblem::ESpellCastProblem res = spell->isImmuneByStack(caster,mode,s); | ||||
| 			ESpellCastProblem::ESpellCastProblem res = spell->isImmuneByStack(caster,s); | ||||
| 			 | ||||
| 			if(res == ESpellCastProblem::OK) | ||||
| 			{ | ||||
| @@ -1661,7 +1661,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell | ||||
| 		auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks(); | ||||
| 		for(auto stack : stacks) | ||||
| 		{ | ||||
| 			if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, mode, stack)) | ||||
| 			if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, stack)) | ||||
| 			{ | ||||
| 				allStacksImmune = false; | ||||
| 				break; | ||||
| @@ -1705,7 +1705,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell | ||||
|  | ||||
|             for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway | ||||
| 			{ | ||||
| 				bool immune =  ESpellCastProblem::OK != spell->isImmuneByStack(caster, mode, stack); | ||||
| 				bool immune =  ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack); | ||||
| 				bool casterStack = stack->owner == caster->getOwner(); | ||||
| 				 | ||||
|                 if(spell->id == SpellID::SACRIFICE) | ||||
| @@ -1763,8 +1763,6 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor | ||||
| 	std::vector<BattleHex> ret; | ||||
| 	RETURN_IF_NOT_BATTLE(ret); | ||||
|  | ||||
| 	auto mode = ECastingMode::HERO_CASTING; //TODO get rid of this! | ||||
|  | ||||
| 	switch(spell->getTargetType()) | ||||
| 	{ | ||||
| 	case CSpell::CREATURE: | ||||
| @@ -1774,7 +1772,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor | ||||
| 			 | ||||
| 			for(const CStack * stack : battleAliveStacks()) | ||||
| 			{ | ||||
| 				bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, mode, stack); | ||||
| 				bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack); | ||||
| 				bool casterStack = stack->owner == caster->getOwner(); | ||||
| 				 | ||||
| 				if(!immune) | ||||
| @@ -1908,117 +1906,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell | ||||
| 		return battleIsImmune(nullptr, spell, mode, dest); | ||||
| } | ||||
|  | ||||
| std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell * spell, int skillLevel, PlayerColor attackerOwner, BattleHex destinationTile) | ||||
| { | ||||
| 	std::set<const CStack*> attackedCres; //std::set to exclude multiple occurrences of two hex creatures | ||||
|  | ||||
| 	const ui8 attackerSide = playerToSide(attackerOwner) == 1; | ||||
| 	const auto attackedHexes = spell->rangeInHexes(destinationTile, skillLevel, attackerSide); | ||||
| 	 | ||||
| 	const CSpell::TargetInfo ti = spell->getTargetInfo(skillLevel); | ||||
| 	//TODO: more generic solution for mass spells | ||||
| 	if (spell->id == SpellID::CHAIN_LIGHTNING) | ||||
| 	{ | ||||
| 		std::set<BattleHex> possibleHexes; | ||||
| 		for (auto stack : battleGetAllStacks()) | ||||
| 		{ | ||||
| 			if (stack->isValidTarget()) | ||||
| 			{ | ||||
| 				for (auto hex : stack->getHexes()) | ||||
| 				{ | ||||
| 					possibleHexes.insert (hex); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		int targetsOnLevel[4] = {4, 4, 5, 5}; | ||||
|  | ||||
| 		BattleHex lightningHex =  destinationTile; | ||||
| 		for (int i = 0; i < targetsOnLevel[skillLevel]; ++i) | ||||
| 		{ | ||||
| 			auto stack = battleGetStackByPos (lightningHex, true); | ||||
| 			if (!stack) | ||||
| 				break; | ||||
| 			attackedCres.insert (stack); | ||||
| 			for (auto hex : stack->getHexes()) | ||||
| 			{ | ||||
| 				possibleHexes.erase (hex); //can't hit same place twice | ||||
| 			} | ||||
| 			if (possibleHexes.empty()) //not enough targets | ||||
| 				break; | ||||
| 			lightningHex = BattleHex::getClosestTile (stack->attackerOwned, destinationTile, possibleHexes); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (spell->getLevelInfo(skillLevel).range.size() > 1) //custom many-hex range | ||||
| 	{ | ||||
| 		for(BattleHex hex : attackedHexes) | ||||
| 		{ | ||||
| 			if(const CStack * st = battleGetStackByPos(hex, ti.onlyAlive)) | ||||
| 			{ | ||||
| 				if (spell->id == SpellID::DEATH_CLOUD) //Death Cloud //TODO: fireball and fire immunity | ||||
| 				{ | ||||
| 					if (st->isLiving() || st->coversPos(destinationTile)) //directly hit or alive | ||||
| 					{ | ||||
| 						attackedCres.insert(st); | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 					attackedCres.insert(st); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else if(spell->getTargetType() == CSpell::CREATURE) | ||||
| 	{ | ||||
| 		auto predicate = [=](const CStack * s){ | ||||
| 			const bool positiveToAlly = spell->isPositive() && s->owner == attackerOwner; | ||||
| 			const bool negativeToEnemy = spell->isNegative() && s->owner != attackerOwner; | ||||
| 			const bool validTarget = s->isValidTarget(!ti.onlyAlive); //todo: this should be handled by spell class | ||||
| 	 | ||||
| 			//for single target spells select stacks covering destination tile | ||||
| 			const bool rangeCovers = ti.massive || s->coversPos(destinationTile); | ||||
| 			//handle smart targeting | ||||
| 			const bool positivenessFlag = !ti.smart || spell->isNeutral() || positiveToAlly || negativeToEnemy; | ||||
| 			 | ||||
| 			return rangeCovers  && positivenessFlag && validTarget;		 | ||||
| 		}; | ||||
| 		 | ||||
| 		TStacks stacks = battleGetStacksIf(predicate); | ||||
| 		 | ||||
| 		if (ti.massive) | ||||
| 		{ | ||||
| 			//for massive spells add all targets | ||||
| 			for (auto stack : stacks) | ||||
| 				attackedCres.insert(stack); | ||||
|  | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			//for single target spells we must select one target. Alive stack is preferred (issue #1763) | ||||
| 			for(auto stack : stacks) | ||||
| 			{ | ||||
| 				if(stack->alive()) | ||||
| 				{ | ||||
| 					attackedCres.insert(stack); | ||||
| 					break; | ||||
| 				}				 | ||||
| 			}	 | ||||
| 			 | ||||
| 			if(attackedCres.empty() && !stacks.empty()) | ||||
| 			{ | ||||
| 				attackedCres.insert(stacks.front()); | ||||
| 			}						 | ||||
| 		} | ||||
| 	} | ||||
| 	else //custom range from attackedHexes | ||||
| 	{ | ||||
| 		for(BattleHex hex : attackedHexes) | ||||
| 		{ | ||||
| 			if(const CStack * st = battleGetStackByPos(hex, ti.onlyAlive)) | ||||
| 				attackedCres.insert(st); | ||||
| 		} | ||||
| 	} | ||||
| 	return attackedCres; | ||||
| } | ||||
|  | ||||
| const CStack * CBattleInfoCallback::getStackIf(std::function<bool(const CStack*)> pred) const | ||||
| { | ||||
| 	RETURN_IF_NOT_BATTLE(nullptr); | ||||
|   | ||||
| @@ -283,7 +283,6 @@ public: | ||||
| 	std::vector<BattleHex> battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const; | ||||
| 	ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel | ||||
| 	ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //healing spells casted by stacks | ||||
| 	std::set<const CStack*> getAffectedCreatures(const CSpell * s, int skillLevel, PlayerColor attackerOwner, BattleHex destinationTile); //calculates stack affected by given spell | ||||
|  | ||||
| 	SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const; | ||||
| 	SpellID getRandomBeneficialSpell(const CStack * subject) const; | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
|  | ||||
| #include "mapObjects/CGHeroInstance.h" | ||||
| #include "BattleState.h" | ||||
| #include "CBattleCallback.h" | ||||
|  | ||||
| #include "SpellMechanics.h" | ||||
|  | ||||
| @@ -380,12 +381,130 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| std::set<const CStack* > CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster) const | ||||
| { | ||||
| 	std::set<const CStack* > attackedCres;//std::set to exclude multiple occurrences of two hex creatures | ||||
| 	 | ||||
| 	const ui8 attackerSide = cb->playerToSide(casterColor) == 1; | ||||
| 	const auto attackedHexes = rangeInHexes(destination, spellLvl, attackerSide); | ||||
| 	 | ||||
| 	const CSpell::TargetInfo ti = getTargetInfoEx(spellLvl, mode); | ||||
| 	 | ||||
| 	 | ||||
| 	//TODO: more generic solution for mass spells | ||||
| 	if (id == SpellID::CHAIN_LIGHTNING) | ||||
| 	{ | ||||
| 		std::set<BattleHex> possibleHexes; | ||||
| 		for (auto stack : cb->battleGetAllStacks()) | ||||
| 		{ | ||||
| 			if (stack->isValidTarget()) | ||||
| 			{ | ||||
| 				for (auto hex : stack->getHexes()) | ||||
| 				{ | ||||
| 					possibleHexes.insert (hex); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		int targetsOnLevel[4] = {4, 4, 5, 5}; | ||||
|  | ||||
| 		BattleHex lightningHex =  destination; | ||||
| 		for (int i = 0; i < targetsOnLevel[spellLvl]; ++i) | ||||
| 		{ | ||||
| 			auto stack = cb->battleGetStackByPos (lightningHex, true); | ||||
| 			if (!stack) | ||||
| 				break; | ||||
| 			attackedCres.insert (stack); | ||||
| 			for (auto hex : stack->getHexes()) | ||||
| 			{ | ||||
| 				possibleHexes.erase (hex); //can't hit same place twice | ||||
| 			} | ||||
| 			if (possibleHexes.empty()) //not enough targets | ||||
| 				break; | ||||
| 			lightningHex = BattleHex::getClosestTile (stack->attackerOwned, destination, possibleHexes); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (getLevelInfo(spellLvl).range.size() > 1) //custom many-hex range | ||||
| 	{ | ||||
| 		for(BattleHex hex : attackedHexes) | ||||
| 		{ | ||||
| 			if(const CStack * st = cb->battleGetStackByPos(hex, ti.onlyAlive)) | ||||
| 			{ | ||||
| 				attackedCres.insert(st); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else if(getTargetType() == CSpell::CREATURE) | ||||
| 	{ | ||||
| 		auto predicate = [=](const CStack * s){ | ||||
| 			const bool positiveToAlly = isPositive() && s->owner == casterColor; | ||||
| 			const bool negativeToEnemy = isNegative() && s->owner != casterColor; | ||||
| 			const bool validTarget = s->isValidTarget(!ti.onlyAlive); //todo: this should be handled by spell class | ||||
| 	 | ||||
| 			//for single target spells select stacks covering destination tile | ||||
| 			const bool rangeCovers = ti.massive || s->coversPos(destination); | ||||
| 			//handle smart targeting | ||||
| 			const bool positivenessFlag = !ti.smart || isNeutral() || positiveToAlly || negativeToEnemy; | ||||
| 			 | ||||
| 			return rangeCovers  && positivenessFlag && validTarget;		 | ||||
| 		}; | ||||
| 		 | ||||
| 		TStacks stacks = cb->battleGetStacksIf(predicate); | ||||
| 		 | ||||
| 		if (ti.massive) | ||||
| 		{ | ||||
| 			//for massive spells add all targets | ||||
| 			for (auto stack : stacks) | ||||
| 				attackedCres.insert(stack); | ||||
|  | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			//for single target spells we must select one target. Alive stack is preferred (issue #1763) | ||||
| 			for(auto stack : stacks) | ||||
| 			{ | ||||
| 				if(stack->alive()) | ||||
| 				{ | ||||
| 					attackedCres.insert(stack); | ||||
| 					break; | ||||
| 				}				 | ||||
| 			}	 | ||||
| 			 | ||||
| 			if(attackedCres.empty() && !stacks.empty()) | ||||
| 			{ | ||||
| 				attackedCres.insert(stacks.front()); | ||||
| 			}						 | ||||
| 		} | ||||
| 	} | ||||
| 	else //custom range from attackedHexes | ||||
| 	{ | ||||
| 		for(BattleHex hex : attackedHexes) | ||||
| 		{ | ||||
| 			if(const CStack * st = cb->battleGetStackByPos(hex, ti.onlyAlive)) | ||||
| 				attackedCres.insert(st); | ||||
| 		} | ||||
| 	}	 | ||||
| 	 | ||||
| 	//now handle immunities		 | ||||
| 	auto predicate = [&, this](const CStack * s)->bool | ||||
| 	{ | ||||
| 		bool hitDirectly = ti.alwaysHitDirectly && s->coversPos(destination); | ||||
| 		bool notImmune = (ESpellCastProblem::OK == isImmuneByStack(caster, s)); | ||||
| 		 | ||||
| 		return !(hitDirectly || notImmune);   | ||||
| 	}; | ||||
| 	 | ||||
| 	vstd::erase_if(attackedCres, predicate); | ||||
| 	 | ||||
| 	return attackedCres; | ||||
| } | ||||
|  | ||||
|  | ||||
| CSpell::ETargetType CSpell::getTargetType() const | ||||
| { | ||||
| 	return targetType; | ||||
| } | ||||
|  | ||||
| const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const | ||||
| CSpell::TargetInfo CSpell::getTargetInfo(const int level) const | ||||
| { | ||||
| 	TargetInfo info; | ||||
|  | ||||
| @@ -395,6 +514,24 @@ const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const | ||||
| 	info.smart = levelInfo.smartTarget; | ||||
| 	info.massive = levelInfo.range == "X"; | ||||
| 	info.onlyAlive = !isRisingSpell(); | ||||
| 	info.alwaysHitDirectly = false; | ||||
|  | ||||
| 	return info; | ||||
| } | ||||
|  | ||||
| CSpell::TargetInfo CSpell::getTargetInfoEx(const int level, ECastingMode::ECastingMode mode) const | ||||
| { | ||||
| 	TargetInfo info = getTargetInfo(level); | ||||
|  | ||||
| 	if(mode == ECastingMode::ENCHANTER_CASTING) | ||||
| 	{ | ||||
| 		info.smart = true; //FIXME: not sure about that, this makes all spells smart in this mode | ||||
| 		info.massive = true; | ||||
| 	} | ||||
| 	else if(mode == ECastingMode::SPELL_LIKE_ATTACK) | ||||
| 	{ | ||||
| 		info.alwaysHitDirectly = true; | ||||
| 	} | ||||
| 	 | ||||
| 	return info; | ||||
| } | ||||
| @@ -595,9 +732,9 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) | ||||
| 	return ESpellCastProblem::NOT_DECIDED; | ||||
| } | ||||
|  | ||||
| ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) const | ||||
| ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const | ||||
| { | ||||
| 	const auto immuneResult = mechanics->isImmuneByStack(caster,mode,obj); | ||||
| 	const auto immuneResult = mechanics->isImmuneByStack(caster,obj); | ||||
| 	 | ||||
| 	if (ESpellCastProblem::NOT_DECIDED != immuneResult)  | ||||
| 		return immuneResult; | ||||
| @@ -649,7 +786,7 @@ void CSpell::setupMechanics() | ||||
| 	switch (id) | ||||
| 	{ | ||||
| 	case SpellID::CLONE: | ||||
| 		mechanics = new CloneMechnics(this); | ||||
| 		mechanics = new CloneMechanics(this); | ||||
| 		break; | ||||
| 	case SpellID::DISPEL_HELPFUL_SPELLS: | ||||
| 		mechanics = new DispellHelpfulMechanics(this); | ||||
| @@ -660,6 +797,8 @@ void CSpell::setupMechanics() | ||||
| 	default:		 | ||||
| 		if(isRisingSpell()) | ||||
| 			mechanics = new SpecialRisingSpellMechanics(this); | ||||
| 		else if(isOffensiveSpell()) | ||||
| 			mechanics = new OffenciveSpellMechnics(this); | ||||
| 		else	 | ||||
| 			mechanics = new DefaultSpellMechanics(this); | ||||
| 		break; | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "../lib/ConstTransitivePtr.h" | ||||
| #include "int3.h" | ||||
| #include "GameConstants.h" | ||||
| #include "BattleHex.h" | ||||
| #include "HeroBonus.h" | ||||
|  | ||||
|  | ||||
| @@ -17,12 +18,16 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| class CLegacyConfigParser; | ||||
| struct BattleHex; | ||||
| class CSpell; | ||||
| class ISpellMechanics; | ||||
|  | ||||
| class CLegacyConfigParser; | ||||
|  | ||||
| class CGHeroInstance; | ||||
| class CStack; | ||||
|  | ||||
| class CBattleInfoCallback; | ||||
|  | ||||
| struct CPackForClient; | ||||
|  | ||||
| struct SpellSchoolInfo | ||||
| @@ -73,28 +78,19 @@ struct DLL_LINKAGE SpellCastContext | ||||
| { | ||||
| public: | ||||
| 	SpellCastEnvironment * env; | ||||
| 	 | ||||
| 	int spellLvl; | ||||
| //	BattleHex destination; | ||||
| 	ui8 casterSide; | ||||
| 	PlayerColor casterColor; | ||||
| 	CGHeroInstance * caster; | ||||
| 	CGHeroInstance * secHero; | ||||
| 	int usedSpellPower; | ||||
| 	ECastingMode::ECastingMode mode; | ||||
| 	CStack * targetStack; | ||||
| 	CStack * selectedStack;	 | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE ISpellMechanics | ||||
| { | ||||
| public: | ||||
| 	ISpellMechanics(CSpell * s); | ||||
| 	virtual ~ISpellMechanics(){};	 | ||||
| 	 | ||||
| 	virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) = 0; | ||||
| 	 | ||||
|     /** \brief  | ||||
|      * | ||||
|      * \param  | ||||
|      * \return true if no error | ||||
|      * | ||||
|      */                            | ||||
| 	virtual bool adventureCast(SpellCastContext & context) = 0;  | ||||
| 	virtual bool battleCast(SpellCastContext & context) = 0; 	 | ||||
| 	 | ||||
| protected: | ||||
| 	CSpell * owner;	 | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CSpell | ||||
| { | ||||
| @@ -137,6 +133,8 @@ public: | ||||
| 		bool smart; | ||||
| 		bool massive; | ||||
| 		bool onlyAlive; | ||||
| 		///no immunity on primary target (mostly spell-like attack) | ||||
| 		bool alwaysHitDirectly; | ||||
| 	}; | ||||
|  | ||||
| 	SpellID id; | ||||
| @@ -168,7 +166,8 @@ public: | ||||
| 	si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none) | ||||
| 	ETargetType getTargetType() const; //deprecated | ||||
|  | ||||
| 	const CSpell::TargetInfo getTargetInfo(const int level) const; | ||||
| 	CSpell::TargetInfo getTargetInfo(const int level) const; | ||||
| 	CSpell::TargetInfo getTargetInfoEx(const int level, ECastingMode::ECastingMode mode) const; | ||||
|  | ||||
| 	bool isCombatSpell() const; | ||||
| 	bool isAdventureSpell() const; | ||||
| @@ -192,7 +191,7 @@ public: | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const; | ||||
| 	 | ||||
| 	//checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into acount general problems such as not having spellbook or mana points etc. | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) const; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const; | ||||
| 	 | ||||
| 	//internal, for use only by Mechanics classes. applying secondary skills | ||||
| 	ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const; | ||||
| @@ -202,7 +201,8 @@ public: | ||||
| 	///calculate healed HP for all spells casted by hero | ||||
| 	ui32 calculateHealedHP(const CGHeroInstance * caster, const CStack * stack, const CStack * sacrificedStack = nullptr) const; | ||||
| 	 | ||||
| 	 | ||||
| 	///selects from allStacks actually affected stacks | ||||
| 	std::set<const CStack *> getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster = nullptr) const; | ||||
|  | ||||
| 	si32 getCost(const int skillLevel) const; | ||||
|  | ||||
| @@ -289,6 +289,38 @@ private: | ||||
| 	ISpellMechanics * mechanics;//(!) do not serialize | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE ISpellMechanics | ||||
| { | ||||
| public: | ||||
| 	 | ||||
| 	struct SpellTargetingContext | ||||
| 	{ | ||||
| 		CBattleInfoCallback * cb; | ||||
| 		 | ||||
| 		CSpell::TargetInfo ti; | ||||
| 	}; | ||||
| 	 | ||||
| public: | ||||
| 	ISpellMechanics(CSpell * s); | ||||
| 	virtual ~ISpellMechanics(){};	 | ||||
| 	 | ||||
| 	virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0; | ||||
| 	 | ||||
| 	virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0; | ||||
| 	 | ||||
| 	 | ||||
|     /** \brief  | ||||
|      * | ||||
|      * \param  | ||||
|      * \return true if no error | ||||
|      * | ||||
|      */                            | ||||
| 	virtual bool adventureCast(SpellCastContext & context) const = 0;  | ||||
| 	virtual bool battleCast(SpellCastContext & context) const = 0; 	 | ||||
| 	 | ||||
| protected: | ||||
| 	CSpell * owner;	 | ||||
| }; | ||||
|  | ||||
|  | ||||
| bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spells like Dimension Door | ||||
|   | ||||
| @@ -402,8 +402,12 @@ namespace ESpellCastProblem | ||||
|  | ||||
| namespace ECastingMode | ||||
| { | ||||
| 	enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack | ||||
| 		MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING}; | ||||
| 	enum ECastingMode  | ||||
| 	{ | ||||
| 		HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack | ||||
| 		MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING, | ||||
| 		SPELL_LIKE_ATTACK	 | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| namespace EMarketMode | ||||
|   | ||||
| @@ -14,15 +14,43 @@ | ||||
| #include "mapObjects/CGHeroInstance.h" | ||||
| #include "BattleState.h" | ||||
|  | ||||
| #include "NetPacks.h" | ||||
|  | ||||
| ///DefaultSpellMechanics | ||||
| ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) | ||||
|  | ||||
| std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargetingContext & ctx) const | ||||
| { | ||||
| 	 | ||||
| } | ||||
|  | ||||
|  | ||||
| ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const | ||||
| { | ||||
| 	//by default use general algorithm | ||||
| 	return owner->isImmuneBy(obj); | ||||
| } | ||||
|  | ||||
| bool DefaultSpellMechanics::adventureCast(SpellCastContext& context) const | ||||
| { | ||||
| 	return false; //there is no general algorithm for castind adventure spells | ||||
| } | ||||
|  | ||||
| bool DefaultSpellMechanics::battleCast(SpellCastContext& context) const | ||||
| { | ||||
| 	return false; //todo; DefaultSpellMechanics::battleCast | ||||
| } | ||||
|  | ||||
| ///OffenciveSpellMechnics | ||||
| bool OffenciveSpellMechnics::battleCast(SpellCastContext& context) const | ||||
| { | ||||
| 	assert(owner->isOffensiveSpell()); | ||||
| 	 | ||||
| 	//todo:OffenciveSpellMechnics::battleCast | ||||
| } | ||||
|  | ||||
|  | ||||
| ///CloneMechanics | ||||
| ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack * obj) | ||||
| ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance* caster, const CStack * obj) const | ||||
| { | ||||
| 	//can't clone already cloned creature | ||||
| 	if (vstd::contains(obj->state, EBattleStackState::CLONED)) | ||||
| @@ -47,11 +75,11 @@ ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHero | ||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 	} | ||||
| 	//use default algorithm only if there is no mechanics-related problem		 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);	 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);	 | ||||
| } | ||||
|  | ||||
| ///DispellHelpfulMechanics | ||||
| ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) | ||||
| ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance* caster,  const CStack* obj) const | ||||
| { | ||||
| 	TBonusListPtr spellBon = obj->getSpellBonuses(); | ||||
| 	bool hasPositiveSpell = false; | ||||
| @@ -69,11 +97,11 @@ ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(co | ||||
| 	} | ||||
| 	 | ||||
| 	//use default algorithm only if there is no mechanics-related problem		 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);	 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);	 | ||||
| } | ||||
|  | ||||
| ///HypnotizeMechanics | ||||
| ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) | ||||
| ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const | ||||
| { | ||||
| 	if(nullptr != caster) //do not resist hypnotize casted after attack, for example | ||||
| 	{ | ||||
| @@ -85,12 +113,12 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const C | ||||
| 		if (subjectHealth > maxHealth) | ||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 	}			 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj); | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,obj); | ||||
| } | ||||
|  | ||||
|  | ||||
| ///SpecialRisingSpellMechanics | ||||
| ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance* caster, ECastingMode::ECastingMode mode, const CStack* obj) | ||||
| ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const | ||||
| { | ||||
| 	// following does apply to resurrect and animate dead(?) only | ||||
| 	// for sacrifice health calculation and health limit check don't matter | ||||
| @@ -105,7 +133,7 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStac | ||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 	}	 | ||||
| 	 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);	 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);	 | ||||
| } | ||||
|  | ||||
| 	 | ||||
|   | ||||
| @@ -16,31 +16,41 @@ class DefaultSpellMechanics: public ISpellMechanics | ||||
| { | ||||
| public: | ||||
| 	DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override; | ||||
| 	 | ||||
| 	bool adventureCast(SpellCastContext & context) override;  | ||||
| 	bool battleCast(SpellCastContext & context) override;  | ||||
| 	std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override; | ||||
| 	 | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; | ||||
| 	 | ||||
| 	bool adventureCast(SpellCastContext & context) const override;  | ||||
| 	bool battleCast(SpellCastContext & context) const override;  | ||||
| }; | ||||
|  | ||||
| class CloneMechnics: public DefaultSpellMechanics | ||||
| class OffenciveSpellMechnics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	CloneMechnics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override; | ||||
| 	OffenciveSpellMechnics(CSpell * s): DefaultSpellMechanics(s){};	 | ||||
| 	bool battleCast(SpellCastContext & context) const override; | ||||
| }; | ||||
|  | ||||
| class CloneMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	CloneMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; | ||||
| }; | ||||
|  | ||||
| class DispellHelpfulMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	DispellHelpfulMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;	 | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;	 | ||||
| }; | ||||
|  | ||||
| class HypnotizeMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){};	 | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;	 | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;	 | ||||
| };  | ||||
|  | ||||
| ///all rising spells | ||||
| @@ -56,7 +66,7 @@ class SpecialRisingSpellMechanics: public RisingSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){}; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override;						 | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;						 | ||||
| }; | ||||
|  | ||||
| class SacrificeMechanics: public RisingSpellMechanics | ||||
|   | ||||
| @@ -787,7 +787,8 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt | ||||
| 		bat.bsa.front().flags |= BattleStackAttacked::EFFECT; | ||||
| 		bat.bsa.front().effect = VLC->spellh->objects.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other effect? | ||||
|  | ||||
| 		std::set<const CStack*> attackedCreatures = gs->curB->getAffectedCreatures(SpellID(bonus->subtype).toSpell(), bonus->val, att->owner, targetHex); | ||||
| 		std::set<const CStack*> attackedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att->owner, bonus->val, targetHex); | ||||
| 	 | ||||
| 		//TODO: get exact attacked hex for defender | ||||
|  | ||||
| 		for(const CStack * stack : attackedCreatures) | ||||
| @@ -4018,30 +4019,8 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex | ||||
| 	//must be vector, as in Chain Lightning order matters | ||||
| 	std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector | ||||
|  | ||||
| 	if (mode != ECastingMode::ENCHANTER_CASTING) | ||||
| 	{ | ||||
| 		auto creatures = gs->curB->getAffectedCreatures(spell, spellLvl, casterColor, destination); | ||||
| 	auto creatures = spell->getAffectedStacks(gs->curB, mode, casterColor, spellLvl, destination, caster); | ||||
| 	std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres)); | ||||
| 	} | ||||
| 	else //enchanter - hit all possible stacks | ||||
| 	{ | ||||
| 		for (const CStack * stack : gs->curB->stacks) | ||||
| 		{ | ||||
| 			/*if it's non negative spell and our unit or non positive spell and hostile unit */ | ||||
| 			if((!spell->isNegative() && stack->owner == casterColor) | ||||
| 				|| (!spell->isPositive() && stack->owner != casterColor)) | ||||
| 			{ | ||||
| 				if(stack->isValidTarget()) //TODO: allow dead targets somewhere in the future | ||||
| 				{ | ||||
| 					attackedCres.push_back(stack); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	vstd::erase_if(attackedCres,[=](const CStack * s){ | ||||
| 		return ESpellCastProblem::OK != spell->isImmuneByStack(caster,mode,s);		 | ||||
| 	}); | ||||
| 	 | ||||
| 	for (auto cre : attackedCres) | ||||
| 	{ | ||||
| @@ -4453,7 +4432,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex | ||||
| 				{ | ||||
| 					if(battleStack->owner == gs->curB->sides.at(casterSide).color) //get enemy stacks which can be affected by this spell | ||||
| 					{ | ||||
| 						if (ESpellCastProblem::OK == spell->isImmuneByStack(nullptr, ECastingMode::MAGIC_MIRROR, battleStack)) | ||||
| 						if (ESpellCastProblem::OK == spell->isImmuneByStack(nullptr, battleStack)) | ||||
| 							mirrorTargets.push_back(battleStack); | ||||
| 					} | ||||
| 				} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user