Răsfoiți Sursa

Fix handling damage when stack wait in moat.

Currently stack don't take any damage from moat when he stay in same
place.
FeniksFire 8 ani în urmă
părinte
comite
43d324b561

+ 18 - 0
lib/battle/CBattleInfoCallback.cpp

@@ -843,6 +843,24 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
 	return obstacles;
 }
 
+std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const CStack * stack) const
+{
+	std::vector<std::shared_ptr<const CObstacleInstance>> affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
+	RETURN_IF_NOT_BATTLE(affectedObstacles);
+	if(stack->alive())
+	{
+		affectedObstacles = battleGetAllObstaclesOnPos(stack->position, false);
+		if(stack->doubleWide())
+		{
+			BattleHex otherHex = stack->occupiedHex(stack->position);
+			if(otherHex.isValid())
+				for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
+					affectedObstacles.push_back(i);
+		}
+	}
+	return affectedObstacles;
+}
+
 AccessibilityInfo CBattleInfoCallback::getAccesibility() const
 {
 	AccessibilityInfo ret;

+ 1 - 0
lib/battle/CBattleInfoCallback.h

@@ -41,6 +41,7 @@ public:
 	boost::optional<int> battleIsFinished() const; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw
 
 	std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands)
+	std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const CStack * stack) const;
 
 	const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const; //returns stack info by given pos
 	void battleGetStackQueue(std::vector<const CStack *> &out, const int howMany, const int turn = 0, int lastMoved = -1) const;

+ 86 - 111
server/CGameHandler.cpp

@@ -1232,7 +1232,6 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 	}
 	else //for non-flying creatures
 	{
-		std::vector<std::shared_ptr<const CObstacleInstance>> obstacle, obstacle2; //obstacle that interrupted movement
 		std::vector<BattleHex> tiles;
 		const int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0);
 		int v = path.first.size()-1;
@@ -1331,16 +1330,14 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 					}
 
 					//if we walked onto something, finalize this portion of stack movement check into obstacle
-					obstacle = battleGetAllObstaclesOnPos(hex, false);
-					if(!obstacle.empty())
+					if(!battleGetAllObstaclesOnPos(hex, false).empty())
 						obstacleHit = true;
 
 					if (curStack->doubleWide())
 					{
 						BattleHex otherHex = curStack->occupiedHex(hex);
-
 						//two hex creature hit obstacle by backside
-						obstacle2 = battleGetAllObstaclesOnPos(otherHex, false);
+						auto obstacle2 = battleGetAllObstaclesOnPos(otherHex, false);
 						if(otherHex.isValid() && !obstacle2.empty())
 							obstacleHit = true;
 					}
@@ -1362,26 +1359,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 			//we don't handle obstacle at the destination tile -> it's handled separately in the if at the end
 			if (curStack->position != dest)
 			{
-				auto processObstacle = [&](std::vector<std::shared_ptr<const CObstacleInstance>> & obs)
-				{
-					if(!obs.empty())
-					{
-						for(auto & i : obs)
-						{
-							//if stack die in explosion or interrupted by obstacle, abort movement
-							if(i->stopsMovement() || !curStack->alive())
-								stackIsMoving = false;
-							else if(stackIsMoving)
-								handleDamageFromObstacle(*i, curStack);
-							i.reset();
-						}
-					}
-				};
-
-				processObstacle(obstacle);
-				if (curStack->alive())
-					processObstacle(obstacle2);
-
+				if(stackIsMoving && start != curStack->position)
+					stackIsMoving = handleDamageFromObstacle(curStack, stackIsMoving);
 				if (gateStateChanging)
 				{
 					if (curStack->position == openGateAtHex)
@@ -1409,26 +1388,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 	}
 
 	//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
-	if(curStack->alive())
-	{
-		auto theLastObstacle = battleGetAllObstaclesOnPos(curStack->position, false);
-		if(curStack->doubleWide())
-		{
-			BattleHex otherHex = curStack->occupiedHex(curStack->position);
-			if(otherHex.isValid())
-				for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
-					theLastObstacle.push_back(i);
-		}
-		bool containDamageFromMoat = false;
-		for(auto & i : theLastObstacle)
-		{
-			if(curStack->alive())
-				if(i->obstacleType != CObstacleInstance::MOAT || !containDamageFromMoat)
-					handleDamageFromObstacle(*i, curStack);
-			if(i->obstacleType == CObstacleInstance::MOAT)
-				containDamageFromMoat = true;
-		}
-	}
+	handleDamageFromObstacle(curStack);
+
 	return ret;
 }
 
@@ -4032,10 +3993,10 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 
 			const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
 
-			if (destinationStack->alive()
-			    && (stack->getCreature()->idNumber == CreatureID::BALLISTA)
-			    && (attackingHero->getSecSkillLevel(SecondarySkill::ARTILLERY) >= SecSkillLevel::ADVANCED)
-			   )
+			if(destinationStack->alive()
+				&& (stack->getCreature()->idNumber == CreatureID::BALLISTA)
+				&& (attackingHero->getSecSkillLevel(SecondarySkill::ARTILLERY) >= SecSkillLevel::ADVANCED)
+			)
 			{
 				BattleAttack bat2;
 				bat2.flags |= BattleAttack::SHOT;
@@ -4133,8 +4094,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 						std::vector<EWallPart::EWallPart> allowedTargets;
 						for (size_t i=0; i< currentHP.size(); i++)
 						{
-							if (currentHP.at(i) != EWallState::DESTROYED &&
-							    currentHP.at(i) != EWallState::NONE)
+							if(currentHP.at(i) != EWallState::DESTROYED &&
+								currentHP.at(i) != EWallState::NONE)
 								allowedTargets.push_back(EWallPart::EWallPart(i));
 						}
 						if (allowedTargets.empty())
@@ -4328,7 +4289,10 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 			break;
 		}
 	}
-	if (ba.stackNumber == gs->curB->activeStack  ||  battleResult.get()) //active stack has moved or battle has finished
+	if(ba.actionType == Battle::DAEMON_SUMMONING || ba.actionType == Battle::WAIT || ba.actionType == Battle::DEFEND
+			|| ba.actionType == Battle::SHOOT || ba.actionType == Battle::MONSTER_SPELL)
+		handleDamageFromObstacle(stack);
+	if(ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
 		battleMadeAction.setn(true);
 	return ok;
 }
@@ -4620,77 +4584,88 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
 	}
 }
 
-void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack)
+bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving)
 {
-	//we want to determine following vars depending on obstacle type
-	int damage = -1;
-	int effect = -1;
-	bool oneTimeObstacle = false;
+	if(!curStack->alive())
+		return false;
+	bool containDamageFromMoat = false;
+	for(auto & obstacle : getAllAffectedObstaclesByStack(curStack))
+	{
+		if(!curStack->alive() || obstacle->stopsMovement() && stackIsMoving == true)
+			return false;
+		//we want to determine following vars depending on obstacle type
+		int damage = -1;
+		int effect = -1;
+		bool oneTimeObstacle = false;
 
-	//helper info
-	const SpellCreatedObstacle *spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(&obstacle); //not nice but we may need spell params
+		//helper info
+		const SpellCreatedObstacle * spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle.get()); //not nice but we may need spell params
 
-	const ui8 side = curStack->side; //if enemy is defending (false = 0), side of enemy hero is 1 (true)
-	const CGHeroInstance *hero = gs->curB->battleGetFightingHero(side);//FIXME: there may be no hero - landmines in Tower
+		const ui8 side = curStack->side; //if enemy is defending (false = 0), side of enemy hero is 1 (true)
+		const CGHeroInstance * hero = gs->curB->battleGetFightingHero(side);//FIXME: there may be no hero - landmines in Tower
 
-	if (obstacle.obstacleType == CObstacleInstance::MOAT)
-	{
-		damage = battleGetMoatDmg();
-	}
-	else if (obstacle.obstacleType == CObstacleInstance::LAND_MINE)
-	{
-		COMPLAIN_RET_IF((!spellObstacle), "Invalid obstacle instance");
-		//You don't get hit by a Mine you can see.
-		if (gs->curB->battleIsObstacleVisibleForSide(obstacle, (BattlePerspective::BattlePerspective)side))
-			return;
+		if(obstacle->obstacleType == CObstacleInstance::MOAT)
+		{
+			damage = battleGetMoatDmg();
+			if(!containDamageFromMoat)
+				containDamageFromMoat = true;
+			else
+				continue;
+		}
+		else if(obstacle->obstacleType == CObstacleInstance::LAND_MINE)
+		{
+			if(!spellObstacle)
+				COMPLAIN_RET("Invalid obstacle instance");
+			//You don't get hit by a Mine you can see.
+			if(gs->curB->battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side))
+				continue;
+			oneTimeObstacle = true;
+			effect = 82;
+			const CSpell * sp = SpellID(SpellID::LAND_MINE).toSpell();
 
-		oneTimeObstacle = true;
-		effect = 82; //makes
+			if(sp->isImmuneByStack(hero, curStack))
+				continue;
 
-		const CSpell * sp = SpellID(SpellID::LAND_MINE).toSpell();
+			damage = sp->calculateDamage(hero, curStack, spellObstacle->spellLevel, spellObstacle->casterSpellPower);
+			//TODO even if obstacle wasn't created by hero (Tower "moat") it should deal dmg as if cast by hero,
+			//if it is bigger than default dmg. Or is it just irrelevant H3 implementation quirk
+		}
+		else if(obstacle->obstacleType == CObstacleInstance::FIRE_WALL)
+		{
+			if(!spellObstacle)
+				COMPLAIN_RET("Invalid obstacle instance");
+			const CSpell * sp = SpellID(SpellID::FIRE_WALL).toSpell();
 
-		if (sp->isImmuneByStack(hero, curStack))
-			return;
+			if(sp->isImmuneByStack(hero, curStack))
+				continue;
 
-		damage = sp->calculateDamage(hero, curStack,
-											 spellObstacle->spellLevel, spellObstacle->casterSpellPower);
-		//TODO even if obstacle wasn't created by hero (Tower "moat") it should deal dmg as if cast by hero,
-		//if it is bigger than default dmg. Or is it just irrelevant H3 implementation quirk
-	}
-	else if (obstacle.obstacleType == CObstacleInstance::FIRE_WALL)
-	{
-		COMPLAIN_RET_IF((!spellObstacle), "Invalid obstacle instance");
-		const CSpell * sp = SpellID(SpellID::FIRE_WALL).toSpell();
+			damage = sp->calculateDamage(hero, curStack,
+										 spellObstacle->spellLevel, spellObstacle->casterSpellPower);
+		}
+		else
+			continue;
 
-		if (sp->isImmuneByStack(hero, curStack))
-			return;
+		BattleStackAttacked bsa;
+		if(effect >= 0)
+		{
+			bsa.flags |= BattleStackAttacked::EFFECT;
+			bsa.effect = effect; //makes POOF
+		}
+		bsa.damageAmount = damage;
+		bsa.stackAttacked = curStack->ID;
+		bsa.attackerID = -1;
+		curStack->prepareAttacked(bsa, getRandomGenerator());
 
-		damage = sp->calculateDamage(hero, curStack,
-											 spellObstacle->spellLevel, spellObstacle->casterSpellPower);
-	}
-	else
-	{
-		//no other obstacle does damage to stack
-		return;
-	}
+		StacksInjured si;
+		si.stacks.push_back(bsa);
+		sendAndApply(&si);
 
-	BattleStackAttacked bsa;
-	if (effect >= 0)
-	{
-		bsa.flags |= BattleStackAttacked::EFFECT;
-		bsa.effect = effect; //makes POOF
+		if(oneTimeObstacle)
+			removeObstacle(*obstacle);
 	}
-	bsa.damageAmount = damage;
-	bsa.stackAttacked = curStack->ID;
-	bsa.attackerID = -1;
-	curStack->prepareAttacked(bsa, getRandomGenerator());
-
-	StacksInjured si;
-	si.stacks.push_back(bsa);
-	sendAndApply(&si);
-
-	if (oneTimeObstacle)
-		removeObstacle(obstacle);
+	if(!curStack->alive())
+		return false;
+	return true;
 }
 
 void CGameHandler::handleTimeEvents()

+ 2 - 1
server/CGameHandler.h

@@ -199,7 +199,8 @@ public:
 	bool makeCustomAction(BattleAction &ba);
 	void stackEnchantedTrigger(const CStack * stack);
 	void stackTurnTrigger(const CStack *stack);
-	void handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack); //checks if obstacle is land mine and handles possible consequences
+	bool handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving = false); //checks if obstacle is land mine and handles possible consequences
+
 	void removeObstacle(const CObstacleInstance &obstacle);
 	bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
 	bool hireHero( const CGObjectInstance *obj, ui8 hid, PlayerColor player );