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:
		| @@ -457,12 +457,8 @@ void CBattleAI::attemptCastingSpell() | |||||||
| 		case OFFENSIVE_SPELL: | 		case OFFENSIVE_SPELL: | ||||||
| 			{ | 			{ | ||||||
| 				int damageDealt = 0, damageReceived = 0; | 				int damageDealt = 0, damageReceived = 0; | ||||||
|  | 				 | ||||||
| 				auto stacksSuffering = cb->getAffectedCreatures(ps.spell, skillLevel, playerID, ps.dest); | 				auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, playerID, skillLevel, ps.dest, hero); | ||||||
| 				vstd::erase_if(stacksSuffering, [&](const CStack * s) -> bool |  | ||||||
| 				{ |  | ||||||
| 					return  ESpellCastProblem::OK !=  ps.spell->isImmuneByStack(hero, ECastingMode::HERO_CASTING, s); |  | ||||||
| 				}); |  | ||||||
|  |  | ||||||
| 				if(stacksSuffering.empty()) | 				if(stacksSuffering.empty()) | ||||||
| 					return -1; | 					return -1; | ||||||
|   | |||||||
| @@ -1582,7 +1582,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C | |||||||
| 		 | 		 | ||||||
| 		for(auto s : stacks) | 		for(auto s : stacks) | ||||||
| 		{ | 		{ | ||||||
| 			ESpellCastProblem::ESpellCastProblem res = spell->isImmuneByStack(caster,mode,s); | 			ESpellCastProblem::ESpellCastProblem res = spell->isImmuneByStack(caster,s); | ||||||
| 			 | 			 | ||||||
| 			if(res == ESpellCastProblem::OK) | 			if(res == ESpellCastProblem::OK) | ||||||
| 			{ | 			{ | ||||||
| @@ -1661,7 +1661,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell | |||||||
| 		auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks(); | 		auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks(); | ||||||
| 		for(auto stack : stacks) | 		for(auto stack : stacks) | ||||||
| 		{ | 		{ | ||||||
| 			if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, mode, stack)) | 			if(ESpellCastProblem::OK == spell->isImmuneByStack(castingHero, stack)) | ||||||
| 			{ | 			{ | ||||||
| 				allStacksImmune = false; | 				allStacksImmune = false; | ||||||
| 				break; | 				break; | ||||||
| @@ -1705,7 +1705,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell | |||||||
|  |  | ||||||
|             for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway |             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(); | 				bool casterStack = stack->owner == caster->getOwner(); | ||||||
| 				 | 				 | ||||||
|                 if(spell->id == SpellID::SACRIFICE) |                 if(spell->id == SpellID::SACRIFICE) | ||||||
| @@ -1763,8 +1763,6 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor | |||||||
| 	std::vector<BattleHex> ret; | 	std::vector<BattleHex> ret; | ||||||
| 	RETURN_IF_NOT_BATTLE(ret); | 	RETURN_IF_NOT_BATTLE(ret); | ||||||
|  |  | ||||||
| 	auto mode = ECastingMode::HERO_CASTING; //TODO get rid of this! |  | ||||||
|  |  | ||||||
| 	switch(spell->getTargetType()) | 	switch(spell->getTargetType()) | ||||||
| 	{ | 	{ | ||||||
| 	case CSpell::CREATURE: | 	case CSpell::CREATURE: | ||||||
| @@ -1774,7 +1772,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor | |||||||
| 			 | 			 | ||||||
| 			for(const CStack * stack : battleAliveStacks()) | 			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(); | 				bool casterStack = stack->owner == caster->getOwner(); | ||||||
| 				 | 				 | ||||||
| 				if(!immune) | 				if(!immune) | ||||||
| @@ -1908,117 +1906,6 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell | |||||||
| 		return battleIsImmune(nullptr, spell, mode, dest); | 		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 | const CStack * CBattleInfoCallback::getStackIf(std::function<bool(const CStack*)> pred) const | ||||||
| { | { | ||||||
| 	RETURN_IF_NOT_BATTLE(nullptr); | 	RETURN_IF_NOT_BATTLE(nullptr); | ||||||
|   | |||||||
| @@ -283,7 +283,6 @@ public: | |||||||
| 	std::vector<BattleHex> battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const; | 	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(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 | 	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 battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const; | ||||||
| 	SpellID getRandomBeneficialSpell(const CStack * subject) const; | 	SpellID getRandomBeneficialSpell(const CStack * subject) const; | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
|  |  | ||||||
| #include "mapObjects/CGHeroInstance.h" | #include "mapObjects/CGHeroInstance.h" | ||||||
| #include "BattleState.h" | #include "BattleState.h" | ||||||
|  | #include "CBattleCallback.h" | ||||||
|  |  | ||||||
| #include "SpellMechanics.h" | #include "SpellMechanics.h" | ||||||
|  |  | ||||||
| @@ -380,12 +381,130 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, | |||||||
| 	return ret; | 	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 | CSpell::ETargetType CSpell::getTargetType() const | ||||||
| { | { | ||||||
| 	return targetType; | 	return targetType; | ||||||
| } | } | ||||||
|  |  | ||||||
| const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const | CSpell::TargetInfo CSpell::getTargetInfo(const int level) const | ||||||
| { | { | ||||||
| 	TargetInfo info; | 	TargetInfo info; | ||||||
|  |  | ||||||
| @@ -395,10 +514,28 @@ const CSpell::TargetInfo CSpell::getTargetInfo(const int level) const | |||||||
| 	info.smart = levelInfo.smartTarget; | 	info.smart = levelInfo.smartTarget; | ||||||
| 	info.massive = levelInfo.range == "X"; | 	info.massive = levelInfo.range == "X"; | ||||||
| 	info.onlyAlive = !isRisingSpell(); | 	info.onlyAlive = !isRisingSpell(); | ||||||
|  | 	info.alwaysHitDirectly = false; | ||||||
|  |  | ||||||
| 	return info; | 	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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| bool CSpell::isCombatSpell() const | bool CSpell::isCombatSpell() const | ||||||
| { | { | ||||||
| @@ -595,9 +732,9 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) | |||||||
| 	return ESpellCastProblem::NOT_DECIDED; | 	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)  | 	if (ESpellCastProblem::NOT_DECIDED != immuneResult)  | ||||||
| 		return immuneResult; | 		return immuneResult; | ||||||
| @@ -649,7 +786,7 @@ void CSpell::setupMechanics() | |||||||
| 	switch (id) | 	switch (id) | ||||||
| 	{ | 	{ | ||||||
| 	case SpellID::CLONE: | 	case SpellID::CLONE: | ||||||
| 		mechanics = new CloneMechnics(this); | 		mechanics = new CloneMechanics(this); | ||||||
| 		break; | 		break; | ||||||
| 	case SpellID::DISPEL_HELPFUL_SPELLS: | 	case SpellID::DISPEL_HELPFUL_SPELLS: | ||||||
| 		mechanics = new DispellHelpfulMechanics(this); | 		mechanics = new DispellHelpfulMechanics(this); | ||||||
| @@ -660,6 +797,8 @@ void CSpell::setupMechanics() | |||||||
| 	default:		 | 	default:		 | ||||||
| 		if(isRisingSpell()) | 		if(isRisingSpell()) | ||||||
| 			mechanics = new SpecialRisingSpellMechanics(this); | 			mechanics = new SpecialRisingSpellMechanics(this); | ||||||
|  | 		else if(isOffensiveSpell()) | ||||||
|  | 			mechanics = new OffenciveSpellMechnics(this); | ||||||
| 		else	 | 		else	 | ||||||
| 			mechanics = new DefaultSpellMechanics(this); | 			mechanics = new DefaultSpellMechanics(this); | ||||||
| 		break; | 		break; | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
| #include "../lib/ConstTransitivePtr.h" | #include "../lib/ConstTransitivePtr.h" | ||||||
| #include "int3.h" | #include "int3.h" | ||||||
| #include "GameConstants.h" | #include "GameConstants.h" | ||||||
|  | #include "BattleHex.h" | ||||||
| #include "HeroBonus.h" | #include "HeroBonus.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -17,12 +18,16 @@ | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| class CLegacyConfigParser; |  | ||||||
| struct BattleHex; |  | ||||||
| class CSpell; | class CSpell; | ||||||
|  | class ISpellMechanics; | ||||||
|  |  | ||||||
|  | class CLegacyConfigParser; | ||||||
|  |  | ||||||
| class CGHeroInstance; | class CGHeroInstance; | ||||||
| class CStack; | class CStack; | ||||||
|  |  | ||||||
|  | class CBattleInfoCallback; | ||||||
|  |  | ||||||
| struct CPackForClient; | struct CPackForClient; | ||||||
|  |  | ||||||
| struct SpellSchoolInfo | struct SpellSchoolInfo | ||||||
| @@ -73,28 +78,19 @@ struct DLL_LINKAGE SpellCastContext | |||||||
| { | { | ||||||
| public: | public: | ||||||
| 	SpellCastEnvironment * env; | 	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 | class DLL_LINKAGE CSpell | ||||||
| { | { | ||||||
| @@ -137,6 +133,8 @@ public: | |||||||
| 		bool smart; | 		bool smart; | ||||||
| 		bool massive; | 		bool massive; | ||||||
| 		bool onlyAlive; | 		bool onlyAlive; | ||||||
|  | 		///no immunity on primary target (mostly spell-like attack) | ||||||
|  | 		bool alwaysHitDirectly; | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	SpellID id; | 	SpellID id; | ||||||
| @@ -168,7 +166,8 @@ public: | |||||||
| 	si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none) | 	si16 mainEffectAnim; //main spell effect animation, in AC format (or -1 when none) | ||||||
| 	ETargetType getTargetType() const; //deprecated | 	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 isCombatSpell() const; | ||||||
| 	bool isAdventureSpell() const; | 	bool isAdventureSpell() const; | ||||||
| @@ -192,7 +191,7 @@ public: | |||||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const; | 	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. | 	//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 | 	//internal, for use only by Mechanics classes. applying secondary skills | ||||||
| 	ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const; | 	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 | 	///calculate healed HP for all spells casted by hero | ||||||
| 	ui32 calculateHealedHP(const CGHeroInstance * caster, const CStack * stack, const CStack * sacrificedStack = nullptr) const; | 	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; | 	si32 getCost(const int skillLevel) const; | ||||||
|  |  | ||||||
| @@ -289,6 +289,38 @@ private: | |||||||
| 	ISpellMechanics * mechanics;//(!) do not serialize | 	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 | bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spells like Dimension Door | ||||||
|   | |||||||
| @@ -402,8 +402,12 @@ namespace ESpellCastProblem | |||||||
|  |  | ||||||
| namespace ECastingMode | namespace ECastingMode | ||||||
| { | { | ||||||
| 	enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack | 	enum ECastingMode  | ||||||
| 		MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING}; | 	{ | ||||||
|  | 		HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack | ||||||
|  | 		MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING, | ||||||
|  | 		SPELL_LIKE_ATTACK	 | ||||||
|  | 	}; | ||||||
| } | } | ||||||
|  |  | ||||||
| namespace EMarketMode | namespace EMarketMode | ||||||
|   | |||||||
| @@ -14,15 +14,43 @@ | |||||||
| #include "mapObjects/CGHeroInstance.h" | #include "mapObjects/CGHeroInstance.h" | ||||||
| #include "BattleState.h" | #include "BattleState.h" | ||||||
|  |  | ||||||
|  | #include "NetPacks.h" | ||||||
|  |  | ||||||
| ///DefaultSpellMechanics | ///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 | 	//by default use general algorithm | ||||||
| 	return owner->isImmuneBy(obj); | 	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 | ///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 | 	//can't clone already cloned creature | ||||||
| 	if (vstd::contains(obj->state, EBattleStackState::CLONED)) | 	if (vstd::contains(obj->state, EBattleStackState::CLONED)) | ||||||
| @@ -47,11 +75,11 @@ ESpellCastProblem::ESpellCastProblem CloneMechnics::isImmuneByStack(const CGHero | |||||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||||
| 	} | 	} | ||||||
| 	//use default algorithm only if there is no mechanics-related problem		 | 	//use default algorithm only if there is no mechanics-related problem		 | ||||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);	 | 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);	 | ||||||
| } | } | ||||||
|  |  | ||||||
| ///DispellHelpfulMechanics | ///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(); | 	TBonusListPtr spellBon = obj->getSpellBonuses(); | ||||||
| 	bool hasPositiveSpell = false; | 	bool hasPositiveSpell = false; | ||||||
| @@ -69,11 +97,11 @@ ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(co | |||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	//use default algorithm only if there is no mechanics-related problem		 | 	//use default algorithm only if there is no mechanics-related problem		 | ||||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);	 | 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);	 | ||||||
| } | } | ||||||
|  |  | ||||||
| ///HypnotizeMechanics | ///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 | 	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) | 		if (subjectHealth > maxHealth) | ||||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||||
| 	}			 | 	}			 | ||||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj); | 	return DefaultSpellMechanics::isImmuneByStack(caster,obj); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| ///SpecialRisingSpellMechanics | ///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 | 	// following does apply to resurrect and animate dead(?) only | ||||||
| 	// for sacrifice health calculation and health limit check don't matter | 	// 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 ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||||
| 	}	 | 	}	 | ||||||
| 	 | 	 | ||||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,mode,obj);	 | 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);	 | ||||||
| } | } | ||||||
|  |  | ||||||
| 	 | 	 | ||||||
|   | |||||||
| @@ -16,32 +16,42 @@ class DefaultSpellMechanics: public ISpellMechanics | |||||||
| { | { | ||||||
| public: | public: | ||||||
| 	DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; | 	DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; | ||||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override; |  | ||||||
| 	 | 	 | ||||||
| 	bool adventureCast(SpellCastContext & context) override;  | 	std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override; | ||||||
| 	bool battleCast(SpellCastContext & context) 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: | public: | ||||||
| 	CloneMechnics(CSpell * s): DefaultSpellMechanics(s){}; | 	OffenciveSpellMechnics(CSpell * s): DefaultSpellMechanics(s){};	 | ||||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, ECastingMode::ECastingMode mode, const CStack * obj) override; | 	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 | class DispellHelpfulMechanics: public DefaultSpellMechanics | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	DispellHelpfulMechanics(CSpell * s): DefaultSpellMechanics(s){}; | 	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 | class HypnotizeMechanics: public DefaultSpellMechanics | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){};	 | 	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 | ///all rising spells | ||||||
| class RisingSpellMechanics: public DefaultSpellMechanics | class RisingSpellMechanics: public DefaultSpellMechanics | ||||||
| @@ -56,7 +66,7 @@ class SpecialRisingSpellMechanics: public RisingSpellMechanics | |||||||
| { | { | ||||||
| public: | public: | ||||||
| 	SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){}; | 	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 | 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().flags |= BattleStackAttacked::EFFECT; | ||||||
| 		bat.bsa.front().effect = VLC->spellh->objects.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other 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 | 		//TODO: get exact attacked hex for defender | ||||||
|  |  | ||||||
| 		for(const CStack * stack : attackedCreatures) | 		for(const CStack * stack : attackedCreatures) | ||||||
| @@ -4018,31 +4019,9 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex | |||||||
| 	//must be vector, as in Chain Lightning order matters | 	//must be vector, as in Chain Lightning order matters | ||||||
| 	std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector | 	std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector | ||||||
|  |  | ||||||
| 	if (mode != ECastingMode::ENCHANTER_CASTING) | 	auto creatures = spell->getAffectedStacks(gs->curB, mode, casterColor, spellLvl, destination, caster); | ||||||
| 	{ | 	std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres)); | ||||||
| 		auto creatures = gs->curB->getAffectedCreatures(spell, spellLvl, casterColor, destination); | 	 | ||||||
| 		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) | 	for (auto cre : attackedCres) | ||||||
| 	{ | 	{ | ||||||
| 		sc.affectedCres.insert (cre->ID); | 		sc.affectedCres.insert (cre->ID); | ||||||
| @@ -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(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); | 							mirrorTargets.push_back(battleStack); | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user