mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	formatting
This commit is contained in:
		| @@ -22,7 +22,7 @@ | ||||
| ///SummonBoatMechanics | ||||
| bool SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const | ||||
| { | ||||
| 	const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);	 | ||||
| 	const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); | ||||
| 	//check if spell works at all | ||||
| 	if(env->getRandomGenerator().nextInt(99) >= owner->getPower(schoolLevel)) //power is % chance of success | ||||
| 	{ | ||||
| @@ -42,14 +42,14 @@ bool SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env | ||||
| 	{ | ||||
| 		env->complain("There is no water tile available!"); | ||||
| 		return false; | ||||
| 	}	 | ||||
| 	 | ||||
| 	} | ||||
|  | ||||
| 	for(const CGObjectInstance * obj : env->getMap()->objects) | ||||
| 	{ | ||||
| 		if(obj && obj->ID == Obj::BOAT) | ||||
| 		{ | ||||
| 			const CGBoat *b = static_cast<const CGBoat*>(obj); | ||||
| 			if(b->hero)  | ||||
| 			if(b->hero) | ||||
| 				continue; //we're looking for unoccupied boat | ||||
|  | ||||
| 			double nDist = b->pos.dist2d(parameters.caster->getPosition()); | ||||
| @@ -58,7 +58,7 @@ bool SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env | ||||
| 				nearest = b; | ||||
| 				dist = nDist; | ||||
| 			} | ||||
| 		}						 | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(nullptr != nearest) //we found boat to summon | ||||
| @@ -83,8 +83,8 @@ bool SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env | ||||
| 		no.subID = parameters.caster->getBoatType(); | ||||
| 		no.pos = summonPos + int3(1,0,0);; | ||||
| 		env->sendAndApply(&no); | ||||
| 	}	 | ||||
| 	return true;	 | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| ///ScuttleBoatMechanics | ||||
| @@ -101,7 +101,7 @@ bool ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvironment* env | ||||
| 		env->sendAndApply(&iw); | ||||
| 		return true; | ||||
| 	} | ||||
| 		 | ||||
|  | ||||
| 	if(!env->getMap()->isInTheMap(parameters.pos)) | ||||
| 	{ | ||||
| 		env->complain("Invalid dst tile for scuttle!"); | ||||
| @@ -115,12 +115,11 @@ bool ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvironment* env | ||||
| 		env->complain("There is no boat to scuttle!"); | ||||
| 		return false; | ||||
| 	} | ||||
| 		 | ||||
|  | ||||
| 	RemoveObject ro; | ||||
| 	ro.id = t->visitableObjects.back()->id; | ||||
| 	env->sendAndApply(&ro); | ||||
| 	return true;	 | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| ///DimensionDoorMechanics | ||||
| @@ -130,8 +129,8 @@ bool DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment* e | ||||
| 	{ | ||||
| 		env->complain("Destination is out of map!"); | ||||
| 		return false; | ||||
| 	}	 | ||||
| 	 | ||||
| 	} | ||||
|  | ||||
| 	const TerrainTile * dest = env->getCb()->getTile(parameters.pos); | ||||
| 	const TerrainTile * curr = env->getCb()->getTile(parameters.caster->getSightCenter()); | ||||
|  | ||||
| @@ -140,21 +139,21 @@ bool DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment* e | ||||
| 		env->complain("Destination tile doesn't exist!"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if(nullptr == curr) | ||||
| 	{ | ||||
| 		env->complain("Source tile doesn't exist!"); | ||||
| 		return false; | ||||
| 	}	 | ||||
| 		 | ||||
| 	} | ||||
|  | ||||
| 	if(parameters.caster->movement <= 0) | ||||
| 	{ | ||||
| 		env->complain("Hero needs movement points to cast Dimension Door!"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); | ||||
| 			 | ||||
|  | ||||
| 	if(parameters.caster->getBonusesCount(Bonus::SPELL_EFFECT, SpellID::DIMENSION_DOOR) >= owner->getPower(schoolLevel)) //limit casts per turn | ||||
| 	{ | ||||
| 		InfoWindow iw; | ||||
| @@ -175,7 +174,7 @@ bool DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment* e | ||||
| 		InfoWindow iw; | ||||
| 		iw.player = parameters.caster->tempOwner; | ||||
| 		iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed! | ||||
| 		env->sendAndApply(&iw);		 | ||||
| 		env->sendAndApply(&iw); | ||||
| 	} | ||||
| 	else if(env->moveHero(parameters.caster->id, parameters.pos + parameters.caster->getVisitableOffset(), true)) | ||||
| 	{ | ||||
| @@ -184,7 +183,7 @@ bool DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment* e | ||||
| 		smp.val = std::max<ui32>(0, parameters.caster->movement - 300); | ||||
| 		env->sendAndApply(&smp); | ||||
| 	} | ||||
| 	return true; 	 | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| ///TownPortalMechanics | ||||
| @@ -194,28 +193,28 @@ bool TownPortalMechanics::applyAdventureEffects(const SpellCastEnvironment * env | ||||
| 	{ | ||||
| 		env->complain("Destination tile not present!"); | ||||
| 		return false; | ||||
| 	}	 | ||||
| 	} | ||||
|  | ||||
| 	TerrainTile tile = env->getMap()->getTile(parameters.pos); | ||||
| 	if (tile.visitableObjects.empty() || tile.visitableObjects.back()->ID != Obj::TOWN) | ||||
| 	{ | ||||
| 		env->complain("Town not found for Town Portal!");	 | ||||
| 		env->complain("Town not found for Town Portal!"); | ||||
| 		return false; | ||||
| 	}		 | ||||
| 	} | ||||
|  | ||||
| 	CGTownInstance * town = static_cast<CGTownInstance*>(tile.visitableObjects.back()); | ||||
| 	if (town->tempOwner != parameters.caster->tempOwner) | ||||
| 	{ | ||||
| 		env->complain("Can't teleport to another player!"); | ||||
| 		return false; | ||||
| 	}			 | ||||
| 	 | ||||
| 	} | ||||
|  | ||||
| 	if (town->visitingHero) | ||||
| 	{ | ||||
| 		env->complain("Can't teleport to occupied town!"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if (parameters.caster->getSpellSchoolLevel(owner) < 2) | ||||
| 	{ | ||||
| 		si32 dist = town->pos.dist2dSQ(parameters.caster->pos); | ||||
| @@ -234,31 +233,31 @@ bool TownPortalMechanics::applyAdventureEffects(const SpellCastEnvironment * env | ||||
| 			env->complain("This hero can only teleport to nearest town!"); | ||||
| 			return false; | ||||
| 		} | ||||
| 			 | ||||
|  | ||||
| 	} | ||||
| 	env->moveHero(parameters.caster->id, town->visitablePos() + parameters.caster->getVisitableOffset() ,1);	 | ||||
| 	env->moveHero(parameters.caster->id, town->visitablePos() + parameters.caster->getVisitableOffset() ,1); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool ViewMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const | ||||
| { | ||||
| 	ShowWorldViewEx pack; | ||||
| 	 | ||||
|  | ||||
| 	pack.player = parameters.caster->tempOwner; | ||||
| 	 | ||||
|  | ||||
| 	const int spellLevel = parameters.caster->getSpellSchoolLevel(owner); | ||||
| 	 | ||||
|  | ||||
| 	for(const CGObjectInstance * obj : env->getMap()->objects) | ||||
| 	{ | ||||
| 		//we need to send only not visible objects | ||||
| 		 | ||||
| 		//todo:we need to send only not visible objects | ||||
|  | ||||
| 		if(filterObject(obj, spellLevel)) | ||||
| 			pack.objectPositions.push_back(ObjectPosInfo(obj)); | ||||
| 	}	 | ||||
| 	 | ||||
| 	} | ||||
|  | ||||
| 	env->sendAndApply(&pack); | ||||
| 	 | ||||
| 	return true;	 | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int spellLevel) const | ||||
|   | ||||
| @@ -7,67 +7,66 @@ | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|   | ||||
|  #pragma once | ||||
|   | ||||
|  #include "CDefaultSpellMechanics.h" | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| class SummonBoatMechanics: public DefaultSpellMechanics  | ||||
| #include "CDefaultSpellMechanics.h" | ||||
|  | ||||
| class SummonBoatMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	SummonBoatMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;	 | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; | ||||
| }; | ||||
|  | ||||
| class ScuttleBoatMechanics: public DefaultSpellMechanics  | ||||
| class ScuttleBoatMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	ScuttleBoatMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;	 | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; | ||||
| }; | ||||
|  | ||||
| class DimensionDoorMechanics: public DefaultSpellMechanics  | ||||
| class DimensionDoorMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public:	 | ||||
| 	DimensionDoorMechanics(CSpell * s): DefaultSpellMechanics(s){};	 | ||||
| public: | ||||
| 	DimensionDoorMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;	 | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; | ||||
| }; | ||||
|  | ||||
| class TownPortalMechanics: public DefaultSpellMechanics  | ||||
| class TownPortalMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public:	 | ||||
| 	TownPortalMechanics(CSpell * s): DefaultSpellMechanics(s){};	 | ||||
| public: | ||||
| 	TownPortalMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;	 | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; | ||||
| }; | ||||
|  | ||||
| class ViewMechanics: public DefaultSpellMechanics  | ||||
| class ViewMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public:	 | ||||
| public: | ||||
| 	ViewMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;	 | ||||
| 	bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; | ||||
| 	virtual bool filterObject(const CGObjectInstance * obj, const int spellLevel) const = 0; | ||||
| }; | ||||
|  | ||||
| class ViewAirMechanics: public ViewMechanics  | ||||
| class ViewAirMechanics: public ViewMechanics | ||||
| { | ||||
| public:	 | ||||
| 	ViewAirMechanics(CSpell * s): ViewMechanics(s){};	 | ||||
| public: | ||||
| 	ViewAirMechanics(CSpell * s): ViewMechanics(s){}; | ||||
| protected: | ||||
| 	bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override; | ||||
| }; | ||||
|  | ||||
| class ViewEarthMechanics: public ViewMechanics  | ||||
| class ViewEarthMechanics: public ViewMechanics | ||||
| { | ||||
| public:	 | ||||
| 	ViewEarthMechanics(CSpell * s): ViewMechanics(s){};	 | ||||
| public: | ||||
| 	ViewEarthMechanics(CSpell * s): ViewMechanics(s){}; | ||||
| protected: | ||||
| 	bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override;	 | ||||
| 	bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override; | ||||
| }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include "BattleSpellMechanics.h" | ||||
| @@ -19,7 +18,7 @@ | ||||
| std::set<const CStack *> ChainLightningMechanics::getAffectedStacks(SpellTargetingContext & ctx) const | ||||
| { | ||||
| 	std::set<const CStack* > attackedCres; | ||||
| 	 | ||||
|  | ||||
| 	std::set<BattleHex> possibleHexes; | ||||
| 	for(auto stack : ctx.cb->battleGetAllStacks()) | ||||
| 	{ | ||||
| @@ -47,8 +46,8 @@ std::set<const CStack *> ChainLightningMechanics::getAffectedStacks(SpellTargeti | ||||
| 		if(possibleHexes.empty()) //not enough targets | ||||
| 			break; | ||||
| 		lightningHex = BattleHex::getClosestTile(stack->attackerOwned, ctx.destination, possibleHexes); | ||||
| 	}	 | ||||
| 		 | ||||
| 	} | ||||
|  | ||||
| 	return attackedCres; | ||||
| } | ||||
|  | ||||
| @@ -63,7 +62,7 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battle | ||||
| 		env->complain ("No target stack to clone!"); | ||||
| 		return; | ||||
| 	} | ||||
| 	const int attacker = !(bool)parameters.casterSide;  | ||||
| 	const int attacker = !(bool)parameters.casterSide; | ||||
|  | ||||
| 	BattleStackAdded bsa; | ||||
| 	bsa.creID = clonedStack->type->idNumber; | ||||
| @@ -78,7 +77,7 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battle | ||||
| 	ssp.which = BattleSetStackProperty::CLONED; | ||||
| 	ssp.val = 0; | ||||
| 	ssp.absolute = 1; | ||||
| 	env->sendAndApply(&ssp);	 | ||||
| 	env->sendAndApply(&ssp); | ||||
| } | ||||
|  | ||||
| ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const | ||||
| @@ -105,22 +104,22 @@ ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHer | ||||
| 		if(maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert | ||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 	} | ||||
| 	//use default algorithm only if there is no mechanics-related problem		 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster, obj);	 | ||||
| 	//use default algorithm only if there is no mechanics-related problem | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster, obj); | ||||
| } | ||||
|  | ||||
| ///CureMechanics | ||||
| void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const | ||||
| { | ||||
| 	DefaultSpellMechanics::applyBattle(battle, packet); | ||||
| 	 | ||||
|  | ||||
| 	for(auto stackID : packet->affectedCres) | ||||
| 	{ | ||||
| 		if(vstd::contains(packet->resisted, stackID)) | ||||
| 		{ | ||||
| 			logGlobal->errorStream() << "Resistance to positive spell CURE"; | ||||
| 			continue; | ||||
| 		}			 | ||||
| 		} | ||||
|  | ||||
| 		CStack *s = battle->getStack(stackID); | ||||
| 		s->popBonuses([&](const Bonus *b) -> bool | ||||
| @@ -132,14 +131,14 @@ void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * pac | ||||
| 			} | ||||
| 			return false; //not a spell effect | ||||
| 		}); | ||||
| 	}		 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ///DispellMechanics | ||||
| void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const | ||||
| { | ||||
| 	DefaultSpellMechanics::applyBattle(battle, packet); | ||||
| 	 | ||||
|  | ||||
| 	for(auto stackID : packet->affectedCres) | ||||
| 	{ | ||||
| 		if(vstd::contains(packet->resisted, stackID)) | ||||
| @@ -150,7 +149,7 @@ void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * | ||||
| 		{ | ||||
| 			return Selector::sourceType(Bonus::SPELL_EFFECT)(b); | ||||
| 		}); | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -166,7 +165,7 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const C | ||||
| 			* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), caster, obj); | ||||
| 		if (subjectHealth > maxHealth) | ||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 	}			 | ||||
| 	} | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster, obj); | ||||
| } | ||||
|  | ||||
| @@ -217,8 +216,8 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat | ||||
| 		BattleObstaclePlaced bop; | ||||
| 		bop.obstacle = obstacle; | ||||
| 		env->sendAndApply(&bop); | ||||
| 	};	 | ||||
| 	 | ||||
| 	}; | ||||
|  | ||||
| 	switch(owner->id) | ||||
| 	{ | ||||
| 	case SpellID::QUICKSAND: | ||||
| @@ -253,17 +252,17 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat | ||||
| 				placeObstacle(hex); | ||||
| 		} | ||||
| 		break; | ||||
| 	default:		 | ||||
| 	default: | ||||
| 		assert(0); | ||||
| 	}			 | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| ///WallMechanics | ||||
| std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes) const | ||||
| { | ||||
| 	std::vector<BattleHex> ret;	 | ||||
| 	 | ||||
| 	std::vector<BattleHex> ret; | ||||
|  | ||||
| 	//Special case - shape of obstacle depends on caster's side | ||||
| 	//TODO make it possible through spell config | ||||
|  | ||||
| @@ -293,7 +292,7 @@ std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 sch | ||||
| 	if(schoolLvl >= 2) //advanced versions of fire wall / force field cotnains of 3 hexes | ||||
| 		addIfValid(centralHex.moveInDir(secondStep, false)); //moveInDir function modifies subject hex | ||||
|  | ||||
| 	return ret;	 | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| ///RemoveObstacleMechanics | ||||
| @@ -306,7 +305,7 @@ void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * en | ||||
| 		env->sendAndApply(&obr); | ||||
| 	} | ||||
| 	else | ||||
| 		env->complain("There's no obstacle to remove!");	 | ||||
| 		env->complain("There's no obstacle to remove!"); | ||||
| } | ||||
|  | ||||
| ///SpecialRisingSpellMechanics | ||||
| @@ -338,7 +337,6 @@ void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, Ba | ||||
| 	BattleStacksRemoved bsr; | ||||
| 	bsr.stackIDs.insert(parameters.selectedStack->ID); //somehow it works for teleport? | ||||
| 	env->sendAndApply(&bsr); | ||||
| 		 | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -350,15 +348,15 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStac | ||||
|  | ||||
| 	if(obj->count >= obj->baseAmount) | ||||
| 		return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 	 | ||||
|  | ||||
| 	if(caster) //FIXME: Archangels can cast immune stack | ||||
| 	{ | ||||
| 		auto maxHealth = calculateHealedHP(caster, obj, nullptr); | ||||
| 		if (maxHealth < obj->MaxHealth()) //must be able to rise at least one full creature | ||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 	}	 | ||||
| 	 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);	 | ||||
| 	} | ||||
|  | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,obj); | ||||
| } | ||||
|  | ||||
| ///SummonMechanics | ||||
| @@ -400,7 +398,7 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battl | ||||
| 	if(bsa.amount) | ||||
| 		env->sendAndApply(&bsa); | ||||
| 	else | ||||
| 		env->complain("Summoning didn't summon any!");	 | ||||
| 		env->complain("Summoning didn't summon any!"); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -414,6 +412,6 @@ void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat | ||||
| 	tiles.push_back(parameters.destination); | ||||
| 	bsm.tilesToMove = tiles; | ||||
| 	bsm.teleporting = true; | ||||
| 	env->sendAndApply(&bsm);	 | ||||
| 	env->sendAndApply(&bsm); | ||||
| } | ||||
| 	 | ||||
|  | ||||
|   | ||||
| @@ -7,16 +7,15 @@ | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|   | ||||
|  #pragma once | ||||
|   | ||||
|  #include "CDefaultSpellMechanics.h" | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "CDefaultSpellMechanics.h" | ||||
|  | ||||
| class ChainLightningMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	ChainLightningMechanics(CSpell * s): DefaultSpellMechanics(s){};	 | ||||
| 	ChainLightningMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| 	std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override; | ||||
| }; | ||||
|  | ||||
| @@ -26,48 +25,46 @@ public: | ||||
| 	CloneMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; | ||||
| protected: | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;	 | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; | ||||
| }; | ||||
|  | ||||
| class CureMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	CureMechanics(CSpell * s): DefaultSpellMechanics(s){};	 | ||||
| 	 | ||||
| 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;	 | ||||
| 	CureMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
|  | ||||
| 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override; | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| class DispellMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	DispellMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| 	 | ||||
| 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;	 | ||||
|  | ||||
| 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override; | ||||
| }; | ||||
|  | ||||
| class HypnotizeMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){};	 | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;	 | ||||
| };  | ||||
| 	HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; | ||||
| }; | ||||
|  | ||||
| class ObstacleMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};		 | ||||
| 	ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
|  | ||||
| protected: | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;	 | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; | ||||
| }; | ||||
|  | ||||
| class WallMechanics: public ObstacleMechanics | ||||
| { | ||||
| public: | ||||
| 	WallMechanics(CSpell * s): ObstacleMechanics(s){};	 | ||||
| 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override;		 | ||||
| 	WallMechanics(CSpell * s): ObstacleMechanics(s){}; | ||||
| 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override; | ||||
| }; | ||||
|  | ||||
| class RemoveObstacleMechanics: public DefaultSpellMechanics | ||||
| @@ -75,23 +72,23 @@ class RemoveObstacleMechanics: public DefaultSpellMechanics | ||||
| public: | ||||
| 	RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;		 | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; | ||||
| }; | ||||
|  | ||||
| ///all rising spells | ||||
| class RisingSpellMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	RisingSpellMechanics(CSpell * s): DefaultSpellMechanics(s){};		 | ||||
| 	 | ||||
| 	RisingSpellMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
|  | ||||
| }; | ||||
|  | ||||
| class SacrificeMechanics: public RisingSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};	 | ||||
| 	SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){}; | ||||
| protected: | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;		 | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; | ||||
| }; | ||||
|  | ||||
| ///all rising spells but SACRIFICE | ||||
| @@ -99,7 +96,7 @@ class SpecialRisingSpellMechanics: public RisingSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){}; | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;						 | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; | ||||
| }; | ||||
|  | ||||
| class SummonMechanics: public DefaultSpellMechanics | ||||
| @@ -107,7 +104,7 @@ class SummonMechanics: public DefaultSpellMechanics | ||||
| public: | ||||
| 	SummonMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;		 | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; | ||||
| }; | ||||
|  | ||||
| class TeleportMechanics: public DefaultSpellMechanics | ||||
| @@ -115,5 +112,5 @@ class TeleportMechanics: public DefaultSpellMechanics | ||||
| public: | ||||
| 	TeleportMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;		 | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; | ||||
| }; | ||||
|   | ||||
| @@ -117,7 +117,6 @@ namespace SRSLPraserHelpers | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| ///DefaultSpellMechanics | ||||
| void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const | ||||
| { | ||||
| @@ -128,7 +127,7 @@ void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCa | ||||
| 			battle->sides[packet->side].castSpellsCount++; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	//handle countering spells | ||||
| 	for(auto stackID : packet->affectedCres) | ||||
| 	{ | ||||
| @@ -144,7 +143,7 @@ void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCa | ||||
|  | ||||
| 			return isSpellEffect && vstd::contains(owner->counteredSpells, spellID); | ||||
| 		}); | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const | ||||
| @@ -154,29 +153,29 @@ bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, Adve | ||||
| 		env->complain("Attempt to cast non adventure spell in adventure mode"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	const CGHeroInstance * caster = parameters.caster; | ||||
| 	const int cost = caster->getSpellCost(owner); | ||||
| 	 | ||||
|  | ||||
| 	if(!caster->canCastThisSpell(owner)) | ||||
| 	{ | ||||
| 		env->complain("Hero cannot cast this spell!"); | ||||
| 		return false;		 | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if(caster->mana < cost) | ||||
| 	{ | ||||
| 		env->complain("Hero doesn't have enough spell points to cast this spell!"); | ||||
| 		return false;		 | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		AdvmapSpellCast asc; | ||||
| 		asc.caster = caster; | ||||
| 		asc.spellID = owner->id; | ||||
| 		env->sendAndApply(&asc);		 | ||||
| 		env->sendAndApply(&asc); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if(applyAdventureEffects(env, parameters)) | ||||
| 	{ | ||||
| 		SetMana sm; | ||||
| @@ -194,27 +193,27 @@ bool DefaultSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * e | ||||
| 	if(owner->hasEffects()) | ||||
| 	{ | ||||
| 		const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); | ||||
| 		 | ||||
|  | ||||
| 		std::vector<Bonus> bonuses; | ||||
| 		 | ||||
|  | ||||
| 		owner->getEffects(bonuses, schoolLevel); | ||||
| 		 | ||||
|  | ||||
| 		for(Bonus b : bonuses) | ||||
| 		{ | ||||
| 			GiveBonus gb; | ||||
| 			gb.id = parameters.caster->id.getNum(); | ||||
| 			gb.bonus = b; | ||||
| 			env->sendAndApply(&gb);			 | ||||
| 			env->sendAndApply(&gb); | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		//There is no generic algorithm of adventure cast | ||||
| 		env->complain("Unimplemented adventure spell"); | ||||
| 		return false;				 | ||||
| 	}	 | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -229,11 +228,11 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS | ||||
| 	sc.castedByHero = nullptr != parameters.caster; | ||||
| 	sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1); | ||||
| 	sc.manaGained = 0; | ||||
| 	 | ||||
| 	int spellCost = 0;	 | ||||
| 	 | ||||
|  | ||||
| 	int spellCost = 0; | ||||
|  | ||||
| 	//calculate spell cost | ||||
| 	if(parameters.caster)  | ||||
| 	if(parameters.caster) | ||||
| 	{ | ||||
| 		spellCost = parameters.cb->battleGetSpellCost(owner, parameters.caster); | ||||
|  | ||||
| @@ -249,21 +248,20 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS | ||||
| 			} | ||||
| 			sc.manaGained = (manaChannel * spellCost) / 100; | ||||
| 		} | ||||
| 	}	 | ||||
| 	 | ||||
| 	 | ||||
| 	} | ||||
|  | ||||
| 	//calculating affected creatures for all spells | ||||
| 	//must be vector, as in Chain Lightning order matters | ||||
| 	std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector | ||||
|  | ||||
| 	auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, parameters.destination, parameters.caster); | ||||
| 	std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres)); | ||||
| 	 | ||||
|  | ||||
| 	for (auto cre : attackedCres) | ||||
| 	{ | ||||
| 		sc.affectedCres.insert(cre->ID); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	//checking if creatures resist | ||||
| 	//resistance is applied only to negative spells | ||||
| 	if(owner->isNegative()) | ||||
| @@ -271,52 +269,51 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS | ||||
| 		for(auto s : attackedCres) | ||||
| 		{ | ||||
| 			const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in % | ||||
| 			 | ||||
|  | ||||
| 			if(env->getRandomGenerator().nextInt(99) < prob) | ||||
| 			{ | ||||
| 				sc.resisted.push_back(s->ID); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	StacksInjured si;	 | ||||
|  | ||||
| 	StacksInjured si; | ||||
| 	SpellCastContext ctx(attackedCres, sc, si); | ||||
| 	 | ||||
|  | ||||
| 	applyBattleEffects(env, parameters, ctx); | ||||
| 	 | ||||
|  | ||||
| 	env->sendAndApply(&sc); | ||||
| 	 | ||||
|  | ||||
| 	//spend mana | ||||
| 	if(parameters.caster)  | ||||
| 	if(parameters.caster) | ||||
| 	{ | ||||
| 		SetMana sm; | ||||
| 		sm.absolute = false; | ||||
| 		 | ||||
|  | ||||
| 		sm.hid = parameters.caster->id; | ||||
| 		sm.val = -spellCost; | ||||
| 		 | ||||
|  | ||||
| 		env->sendAndApply(&sm); | ||||
| 		 | ||||
|  | ||||
| 		if(sc.manaGained > 0) | ||||
| 		{ | ||||
| 			assert(parameters.secHero); | ||||
| 			 | ||||
|  | ||||
| 			sm.hid = parameters.secHero->id; | ||||
| 			sm.val = sc.manaGained; | ||||
| 			env->sendAndApply(&sm); | ||||
| 		}		 | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if(!si.stacks.empty()) //after spellcast info shows | ||||
| 		env->sendAndApply(&si); | ||||
| 	 | ||||
|  | ||||
| 	//reduce number of casts remaining | ||||
| 	//TODO: this should be part of BattleSpellCast apply | ||||
| 	if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING)  | ||||
| 	if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING) | ||||
| 	{ | ||||
| 		assert(parameters.casterStack); | ||||
| 		 | ||||
|  | ||||
| 		BattleSetStackProperty ssp; | ||||
| 		ssp.stackID = parameters.casterStack->ID; | ||||
| 		ssp.which = BattleSetStackProperty::CASTS; | ||||
| @@ -346,7 +343,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS | ||||
| 				if(!mirrorTargets.empty()) | ||||
| 				{ | ||||
| 					int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position; | ||||
| 					 | ||||
|  | ||||
| 					BattleSpellCastParameters mirrorParameters = parameters; | ||||
| 					mirrorParameters.spellLvl = 0; | ||||
| 					mirrorParameters.casterSide = 1-parameters.casterSide; | ||||
| @@ -357,12 +354,12 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS | ||||
| 					mirrorParameters.mode = ECastingMode::MAGIC_MIRROR; | ||||
| 					mirrorParameters.casterStack = (attackedCre); | ||||
| 					mirrorParameters.selectedStack = nullptr; | ||||
| 					 | ||||
| 					battleCast(env, mirrorParameters);					 | ||||
|  | ||||
| 					battleCast(env, mirrorParameters); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int DefaultSpellMechanics::calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const | ||||
| @@ -380,28 +377,28 @@ int DefaultSpellMechanics::calculateDuration(const CGHeroInstance * caster, int | ||||
| 		return 1; | ||||
| 	default: //other spells | ||||
| 		return caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + caster->valOfBonuses(Bonus::SPELL_DURATION); | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ui32 DefaultSpellMechanics::calculateHealedHP(const CGHeroInstance* caster, const CStack* stack, const CStack* sacrificedStack) const | ||||
| { | ||||
| 	int healedHealth; | ||||
| 	 | ||||
|  | ||||
| 	if(!owner->isHealingSpell()) | ||||
| 	{ | ||||
| 		logGlobal->errorStream() << "calculateHealedHP called for nonhealing spell "<< owner->name; | ||||
| 		return 0; | ||||
| 	}		 | ||||
| 	 | ||||
| 	} | ||||
|  | ||||
| 	const int spellPowerSkill = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER); | ||||
| 	const int levelPower = owner->getPower(caster->getSpellSchoolLevel(owner)); | ||||
| 	 | ||||
|  | ||||
| 	if (owner->id == SpellID::SACRIFICE && sacrificedStack) | ||||
| 		healedHealth = (spellPowerSkill + sacrificedStack->MaxHealth() + levelPower) * sacrificedStack->count; | ||||
| 	else | ||||
| 		healedHealth = spellPowerSkill * owner->power + levelPower; //??? | ||||
| 	healedHealth = owner->calculateBonus(healedHealth, caster, stack); | ||||
| 	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (owner->isRisingSpell() ? stack->baseAmount * stack->MaxHealth() : 0));		 | ||||
| 	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (owner->isRisingSpell() ? stack->baseAmount * stack->MaxHealth() : 0)); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -448,7 +445,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, | ||||
| 				++chainLightningModifier; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if(owner->hasEffects()) | ||||
| 	{ | ||||
| 		int stackSpellPower = 0; | ||||
| @@ -531,7 +528,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, | ||||
| 			env->sendAndApply(&sse); | ||||
|  | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	if(owner->isHealingSpell()) | ||||
| 	{ | ||||
| 		int hpGained = 0; | ||||
| @@ -563,7 +560,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, | ||||
| 					//any typical spell (commander's cure or animate dead) | ||||
| 					int healedHealth = parameters.usedSpellPower * owner->power + owner->getPower(parameters.spellLvl); | ||||
| 					hi.healedHP = std::min<ui32>(healedHealth, attackedCre->MaxHealth() - attackedCre->firstHPleft + (resurrect ? attackedCre->baseAmount * attackedCre->MaxHealth() : 0)); | ||||
| 				}					 | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 				hi.healedHP = calculateHealedHP(parameters.caster, attackedCre, parameters.selectedStack); //Casted by hero | ||||
| @@ -572,14 +569,13 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, | ||||
| 		} | ||||
| 		if(!shr.healedStacks.empty()) | ||||
| 			env->sendAndApply(&shr); | ||||
| 	}		 | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const | ||||
| { | ||||
| 	using namespace SRSLPraserHelpers; | ||||
| 	 | ||||
|  | ||||
| 	std::vector<BattleHex> ret; | ||||
| 	std::string rng = owner->getLevelInfo(schoolLvl).range + ','; //copy + artificial comma for easier handling | ||||
|  | ||||
| @@ -639,19 +635,18 @@ std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, | ||||
|  | ||||
| 	//remove duplicates (TODO check if actually needed) | ||||
| 	range::unique(ret); | ||||
| 	return ret;		 | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargetingContext & ctx) const | ||||
| { | ||||
| 	std::set<const CStack* > attackedCres;//std::set to exclude multiple occurrences of two hex creatures | ||||
| 	 | ||||
|  | ||||
| 	const ui8 attackerSide = ctx.cb->playerToSide(ctx.casterColor) == 1; | ||||
| 	const auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide); | ||||
|  | ||||
| 	const CSpell::TargetInfo ti(owner, ctx.schoolLvl, ctx.mode); | ||||
| 	 | ||||
|  | ||||
| 	//TODO: more generic solution for mass spells | ||||
| 	if(owner->getLevelInfo(ctx.schoolLvl).range.size() > 1) //custom many-hex range | ||||
| 	{ | ||||
| @@ -669,17 +664,17 @@ std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargeting | ||||
| 			const bool positiveToAlly = owner->isPositive() && s->owner == ctx.casterColor; | ||||
| 			const bool negativeToEnemy = owner->isNegative() && s->owner != ctx.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(ctx.destination); | ||||
| 			//handle smart targeting | ||||
| 			const bool positivenessFlag = !ti.smart || owner->isNeutral() || positiveToAlly || negativeToEnemy; | ||||
| 			 | ||||
| 			return rangeCovers && positivenessFlag && validTarget;		 | ||||
|  | ||||
| 			return rangeCovers && positivenessFlag && validTarget; | ||||
| 		}; | ||||
| 		 | ||||
|  | ||||
| 		TStacks stacks = ctx.cb->battleGetStacksIf(predicate); | ||||
| 		 | ||||
|  | ||||
| 		if(ti.massive) | ||||
| 		{ | ||||
| 			//for massive spells add all targets | ||||
| @@ -696,13 +691,13 @@ std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargeting | ||||
| 				{ | ||||
| 					attackedCres.insert(stack); | ||||
| 					break; | ||||
| 				}				 | ||||
| 			}	 | ||||
| 			 | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if(attackedCres.empty() && !stacks.empty()) | ||||
| 			{ | ||||
| 				attackedCres.insert(stacks.front()); | ||||
| 			}						 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else //custom range from attackedHexes | ||||
| @@ -712,12 +707,11 @@ std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargeting | ||||
| 			if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ti.onlyAlive)) | ||||
| 				attackedCres.insert(st); | ||||
| 		} | ||||
| 	}	 | ||||
| 	 | ||||
| 	} | ||||
|  | ||||
| 	return attackedCres; | ||||
| } | ||||
|  | ||||
|  | ||||
| ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const | ||||
| { | ||||
| 	//by default use general algorithm | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
|  | ||||
| #include "ISpellMechanics.h" | ||||
|  | ||||
| class BattleSpellCast; | ||||
| @@ -18,9 +17,9 @@ class StacksInjured; | ||||
|  | ||||
| struct SpellCastContext | ||||
| { | ||||
| 	SpellCastContext(std::vector<const CStack*> & attackedCres, BattleSpellCast & sc, StacksInjured & si): | ||||
| 		attackedCres(attackedCres), sc(sc), si(si){};  | ||||
| 	std::vector<const CStack*> & attackedCres; | ||||
| 	SpellCastContext(std::vector<const CStack *> & attackedCres, BattleSpellCast & sc, StacksInjured & si): | ||||
| 		attackedCres(attackedCres), sc(sc), si(si){}; | ||||
| 	std::vector<const CStack *> & attackedCres; | ||||
| 	BattleSpellCast & sc; | ||||
| 	StacksInjured & si; | ||||
| }; | ||||
| @@ -29,25 +28,24 @@ class DefaultSpellMechanics: public ISpellMechanics | ||||
| { | ||||
| public: | ||||
| 	DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; | ||||
| 	 | ||||
|  | ||||
| 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override; | ||||
| 	std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override; | ||||
| 	 | ||||
|  | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; | ||||
| 	 | ||||
| 	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final;  | ||||
|  | ||||
| 	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final; | ||||
| 	void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override; | ||||
| 	 | ||||
|  | ||||
| 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override; | ||||
| protected: | ||||
| 	 | ||||
| 	virtual void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const; | ||||
| 	 | ||||
|  | ||||
| 	virtual int calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const; | ||||
| 	 | ||||
|  | ||||
| 	///calculate healed HP for all spells casted by hero | ||||
| 	ui32 calculateHealedHP(const CGHeroInstance* caster, const CStack* stack, const CStack* sacrificedStack) const; | ||||
| 	 | ||||
| 	ui32 calculateHealedHP(const CGHeroInstance * caster, const CStack * stack, const CStack * sacrificedStack) const; | ||||
|  | ||||
| 	///actual adventure cast implementation | ||||
| 	virtual bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; | ||||
| }; | ||||
|   | ||||
| @@ -1,3 +1,13 @@ | ||||
| /* | ||||
|  * CSpellHandler.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include <cctype> | ||||
| @@ -19,23 +29,11 @@ | ||||
|  | ||||
| #include "ISpellMechanics.h" | ||||
|  | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * CSpellHandler.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| namespace SpellConfig | ||||
| { | ||||
| 	static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"}; | ||||
| 	 | ||||
| 	static const SpellSchoolInfo SCHOOL[4] =  | ||||
|  | ||||
| 	static const SpellSchoolInfo SCHOOL[4] = | ||||
| 	{ | ||||
| 		{ | ||||
| 			ESpellSchool::AIR, | ||||
| @@ -69,16 +67,15 @@ namespace SpellConfig | ||||
| 			SecondarySkill::EARTH_MAGIC, | ||||
| 			Bonus::EARTH_SPELLS | ||||
| 		} | ||||
| 	};	 | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo* cb) | ||||
| 	: spellLvl(0), destination(BattleHex::INVALID), casterSide(0),casterColor(PlayerColor::CANNOT_DETERMINE),caster(nullptr), secHero(nullptr), | ||||
| 	usedSpellPower(0),mode(ECastingMode::HERO_CASTING), casterStack(nullptr), selectedStack(nullptr), cb(cb) | ||||
| { | ||||
| 	 | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| ///CSpell::LevelInfo | ||||
| CSpell::LevelInfo::LevelInfo() | ||||
| @@ -118,14 +115,14 @@ void CSpell::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) co | ||||
| bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const | ||||
| { | ||||
| 	assert(env); | ||||
| 		 | ||||
|  | ||||
| 	return mechanics->adventureCast(env, parameters); | ||||
| } | ||||
|  | ||||
| void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const | ||||
| { | ||||
| 	assert(env); | ||||
| 	 | ||||
|  | ||||
| 	mechanics->battleCast(env, parameters); | ||||
| } | ||||
|  | ||||
| @@ -133,19 +130,19 @@ bool CSpell::isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const | ||||
| { | ||||
| 	if(!hasSpellBook) | ||||
| 		return false; | ||||
| 	 | ||||
|  | ||||
| 	const bool inSpellBook = vstd::contains(spellBook, id); | ||||
| 	const bool isBonus = caster->hasBonusOfType(Bonus::SPELL, id); | ||||
| 	 | ||||
|  | ||||
| 	bool inTome = false; | ||||
| 	 | ||||
|  | ||||
| 	forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop) | ||||
| 	{ | ||||
| 		if(caster->hasBonusOfType(cnf.knoledgeBonus)) | ||||
| 		{ | ||||
| 			inTome = stop = true; | ||||
| 		}				 | ||||
| 	});	 | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
|     if (isSpecialSpell()) | ||||
|     { | ||||
| @@ -158,7 +155,7 @@ bool CSpell::isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const | ||||
|     else | ||||
|     { | ||||
|        return inSpellBook || inTome || isBonus || caster->hasBonusOfType(Bonus::SPELLS_OF_LEVEL, level); | ||||
|     }	 | ||||
|     } | ||||
| } | ||||
|  | ||||
| const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const | ||||
| @@ -172,7 +169,7 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const | ||||
| 	return levels.at(level); | ||||
| } | ||||
|  | ||||
| ui32 CSpell::calculateBonus(ui32 baseDamage, const CGHeroInstance* caster, const CStack* affectedCreature) const | ||||
| ui32 CSpell::calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const | ||||
| { | ||||
| 	ui32 ret = baseDamage; | ||||
| 	//applying sorcery secondary skill | ||||
| @@ -180,17 +177,17 @@ ui32 CSpell::calculateBonus(ui32 baseDamage, const CGHeroInstance* caster, const | ||||
| 	{ | ||||
| 		ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0; | ||||
| 		ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, id.toEnum())) / 100.0; | ||||
| 		 | ||||
|  | ||||
| 		forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop) | ||||
| 		{ | ||||
| 			ret *= (100.0 + caster->valOfBonuses(cnf.damagePremyBonus)) / 100.0; | ||||
| 			stop = true; //only bonus from one school is used | ||||
| 		});		 | ||||
| 		}); | ||||
|  | ||||
| 		if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer | ||||
| 			ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, id.toEnum()) * caster->level) / affectedCreature->getCreature()->level)) / 100.0; | ||||
| 	} | ||||
| 	return ret;	 | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const | ||||
| @@ -208,15 +205,15 @@ ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affec | ||||
| 	if(nullptr != affectedCreature) | ||||
| 	{ | ||||
| 		//applying protections - when spell has more then one elements, only one protection should be applied (I think) | ||||
| 		 | ||||
|  | ||||
| 		forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop) | ||||
| 		{ | ||||
| 			if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id)) | ||||
| 			{ | ||||
| 				ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id); | ||||
| 				ret /= 100; | ||||
| 				stop = true;//only bonus from one school is used	 | ||||
| 			}				 | ||||
| 				stop = true;//only bonus from one school is used | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		//general spell dmg reduction | ||||
| @@ -233,7 +230,7 @@ ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affec | ||||
| 		} | ||||
| 	} | ||||
| 	ret = calculateBonus(ret, caster, affectedCreature); | ||||
| 	return ret;	 | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const | ||||
| @@ -241,26 +238,25 @@ std::vector<BattleHex> CSpell::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, | ||||
| 	return mechanics->rangeInHexes(centralHex,schoolLvl,side,outDroppedHexes); | ||||
| } | ||||
|  | ||||
| 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 *> CSpell::getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster) const | ||||
| { | ||||
| 	ISpellMechanics::SpellTargetingContext ctx(this, cb,mode,casterColor,spellLvl,destination); | ||||
|  | ||||
| 	std::set<const CStack* > attackedCres = mechanics->getAffectedStacks(ctx); | ||||
| 	 | ||||
| 	//now handle immunities		 | ||||
|  | ||||
| 	//now handle immunities | ||||
| 	auto predicate = [&, this](const CStack * s)->bool | ||||
| 	{ | ||||
| 		bool hitDirectly = ctx.ti.alwaysHitDirectly && s->coversPos(destination); | ||||
| 		bool notImmune = (ESpellCastProblem::OK == isImmuneByStack(caster, s)); | ||||
| 		 | ||||
| 		return !(hitDirectly || notImmune);   | ||||
| 	};	 | ||||
|  | ||||
| 		return !(hitDirectly || notImmune); | ||||
| 	}; | ||||
| 	vstd::erase_if(attackedCres, predicate); | ||||
| 	 | ||||
|  | ||||
| 	return attackedCres; | ||||
| } | ||||
|  | ||||
|  | ||||
| CSpell::ETargetType CSpell::getTargetType() const | ||||
| { | ||||
| 	return targetType; | ||||
| @@ -280,14 +276,13 @@ void CSpell::forEachSchool(const std::function<void(const SpellSchoolInfo &, boo | ||||
| 		if(school.at(cnf.id)) | ||||
| 		{ | ||||
| 			cb(cnf, stop); | ||||
| 			 | ||||
|  | ||||
| 			if(stop) | ||||
| 				break; | ||||
| 		}				 | ||||
| 	}	 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| bool CSpell::isCombatSpell() const | ||||
| { | ||||
| 	return combatSpell; | ||||
| @@ -348,12 +343,12 @@ bool CSpell::hasEffects() const | ||||
| 	return !levels[0].effects.empty(); | ||||
| } | ||||
|  | ||||
| const std::string& CSpell::getIconImmune() const | ||||
| const std::string & CSpell::getIconImmune() const | ||||
| { | ||||
| 	return iconImmune; | ||||
| } | ||||
|  | ||||
| const std::string& CSpell::getCastSound() const | ||||
| const std::string & CSpell::getCastSound() const | ||||
| { | ||||
| 	return castSound; | ||||
| } | ||||
| @@ -377,8 +372,7 @@ si32 CSpell::getProbability(const TFaction factionId) const | ||||
| 	return probabilities.at(factionId); | ||||
| } | ||||
|  | ||||
|  | ||||
| void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const | ||||
| void CSpell::getEffects(std::vector<Bonus> & lst, const int level) const | ||||
| { | ||||
| 	if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS) | ||||
| 	{ | ||||
| @@ -408,17 +402,17 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallbac | ||||
| 	TStacks stacks = cb->battleGetStacksIf([=](const CStack * s){ | ||||
| 		return s->coversPos(destination) && (isRisingSpell() || s->alive()); | ||||
| 	}); | ||||
| 	 | ||||
|  | ||||
| 	if(!stacks.empty()) | ||||
| 	{ | ||||
| 		bool allImmune = true; | ||||
| 		 | ||||
| 		ESpellCastProblem::ESpellCastProblem problem;		 | ||||
| 		 | ||||
|  | ||||
| 		ESpellCastProblem::ESpellCastProblem problem; | ||||
|  | ||||
| 		for(auto s : stacks) | ||||
| 		{ | ||||
| 			ESpellCastProblem::ESpellCastProblem res = isImmuneByStack(caster,s); | ||||
| 			 | ||||
|  | ||||
| 			if(res == ESpellCastProblem::OK) | ||||
| 			{ | ||||
| 				allImmune = false; | ||||
| @@ -428,7 +422,7 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallbac | ||||
| 				problem = res; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		if(allImmune) | ||||
| 			return problem; | ||||
| 	} | ||||
| @@ -439,23 +433,22 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallbac | ||||
| 			if(caster && mode == ECastingMode::HERO_CASTING) //TODO why??? | ||||
| 			{ | ||||
| 				const CSpell::TargetInfo ti(this, caster->getSpellSchoolLevel(this), mode); | ||||
| 				 | ||||
|  | ||||
| 				if(!ti.massive) | ||||
| 					return ESpellCastProblem::WRONG_SPELL_TARGET;					 | ||||
| 					return ESpellCastProblem::WRONG_SPELL_TARGET; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
|  				return ESpellCastProblem::WRONG_SPELL_TARGET; | ||||
| 			}			 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ESpellCastProblem::OK;	 | ||||
| 	return ESpellCastProblem::OK; | ||||
| } | ||||
|  | ||||
|  | ||||
| ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) const | ||||
| {	 | ||||
| { | ||||
| 	//todo: use new bonus API | ||||
| 	//1. Check absolute limiters | ||||
| 	for(auto b : absoluteLimiters) | ||||
| @@ -470,16 +463,16 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) | ||||
| 		if (obj->hasBonusOfType(b)) | ||||
| 			return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	//check receptivity | ||||
| 	if (isPositive() && obj->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells | ||||
| 		return ESpellCastProblem::OK;	 | ||||
| 		return ESpellCastProblem::OK; | ||||
|  | ||||
| 	//3. Check negation | ||||
| 	//FIXME: Orb of vulnerability mechanics is not such trivial | ||||
| 	if(obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES)) //Orb of vulnerability | ||||
| 		return ESpellCastProblem::NOT_DECIDED; | ||||
| 		 | ||||
|  | ||||
| 	//4. Check negatable limit | ||||
| 	for(auto b : limiters) | ||||
| 	{ | ||||
| @@ -496,31 +489,31 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) | ||||
| 	} | ||||
|  | ||||
| 	//6. Check elemental immunities | ||||
| 	 | ||||
|  | ||||
| 	ESpellCastProblem::ESpellCastProblem tmp = ESpellCastProblem::NOT_DECIDED; | ||||
| 	 | ||||
|  | ||||
| 	forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop) | ||||
| 	{ | ||||
| 		auto element = cnf.immunityBonus; | ||||
| 		 | ||||
|  | ||||
| 		if(obj->hasBonusOfType(element, 0)) //always resist if immune to all spells altogether | ||||
| 		{ | ||||
| 			tmp = ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 			stop = true; | ||||
| 		}				 | ||||
| 		} | ||||
| 		else if(!isPositive()) //negative or indifferent | ||||
| 		{ | ||||
| 			if((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1)) | ||||
| 			{ | ||||
| 				tmp = ESpellCastProblem::STACK_IMMUNE_TO_SPELL; | ||||
| 				stop = true; | ||||
| 			}			 | ||||
| 		}	 | ||||
| 			} | ||||
| 		} | ||||
| 	}); | ||||
| 	 | ||||
|  | ||||
| 	if(tmp != ESpellCastProblem::NOT_DECIDED) | ||||
| 		return tmp; | ||||
| 	 | ||||
|  | ||||
| 	TBonusListPtr levelImmunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY)); | ||||
|  | ||||
| 	if(obj->hasBonusOfType(Bonus::SPELL_IMMUNITY, id) | ||||
| @@ -532,15 +525,14 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) | ||||
| 	return ESpellCastProblem::NOT_DECIDED; | ||||
| } | ||||
|  | ||||
| ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance* caster, const CStack* obj) const | ||||
| ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const | ||||
| { | ||||
| 	const auto immuneResult = mechanics->isImmuneByStack(caster,obj); | ||||
| 	 | ||||
| 	if (ESpellCastProblem::NOT_DECIDED != immuneResult)  | ||||
| 		return immuneResult; | ||||
| 	return ESpellCastProblem::OK;	 | ||||
| } | ||||
|  | ||||
| 	if (ESpellCastProblem::NOT_DECIDED != immuneResult) | ||||
| 		return immuneResult; | ||||
| 	return ESpellCastProblem::OK; | ||||
| } | ||||
|  | ||||
| void CSpell::setIsOffensive(const bool val) | ||||
| { | ||||
| @@ -568,7 +560,6 @@ void CSpell::setup() | ||||
| 	setupMechanics(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void CSpell::setupMechanics() | ||||
| { | ||||
| 	if(nullptr != mechanics) | ||||
| @@ -576,26 +567,26 @@ void CSpell::setupMechanics() | ||||
| 		logGlobal->errorStream() << "Spell " << this->name << " mechanics already set"; | ||||
| 		delete mechanics; | ||||
| 	} | ||||
| 	 | ||||
| 	mechanics = ISpellMechanics::createMechanics(this);	 | ||||
|  | ||||
| 	mechanics = ISpellMechanics::createMechanics(this); | ||||
| } | ||||
|  | ||||
| ///CSpell::AnimationInfo | ||||
| CSpell::AnimationInfo::AnimationInfo() | ||||
| { | ||||
| 	 | ||||
|  | ||||
| } | ||||
|  | ||||
| CSpell::AnimationInfo::~AnimationInfo() | ||||
| { | ||||
| 	 | ||||
|  | ||||
| } | ||||
|  | ||||
| std::string CSpell::AnimationInfo::selectProjectile(const double angle) const | ||||
| {	 | ||||
| 	std::string res;	 | ||||
| { | ||||
| 	std::string res; | ||||
| 	double maximum = 0.0; | ||||
| 	 | ||||
|  | ||||
| 	for(const auto & info : projectile) | ||||
| 	{ | ||||
| 		if(info.minimumAngle < angle && info.minimumAngle > maximum) | ||||
| @@ -604,10 +595,9 @@ std::string CSpell::AnimationInfo::selectProjectile(const double angle) const | ||||
| 			res = info.resourceName; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	return std::move(res);	 | ||||
| } | ||||
|  | ||||
| 	return std::move(res); | ||||
| } | ||||
|  | ||||
| ///CSpell::TargetInfo | ||||
| CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level) | ||||
| @@ -626,7 +616,7 @@ CSpell::TargetInfo::TargetInfo(const CSpell * spell, const int level, ECastingMo | ||||
| 	else if(mode == ECastingMode::SPELL_LIKE_ATTACK) | ||||
| 	{ | ||||
| 		alwaysHitDirectly = true; | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CSpell::TargetInfo::init(const CSpell * spell, const int level) | ||||
| @@ -642,8 +632,7 @@ void CSpell::TargetInfo::init(const CSpell * spell, const int level) | ||||
| 	clearTarget = levelInfo.clearTarget; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos) | ||||
| bool DLL_LINKAGE isInScreenRange(const int3 & center, const int3 & pos) | ||||
| { | ||||
| 	int3 diff = pos - center; | ||||
| 	if(diff.x >= -9  &&  diff.x <= 9  &&  diff.y >= -8  &&  diff.y <= 8) | ||||
| @@ -665,7 +654,7 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize) | ||||
|  | ||||
| 	CLegacyConfigParser parser("DATA/SPTRAITS.TXT"); | ||||
|  | ||||
| 	auto readSchool = [&](JsonMap& schools, const std::string& name) | ||||
| 	auto readSchool = [&](JsonMap & schools, const std::string & name) | ||||
| 	{ | ||||
| 		if (parser.readString() == "x") | ||||
| 		{ | ||||
| @@ -710,14 +699,14 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize) | ||||
|  | ||||
| 			auto& chances = lineNode["gainChance"].Struct(); | ||||
|  | ||||
| 			for(size_t i = 0; i < GameConstants::F_NUMBER ; i++){ | ||||
| 			for(size_t i = 0; i < GameConstants::F_NUMBER; i++){ | ||||
| 				chances[ETownType::names[i]].Float() = parser.readNumber(); | ||||
| 			} | ||||
|  | ||||
| 			auto AIVals = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS); | ||||
|  | ||||
| 			std::vector<std::string> descriptions; | ||||
| 			for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS ; i++) | ||||
| 			for(size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++) | ||||
| 				descriptions.push_back(parser.readString()); | ||||
|  | ||||
| 			parser.readString(); //ignore attributes. All data present in JSON | ||||
| @@ -733,8 +722,6 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize) | ||||
| 			} | ||||
|  | ||||
| 			legacyData.push_back(lineNode); | ||||
|  | ||||
|  | ||||
| 		} | ||||
| 		while (parser.endLine() && !parser.isNextEntryEmpty()); | ||||
| 	}; | ||||
| @@ -768,7 +755,7 @@ const std::string CSpellHandler::getTypeName() const | ||||
| 	return "spell"; | ||||
| } | ||||
|  | ||||
| CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
| CSpell * CSpellHandler::loadFromJson(const JsonNode & json) | ||||
| { | ||||
| 	using namespace SpellConfig; | ||||
|  | ||||
| @@ -792,7 +779,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
| 	logGlobal->traceStream() << __FUNCTION__ << ": loading spell " << spell->name; | ||||
|  | ||||
| 	const auto schoolNames = json["school"]; | ||||
| 	 | ||||
|  | ||||
| 	for(const SpellSchoolInfo & info : SpellConfig::SCHOOL) | ||||
| 	{ | ||||
| 		spell->school[info.id] = schoolNames[info.jsonName].Bool(); | ||||
| @@ -823,7 +810,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
| 		spell->targetType = CSpell::OBSTACLE; | ||||
| 	else if(targetType == "LOCATION") | ||||
| 		spell->targetType = CSpell::LOCATION; | ||||
| 	else  | ||||
| 	else | ||||
| 		logGlobal->warnStream() << "Spell " << spell->name << ". Target type " << (targetType.empty() ? "empty" : "unknown ("+targetType+")") << ". Assumed NO_TARGET."; | ||||
|  | ||||
| 	for(const auto & counteredSpell: json["counters"].Struct()) | ||||
| @@ -874,7 +861,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
|  | ||||
| 	spell->isSpecial = flags["special"].Bool(); | ||||
|  | ||||
| 	auto findBonus = [&](std::string name, std::vector<Bonus::BonusType> &vec) | ||||
| 	auto findBonus = [&](std::string name, std::vector<Bonus::BonusType> & vec) | ||||
| 	{ | ||||
| 		auto it = bonusNameMap.find(name); | ||||
| 		if(it == bonusNameMap.end()) | ||||
| @@ -887,7 +874,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	auto readBonusStruct = [&](std::string name, std::vector<Bonus::BonusType> &vec) | ||||
| 	auto readBonusStruct = [&](std::string name, std::vector<Bonus::BonusType> & vec) | ||||
| 	{ | ||||
| 		for(auto bonusData: json[name].Struct()) | ||||
| 		{ | ||||
| @@ -901,7 +888,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
|  | ||||
| 	readBonusStruct("immunity", spell->immunities); | ||||
| 	readBonusStruct("absoluteImmunity", spell->absoluteImmunities); | ||||
| 	readBonusStruct("limit", spell->limiters);	 | ||||
| 	readBonusStruct("limit", spell->limiters); | ||||
| 	readBonusStruct("absoluteLimit", spell->absoluteLimiters); | ||||
|  | ||||
|  | ||||
| @@ -914,41 +901,41 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
| 	spell->iconScroll = graphicsNode["iconScroll"].String(); | ||||
|  | ||||
| 	const JsonNode & animationNode = json["animation"]; | ||||
| 	 | ||||
| 	auto loadAnimationQueue = [&](const std::string & jsonName, CSpell::TAnimationQueue & q)	 | ||||
|  | ||||
| 	auto loadAnimationQueue = [&](const std::string & jsonName, CSpell::TAnimationQueue & q) | ||||
| 	{ | ||||
| 		auto queueNode = animationNode[jsonName].Vector();		 | ||||
| 		auto queueNode = animationNode[jsonName].Vector(); | ||||
| 		for(const JsonNode & item : queueNode) | ||||
| 		{	 | ||||
| 		{ | ||||
| 			CSpell::TAnimation newItem; | ||||
| 			newItem.verticalPosition = VerticalPosition::TOP; | ||||
| 			 | ||||
|  | ||||
| 			if(item.getType() == JsonNode::DATA_STRING) | ||||
| 				newItem.resourceName = item.String(); | ||||
| 			else if(item.getType() == JsonNode::DATA_STRUCT) | ||||
| 			{ | ||||
| 				newItem.resourceName = item["defName"].String(); | ||||
| 				 | ||||
|  | ||||
| 				auto vPosStr = item["verticalPosition"].String(); | ||||
| 				if("bottom" == vPosStr) | ||||
| 					newItem.verticalPosition = VerticalPosition::BOTTOM; | ||||
| 			}	 | ||||
| 			q.push_back(newItem);		 | ||||
| 			} | ||||
| 			q.push_back(newItem); | ||||
| 		} | ||||
| 	}; | ||||
| 	 | ||||
|  | ||||
| 	loadAnimationQueue("affect", spell->animationInfo.affect); | ||||
| 	loadAnimationQueue("cast", spell->animationInfo.cast); | ||||
| 	loadAnimationQueue("hit", spell->animationInfo.hit);	 | ||||
| 	 | ||||
| 	loadAnimationQueue("hit", spell->animationInfo.hit); | ||||
|  | ||||
| 	const JsonVector & projectile = animationNode["projectile"].Vector(); | ||||
| 	 | ||||
|  | ||||
| 	for(const JsonNode & item : projectile) | ||||
| 	{ | ||||
| 		CSpell::ProjectileInfo info; | ||||
| 		info.resourceName = item["defName"].String(); | ||||
| 		info.minimumAngle = item["minimumAngle"].Float(); | ||||
| 		 | ||||
|  | ||||
| 		spell->animationInfo.projectile.push_back(info); | ||||
| 	} | ||||
|  | ||||
| @@ -963,19 +950,19 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
| 	for(int levelIndex = 0; levelIndex < levelsCount; levelIndex++) | ||||
| 	{ | ||||
| 		const JsonNode & levelNode = json["levels"][LEVEL_NAMES[levelIndex]]; | ||||
| 		 | ||||
|  | ||||
| 		CSpell::LevelInfo & levelObject = spell->levels[levelIndex]; | ||||
|  | ||||
| 		const si32 levelPower     = levelObject.power = levelNode["power"].Float();		 | ||||
| 		const si32 levelPower     = levelObject.power = levelNode["power"].Float(); | ||||
|  | ||||
| 		levelObject.description   = levelNode["description"].String(); | ||||
| 		levelObject.cost          = levelNode["cost"].Float(); | ||||
| 		levelObject.AIValue       = levelNode["aiValue"].Float(); | ||||
| 		levelObject.smartTarget   = levelNode["targetModifier"]["smart"].Bool(); | ||||
| 		levelObject.clearTarget   = levelNode["targetModifier"]["clearTarget"].Bool(); | ||||
| 		levelObject.clearAffected = levelNode["targetModifier"]["clearAffected"].Bool();			 | ||||
| 		levelObject.clearAffected = levelNode["targetModifier"]["clearAffected"].Bool(); | ||||
| 		levelObject.range         = levelNode["range"].String(); | ||||
| 		 | ||||
|  | ||||
| 		for(const auto & elem : levelNode["effects"].Struct()) | ||||
| 		{ | ||||
| 			const JsonNode & bonusNode = elem.second; | ||||
| @@ -992,7 +979,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json) | ||||
|  | ||||
| 			levelObject.effects.push_back(*b); | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return spell; | ||||
| @@ -1013,19 +1000,18 @@ void CSpellHandler::afterLoadFinalization() | ||||
| void CSpellHandler::beforeValidate(JsonNode & object) | ||||
| { | ||||
| 	//handle "base" level info | ||||
| 	 | ||||
| 	JsonNode& levels = object["levels"]; | ||||
| 	JsonNode& base = levels["base"]; | ||||
| 	 | ||||
|  | ||||
| 	JsonNode & levels = object["levels"]; | ||||
| 	JsonNode & base = levels["base"]; | ||||
|  | ||||
| 	auto inheritNode = [&](const std::string & name){ | ||||
| 		JsonUtils::inherit(levels[name],base); | ||||
| 	}; | ||||
| 	 | ||||
|  | ||||
| 	inheritNode("none"); | ||||
| 	inheritNode("basic"); | ||||
| 	inheritNode("advanced"); | ||||
| 	inheritNode("expert"); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,3 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../IHandlerBase.h" | ||||
| #include "../ConstTransitivePtr.h" | ||||
| #include "../int3.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../BattleHex.h" | ||||
| #include "../HeroBonus.h" | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * CSpellHandler.h, part of VCMI engine | ||||
|  * | ||||
| @@ -18,22 +8,25 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| class CGObjectInstance; | ||||
| #pragma once | ||||
|  | ||||
| #include "../IHandlerBase.h" | ||||
| #include "../ConstTransitivePtr.h" | ||||
| #include "../int3.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../BattleHex.h" | ||||
| #include "../HeroBonus.h" | ||||
|  | ||||
| class CGObjectInstance; | ||||
| class CSpell; | ||||
| class ISpellMechanics; | ||||
|  | ||||
| class CLegacyConfigParser; | ||||
|  | ||||
| class CGHeroInstance; | ||||
| class CStack; | ||||
|  | ||||
| class CBattleInfoCallback; | ||||
| class BattleInfo; | ||||
|  | ||||
| struct CPackForClient; | ||||
| struct BattleSpellCast; | ||||
|  | ||||
| class CGameInfoCallback; | ||||
| class CRandomGenerator; | ||||
| class CMap; | ||||
| @@ -42,10 +35,10 @@ struct SpellSchoolInfo | ||||
| { | ||||
| 	ESpellSchool id; //backlink | ||||
| 	Bonus::BonusType damagePremyBonus; | ||||
| 	Bonus::BonusType immunityBonus;	 | ||||
| 	Bonus::BonusType immunityBonus; | ||||
| 	std::string jsonName; | ||||
| 	SecondarySkill::ESecondarySkill skill; | ||||
| 	Bonus::BonusType knoledgeBonus;			 | ||||
| 	Bonus::BonusType knoledgeBonus; | ||||
| }; | ||||
|  | ||||
| ///callback to be provided by server | ||||
| @@ -54,13 +47,13 @@ class DLL_LINKAGE SpellCastEnvironment | ||||
| public: | ||||
| 	virtual ~SpellCastEnvironment(){}; | ||||
| 	virtual void sendAndApply(CPackForClient * info) const = 0; | ||||
| 	 | ||||
|  | ||||
| 	virtual CRandomGenerator & getRandomGenerator() const = 0; | ||||
| 	virtual void complain(const std::string & problem) const = 0; | ||||
| 	 | ||||
|  | ||||
| 	virtual const CMap * getMap() const = 0; | ||||
| 	virtual const CGameInfoCallback * getCb() const = 0;	 | ||||
| 	 | ||||
| 	virtual const CGameInfoCallback * getCb() const = 0; | ||||
|  | ||||
| 	virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const =0;	//TODO: remove | ||||
| }; | ||||
|  | ||||
| @@ -78,14 +71,14 @@ public: | ||||
| 	int usedSpellPower; | ||||
| 	ECastingMode::ECastingMode mode; | ||||
| 	const CStack * casterStack; | ||||
| 	const CStack * selectedStack;	 | ||||
| 	const BattleInfo * cb;		 | ||||
| 	const CStack * selectedStack; | ||||
| 	const BattleInfo * cb; | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE AdventureSpellCastParameters | ||||
| { | ||||
| 	const CGHeroInstance * caster; | ||||
| 	int3 pos;	 | ||||
| 	int3 pos; | ||||
| }; | ||||
|  | ||||
| enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM}; | ||||
| @@ -93,41 +86,40 @@ enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM}; | ||||
| class DLL_LINKAGE CSpell | ||||
| { | ||||
| public: | ||||
| 	 | ||||
| 	struct ProjectileInfo | ||||
| 	{ | ||||
| 		///in radians. Only positive value. Negative angle is handled by vertical flip | ||||
| 		double minimumAngle;  | ||||
| 		 | ||||
| 		double minimumAngle; | ||||
|  | ||||
| 		///resource name | ||||
| 		std::string resourceName; | ||||
| 		  | ||||
| 		template <typename Handler> void serialize(Handler &h, const int version) | ||||
|  | ||||
| 		template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 		{ | ||||
| 			h & minimumAngle & resourceName;  | ||||
| 		}		 | ||||
| 			h & minimumAngle & resourceName; | ||||
| 		} | ||||
| 	}; | ||||
| 	 | ||||
|  | ||||
| 	struct AnimationItem | ||||
| 	{ | ||||
| 		std::string resourceName; | ||||
| 		VerticalPosition verticalPosition; | ||||
| 		 | ||||
| 		template <typename Handler> void serialize(Handler &h, const int version) | ||||
|  | ||||
| 		template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 		{ | ||||
| 			h & resourceName & verticalPosition;  | ||||
| 		}		 | ||||
| 			h & resourceName & verticalPosition; | ||||
| 		} | ||||
| 	}; | ||||
| 	 | ||||
|  | ||||
| 	typedef AnimationItem TAnimation; | ||||
| 	typedef std::vector<TAnimation> TAnimationQueue;  | ||||
| 	 | ||||
| 	typedef std::vector<TAnimation> TAnimationQueue; | ||||
|  | ||||
| 	struct DLL_LINKAGE AnimationInfo | ||||
| 	{ | ||||
| 		AnimationInfo(); | ||||
| 		~AnimationInfo(); | ||||
|  | ||||
| 		///displayed on all affected targets.  | ||||
| 		///displayed on all affected targets. | ||||
| 		TAnimationQueue affect; | ||||
|  | ||||
| 		///displayed on caster. | ||||
| @@ -140,14 +132,13 @@ public: | ||||
| 		///use selectProjectile to access | ||||
| 		std::vector<ProjectileInfo> projectile; | ||||
|  | ||||
| 		template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 		template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 		{ | ||||
| 			h & projectile & hit & cast; | ||||
| 		} | ||||
|  | ||||
| 		std::string selectProjectile(const double angle) const; | ||||
| 	} animationInfo; | ||||
| 	 | ||||
| public: | ||||
| 	struct LevelInfo | ||||
| 	{ | ||||
| @@ -192,13 +183,13 @@ public: | ||||
| 		bool onlyAlive; | ||||
| 		///no immunity on primary target (mostly spell-like attack) | ||||
| 		bool alwaysHitDirectly; | ||||
| 		 | ||||
|  | ||||
| 		bool clearTarget; | ||||
| 		bool clearAffected; | ||||
| 		 | ||||
|  | ||||
| 		TargetInfo(const CSpell * spell, const int level); | ||||
| 		TargetInfo(const CSpell * spell, const int level, ECastingMode::ECastingMode mode); | ||||
| 		 | ||||
|  | ||||
| 	private: | ||||
| 		void init(const CSpell * spell, const int level); | ||||
| 	}; | ||||
| @@ -210,7 +201,7 @@ public: | ||||
| 	si32 level; | ||||
|  | ||||
| 	std::map<ESpellSchool, bool> school; //todo: use this instead of separate boolean fields | ||||
| 	 | ||||
|  | ||||
| 	si32 power; //spell's power | ||||
|  | ||||
| 	std::map<TFaction, si32> probabilities; //% chance to gain for castles | ||||
| @@ -223,16 +214,14 @@ public: | ||||
|  | ||||
| 	CSpell(); | ||||
| 	~CSpell(); | ||||
| 	 | ||||
| 	bool isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const; | ||||
| 	 | ||||
|  | ||||
| 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr ) const; //convert range to specific hexes; last optional out parameter is set to true, if spell would cover unavailable hexes (that are not included in ret) | ||||
| 	bool isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const; | ||||
|  | ||||
| 	std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr ) const; //convert range to specific hexes; last optional out parameter is set to true, if spell would cover unavailable hexes (that are not included in ret) | ||||
| 	ETargetType getTargetType() const; //deprecated | ||||
|  | ||||
| 	CSpell::TargetInfo getTargetInfo(const int level) const; | ||||
|  | ||||
|  | ||||
| 	bool isCombatSpell() const; | ||||
| 	bool isAdventureSpell() const; | ||||
| 	bool isCreatureAbility() const; | ||||
| @@ -243,29 +232,29 @@ public: | ||||
|  | ||||
| 	bool isDamageSpell() const; | ||||
| 	bool isHealingSpell() const; | ||||
| 	bool isRisingSpell() const;	 | ||||
| 	bool isRisingSpell() const; | ||||
| 	bool isOffensiveSpell() const; | ||||
|  | ||||
| 	bool isSpecialSpell() const; | ||||
|  | ||||
| 	bool hasEffects() const; | ||||
| 	void getEffects(std::vector<Bonus> &lst, const int level) 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 isImmuneAt(const CBattleInfoCallback * cb, const CGHeroInstance * caster, ECastingMode::ECastingMode mode, BattleHex destination) const; | ||||
| 	 | ||||
|  | ||||
| 	//internal, for use only by Mechanics classes | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const; | ||||
| 	 | ||||
|  | ||||
| 	//checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc. | ||||
| 	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; | ||||
| 	 | ||||
|  | ||||
| 	///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account | ||||
| 	ui32 calculateDamage(const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) 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; | ||||
|  | ||||
| @@ -282,7 +271,7 @@ public: | ||||
| 	 * Calls cb for each school this spell belongs to | ||||
| 	 * | ||||
| 	 * Set stop to true to abort looping | ||||
| 	 */	 | ||||
| 	 */ | ||||
| 	void forEachSchool(const std::function<void (const SpellSchoolInfo &, bool &)> & cb) const; | ||||
|  | ||||
| 	/** | ||||
| @@ -303,8 +292,8 @@ public: | ||||
| 		h & defaultProbability; | ||||
| 		h & isSpecial; | ||||
| 		h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll; | ||||
| 		h & levels;		 | ||||
| 		h & school;		 | ||||
| 		h & levels; | ||||
| 		h & school; | ||||
| 		h & animationInfo; | ||||
|  | ||||
| 		if(!h.saving) | ||||
| @@ -315,21 +304,21 @@ public: | ||||
| public: | ||||
| 	///Server logic. Has write access to GameState via packets. | ||||
| 	///May be executed on client side by (future) non-cheat-proof scripts. | ||||
| 	 | ||||
| 	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const;  | ||||
| 	void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; 	 | ||||
| 		 | ||||
| public:	 | ||||
|  | ||||
| 	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; | ||||
| 	void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const; | ||||
|  | ||||
| public: | ||||
| 	///Client-server logic. Has direct write access to GameState. | ||||
| 	///Shall be called (only) when applying packets on BOTH SIDES | ||||
| 	 | ||||
|  | ||||
| 	///implementation of BattleSpellCast applying | ||||
| 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const; | ||||
| 		 | ||||
|  | ||||
| private: | ||||
| 	void setIsOffensive(const bool val); | ||||
| 	void setIsRising(const bool val); | ||||
| 	 | ||||
|  | ||||
| 	//call this after load or deserialization. cant be done in constructor. | ||||
| 	void setup(); | ||||
| 	void setupMechanics(); | ||||
| @@ -351,9 +340,7 @@ private: | ||||
| 	std::vector<Bonus::BonusType> absoluteLimiters; //all of them are required to be affected, can't be negated | ||||
|  | ||||
| 	///graphics related stuff | ||||
|  | ||||
| 	std::string iconImmune; | ||||
|  | ||||
| 	std::string iconBook; | ||||
| 	std::string iconEffect; | ||||
| 	std::string iconScenarioBonus; | ||||
| @@ -363,7 +350,7 @@ private: | ||||
| 	std::string castSound; | ||||
|  | ||||
| 	std::vector<LevelInfo> levels; | ||||
| 	 | ||||
|  | ||||
| 	ISpellMechanics * mechanics;//(!) do not serialize | ||||
| }; | ||||
|  | ||||
| @@ -393,7 +380,7 @@ public: | ||||
| 	{ | ||||
| 		h & objects ; | ||||
| 	} | ||||
| 		 | ||||
|  | ||||
| protected: | ||||
| 	CSpell * loadFromJson(const JsonNode & json) override; | ||||
| }; | ||||
|   | ||||
| @@ -20,7 +20,7 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * | ||||
| { | ||||
| 	//calculating dmg to display | ||||
| 	ctx.sc.dmgToDisplay = parameters.usedSpellPower; | ||||
| 	 | ||||
|  | ||||
| 	for(auto & attackedCre : ctx.attackedCres) //no immunities | ||||
| 	{ | ||||
| 		BattleStackAttacked bsa; | ||||
| @@ -31,7 +31,7 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * | ||||
| 		bsa.attackerID = -1; | ||||
| 		(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator()); | ||||
| 		ctx.si.stacks.push_back(bsa); | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ///DeathStareMechanics | ||||
| @@ -41,7 +41,7 @@ void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, B | ||||
| 	ctx.sc.dmgToDisplay = parameters.usedSpellPower; | ||||
| 	if(!ctx.attackedCres.empty()) | ||||
| 		vstd::amin(ctx.sc.dmgToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack | ||||
| 	 | ||||
|  | ||||
| 	for(auto & attackedCre : ctx.attackedCres) | ||||
| 	{ | ||||
| 		BattleStackAttacked bsa; | ||||
| @@ -52,15 +52,14 @@ void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, B | ||||
| 		bsa.attackerID = -1; | ||||
| 		(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator()); | ||||
| 		ctx.si.stacks.push_back(bsa); | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| ///DispellHelpfulMechanics | ||||
| void DispellHelpfulMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const | ||||
| { | ||||
| 	DefaultSpellMechanics::applyBattle(battle, packet); | ||||
| 	 | ||||
|  | ||||
| 	for(auto stackID : packet->affectedCres) | ||||
| 	{ | ||||
| 		if(vstd::contains(packet->resisted, stackID)) | ||||
| @@ -71,7 +70,7 @@ void DispellHelpfulMechanics::applyBattle(BattleInfo * battle, const BattleSpell | ||||
| 		{ | ||||
| 			return Selector::positiveSpellEffects(b); | ||||
| 		}); | ||||
| 	}	 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance * caster,  const CStack * obj) const | ||||
| @@ -90,7 +89,7 @@ ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(co | ||||
| 	{ | ||||
| 		return ESpellCastProblem::NO_SPELLS_TO_DISPEL; | ||||
| 	} | ||||
| 	 | ||||
| 	//use default algorithm only if there is no mechanics-related problem		 | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,obj);	 | ||||
|  | ||||
| 	//use default algorithm only if there is no mechanics-related problem | ||||
| 	return DefaultSpellMechanics::isImmuneByStack(caster,obj); | ||||
| } | ||||
|   | ||||
| @@ -7,17 +7,17 @@ | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|   | ||||
|  #pragma once | ||||
|   | ||||
|  #include "CDefaultSpellMechanics.h" | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "CDefaultSpellMechanics.h" | ||||
|  | ||||
| class AcidBreathDamageMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	AcidBreathDamageMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;		 | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; | ||||
| }; | ||||
|  | ||||
| class DeathStareMechanics: public DefaultSpellMechanics | ||||
| @@ -25,15 +25,15 @@ class DeathStareMechanics: public DefaultSpellMechanics | ||||
| public: | ||||
| 	DeathStareMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| protected: | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;		 | ||||
| 	void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; | ||||
| }; | ||||
|  | ||||
| class DispellHelpfulMechanics: public DefaultSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	DispellHelpfulMechanics(CSpell * s): DefaultSpellMechanics(s){}; | ||||
| 	 | ||||
|  | ||||
| 	void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override; | ||||
| 	 | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;	 | ||||
|  | ||||
| 	ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override; | ||||
| }; | ||||
|   | ||||
| @@ -17,12 +17,11 @@ | ||||
| #include "BattleSpellMechanics.h" | ||||
| #include "CreatureSpellMechanics.h" | ||||
|  | ||||
|  | ||||
| ///ISpellMechanics | ||||
| ISpellMechanics::ISpellMechanics(CSpell * s): | ||||
| 	owner(s) | ||||
| { | ||||
| 	 | ||||
|  | ||||
| } | ||||
|  | ||||
| ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s) | ||||
| @@ -32,20 +31,20 @@ ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s) | ||||
| 	case SpellID::ACID_BREATH_DAMAGE: | ||||
| 		return new AcidBreathDamageMechanics(s); | ||||
| 	case SpellID::CHAIN_LIGHTNING: | ||||
| 		return new ChainLightningMechanics(s);		 | ||||
| 		return new ChainLightningMechanics(s); | ||||
| 	case SpellID::CLONE: | ||||
| 		return new CloneMechanics(s); | ||||
| 	case SpellID::CURE: | ||||
| 		return new CureMechanics(s); | ||||
| 	case SpellID::DEATH_STARE: | ||||
| 		return new DeathStareMechanics(s);			 | ||||
| 		return new DeathStareMechanics(s); | ||||
| 	case SpellID::DISPEL: | ||||
| 		return new DispellMechanics(s);	 | ||||
| 		return new DispellMechanics(s); | ||||
| 	case SpellID::DISPEL_HELPFUL_SPELLS: | ||||
| 		return new DispellHelpfulMechanics(s); | ||||
| 	case SpellID::FIRE_WALL: | ||||
| 	case SpellID::FORCE_FIELD: | ||||
| 		return new WallMechanics(s);		 | ||||
| 		return new WallMechanics(s); | ||||
| 	case SpellID::HYPNOTIZE: | ||||
| 		return new HypnotizeMechanics(s); | ||||
| 	case SpellID::LAND_MINE: | ||||
| @@ -61,11 +60,11 @@ ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s) | ||||
| 	case SpellID::SUMMON_AIR_ELEMENTAL: | ||||
| 		return new SummonMechanics(s); | ||||
| 	case SpellID::TELEPORT: | ||||
| 		return new TeleportMechanics(s);	 | ||||
| 		return new TeleportMechanics(s); | ||||
| 	case SpellID::SUMMON_BOAT: | ||||
| 		return new SummonBoatMechanics(s); | ||||
| 	case SpellID::SCUTTLE_BOAT:		 | ||||
| 		return new ScuttleBoatMechanics(s);		 | ||||
| 	case SpellID::SCUTTLE_BOAT: | ||||
| 		return new ScuttleBoatMechanics(s); | ||||
| 	case SpellID::DIMENSION_DOOR: | ||||
| 		return new DimensionDoorMechanics(s); | ||||
| 	case SpellID::FLY: | ||||
| @@ -77,12 +76,12 @@ ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s) | ||||
| 		return new TownPortalMechanics(s); | ||||
| 	case SpellID::VIEW_EARTH: | ||||
| 		return new ViewEarthMechanics(s); | ||||
| 	case SpellID::VIEW_AIR:			 | ||||
| 	case SpellID::VIEW_AIR: | ||||
| 		return new ViewAirMechanics(s); | ||||
| 	default:		 | ||||
| 	default: | ||||
| 		if(s->isRisingSpell()) | ||||
| 			return new SpecialRisingSpellMechanics(s); | ||||
| 		else	 | ||||
| 			return new DefaultSpellMechanics(s);		 | ||||
| 	}	 | ||||
| 		else | ||||
| 			return new DefaultSpellMechanics(s); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|   | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "CSpellHandler.h" | ||||
| @@ -16,39 +16,35 @@ | ||||
| class DLL_LINKAGE ISpellMechanics | ||||
| { | ||||
| public: | ||||
| 	 | ||||
| 	struct DLL_LINKAGE SpellTargetingContext | ||||
| 	{ | ||||
| 		const CBattleInfoCallback * cb;		 | ||||
| 		const CBattleInfoCallback * cb; | ||||
| 		CSpell::TargetInfo ti; | ||||
| 		ECastingMode::ECastingMode mode; | ||||
| 		BattleHex destination; | ||||
| 		PlayerColor casterColor; | ||||
| 		int schoolLvl; | ||||
| 		 | ||||
|  | ||||
| 		SpellTargetingContext(const CSpell * s, const CBattleInfoCallback * c, ECastingMode::ECastingMode m, PlayerColor cc, int lvl, BattleHex dest) | ||||
| 			: cb(c), ti(s,lvl, m), mode(m), destination(dest), casterColor(cc), schoolLvl(lvl) | ||||
| 		{}; | ||||
| 		 | ||||
|  | ||||
| 	}; | ||||
| 	 | ||||
| public: | ||||
| 	ISpellMechanics(CSpell * s); | ||||
| 	virtual ~ISpellMechanics(){};	 | ||||
| 	 | ||||
| 	virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const = 0; | ||||
| 	virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0; | ||||
| 	 | ||||
| 	virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0; | ||||
|                     | ||||
| 	virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;  | ||||
| 	virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0; 	 | ||||
| 	 | ||||
| 	static ISpellMechanics * createMechanics(CSpell * s); | ||||
| 	 | ||||
| 	virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0; | ||||
| 	 | ||||
| protected: | ||||
| 	CSpell * owner;	 | ||||
| }; | ||||
| 	virtual ~ISpellMechanics(){}; | ||||
|  | ||||
| 	virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0; | ||||
| 	virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0; | ||||
|  | ||||
| 	virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0; | ||||
|  | ||||
| 	virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0; | ||||
| 	virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0; | ||||
|  | ||||
| 	static ISpellMechanics * createMechanics(CSpell * s); | ||||
|  | ||||
| 	virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0; | ||||
| protected: | ||||
| 	CSpell * owner; | ||||
| }; | ||||
|   | ||||
| @@ -17,11 +17,11 @@ | ||||
| ObjectPosInfo::ObjectPosInfo(): | ||||
| 	pos(),id(Obj::NO_OBJ), subId(-1), owner(PlayerColor::CANNOT_DETERMINE) | ||||
| { | ||||
| 	 | ||||
|  | ||||
| } | ||||
|  | ||||
| ObjectPosInfo::ObjectPosInfo(const CGObjectInstance * obj): | ||||
| 	pos(obj->pos),id(obj->ID), subId(obj->subID), owner(obj->tempOwner) | ||||
| { | ||||
| 	 | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -7,26 +7,26 @@ | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|   | ||||
|  | ||||
|  #pragma once | ||||
|   | ||||
|  #include "../int3.h"  | ||||
|  | ||||
|  #include "../int3.h" | ||||
|  #include "../GameConstants.h" | ||||
|   | ||||
|  | ||||
|  class CGObjectInstance; | ||||
|   | ||||
|  | ||||
|  struct DLL_LINKAGE ObjectPosInfo | ||||
|  { | ||||
|  	int3 pos; 	 | ||||
|  	int3 pos; | ||||
|  	Obj id; | ||||
|  	si32 subId; | ||||
|  	PlayerColor owner; | ||||
|  	ObjectPosInfo(); | ||||
|  	ObjectPosInfo(const CGObjectInstance * obj); | ||||
|  	 | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 	{ | ||||
| 		h & pos & id & subId & owner; | ||||
| 	} 	 | ||||
| 	} | ||||
|  }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user