Browse Source

Simple fix: http://bugs.vcmi.eu/view.php?id=2366

FeniksFire 8 years ago
parent
commit
8577445b10

+ 7 - 8
lib/battle/CBattleInfoCallback.cpp

@@ -828,20 +828,19 @@ std::pair<ui32, ui32> CBattleInfoCallback::battleEstimateDamage(CRandomGenerator
 	return ret;
 }
 
-std::shared_ptr<const CObstacleInstance> CBattleInfoCallback::battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking /*= true*/) const
+std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking) const
 {
-	RETURN_IF_NOT_BATTLE(std::shared_ptr<const CObstacleInstance>());
-
-	for(auto &obs : battleGetAllObstacles())
+	std::vector<std::shared_ptr<const CObstacleInstance>> obstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
+	RETURN_IF_NOT_BATTLE(obstacles);
+	for(auto & obs : battleGetAllObstacles())
 	{
 		if(vstd::contains(obs->getBlockedTiles(), tile)
-		|| (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile)))
+				|| (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile)))
 		{
-			return obs;
+			obstacles.push_back(obs);
 		}
 	}
-
-	return std::shared_ptr<const CObstacleInstance>();
+	return obstacles;
 }
 
 AccessibilityInfo CBattleInfoCallback::getAccesibility() const

+ 2 - 1
lib/battle/CBattleInfoCallback.h

@@ -40,7 +40,8 @@ public:
 	//battle
 	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::shared_ptr<const CObstacleInstance> battleGetObstacleOnPos(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>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands)
+
 	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;
 	void battleGetStackCountOutsideHexes(bool *ac) const; // returns hexes which when in front of a stack cause us to move the amount box back

+ 26 - 17
lib/spells/BattleSpellMechanics.cpp

@@ -438,15 +438,17 @@ bool ObstacleMechanics::isHexAviable(const CBattleInfoCallback * cb, const Battl
 	if(cb->battleGetStackByPos(hex, true))
 		return false;
 
-	auto obst = cb->battleGetObstacleOnPos(hex, false);
-	if(obst /*&& obst->type != CObstacleInstance::MOAT*/)//todo: issue 2366, uncomment once obstacle tile sharing implemented
-		return false;
+	auto obst = cb->battleGetAllObstaclesOnPos(hex, false);
+
+	for(auto & i : obst)
+		if(i->obstacleType != CObstacleInstance::MOAT)
+			return false;
 
-	if(nullptr != cb->battleGetDefendedTown() && CGTownInstance::NONE != cb->battleGetDefendedTown()->fortLevel())
+	if(cb->battleGetDefendedTown() != nullptr && cb->battleGetDefendedTown()->fortLevel() != CGTownInstance::NONE)
 	{
 		EWallPart::EWallPart part = cb->battleHexToWallPart(hex);
 
-		if(part == EWallPart::INVALID)
+		if(part == EWallPart::INVALID || part == EWallPart::INDESTRUCTIBLE_PART_OF_GATE)
 			return true;//no fortification here
 		else if(static_cast<int>(part) < 0)
 			return false;//indestuctible part (cant be checked by battleGetWallState)
@@ -631,18 +633,23 @@ void ForceFieldMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const
 ///RemoveObstacleMechanics
 void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
 {
-	if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.getFirstDestinationHex(), false))
+	auto obstacleToRemove = parameters.cb->battleGetAllObstaclesOnPos(parameters.getFirstDestinationHex(), false);
+	if(!obstacleToRemove.empty())
 	{
-		if(canRemove(obstacleToRemove.get(), parameters.spellLvl))
+		ObstaclesRemoved obr;
+		bool complain = true;
+		for(auto & i : obstacleToRemove)
 		{
-			ObstaclesRemoved obr;
-			obr.obstacles.insert(obstacleToRemove->uniqueID);
-			env->sendAndApply(&obr);
+			if(canRemove(i.get(), parameters.spellLvl))
+			{
+				obr.obstacles.insert(i->uniqueID);
+				complain = false;
+			}
 		}
-		else
-		{
+		if(!complain)
+			env->sendAndApply(&obr);
+		else if(complain || obr.obstacles.empty())
 			env->complain("Cant remove this obstacle!");
-		}
 	}
 	else
 		env->complain("There's no obstacle to remove!");
@@ -667,9 +674,11 @@ ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CB
 
 ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const
 {
-	if(auto obstacle = cb->battleGetObstacleOnPos(ctx.destination, false))
-		if(canRemove(obstacle.get(), ctx.schoolLvl))
-			return ESpellCastProblem::OK;
+	auto obstacles = cb->battleGetAllObstaclesOnPos(ctx.destination, false);
+	if(!obstacles.empty())
+		for(auto & i : obstacles)
+			if(canRemove(i.get(), ctx.schoolLvl))
+				return ESpellCastProblem::OK;
 
 	return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 }
@@ -734,7 +743,7 @@ ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattle
 		//therefore we do not need to check caster and casting mode
 		//TODO: check that we really should check immunity for both stacks
 		ESpellCastProblem::ESpellCastProblem res = owner->internalIsImmune(caster, stack);
-		const bool immune =  ESpellCastProblem::OK != res && ESpellCastProblem::NOT_DECIDED != res;
+		const bool immune = ESpellCastProblem::OK != res && ESpellCastProblem::NOT_DECIDED != res;
 		const bool casterStack = stack->owner == caster->getOwner();
 
 		if(!immune && casterStack)

+ 26 - 22
server/CGameHandler.cpp

@@ -1231,7 +1231,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 	}
 	else //for non-flying creatures
 	{
-		std::shared_ptr<const CObstacleInstance> obstacle, obstacle2; //obstacle that interrupted movement
+		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;
@@ -1330,7 +1330,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 					}
 
 					//if we walked onto something, finalize this portion of stack movement check into obstacle
-					if ((obstacle = battleGetObstacleOnPos(hex, false)))
+					obstacle = battleGetAllObstaclesOnPos(hex, false);
+					if(!obstacle.empty())
 						obstacleHit = true;
 
 					if (curStack->doubleWide())
@@ -1338,7 +1339,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 						BattleHex otherHex = curStack->occupiedHex(hex);
 
 						//two hex creature hit obstacle by backside
-						if (otherHex.isValid() && ((obstacle2 = battleGetObstacleOnPos(otherHex, false))))
+						obstacle2 = battleGetAllObstaclesOnPos(otherHex, false);
+						if(otherHex.isValid() && !obstacle2.empty())
 							obstacleHit = true;
 					}
 				}
@@ -1359,17 +1361,18 @@ 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::shared_ptr<const CObstacleInstance> & obs)
+				auto processObstacle = [&](std::vector<std::shared_ptr<const CObstacleInstance>> & obs)
 				{
-					if (obs)
+					if(!obs.empty())
 					{
-						handleDamageFromObstacle(*obs, curStack);
-
-						//if stack die in explosion or interrupted by obstacle, abort movement
-						if (obs->stopsMovement() || !curStack->alive())
-							stackIsMoving = false;
-
-						obs.reset();
+						for(auto & i : obs)
+						{
+							handleDamageFromObstacle(*i, curStack);
+							//if stack die in explosion or interrupted by obstacle, abort movement
+							if(i->stopsMovement() || !curStack->alive())
+								stackIsMoving = false;
+							i.reset();
+						}
 					}
 				};
 
@@ -1406,22 +1409,23 @@ 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())
 	{
-		if (auto theLastObstacle = battleGetObstacleOnPos(curStack->position, false))
-		{
-			handleDamageFromObstacle(*theLastObstacle, curStack);
-		}
+		auto theLastObstacle = battleGetAllObstaclesOnPos(curStack->position, false);
+		for(auto & i : theLastObstacle)
+			if(curStack->alive())
+				handleDamageFromObstacle(*i, curStack);
 	}
 
 	if (curStack->alive() && curStack->doubleWide())
 	{
 		BattleHex otherHex = curStack->occupiedHex(curStack->position);
-
 		if (otherHex.isValid())
-			if (auto theLastObstacle = battleGetObstacleOnPos(otherHex, false))
-			{
-				//two hex creature hit obstacle by backside
-				handleDamageFromObstacle(*theLastObstacle, curStack);
-			}
+		{
+			//two hex creature hit obstacle by backside
+			auto theLastObstacle = battleGetAllObstaclesOnPos(otherHex, false);
+			for(auto & i : theLastObstacle)
+				if(curStack->alive())
+					handleDamageFromObstacle(*i, curStack);
+		}
 	}
 	return ret;
 }