Selaa lähdekoodia

Teleport: can trigger obstacles now

Konstantin P 2 vuotta sitten
vanhempi
sitoutus
f11fa8f0c8

+ 64 - 0
lib/battle/CBattleInfoCallback.cpp

@@ -14,9 +14,13 @@
 
 #include "../CStack.h"
 #include "BattleInfo.h"
+#include "CObstacleInstance.h"
 #include "DamageCalculator.h"
 #include "PossiblePlayerBattleAction.h"
 #include "../NetPacks.h"
+#include "../spells/ObstacleCasterProxy.h"
+#include "../spells/ISpellMechanics.h"
+#include "../spells/Problem.h"
 #include "../spells/CSpellHandler.h"
 #include "../mapObjects/CGTownInstance.h"
 #include "../BattleFieldHandler.h"
@@ -826,6 +830,66 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAl
 	return affectedObstacles;
 }
 
+bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set<BattleHex> & passed) const
+{
+	if(!unit.alive())
+		return false;
+	bool movementStopped = false;
+	for(auto & obstacle : getAllAffectedObstaclesByStack(&unit, passed))
+	{
+		//helper info
+		const SpellCreatedObstacle * spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle.get());
+
+		if(spellObstacle)
+		{
+			auto revealObstacles = [&](const SpellCreatedObstacle & spellObstacle) -> void
+			{
+				// For the hidden spell created obstacles, e.g. QuickSand, it should be revealed after taking damage
+				auto operation = ObstacleChanges::EOperation::UPDATE;
+				if (spellObstacle.removeOnTrigger)
+					operation = ObstacleChanges::EOperation::REMOVE;
+
+				SpellCreatedObstacle changedObstacle;
+				changedObstacle.uniqueID = spellObstacle.uniqueID;
+				changedObstacle.revealed = true;
+
+				BattleObstaclesChanged bocp;
+				bocp.changes.emplace_back(spellObstacle.uniqueID, operation);
+				changedObstacle.toInfo(bocp.changes.back(), operation);
+				spellEnv.apply(&bocp);
+			};
+			const auto side = unit.unitSide();
+			auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side);
+			const auto * hero = battleGetFightingHero(spellObstacle->casterSide);
+			auto caster = spells::ObstacleCasterProxy(getBattle()->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
+			const auto * sp = obstacle->getTrigger().toSpell();
+			if(obstacle->triggersEffects() && sp)
+			{
+				auto cast = spells::BattleCast(this, &caster, spells::Mode::PASSIVE, sp);
+				spells::detail::ProblemImpl ignored;
+				auto target = spells::Target(1, spells::Destination(&unit));
+				if(sp->battleMechanics(&cast)->canBeCastAt(target, ignored)) // Obstacles should not be revealed by immune creatures
+				{
+					if(shouldReveal) { //hidden obstacle triggers effects after revealed
+						revealObstacles(*spellObstacle);
+						cast.cast(&spellEnv, target);
+					}
+				}
+			}
+			else if(shouldReveal)
+				revealObstacles(*spellObstacle);
+		}
+
+		if(!unit.alive())
+			return false;
+
+		if(obstacle->stopsMovement())
+			movementStopped = true;
+	}
+
+	return unit.alive() && !movementStopped;
+}
+
 AccessibilityInfo CBattleInfoCallback::getAccesibility() const
 {
 	AccessibilityInfo ret;

+ 3 - 0
lib/battle/CBattleInfoCallback.h

@@ -20,6 +20,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 class CGHeroInstance;
 class CStack;
 class ISpellCaster;
+class SpellCastEnvironment;
 class CSpell;
 struct CObstacleInstance;
 class IBonusBearer;
@@ -61,6 +62,8 @@ public:
 
 	std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override;
 	std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const override;
+	//Handle obstacle damage here, requires SpellCastEnvironment
+	bool handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set<BattleHex> & passed = {}) const;
 
 	const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const;
 

+ 1 - 2
lib/battle/CCallbackBase.h

@@ -25,14 +25,13 @@ class DLL_LINKAGE CCallbackBase
 {
 	const IBattleInfo * battle = nullptr; //battle to which the player is engaged, nullptr if none or not applicable
 
-	const IBattleInfo * getBattle() const;
-
 protected:
 	boost::optional<PlayerColor> player; // not set gives access to all information, otherwise callback provides only information "visible" for player
 
 	CCallbackBase(boost::optional<PlayerColor> Player);
 	CCallbackBase() = default;
 
+	const IBattleInfo * getBattle() const;
 	void setBattle(const IBattleInfo * B);
 	bool duringBattle() const;
 

+ 7 - 0
lib/spells/effects/Teleport.cpp

@@ -83,10 +83,17 @@ void Teleport::apply(ServerCallback * server, const Mechanics * m, const EffectT
 	pack.tilesToMove = tiles;
 	pack.teleporting = true;
 	server->apply(&pack);
+
+	if(triggerObstacles)
+	{
+		auto spellEnv = dynamic_cast<SpellCastEnvironment*>(server);
+		m->battle()->handleObstacleTriggersForUnit(*spellEnv, *targetUnit);
+	}
 }
 
 void Teleport::serializeJsonUnitEffect(JsonSerializeFormat & handler)
 {
+	handler.serializeBool("triggerObstacles", triggerObstacles);
 	handler.serializeBool("isWallPassable", isWallPassable);
 	handler.serializeBool("isMoatPassable", isMoatPassable);
 }

+ 1 - 0
lib/spells/effects/Teleport.h

@@ -36,6 +36,7 @@ protected:
 	void serializeJsonUnitEffect(JsonSerializeFormat & handler) override;
 
 private:
+	bool triggerObstacles;
 	bool isWallPassable;
 	bool isMoatPassable;
 };

+ 3 - 63
server/CGameHandler.cpp

@@ -1527,7 +1527,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 			{
 				if(stackIsMoving && start != curStack->getPosition())
 				{
-					stackIsMoving = handleDamageFromObstacle(curStack, passed);
+					stackIsMoving = handleObstacleTriggersForUnit(*spellEnv, *curStack, passed);
 					passed.insert(curStack->getPosition());
 					if(curStack->doubleWide())
 						passed.insert(curStack->occupiedHex());
@@ -1565,7 +1565,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 			passed.clear(); //Just empty passed, obstacles will handled automatically
 	}
 	//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
-	handleDamageFromObstacle(curStack, passed);
+	handleObstacleTriggersForUnit(*spellEnv, *curStack, passed);
 
 	return ret;
 }
@@ -4933,7 +4933,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
 	}
 	if(ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND
 			|| ba.actionType == EActionType::SHOOT || ba.actionType == EActionType::MONSTER_SPELL)
-		handleDamageFromObstacle(stack);
+		handleObstacleTriggersForUnit(*spellEnv, *stack);
 	if(ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
 		battleMadeAction.setn(true);
 	return ok;
@@ -5289,66 +5289,6 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
 	}
 }
 
-bool CGameHandler::handleDamageFromObstacle(const battle::Unit * curStack, const std::set<BattleHex> & passed)
-{
-	if(!curStack->alive())
-		return false;
-	bool movementStopped = false;
-	for(auto & obstacle : getAllAffectedObstaclesByStack(curStack, passed))
-	{
-		//helper info
-		const SpellCreatedObstacle * spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle.get());
-
-		if(spellObstacle)
-		{
-			auto revealObstacles = [&](const SpellCreatedObstacle & spellObstacle) -> void
-			{
-				// For the hidden spell created obstacles, e.g. QuickSand, it should be revealed after taking damage
-				auto operation = ObstacleChanges::EOperation::UPDATE;
-				if (spellObstacle.removeOnTrigger)
-					operation = ObstacleChanges::EOperation::REMOVE;
-
-				SpellCreatedObstacle changedObstacle;
-				changedObstacle.uniqueID = spellObstacle.uniqueID;
-				changedObstacle.revealed = true;
-
-				BattleObstaclesChanged bocp;
-				bocp.changes.emplace_back(spellObstacle.uniqueID, operation);
-				changedObstacle.toInfo(bocp.changes.back(), operation);
-				sendAndApply(&bocp);
-			};
-			const auto side = curStack->unitSide();
-			auto shouldReveal = !spellObstacle->hidden || !gs->curB->battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side);
-			const auto * hero = gs->curB->battleGetFightingHero(spellObstacle->casterSide);
-			auto caster = spells::ObstacleCasterProxy(gs->curB->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
-			const auto * sp = obstacle->getTrigger().toSpell();
-			if(obstacle->triggersEffects() && sp)
-			{
-				auto cast = spells::BattleCast(gs->curB, &caster, spells::Mode::PASSIVE, sp);
-				spells::detail::ProblemImpl ignored;
-				auto target = spells::Target(1, spells::Destination(curStack));
-				if(sp->battleMechanics(&cast)->canBeCastAt(target, ignored)) // Obstacles should not be revealed by immune creatures
-				{
-					if(shouldReveal) { //hidden obstacle triggers effects after revealed
-						revealObstacles(*spellObstacle);
-						cast.cast(spellEnv, target);
-					}
-				}
-			}
-			else if(shouldReveal)
-				revealObstacles(*spellObstacle);
-		}
-
-		if(!curStack->alive())
-			return false;
-
-		if(obstacle->stopsMovement())
-			movementStopped = true;
-	}
-
-	return curStack->alive() && !movementStopped;
-}
-
 void CGameHandler::handleTimeEvents()
 {
 	gs->map->events.sort(evntCmp);

+ 0 - 1
server/CGameHandler.h

@@ -236,7 +236,6 @@ public:
 	bool makeCustomAction(BattleAction &ba);
 	void stackEnchantedTrigger(const CStack * stack);
 	void stackTurnTrigger(const CStack *stack);
-	bool handleDamageFromObstacle(const battle::Unit * curStack, const std::set<BattleHex> & passed = {}); //checks if obstacle is land mine and handles possible consequences
 
 	void removeObstacle(const CObstacleInstance &obstacle);
 	bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );