Преглед изворни кода

Allow multiple destinations in BattleSpellCastParameters

AlexVinS пре 10 година
родитељ
комит
57e5b768e8

+ 18 - 6
lib/spells/BattleSpellMechanics.cpp

@@ -367,7 +367,7 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const I
 ///ObstacleMechanics
 void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	auto placeObstacle = [&, this](BattleHex pos)
+	auto placeObstacle = [&, this](const BattleHex & pos)
 	{
 		static int obstacleIdToGive =  parameters.cb->obstacles.size()
 									? (parameters.cb->obstacles.back()->uniqueID+1)
@@ -413,6 +413,8 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, con
 		env->sendAndApply(&bop);
 	};
 
+	const BattleHex destination = parameters.getFirstDestinationHex();
+
 	switch(owner->id)
 	{
 	case SpellID::QUICKSAND:
@@ -437,12 +439,22 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, con
 
 		break;
 	case SpellID::FORCE_FIELD:
-		placeObstacle(parameters.destination);
+		if(!destination.isValid())
+		{
+			env->complain("Invalid destination for FORCE_FIELD");
+			return;
+		}
+		placeObstacle(destination);
 		break;
 	case SpellID::FIRE_WALL:
 		{
+			if(!destination.isValid())
+			{
+				env->complain("Invalid destination for FIRE_WALL");
+				return;
+			}
 			//fire wall is build from multiple obstacles - one fire piece for each affected hex
-			auto affectedHexes = owner->rangeInHexes(parameters.destination, parameters.spellLvl, parameters.casterSide);
+			auto affectedHexes = owner->rangeInHexes(destination, parameters.spellLvl, parameters.casterSide);
 			for(BattleHex hex : affectedHexes)
 				placeObstacle(hex);
 		}
@@ -493,7 +505,7 @@ std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 sch
 ///RemoveObstacleMechanics
 void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.destination, false))
+	if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.getFirstDestinationHex(), false))
 	{
 		ObstaclesRemoved obr;
 		obr.obstacles.insert(obstacleToRemove->uniqueID);
@@ -659,9 +671,9 @@ void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, con
 	
 	BattleStackMoved bsm;
 	bsm.distance = -1;
-	bsm.stack = parameters.selectedStack->ID;
+	bsm.stack = parameters.selectedStack->ID;//todo: use destinations
 	std::vector<BattleHex> tiles;
-	tiles.push_back(parameters.destination);
+	tiles.push_back(parameters.getFirstDestinationHex());
 	bsm.tilesToMove = tiles;
 	bsm.teleporting = true;
 	env->sendAndApply(&bsm);

+ 6 - 5
lib/spells/CDefaultSpellMechanics.cpp

@@ -265,7 +265,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
 	//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);
+	auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, parameters.getFirstDestinationHex(), parameters.caster);
 	std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
 
 	logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks";
@@ -363,7 +363,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
 
 			BattleSpellCastParameters mirrorParameters(parameters.cb, attackedCre, owner);
 			mirrorParameters.spellLvl = 0;
-			mirrorParameters.destination = targetHex;
+			mirrorParameters.aimToHex(targetHex);
 			mirrorParameters.mode = ECastingMode::MAGIC_MIRROR;
 			mirrorParameters.selectedStack = nullptr;
 			mirrorParameters.spellLvl = parameters.spellLvl;
@@ -766,7 +766,8 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, Bat
 		env->complain("MagicMirror: invalid mode");
 		return;
 	}
-	if(!parameters.destination.isValid())
+	BattleHex destination = parameters.getFirstDestinationHex();
+	if(!destination.isValid())
 	{
 		env->complain("MagicMirror: invalid destination");
 		return;		
@@ -779,7 +780,7 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, Bat
 	//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);
+	auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, destination, parameters.caster);
 	std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
 
 	logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks";
@@ -839,7 +840,7 @@ void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& p
 	sc.side = parameters.casterSide;
 	sc.id = owner->id;
 	sc.skill = parameters.spellLvl;
-	sc.tile = parameters.destination;
+	sc.tile = parameters.getFirstDestinationHex();
 	sc.dmgToDisplay = 0;
 	sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING;
 	sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);

+ 6 - 2
lib/spells/CSpellHandler.cpp

@@ -115,9 +115,13 @@ bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastP
 }
 
 void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
-{
+{	
 	assert(env);
-
+	if(parameters.destinations.size()<1)
+	{
+		env->complain("Spell must have at least one destination");
+		return;
+	}
 	mechanics->battleCast(env, parameters);
 }
 

+ 31 - 1
lib/spells/ISpellMechanics.cpp

@@ -20,9 +20,23 @@
 #include "BattleSpellMechanics.h"
 #include "CreatureSpellMechanics.h"
 
+BattleSpellCastParameters::Destination::Destination(const CStack * destination):
+	stackValue(destination),
+	hexValue(destination->position)
+{
+	
+}
+
+BattleSpellCastParameters::Destination::Destination(const BattleHex & destination):
+	stackValue(nullptr),
+	hexValue(destination)	
+{
+	
+}
+
 BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell)
 	: cb(cb), caster(caster), casterColor(caster->getOwner()), casterSide(cb->whatSide(casterColor)),
-	destination(BattleHex::INVALID),casterHero(nullptr),
+	casterHero(nullptr),
 	mode(ECastingMode::HERO_CASTING), casterStack(nullptr), selectedStack(nullptr),
 	spellLvl(-1),  effectLevel(-1), effectPower(0), enchantPower(0), effectValue(0)
 {
@@ -31,6 +45,22 @@ BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo * cb, cons
 	prepare(spell);
 }
 
+void BattleSpellCastParameters::aimToHex(const BattleHex& destination)
+{
+	destinations.push_back(Destination(destination));
+}
+
+void BattleSpellCastParameters::aimToStack(const CStack * destination)
+{
+	destinations.push_back(Destination(destination));
+}
+
+
+BattleHex BattleSpellCastParameters::getFirstDestinationHex() const
+{
+	return destinations.at(0).hexValue;
+}
+
 void BattleSpellCastParameters::prepare(const CSpell * spell)
 {
 	spellLvl = caster->getSpellSchoolLevel(spell);

+ 19 - 3
lib/spells/ISpellMechanics.h

@@ -30,21 +30,37 @@ public:
 	virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const =0;	//TODO: remove
 };
 
-///helper struct
+///all parameters of particular cast event
 struct DLL_LINKAGE BattleSpellCastParameters
 {
 public:
+	///Single spell destination. 
+	/// (assumes that anything but battle stack can share same hex)
+	struct DLL_LINKAGE Destination
+	{
+		explicit Destination(const CStack * destination); 
+		explicit Destination(const BattleHex & destination);
+		
+		const CStack * stackValue;
+		const BattleHex hexValue;
+	};
+
 	BattleSpellCastParameters(const BattleInfo * cb, const ISpellCaster * caster, const CSpell * spell);
+	void aimToHex(const BattleHex & destination);
+	void aimToStack(const CStack * destination);
+	BattleHex getFirstDestinationHex() const;
+	
 	const BattleInfo * cb;
 	const ISpellCaster * caster;
 	const PlayerColor casterColor;	
 	const ui8 casterSide;
 
-	BattleHex destination;
+	std::vector<Destination> destinations;
+
 	const CGHeroInstance * casterHero; //deprecated
 	ECastingMode::ECastingMode mode;
 	const CStack * casterStack; //deprecated
-	const CStack * selectedStack;
+	const CStack * selectedStack;//deprecated
 
 	///spell school level
 	int spellLvl;	

+ 6 - 7
server/CGameHandler.cpp

@@ -3878,7 +3878,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
 				vstd::amin (parameters.spellLvl, 3);
 				parameters.effectLevel = parameters.spellLvl;
 				parameters.mode = ECastingMode::CREATURE_ACTIVE_CASTING;
-				parameters.destination = destination;
+				parameters.aimToHex(destination);//todo: allow multiple destinations
 				parameters.selectedStack = nullptr;
 				spell->battleCast(spellEnv, parameters);
 			}
@@ -4069,7 +4069,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 			const CSpell * s = SpellID(ba.additionalInfo).toSpell();
 			
 			BattleSpellCastParameters parameters(gs->curB, h, s);
-			parameters.destination = ba.destinationTile;
+			parameters.aimToHex(ba.destinationTile);//todo: allow multiple destinations
 			parameters.mode = ECastingMode::HERO_CASTING;
 			parameters.selectedStack = gs->curB->battleGetStackByID(ba.selectedStack, false);			
 
@@ -4222,7 +4222,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
 					BattleSpellCastParameters parameters(gs->curB, st, spell);
 					parameters.spellLvl = bonus->val;
 					parameters.effectLevel = bonus->val;//todo: recheck
-					parameters.destination = BattleHex::INVALID;
+					parameters.aimToHex(BattleHex::INVALID);
 					parameters.mode = ECastingMode::ENCHANTER_CASTING;
 					parameters.selectedStack = nullptr;
 					
@@ -4913,7 +4913,6 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
 			}
 			int chance = attacker->valOfBonuses((Selector::typeSubtype(attackMode, spellID)));
 			vstd::amin (chance, 100);
-			int destination = oneOfAttacked->position;
 
 			const CSpell * spell = SpellID(spellID).toSpell();
 			if(gs->curB->battleCanCastThisSpellHere(attacker, spell, ECastingMode::AFTER_ATTACK_CASTING, oneOfAttacked->position) != ESpellCastProblem::OK)
@@ -4929,7 +4928,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
 				BattleSpellCastParameters parameters(gs->curB, attacker, spell);
 				parameters.spellLvl = spellLevel;
 				parameters.effectLevel = spellLevel;
-				parameters.destination = destination;
+				parameters.aimToStack(oneOfAttacked);
 				parameters.mode = ECastingMode::AFTER_ATTACK_CASTING;
 				parameters.selectedStack = nullptr;
 
@@ -4958,7 +4957,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 		BattleSpellCastParameters parameters(gs->curB, attacker, spell);
 		parameters.spellLvl = 0;
 		parameters.effectLevel = 0;
-		parameters.destination = gs->curB->battleGetStackByID(bat.bsa.at(0).stackAttacked)->position;
+		parameters.aimToStack(gs->curB->battleGetStackByID(bat.bsa.at(0).stackAttacked));
 		parameters.effectPower = power;	
 		parameters.mode = ECastingMode::AFTER_ATTACK_CASTING;
 		parameters.selectedStack = nullptr;
@@ -5266,7 +5265,7 @@ void CGameHandler::runBattle()
 				BattleSpellCastParameters parameters(gs->curB, h, spell);
 				parameters.spellLvl = 3;
 				parameters.effectLevel = 3;
-				parameters.destination = BattleHex::INVALID;
+				parameters.aimToHex(BattleHex::INVALID);
 				parameters.mode = ECastingMode::PASSIVE_CASTING;
 				parameters.selectedStack = nullptr;					
 				parameters.enchantPower = b->val;