ソースを参照

Separated game and battle callback (server & client only)

Ivan Savenko 2 年 前
コミット
3a88180494

+ 1 - 1
AI/BattleAI/BattleEvaluator.cpp

@@ -390,7 +390,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
 		for(auto & round : queue)
 		{
 			if(!firstRound)
-				state->nextRound(0);//todo: set actual value?
+				state->nextRound();
 			for(auto unit : round)
 			{
 				if(!vstd::contains(values, unit->unitId()))

+ 21 - 2
AI/BattleAI/StackWithBonuses.cpp

@@ -325,7 +325,7 @@ int32_t HypotheticBattle::getActiveStackID() const
 	return activeUnitId;
 }
 
-void HypotheticBattle::nextRound(int32_t roundNr)
+void HypotheticBattle::nextRound()
 {
 	//TODO:HypotheticBattle::nextRound
 	for(auto unit : battleAliveUnits())
@@ -462,6 +462,24 @@ int64_t HypotheticBattle::getActualDamage(const DamageRange & damage, int32_t at
 	return (damage.min + damage.max) / 2;
 }
 
+std::vector<SpellID> HypotheticBattle::getUsedSpells(ui8 side) const
+{
+	// TODO
+	return {};
+}
+
+int3 HypotheticBattle::getLocation() const
+{
+	// TODO
+	return int3(-1, -1, -1);
+}
+
+bool HypotheticBattle::isCreatureBank() const
+{
+	// TODO
+	return false;
+}
+
 int64_t HypotheticBattle::getTreeVersion() const
 {
 	return getBonusBearer()->getTreeVersion() + bonusTreeVersion;
@@ -552,8 +570,9 @@ const Services * HypotheticBattle::HypotheticEnvironment::services() const
 	return env->services();
 }
 
-const Environment::BattleCb * HypotheticBattle::HypotheticEnvironment::battle() const
+const Environment::BattleCb * HypotheticBattle::HypotheticEnvironment::battle(const BattleID & battleID) const
 {
+	assert(battleID == owner->getBattleID());
 	return owner;
 }
 

+ 7 - 2
AI/BattleAI/StackWithBonuses.h

@@ -110,11 +110,13 @@ public:
 
 	std::shared_ptr<StackWithBonuses> getForUpdate(uint32_t id);
 
+	BattleID getBattleID() const override;
+
 	int32_t getActiveStackID() const override;
 
 	battle::Units getUnitsIf(battle::UnitFilter predicate) const override;
 
-	void nextRound(int32_t roundNr) override;
+	void nextRound() override;
 	void nextTurn(uint32_t unitId) override;
 
 	void addUnit(uint32_t id, const JsonNode & data) override;
@@ -136,6 +138,9 @@ public:
 	uint32_t nextUnitId() const override;
 
 	int64_t getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const override;
+	std::vector<SpellID> getUsedSpells(ui8 side) const override;
+	int3 getLocation() const override;
+	bool isCreatureBank() const override;
 
 	int64_t getTreeVersion() const;
 
@@ -177,7 +182,7 @@ private:
 		HypotheticEnvironment(HypotheticBattle * owner_, const Environment * upperEnvironment);
 
 		const Services * services() const override;
-		const BattleCb * battle() const override;
+		const BattleCb * battle(const BattleID & battleID) const override;
 		const GameCb * game() const override;
 		vstd::CLoggerBase * logger() const override;
 		events::EventBus * eventBus() const override;

+ 2 - 1
include/vcmi/Environment.h

@@ -16,6 +16,7 @@ class Services;
 
 class IGameInfoCallback;
 class IBattleInfoCallback;
+class BattleID;
 
 namespace events
 {
@@ -31,7 +32,7 @@ public:
 	virtual ~Environment() = default;
 
 	virtual const Services * services() const = 0;
-	virtual const BattleCb * battle() const = 0;
+	virtual const BattleCb * battle(const BattleID & battleID) const = 0;
 	virtual const GameCb * game() const = 0;
 	virtual vstd::CLoggerBase * logger() const = 0;
 	virtual events::EventBus * eventBus() const = 0;

+ 1 - 3
lib/NetPacks.h

@@ -39,6 +39,7 @@ struct StackLocation;
 struct ArtSlotInfo;
 struct QuestInfo;
 class IBattleState;
+class BattleInfo;
 
 // This one teleport-specific, but has to be available everywhere in callbacks and netpacks
 // For now it's will be there till teleports code refactored and moved into own file
@@ -1478,7 +1479,6 @@ struct DLL_LINKAGE MapObjectSelectDialog : public Query
 	}
 };
 
-class BattleInfo;
 struct DLL_LINKAGE BattleStart : public CPackForClient
 {
 	void applyGs(CGameState * gs) const;
@@ -1500,14 +1500,12 @@ struct DLL_LINKAGE BattleNextRound : public CPackForClient
 	void applyGs(CGameState * gs) const;
 
 	BattleID battleID = BattleID::NONE;
-	si32 round = 0;
 
 	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
 		h & battleID;
-		h & round;
 	}
 };
 

+ 1 - 1
lib/NetPacksLib.cpp

@@ -2124,7 +2124,7 @@ void BattleStart::applyGs(CGameState * gs) const
 
 void BattleNextRound::applyGs(CGameState * gs) const
 {
-	gs->getBattle(battleID)->nextRound(round);
+	gs->getBattle(battleID)->nextRound();
 }
 
 void BattleSetActiveStack::applyGs(CGameState * gs) const

+ 33 - 34
lib/battle/BattleInfo.cpp

@@ -26,37 +26,6 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 ///BattleInfo
-std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const
-{
-	auto reachability = getReachability(stack);
-
-	if(reachability.predecessors[dest] == -1) //cannot reach destination
-	{
-		return std::make_pair(std::vector<BattleHex>(), 0);
-	}
-
-	//making the Path
-	std::vector<BattleHex> path;
-	BattleHex curElem = dest;
-	while(curElem != start)
-	{
-		path.push_back(curElem);
-		curElem = reachability.predecessors[curElem];
-	}
-
-	return std::make_pair(path, reachability.distances[dest]);
-}
-
-void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
-{
-	for(const auto & st : stacks) //setting casualties
-	{
-		si32 killed = st->getKilled();
-		if(killed > 0)
-			casualties[st->unitSide()][st->creatureId()] += killed;
-	}
-}
-
 CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base, ui8 side, const SlotID & slot, BattleHex position)
 {
 	PlayerColor owner = sides[side].color;
@@ -560,10 +529,24 @@ BattleInfo::BattleInfo():
 	tacticsSide(0),
 	tacticDistance(0)
 {
-	setBattle(this);
 	setNodeType(BATTLE);
 }
 
+BattleID BattleInfo::getBattleID() const
+{
+	return battleID;
+}
+
+const IBattleInfo * BattleInfo::getBattle() const
+{
+	return this;
+}
+
+std::optional<PlayerColor> BattleInfo::getPlayerID() const
+{
+	return std::nullopt;
+}
+
 BattleInfo::~BattleInfo()
 {
 	for (auto & elem : stacks)
@@ -689,14 +672,30 @@ int64_t BattleInfo::getActualDamage(const DamageRange & damage, int32_t attacker
 	}
 }
 
-void BattleInfo::nextRound(int32_t roundNr)
+int3 BattleInfo::getLocation() const
+{
+	return tile;
+}
+
+bool BattleInfo::isCreatureBank() const
+{
+	return creatureBank;
+}
+
+
+std::vector<SpellID> BattleInfo::getUsedSpells(ui8 side) const
+{
+	return sides.at(side).usedSpellsHistory;
+}
+
+void BattleInfo::nextRound()
 {
 	for(int i = 0; i < 2; ++i)
 	{
 		sides.at(i).castSpellsCount = 0;
 		vstd::amax(--sides.at(i).enchanterCounter, 0);
 	}
-	round = roundNr;
+	round += 1;
 
 	for(CStack * s : stacks)
 	{

+ 11 - 5
lib/battle/BattleInfo.h

@@ -75,9 +75,14 @@ public:
 	BattleInfo();
 	virtual ~BattleInfo();
 
+	const IBattleInfo * getBattle() const override;
+	std::optional<PlayerColor> getPlayerID() const override;
+
 	//////////////////////////////////////////////////////////////////////////
 	// IBattleInfo
 
+	BattleID getBattleID() const override;
+
 	int32_t getActiveStackID() const override;
 
 	TStacks getStacksIf(TStackFilter predicate) const override;
@@ -109,10 +114,15 @@ public:
 
 	int64_t getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const override;
 
+	int3 getLocation() const override;
+	bool isCreatureBank() const override;
+
+	std::vector<SpellID> getUsedSpells(ui8 side) const override;
+
 	//////////////////////////////////////////////////////////////////////////
 	// IBattleState
 
-	void nextRound(int32_t roundNr) override;
+	void nextRound() override;
 	void nextTurn(uint32_t unitId) override;
 
 	void addUnit(uint32_t id, const JsonNode & data) override;
@@ -140,10 +150,6 @@ public:
 	using CBattleInfoEssentials::battleGetFightingHero;
 	CGHeroInstance * battleGetFightingHero(ui8 side) const;
 
-	std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const; //returned value: pair<path, length>; length may be different than number of elements in path since flying creatures jump between distant hexes
-
-	void calculateCasualties(std::map<ui32,si32> * casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
-
 	CStack * generateNewStack(uint32_t id, const CStackInstance & base, ui8 side, const SlotID & slot, BattleHex position);
 	CStack * generateNewStack(uint32_t id, const CStackBasicDescriptor & base, ui8 side, const SlotID & slot, BattleHex position);
 

+ 10 - 3
lib/battle/BattleProxy.cpp

@@ -17,12 +17,19 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 BattleProxy::BattleProxy(Subject subject_): 
 	subject(std::move(subject_))
+{}
+
+BattleProxy::~BattleProxy() = default;
+
+const IBattleInfo * BattleProxy::getBattle() const
 {
-	setBattle(this);
-	player = subject->getPlayerID();
+	return this;
 }
 
-BattleProxy::~BattleProxy() = default;
+std::optional<PlayerColor> BattleProxy::getPlayerID() const
+{
+	return subject->getPlayerID();
+}
 
 int32_t BattleProxy::getActiveStackID() const
 {

+ 2 - 0
lib/battle/BattleProxy.h

@@ -24,6 +24,8 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 	// IBattleInfo
+	const IBattleInfo * getBattle() const override;
+	std::optional<PlayerColor> getPlayerID() const override;
 
 	int32_t getActiveStackID() const override;
 

+ 21 - 0
lib/battle/CBattleInfoCallback.cpp

@@ -136,6 +136,27 @@ ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(const spells::Caster *
 	return ESpellCastProblem::OK;
 }
 
+std::pair< std::vector<BattleHex>, int > CBattleInfoCallback::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const
+{
+	auto reachability = getReachability(stack);
+
+	if(reachability.predecessors[dest] == -1) //cannot reach destination
+	{
+		return std::make_pair(std::vector<BattleHex>(), 0);
+	}
+
+	//making the Path
+	std::vector<BattleHex> path;
+	BattleHex curElem = dest;
+	while(curElem != start)
+	{
+		path.push_back(curElem);
+		curElem = reachability.predecessors[curElem];
+	}
+
+	return std::make_pair(path, reachability.distances[dest]);
+}
+
 bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const
 {
 	auto isTileBlocked = [&](BattleHex tile)

+ 2 - 0
lib/battle/CBattleInfoCallback.h

@@ -89,6 +89,8 @@ public:
 	std::set<BattleHex> battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const;
 	bool isEnemyUnitWithinSpecifiedRange(BattleHex attackerPosition, const battle::Unit * defenderUnit, unsigned int range) const;
 
+	std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const;
+
 	bool battleCanAttack(const battle::Unit * stack, const battle::Unit * target, BattleHex dest) const; //determines if stack with given ID can attack target at the selected destination
 	bool battleCanShoot(const battle::Unit * attacker, BattleHex dest) const; //determines if stack with given ID shoot at the selected destination
 	bool battleCanShoot(const battle::Unit * attacker) const; //determines if stack with given ID shoot in principle

+ 10 - 5
lib/battle/CBattleInfoEssentials.cpp

@@ -17,6 +17,11 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+bool CBattleInfoEssentials::duringBattle() const
+{
+	return getBattle() != nullptr;
+}
+
 TerrainId CBattleInfoEssentials::battleTerrainType() const
 {
 	RETURN_IF_NOT_BATTLE(TerrainId());
@@ -47,7 +52,7 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoEssentials::bat
 	}
 	else
 	{
-		if(!!player && *perspective != battleGetMySide())
+		if(!!getPlayerID() && *perspective != battleGetMySide())
 			logGlobal->warn("Unauthorized obstacles access attempt, assuming massive spell");
 	}
 
@@ -157,14 +162,14 @@ const CGTownInstance * CBattleInfoEssentials::battleGetDefendedTown() const
 BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() const
 {
 	RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID);
-	if(!player || player->isSpectator())
+	if(!getPlayerID() || getPlayerID()->isSpectator())
 		return BattlePerspective::ALL_KNOWING;
-	if(*player == getBattle()->getSidePlayer(BattleSide::ATTACKER))
+	if(*getPlayerID() == getBattle()->getSidePlayer(BattleSide::ATTACKER))
 		return BattlePerspective::LEFT_SIDE;
-	if(*player == getBattle()->getSidePlayer(BattleSide::DEFENDER))
+	if(*getPlayerID() == getBattle()->getSidePlayer(BattleSide::DEFENDER))
 		return BattlePerspective::RIGHT_SIDE;
 
-	logGlobal->error("Cannot find player %s in battle!", player->toString());
+	logGlobal->error("Cannot find player %s in battle!", getPlayerID()->toString());
 	return BattlePerspective::INVALID;
 }
 

+ 2 - 1
lib/battle/CBattleInfoEssentials.h

@@ -34,7 +34,7 @@ namespace BattlePerspective
 	};
 }
 
-class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase, public IBattleInfoCallback
+class DLL_LINKAGE CBattleInfoEssentials : public IBattleInfoCallback
 {
 protected:
 	bool battleDoWeKnowAbout(ui8 side) const;
@@ -45,6 +45,7 @@ public:
 		ONLY_MINE, ONLY_ENEMY, MINE_AND_ENEMY
 	};
 
+	bool duringBattle() const;
 	BattlePerspective::BattlePerspective battleGetMySide() const;
 	const IBonusBearer * getBonusBearer() const override;
 

+ 0 - 15
lib/battle/CCallbackBase.cpp

@@ -13,26 +13,11 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-bool CCallbackBase::duringBattle() const
-{
-	return getBattle() != nullptr;
-}
-
-const IBattleInfo * CCallbackBase::getBattle() const
-{
-	return battle;
-}
-
 CCallbackBase::CCallbackBase(std::optional<PlayerColor> Player):
 	player(std::move(Player))
 {
 }
 
-void CCallbackBase::setBattle(const IBattleInfo * B)
-{
-	battle = B;
-}
-
 std::optional<PlayerColor> CCallbackBase::getPlayerID() const
 {
 	return player;

+ 1 - 10
lib/battle/CCallbackBase.h

@@ -11,7 +11,7 @@
 #include "../GameConstants.h"
 
 #define RETURN_IF_NOT_BATTLE(...) if(!duringBattle()) {logGlobal->error("%s called when no battle!", __FUNCTION__); return __VA_ARGS__; }
-#define ASSERT_IF_CALLED_WITH_PLAYER if(!player) {logGlobal->error(BOOST_CURRENT_FUNCTION); assert(0);}
+#define ASSERT_IF_CALLED_WITH_PLAYER if(!getPlayerID()) {logGlobal->error(BOOST_CURRENT_FUNCTION); assert(0);}
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -19,26 +19,17 @@ class IBattleInfo;
 class BattleInfo;
 class CBattleInfoEssentials;
 
-
 //Basic class for various callbacks (interfaces called by players to get info about game and so forth)
 class DLL_LINKAGE CCallbackBase
 {
-	const IBattleInfo * battle = nullptr; //battle to which the player is engaged, nullptr if none or not applicable
-
 protected:
 	std::optional<PlayerColor> player; // not set gives access to all information, otherwise callback provides only information "visible" for player
 
 	CCallbackBase(std::optional<PlayerColor> Player);
 	CCallbackBase() = default;
 
-	const IBattleInfo * getBattle() const;
-	void setBattle(const IBattleInfo * B);
-	bool duringBattle() const;
-
 public:
 	std::optional<PlayerColor> getPlayerID() const;
-
-	friend class CBattleInfoEssentials;
 };
 
 

+ 4 - 4
lib/battle/CPlayerBattleCallback.cpp

@@ -18,7 +18,7 @@ bool CPlayerBattleCallback::battleCanFlee() const
 {
 	RETURN_IF_NOT_BATTLE(false);
 	ASSERT_IF_CALLED_WITH_PLAYER
-			return CBattleInfoEssentials::battleCanFlee(*player);
+			return CBattleInfoEssentials::battleCanFlee(*getPlayerID());
 }
 
 TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose, bool onlyAlive) const
@@ -30,8 +30,8 @@ TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose, bool onlyA
 
 	return battleGetStacksIf([=](const CStack * s){
 		const bool ownerMatches = (whose == MINE_AND_ENEMY)
-								|| (whose == ONLY_MINE && s->unitOwner() == player)
-								|| (whose == ONLY_ENEMY && s->unitOwner() != player);
+								|| (whose == ONLY_MINE && s->unitOwner() == getPlayerID())
+								|| (whose == ONLY_ENEMY && s->unitOwner() != getPlayerID());
 
 		return ownerMatches && s->isValidTarget(!onlyAlive);
 	});
@@ -41,7 +41,7 @@ int CPlayerBattleCallback::battleGetSurrenderCost() const
 {
 	RETURN_IF_NOT_BATTLE(-3)
 			ASSERT_IF_CALLED_WITH_PLAYER
-			return CBattleInfoCallback::battleGetSurrenderCost(*player);
+			return CBattleInfoCallback::battleGetSurrenderCost(*getPlayerID());
 }
 
 const CGHeroInstance * CPlayerBattleCallback::battleGetMyHero() const

+ 4 - 0
lib/battle/IBattleInfoCallback.h

@@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 struct CObstacleInstance;
 class BattleField;
+class IBattleInfo;
 
 namespace battle
 {
@@ -54,6 +55,9 @@ public:
 	virtual scripting::Pool * getContextPool() const = 0;
 #endif
 
+	virtual const IBattleInfo * getBattle() const = 0;
+	virtual std::optional<PlayerColor> getPlayerID() const = 0;
+
 	virtual TerrainId battleTerrainType() const = 0;
 	virtual BattleField battleGetBattlefieldType() const = 0;
 

+ 9 - 3
lib/battle/IBattleState.h

@@ -19,6 +19,7 @@ struct Bonus;
 class JsonNode;
 class JsonSerializeFormat;
 class BattleField;
+class int3;
 
 namespace vstd
 {
@@ -37,6 +38,8 @@ public:
 
 	virtual ~IBattleInfo() = default;
 
+	virtual BattleID getBattleID() const = 0;
+
 	virtual int32_t getActiveStackID() const = 0;
 
 	virtual TStacks getStacksIf(TStackFilter predicate) const = 0;
@@ -55,6 +58,8 @@ public:
 	virtual PlayerColor getSidePlayer(ui8 side) const = 0;
 	virtual const CArmedInstance * getSideArmy(ui8 side) const = 0;
 	virtual const CGHeroInstance * getSideHero(ui8 side) const = 0;
+	/// Returns list of all spells used by specified side (and that can be learned by opposite hero)
+	virtual std::vector<SpellID> getUsedSpells(ui8 side) const = 0;
 
 	virtual uint32_t getCastSpells(ui8 side) const = 0;
 	virtual int32_t getEnchanterCounter(ui8 side) const = 0;
@@ -65,14 +70,15 @@ public:
 	virtual uint32_t nextUnitId() const = 0;
 
 	virtual int64_t getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const = 0;
+
+	virtual int3 getLocation() const = 0;
+	virtual bool isCreatureBank() const = 0;
 };
 
 class DLL_LINKAGE IBattleState : public IBattleInfo
 {
 public:
-	//TODO: add non-const API
-
-	virtual void nextRound(int32_t roundNr) = 0;
+	virtual void nextRound() = 0;
 	virtual void nextTurn(uint32_t unitId) = 0;
 
 	virtual void addUnit(uint32_t id, const JsonNode & data) = 0;

+ 2 - 2
server/CGameHandler.cpp

@@ -136,9 +136,9 @@ const Services * CGameHandler::services() const
 	return VLC;
 }
 
-const CGameHandler::BattleCb * CGameHandler::battle() const
+const CGameHandler::BattleCb * CGameHandler::battle(const BattleID & battleID) const
 {
-	return this;
+	return gs->getBattle(battleID);
 }
 
 const CGameHandler::GameCb * CGameHandler::game() const

+ 2 - 5
server/CGameHandler.h

@@ -12,7 +12,6 @@
 #include <vcmi/Environment.h>
 
 #include "../lib/IGameCallback.h"
-#include "../lib/battle/CBattleInfoCallback.h"
 #include "../lib/LoadProgress.h"
 #include "../lib/ScriptHandler.h"
 #include "TurnTimerHandler.h"
@@ -53,14 +52,12 @@ class TurnOrderProcessor;
 class QueriesProcessor;
 class CObjectVisitQuery;
 
-class CGameHandler : public IGameCallback, public CBattleInfoCallback, public Environment
+class CGameHandler : public IGameCallback, public Environment
 {
 	CVCMIServer * lobby;
 	std::shared_ptr<CApplier<CBaseForGHApply>> applier;
 
 public:
-	using CCallbackBase::setBattle;
-
 	std::unique_ptr<HeroPoolProcessor> heroPool;
 	std::unique_ptr<BattleProcessor> battles;
 	std::unique_ptr<QueriesProcessor> queries;
@@ -84,7 +81,7 @@ public:
 	TurnTimerHandler turnTimerHandler;
 
 	const Services * services() const override;
-	const BattleCb * battle() const override;
+	const BattleCb * battle(const BattleID & battleID) const override;
 	const GameCb * game() const override;
 	vstd::CLoggerBase * logger() const override;
 	events::EventBus * eventBus() const override;

+ 1 - 1
server/NetPacksServer.cpp

@@ -20,7 +20,7 @@
 #include "../lib/IGameCallback.h"
 #include "../lib/mapObjects/CGTownInstance.h"
 #include "../lib/gameState/CGameState.h"
-#include "../lib/battle/BattleInfo.h"
+#include "../lib/battle/IBattleState.h"
 #include "../lib/battle/BattleAction.h"
 #include "../lib/battle/Unit.h"
 #include "../lib/serializer/Connection.h"

+ 60 - 59
server/battles/BattleActionProcessor.cpp

@@ -17,7 +17,8 @@
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CStack.h"
 #include "../../lib/GameSettings.h"
-#include "../../lib/battle/BattleInfo.h"
+#include "../../lib/battle/CBattleInfoCallback.h"
+#include "../../lib/battle/IBattleState.h"
 #include "../../lib/battle/BattleAction.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/NetPacks.h"
@@ -37,19 +38,19 @@ void BattleActionProcessor::setGameHandler(CGameHandler * newGameHandler)
 	gameHandler = newGameHandler;
 }
 
-bool BattleActionProcessor::doEmptyAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doEmptyAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
 	return true;
 }
 
-bool BattleActionProcessor::doEndTacticsAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doEndTacticsAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
 	return true;
 }
 
-bool BattleActionProcessor::doWaitAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doWaitAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
-	const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
+	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
 
 	if (!canStackAct(battle, stack))
 		return false;
@@ -57,9 +58,9 @@ bool BattleActionProcessor::doWaitAction(const BattleInfo & battle, const Battle
 	return true;
 }
 
-bool BattleActionProcessor::doRetreatAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doRetreatAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
-	if (!battle.battleCanFlee(battle.sides.at(ba.side).color))
+	if (!battle.battleCanFlee(battle.sideToPlayer(ba.side)))
 	{
 		gameHandler->complain("Cannot retreat!");
 		return false;
@@ -69,9 +70,9 @@ bool BattleActionProcessor::doRetreatAction(const BattleInfo & battle, const Bat
 	return true;
 }
 
-bool BattleActionProcessor::doSurrenderAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doSurrenderAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
-	PlayerColor player = battle.sides.at(ba.side).color;
+	PlayerColor player = battle.sideToPlayer(ba.side);
 	int cost = battle.battleGetSurrenderCost(player);
 	if (cost < 0)
 	{
@@ -90,7 +91,7 @@ bool BattleActionProcessor::doSurrenderAction(const BattleInfo & battle, const B
 	return true;
 }
 
-bool BattleActionProcessor::doHeroSpellAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doHeroSpellAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
 	const CGHeroInstance *h = battle.battleGetFightingHero(ba.side);
 	if (!h)
@@ -127,9 +128,9 @@ bool BattleActionProcessor::doHeroSpellAction(const BattleInfo & battle, const B
 	return true;
 }
 
-bool BattleActionProcessor::doWalkAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doWalkAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
-	const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
+	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
 	battle::Target target = ba.getTarget(&battle);
 
 	if (!canStackAct(battle, stack))
@@ -150,9 +151,9 @@ bool BattleActionProcessor::doWalkAction(const BattleInfo & battle, const Battle
 	return true;
 }
 
-bool BattleActionProcessor::doDefendAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doDefendAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
-	const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
+	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
 
 	if (!canStackAct(battle, stack))
 		return false;
@@ -199,9 +200,9 @@ bool BattleActionProcessor::doDefendAction(const BattleInfo & battle, const Batt
 	return true;
 }
 
-bool BattleActionProcessor::doAttackAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doAttackAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
-	const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
+	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
 	battle::Target target = ba.getTarget(&battle);
 
 	if (!canStackAct(battle, stack))
@@ -302,9 +303,9 @@ bool BattleActionProcessor::doAttackAction(const BattleInfo & battle, const Batt
 	return true;
 }
 
-bool BattleActionProcessor::doShootAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doShootAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
-	const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
+	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
 	battle::Target target = ba.getTarget(&battle);
 
 	if (!canStackAct(battle, stack))
@@ -369,7 +370,7 @@ bool BattleActionProcessor::doShootAction(const BattleInfo & battle, const Battl
 	return true;
 }
 
-bool BattleActionProcessor::doCatapultAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doCatapultAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
 	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
 	battle::Target target = ba.getTarget(&battle);
@@ -393,7 +394,7 @@ bool BattleActionProcessor::doCatapultAction(const BattleInfo & battle, const Ba
 	return true;
 }
 
-bool BattleActionProcessor::doUnitSpellAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doUnitSpellAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
 	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
 	battle::Target target = ba.getTarget(&battle);
@@ -406,8 +407,8 @@ bool BattleActionProcessor::doUnitSpellAction(const BattleInfo & battle, const B
 	std::shared_ptr<const Bonus> spellcaster = stack->getBonus(Selector::typeSubtype(BonusType::SPELLCASTER, spellID));
 
 	//TODO special bonus for genies ability
-	if (randSpellcaster && gameHandler->battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) == SpellID::NONE)
-		spellID = gameHandler->battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_GENIE);
+	if (randSpellcaster && battle.battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) == SpellID::NONE)
+		spellID = battle.battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_GENIE);
 
 	if (spellID == SpellID::NONE)
 		gameHandler->complain("That stack can't cast spells!");
@@ -426,7 +427,7 @@ bool BattleActionProcessor::doUnitSpellAction(const BattleInfo & battle, const B
 	return true;
 }
 
-bool BattleActionProcessor::doHealAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::doHealAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
 	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
 	battle::Target target = ba.getTarget(&battle);
@@ -463,7 +464,7 @@ bool BattleActionProcessor::doHealAction(const BattleInfo & battle, const Battle
 	return true;
 }
 
-bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack * stack)
+bool BattleActionProcessor::canStackAct(const CBattleInfoCallback & battle, const CStack * stack)
 {
 	if (!stack)
 	{
@@ -476,9 +477,9 @@ bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack
 		return false;
 	}
 
-	if (gameHandler->battleTacticDist())
+	if (battle.battleTacticDist())
 	{
-		if (stack && stack->unitSide() != gameHandler->battleGetTacticsSide())
+		if (stack && stack->unitSide() != battle.battleGetTacticsSide())
 		{
 			gameHandler->complain("This is not a stack of side that has tactics!");
 			return false;
@@ -486,7 +487,7 @@ bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack
 	}
 	else
 	{
-		if (stack->unitId() != battle.getActiveStackID())
+		if (stack != battle.battleActiveUnit())
 		{
 			gameHandler->complain("Action has to be about active stack!");
 			return false;
@@ -495,7 +496,7 @@ bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack
 	return true;
 }
 
-bool BattleActionProcessor::dispatchBattleAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::dispatchBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
 	switch(ba.actionType)
 	{
@@ -531,7 +532,7 @@ bool BattleActionProcessor::dispatchBattleAction(const BattleInfo & battle, cons
 	return false;
 }
 
-bool BattleActionProcessor::makeBattleActionImpl(const BattleInfo & battle, const BattleAction &ba)
+bool BattleActionProcessor::makeBattleActionImpl(const CBattleInfoCallback & battle, const BattleAction &ba)
 {
 	logGlobal->trace("Making action: %s", ba.toString());
 	const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
@@ -552,22 +553,22 @@ bool BattleActionProcessor::makeBattleActionImpl(const BattleInfo & battle, cons
 	}
 
 	if(ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND || ba.actionType == EActionType::SHOOT || ba.actionType == EActionType::MONSTER_SPELL)
-		gameHandler->handleObstacleTriggersForUnit(*gameHandler->spellEnv, *stack);
+		battle.handleObstacleTriggersForUnit(*gameHandler->spellEnv, *stack);
 
 	return result;
 }
 
-int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, BattleHex dest)
+int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int stack, BattleHex dest)
 {
 	int ret = 0;
 
-	const CStack *curStack = gameHandler->battleGetStackByID(stack);
+	const CStack *curStack = battle.battleGetStackByID(stack);
 	const CStack *stackAtEnd = battle.battleGetStackByPos(dest);
 
 	assert(curStack);
 	assert(dest < GameConstants::BFIELD_SIZE);
 
-	if (battle.tacticDistance)
+	if (battle.battleGetTacticDist())
 	{
 		assert(battle.isInTacticRange(dest));
 	}
@@ -577,7 +578,7 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
 		return 0;
 
 	//initing necessary tables
-	auto accessibility = gameHandler->getAccesibility(curStack);
+	auto accessibility = battle.getAccesibility(curStack);
 	std::set<BattleHex> passed;
 	//Ignore obstacles on starting position
 	passed.insert(curStack->getPosition());
@@ -600,8 +601,8 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
 	}
 
 	bool canUseGate = false;
-	auto dbState = battle.si.gateState;
-	if(gameHandler->battleGetSiegeLevel() > 0 && curStack->unitSide() == BattleSide::DEFENDER &&
+	auto dbState = battle.battleGetGateState();
+	if(battle.battleGetSiegeLevel() > 0 && curStack->unitSide() == BattleSide::DEFENDER &&
 		dbState != EGateState::DESTROYED &&
 		dbState != EGateState::BLOCKED)
 	{
@@ -614,10 +615,10 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
 
 	int creSpeed = curStack->speed(0, true);
 
-	if (battle.tacticDistance > 0 && creSpeed > 0)
+	if (battle.battleGetTacticDist() > 0 && creSpeed > 0)
 		creSpeed = GameConstants::BFIELD_SIZE;
 
-	bool hasWideMoat = vstd::contains_if(gameHandler->battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
+	bool hasWideMoat = vstd::contains_if(battle.battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
 	{
 		return obst->obstacleType == CObstacleInstance::MOAT;
 	});
@@ -772,14 +773,14 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
 					}
 
 					//if we walked onto something, finalize this portion of stack movement check into obstacle
-					if(!gameHandler->battleGetAllObstaclesOnPos(hex, false).empty())
+					if(!battle.battleGetAllObstaclesOnPos(hex, false).empty())
 						obstacleHit = true;
 
 					if (curStack->doubleWide())
 					{
 						BattleHex otherHex = curStack->occupiedHex(hex);
 						//two hex creature hit obstacle by backside
-						auto obstacle2 = gameHandler->battleGetAllObstaclesOnPos(otherHex, false);
+						auto obstacle2 = battle.battleGetAllObstaclesOnPos(otherHex, false);
 						if(otherHex.isValid() && !obstacle2.empty())
 							obstacleHit = true;
 					}
@@ -805,7 +806,7 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
 			{
 				if(stackIsMoving && start != curStack->getPosition())
 				{
-					stackIsMoving = gameHandler->handleObstacleTriggersForUnit(*gameHandler->spellEnv, *curStack, passed);
+					stackIsMoving = battle.handleObstacleTriggersForUnit(*gameHandler->spellEnv, *curStack, passed);
 					passed.insert(curStack->getPosition());
 					if(curStack->doubleWide())
 						passed.insert(curStack->occupiedHex());
@@ -843,12 +844,12 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
 			passed.clear(); //Just empty passed, obstacles will handled automatically
 	}
 	//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
-	gameHandler->handleObstacleTriggersForUnit(*gameHandler->spellEnv, *curStack, passed);
+	battle.handleObstacleTriggersForUnit(*gameHandler->spellEnv, *curStack, passed);
 
 	return ret;
 }
 
-void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter)
+void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter)
 {
 	if(first && !counter)
 		handleAttackBeforeCasting(battle, ranged, attacker, defender);
@@ -891,7 +892,7 @@ void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack *
 		bat.flags |= BattleAttack::DEATH_BLOW;
 	}
 
-	const auto * owner = battle.getHero(attacker->unitOwner());
+	const auto * owner = battle.battleGetFightingHero(attacker->unitOwner());
 	if(owner)
 	{
 		int chance = owner->valOfBonuses(BonusType::BONUS_DAMAGE_CHANCE, attacker->creatureIndex());
@@ -1012,7 +1013,7 @@ void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack *
 			const CStack * actor = item.first;
 			int64_t rawDamage = item.second;
 
-			const CGHeroInstance * actorOwner = battle.getHero(actor->unitOwner());
+			const CGHeroInstance * actorOwner = battle.battleGetFightingHero(actor->unitOwner());
 
 			if(actorOwner)
 			{
@@ -1058,7 +1059,7 @@ void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack *
 	handleAfterAttackCasting(battle, ranged, attacker, defender);
 }
 
-void BattleActionProcessor::attackCasting(const BattleInfo & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender)
+void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender)
 {
 	if(attacker->hasBonusOfType(attackMode))
 	{
@@ -1126,12 +1127,12 @@ void BattleActionProcessor::attackCasting(const BattleInfo & battle, bool ranged
 	}
 }
 
-void BattleActionProcessor::handleAttackBeforeCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender)
+void BattleActionProcessor::handleAttackBeforeCasting(const CBattleInfoCallback & battle, bool ranged, const CStack * attacker, const CStack * defender)
 {
 	attackCasting(battle, ranged, BonusType::SPELL_BEFORE_ATTACK, attacker, defender); //no death stare / acid breath needed?
 }
 
-void BattleActionProcessor::handleAfterAttackCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender)
+void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback & battle, bool ranged, const CStack * attacker, const CStack * defender)
 {
 	if(!attacker->alive() || !defender->alive()) // can be already dead
 		return;
@@ -1286,7 +1287,7 @@ void BattleActionProcessor::handleAfterAttackCasting(const BattleInfo & battle,
 	}
 }
 
-int64_t BattleActionProcessor::applyBattleEffects(const BattleInfo & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary)
+int64_t BattleActionProcessor::applyBattleEffects(const CBattleInfoCallback & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary)
 {
 	BattleStackAttacked bsa;
 	if(secondary)
@@ -1303,7 +1304,7 @@ int64_t BattleActionProcessor::applyBattleEffects(const BattleInfo & battle, Bat
 		bai.unluckyStrike  = bat.unlucky();
 
 		auto range = battle.calculateDmgRange(bai);
-		bsa.damageAmount = battle.getActualDamage(range.damage, attackerState->getCount(), gameHandler->getRandomGenerator());
+		bsa.damageAmount = battle.getBattle()->getActualDamage(range.damage, attackerState->getCount(), gameHandler->getRandomGenerator());
 		CStack::prepareAttacked(bsa, gameHandler->getRandomGenerator(), bai.defender->acquireState()); //calculate casualties
 	}
 
@@ -1390,17 +1391,17 @@ void BattleActionProcessor::addGenericKilledLog(BattleLogMessage & blm, const CS
 	}
 }
 
-bool BattleActionProcessor::makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba)
+bool BattleActionProcessor::makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba)
 {
 	return makeBattleActionImpl(battle, ba);
 }
 
-bool BattleActionProcessor::makePlayerBattleAction(const BattleInfo & battle, PlayerColor player, const BattleAction &ba)
+bool BattleActionProcessor::makePlayerBattleAction(const CBattleInfoCallback & battle, PlayerColor player, const BattleAction &ba)
 {
 	if (ba.side != 0 && ba.side != 1 && gameHandler->complain("Can not make action - invalid battle side!"))
 		return false;
 
-	if(battle.tacticDistance != 0)
+	if(battle.battleGetTacticDist() != 0)
 	{
 		if(!ba.isTacticsAction())
 		{
@@ -1408,7 +1409,7 @@ bool BattleActionProcessor::makePlayerBattleAction(const BattleInfo & battle, Pl
 			return false;
 		}
 
-		if(player != battle.sides[ba.side].color)
+		if(player != battle.sideToPlayer(ba.side))
 		{
 			gameHandler->complain("Can not make actions in battles you are not part of!");
 			return false;
@@ -1416,16 +1417,16 @@ bool BattleActionProcessor::makePlayerBattleAction(const BattleInfo & battle, Pl
 	}
 	else
 	{
-		if (ba.isUnitAction() && ba.stackNumber != battle.getActiveStackID())
+		auto active = battle.battleActiveUnit();
+		if(!active && gameHandler->complain("No active unit in battle!"))
+			return false;
+
+		if (ba.isUnitAction() && ba.stackNumber != active->unitId())
 		{
 			gameHandler->complain("Can not make actions - stack is not active!");
 			return false;
 		}
 
-		auto active = battle.battleActiveUnit();
-		if(!active && gameHandler->complain("No active unit in battle!"))
-			return false;
-
 		auto unitOwner = battle.battleGetOwner(active);
 
 		if(player != unitOwner && gameHandler->complain("Can not make actions in battles you are not part of!"))

+ 27 - 27
server/battles/BattleActionProcessor.h

@@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 struct BattleLogMessage;
 struct BattleAttack;
 class BattleAction;
-class BattleInfo;
+class CBattleInfoCallback;
 struct BattleHex;
 class CStack;
 class PlayerColor;
@@ -39,42 +39,42 @@ class BattleActionProcessor : boost::noncopyable
 	BattleProcessor * owner;
 	CGameHandler * gameHandler;
 
-	int moveStack(const BattleInfo & battle, int stack, BattleHex dest); //returned value - travelled distance
-	void makeAttack(const BattleInfo & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter);
+	int moveStack(const CBattleInfoCallback & battle, int stack, BattleHex dest); //returned value - travelled distance
+	void makeAttack(const CBattleInfoCallback & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter);
 
-	void handleAttackBeforeCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender);
-	void handleAfterAttackCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender);
-	void attackCasting(const BattleInfo & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender);
+	void handleAttackBeforeCasting(const CBattleInfoCallback & battle, bool ranged, const CStack * attacker, const CStack * defender);
+	void handleAfterAttackCasting(const CBattleInfoCallback & battle, bool ranged, const CStack * attacker, const CStack * defender);
+	void attackCasting(const CBattleInfoCallback & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender);
 
 	// damage, drain life & fire shield; returns amount of drained life
-	int64_t applyBattleEffects(const BattleInfo & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary);
+	int64_t applyBattleEffects(const CBattleInfoCallback & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary);
 
 	void sendGenericKilledLog(const CStack * defender, int32_t killed, bool multiple);
 	void addGenericKilledLog(BattleLogMessage & blm, const CStack * defender, int32_t killed, bool multiple);
 
-	bool canStackAct(const BattleInfo & battle, const CStack * stack);
-
-	bool doEmptyAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doEndTacticsAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doRetreatAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doSurrenderAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doHeroSpellAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doWalkAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doWaitAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doDefendAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doAttackAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doShootAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doCatapultAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doUnitSpellAction(const BattleInfo & battle, const BattleAction & ba);
-	bool doHealAction(const BattleInfo & battle, const BattleAction & ba);
-
-	bool dispatchBattleAction(const BattleInfo & battle, const BattleAction & ba);
-	bool makeBattleActionImpl(const BattleInfo & battle, const BattleAction & ba);
+	bool canStackAct(const CBattleInfoCallback & battle, const CStack * stack);
+
+	bool doEmptyAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doEndTacticsAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doRetreatAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doSurrenderAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doHeroSpellAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doWalkAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doWaitAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doDefendAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doAttackAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doShootAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doCatapultAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doUnitSpellAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool doHealAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+
+	bool dispatchBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool makeBattleActionImpl(const CBattleInfoCallback & battle, const BattleAction & ba);
 
 public:
 	explicit BattleActionProcessor(BattleProcessor * owner);
 	void setGameHandler(CGameHandler * newGameHandler);
 
-	bool makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba);
-	bool makePlayerBattleAction(const BattleInfo & battle, PlayerColor player, const BattleAction & ba);
+	bool makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba);
+	bool makePlayerBattleAction(const CBattleInfoCallback & battle, PlayerColor player, const BattleAction & ba);
 };

+ 52 - 53
server/battles/BattleFlowProcessor.cpp

@@ -16,7 +16,8 @@
 
 #include "../../lib/CStack.h"
 #include "../../lib/GameSettings.h"
-#include "../../lib/battle/BattleInfo.h"
+#include "../../lib/battle/CBattleInfoCallback.h"
+#include "../../lib/battle/IBattleState.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/NetPacks.h"
@@ -35,7 +36,7 @@ void BattleFlowProcessor::setGameHandler(CGameHandler * newGameHandler)
 	gameHandler = newGameHandler;
 }
 
-void BattleFlowProcessor::summonGuardiansHelper(const BattleInfo & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
+void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
 {
 	int x = targetPosition.getX();
 	int y = targetPosition.getY();
@@ -110,39 +111,39 @@ void BattleFlowProcessor::summonGuardiansHelper(const BattleInfo & battle, std::
 	}
 }
 
-void BattleFlowProcessor::tryPlaceMoats(const BattleInfo & battle)
+void BattleFlowProcessor::tryPlaceMoats(const CBattleInfoCallback & battle)
 {
+	const auto * town = battle.battleGetDefendedTown();
+
 	//Moat should be initialized here, because only here we can use spellcasting
-	if (battle.town && battle.town->fortLevel() >= CGTownInstance::CITADEL)
+	if (town && town->fortLevel() >= CGTownInstance::CITADEL)
 	{
 		const auto * h = battle.battleGetFightingHero(BattleSide::DEFENDER);
 		const auto * actualCaster = h ? static_cast<const spells::Caster*>(h) : nullptr;
-		auto moatCaster = spells::SilentCaster(battle.getSidePlayer(BattleSide::DEFENDER), actualCaster);
-		auto cast = spells::BattleCast(&battle, &moatCaster, spells::Mode::PASSIVE, battle.town->town->moatAbility.toSpell());
+		auto moatCaster = spells::SilentCaster(battle.sideToPlayer(BattleSide::DEFENDER), actualCaster);
+		auto cast = spells::BattleCast(&battle, &moatCaster, spells::Mode::PASSIVE, town->town->moatAbility.toSpell());
 		auto target = spells::Target();
 		cast.cast(gameHandler->spellEnv, target);
 	}
 }
 
-void BattleFlowProcessor::onBattleStarted(const BattleInfo & battle)
+void BattleFlowProcessor::onBattleStarted(const CBattleInfoCallback & battle)
 {
-	gameHandler->setBattle(&battle);
-
 	tryPlaceMoats(battle);
 	
-	gameHandler->turnTimerHandler.onBattleStart(battle.battleID);
+	gameHandler->turnTimerHandler.onBattleStart(battle.getBattle()->getBattleID());
 
-	if (battle.tacticDistance == 0)
+	if (battle.battleGetTacticDist() == 0)
 		onTacticsEnded(battle);
 }
 
-void BattleFlowProcessor::trySummonGuardians(const BattleInfo & battle, const CStack * stack)
+void BattleFlowProcessor::trySummonGuardians(const CBattleInfoCallback & battle, const CStack * stack)
 {
 	if (!stack->hasBonusOfType(BonusType::SUMMON_GUARDIANS))
 		return;
 
 	std::shared_ptr<const Bonus> summonInfo = stack->getBonus(Selector::type()(BonusType::SUMMON_GUARDIANS));
-	auto accessibility = gameHandler->getAccesibility();
+	auto accessibility = battle.getAccesibility();
 	CreatureID creatureData = CreatureID(summonInfo->subtype);
 	std::vector<BattleHex> targetHexes;
 	const bool targetIsBig = stack->unitType()->isDoubleWide(); //target = creature to guard
@@ -177,7 +178,7 @@ void BattleFlowProcessor::trySummonGuardians(const BattleInfo & battle, const CS
 	}
 }
 
-void BattleFlowProcessor::castOpeningSpells(const BattleInfo & battle)
+void BattleFlowProcessor::castOpeningSpells(const CBattleInfoCallback & battle)
 {
 	for (int i = 0; i < 2; ++i)
 	{
@@ -202,12 +203,12 @@ void BattleFlowProcessor::castOpeningSpells(const BattleInfo & battle)
 	}
 }
 
-void BattleFlowProcessor::onTacticsEnded(const BattleInfo & battle)
+void BattleFlowProcessor::onTacticsEnded(const CBattleInfoCallback & battle)
 {
 	//initial stacks appearance triggers, e.g. built-in bonus spells
-	auto initialStacks = battle.stacks; //use temporary variable to outclude summoned stacks added to battle.stacks from processing
+	auto initialStacks = battle.battleGetAllStacks(true);
 
-	for (CStack * stack : initialStacks)
+	for (const CStack * stack : initialStacks)
 	{
 		trySummonGuardians(battle, stack);
 		stackEnchantedTrigger(battle, stack);
@@ -223,14 +224,14 @@ void BattleFlowProcessor::onTacticsEnded(const BattleInfo & battle)
 	activateNextStack(battle);
 }
 
-void BattleFlowProcessor::startNextRound(const BattleInfo & battle, bool isFirstRound)
+void BattleFlowProcessor::startNextRound(const CBattleInfoCallback & battle, bool isFirstRound)
 {
 	BattleNextRound bnr;
-	bnr.round = battle.round + 1;
-	logGlobal->debug("Round %d", bnr.round);
+	logGlobal->debug("Next round starts");
 	gameHandler->sendAndApply(&bnr);
 
-	auto obstacles = battle.obstacles; //we copy container, because we're going to modify it
+	// operate on copy - removing obstacles will invalidate iterator on 'battle' container
+	auto obstacles = battle.battleGetAllObstacles();
 	for (auto &obstPtr : obstacles)
 	{
 		if (const SpellCreatedObstacle *sco = dynamic_cast<const SpellCreatedObstacle *>(obstPtr.get()))
@@ -238,16 +239,14 @@ void BattleFlowProcessor::startNextRound(const BattleInfo & battle, bool isFirst
 				removeObstacle(battle, *obstPtr);
 	}
 
-	const BattleInfo & curB = *&battle;
-
-	for(auto stack : curB.stacks)
+	for(auto stack : battle.battleGetAllStacks(true))
 	{
 		if(stack->alive() && !isFirstRound)
 			stackEnchantedTrigger(battle, stack);
 	}
 }
 
-const CStack * BattleFlowProcessor::getNextStack(const BattleInfo & battle)
+const CStack * BattleFlowProcessor::getNextStack(const CBattleInfoCallback & battle)
 {
 	std::vector<battle::Units> q;
 	battle.battleGetTurnOrder(q, 1, 0, -1); //todo: get rid of "turn -1"
@@ -283,7 +282,7 @@ const CStack * BattleFlowProcessor::getNextStack(const BattleInfo & battle)
 	return stack;
 }
 
-void BattleFlowProcessor::activateNextStack(const BattleInfo & battle)
+void BattleFlowProcessor::activateNextStack(const CBattleInfoCallback & battle)
 {
 	// Find next stack that requires manual control
 	for (;;)
@@ -305,16 +304,17 @@ void BattleFlowProcessor::activateNextStack(const BattleInfo & battle)
 
 		BattleUnitsChanged removeGhosts;
 
-		for(auto stack : battle.stacks)
-		{
-			if(stack->ghostPending)
-				removeGhosts.changedStacks.emplace_back(stack->unitId(), UnitChanges::EOperation::REMOVE);
-		}
+		auto pendingGhosts = battle.battleGetStacksIf([](const CStack * stack){
+			return stack->ghostPending;
+		});
+
+		for(auto stack : pendingGhosts)
+			removeGhosts.changedStacks.emplace_back(stack->unitId(), UnitChanges::EOperation::REMOVE);
 
 		if(!removeGhosts.changedStacks.empty())
 			gameHandler->sendAndApply(&removeGhosts);
 		
-		gameHandler->turnTimerHandler.onBattleNextStack(battle.battleID, *next);
+		gameHandler->turnTimerHandler.onBattleNextStack(battle.getBattle()->getBattleID(), *next);
 
 		if (!tryMakeAutomaticAction(battle, next))
 		{
@@ -324,7 +324,7 @@ void BattleFlowProcessor::activateNextStack(const BattleInfo & battle)
 	}
 }
 
-bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, const CStack * next)
+bool BattleFlowProcessor::tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * next)
 {
 	// check for bad morale => freeze
 	int nextStackMorale = next->moraleVal();
@@ -370,7 +370,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, cons
 		return true;
 	}
 
-	const CGHeroInstance * curOwner = gameHandler->battleGetOwnerHero(next);
+	const CGHeroInstance * curOwner = battle.battleGetOwnerHero(next);
 	const int stackCreatureId = next->unitType()->getId();
 
 	if ((stackCreatureId == CreatureID::ARROW_TOWERS || stackCreatureId == CreatureID::BALLISTA)
@@ -385,7 +385,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, cons
 
 		const battle::Unit * target = nullptr;
 
-		for(auto & elem : battle.stacks)
+		for(auto & elem : battle.battleGetAllStacks(true))
 		{
 			if(elem->unitType()->getId() != CreatureID::CATAPULT
 			   && elem->unitOwner() != next->unitOwner()
@@ -433,7 +433,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, cons
 
 	if (next->unitType()->getId() == CreatureID::FIRST_AID_TENT)
 	{
-		TStacks possibleStacks = gameHandler->battleGetStacksIf([=](const CStack * s)
+		TStacks possibleStacks = battle.battleGetStacksIf([=](const CStack * s)
 		{
 			return s->unitOwner() == next->unitOwner() && s->canBeHealed();
 		});
@@ -470,7 +470,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, cons
 	return false;
 }
 
-bool BattleFlowProcessor::rollGoodMorale(const BattleInfo & battle, const CStack * next)
+bool BattleFlowProcessor::rollGoodMorale(const CBattleInfoCallback & battle, const CStack * next)
 {
 	//check for good morale
 	auto nextStackMorale = next->moraleVal();
@@ -498,10 +498,10 @@ bool BattleFlowProcessor::rollGoodMorale(const BattleInfo & battle, const CStack
 	return false;
 }
 
-void BattleFlowProcessor::onActionMade(const BattleInfo & battle, const BattleAction &ba)
+void BattleFlowProcessor::onActionMade(const CBattleInfoCallback & battle, const BattleAction &ba)
 {
-	const CStack * actedStack = battle.battleGetStackByID(ba.stackNumber, false);
-	const CStack * activeStack = battle.battleGetStackByID(battle.getActiveStackID(), false);
+	const auto * actedStack = battle.battleGetStackByID(ba.stackNumber, false);
+	const auto * activeStack = battle.battleActiveUnit();
 	if (ba.actionType == EActionType::END_TACTIC_PHASE)
 	{
 		onTacticsEnded(battle);
@@ -516,7 +516,7 @@ void BattleFlowProcessor::onActionMade(const BattleInfo & battle, const BattleAc
 		return;
 
 	// tactics - next stack will be selected by player
-	if(battle.tacticDistance != 0)
+	if(battle.battleGetTacticDist() != 0)
 		return;
 
 	if (ba.isUnitAction())
@@ -545,7 +545,7 @@ void BattleFlowProcessor::onActionMade(const BattleInfo & battle, const BattleAc
 	activateNextStack(battle);
 }
 
-void BattleFlowProcessor::makeStackDoNothing(const BattleInfo & battle, const CStack * next)
+void BattleFlowProcessor::makeStackDoNothing(const CBattleInfoCallback & battle, const CStack * next)
 {
 	BattleAction doNothing;
 	doNothing.actionType = EActionType::NO_ACTION;
@@ -555,7 +555,7 @@ void BattleFlowProcessor::makeStackDoNothing(const BattleInfo & battle, const CS
 	makeAutomaticAction(battle, next, doNothing);
 }
 
-bool BattleFlowProcessor::makeAutomaticAction(const BattleInfo & battle, const CStack *stack, BattleAction &ba)
+bool BattleFlowProcessor::makeAutomaticAction(const CBattleInfoCallback & battle, const CStack *stack, BattleAction &ba)
 {
 	BattleSetActiveStack bsa;
 	bsa.stack = stack->unitId();
@@ -566,7 +566,7 @@ bool BattleFlowProcessor::makeAutomaticAction(const BattleInfo & battle, const C
 	return ret;
 }
 
-void BattleFlowProcessor::stackEnchantedTrigger(const BattleInfo & battle, const CStack * st)
+void BattleFlowProcessor::stackEnchantedTrigger(const CBattleInfoCallback & battle, const CStack * st)
 {
 	auto bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTED)));
 	for(auto b : bl)
@@ -587,7 +587,7 @@ void BattleFlowProcessor::stackEnchantedTrigger(const BattleInfo & battle, const
 		if(val > 3)
 		{
 			for(auto s : battle.battleGetAllStacks())
-				if(gameHandler->battleMatchOwner(st, s, true) && s->isValidTarget()) //all allied
+				if(battle.battleMatchOwner(st, s, true) && s->isValidTarget()) //all allied
 					target.emplace_back(s);
 		}
 		else
@@ -598,14 +598,14 @@ void BattleFlowProcessor::stackEnchantedTrigger(const BattleInfo & battle, const
 	}
 }
 
-void BattleFlowProcessor::removeObstacle(const BattleInfo & battle, const CObstacleInstance & obstacle)
+void BattleFlowProcessor::removeObstacle(const CBattleInfoCallback & battle, const CObstacleInstance & obstacle)
 {
 	BattleObstaclesChanged obsRem;
 	obsRem.changes.emplace_back(obstacle.uniqueID, ObstacleChanges::EOperation::REMOVE);
 	gameHandler->sendAndApply(&obsRem);
 }
 
-void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CStack *st)
+void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, const CStack *st)
 {
 	BattleTriggerEffect bte;
 	bte.stackID = st->unitId();
@@ -662,7 +662,7 @@ void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CSta
 		if(st->hasBonusOfType(BonusType::MANA_DRAIN) && !st->drainedMana)
 		{
 			const PlayerColor opponent = battle.otherPlayer(battle.battleGetOwner(st));
-			const CGHeroInstance * opponentHero = battle.getHero(opponent);
+			const CGHeroInstance * opponentHero = battle.battleGetFightingHero(opponent);
 			if(opponentHero)
 			{
 				ui32 manaDrained = st->valOfBonuses(BonusType::MANA_DRAIN);
@@ -679,9 +679,9 @@ void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CSta
 		if (st->isLiving() && !st->hasBonusOfType(BonusType::FEARLESS))
 		{
 			bool fearsomeCreature = false;
-			for (CStack * stack : battle.stacks)
+			for (const CStack * stack : battle.battleGetAllStacks(true))
 			{
-				if (gameHandler->battleMatchOwner(st, stack) && stack->alive() && stack->hasBonusOfType(BonusType::FEAR))
+				if (battle.battleMatchOwner(st, stack) && stack->alive() && stack->hasBonusOfType(BonusType::FEAR))
 				{
 					fearsomeCreature = true;
 					break;
@@ -697,7 +697,7 @@ void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CSta
 			}
 		}
 		BonusList bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTER)));
-		int side = battle.whatSide(st->unitOwner());
+		int side = *battle.playerToSide(st->unitOwner());
 		if(st->canCast() && battle.battleGetEnchanterCounter(side) == 0)
 		{
 			bool cast = false;
@@ -732,11 +732,10 @@ void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CSta
 	}
 }
 
-void BattleFlowProcessor::setActiveStack(const BattleInfo & battle, const CStack * stack)
+void BattleFlowProcessor::setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack)
 {
 	assert(stack);
 
-	logGlobal->trace("Activating %s", stack->nodeName());
 	BattleSetActiveStack sas;
 	sas.stack = stack->unitId();
 	gameHandler->sendAndApply(&sas);

+ 23 - 19
server/battles/BattleFlowProcessor.h

@@ -13,8 +13,12 @@ VCMI_LIB_NAMESPACE_BEGIN
 class CStack;
 struct BattleHex;
 class BattleAction;
-class BattleInfo;
+class CBattleInfoCallback;
 struct CObstacleInstance;
+namespace battle
+{
+class Unit;
+}
 VCMI_LIB_NAMESPACE_END
 
 class CGameHandler;
@@ -26,31 +30,31 @@ class BattleFlowProcessor : boost::noncopyable
 	BattleProcessor * owner;
 	CGameHandler * gameHandler;
 
-	const CStack * getNextStack(const BattleInfo & battle);
+	const CStack * getNextStack(const CBattleInfoCallback & battle);
 
-	bool rollGoodMorale(const BattleInfo & battle, const CStack * stack);
-	bool tryMakeAutomaticAction(const BattleInfo & battle, const CStack * stack);
+	bool rollGoodMorale(const CBattleInfoCallback & battle, const CStack * stack);
+	bool tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack);
 
-	void summonGuardiansHelper(const BattleInfo & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex);
-	void trySummonGuardians(const BattleInfo & battle, const CStack * stack);
-	void tryPlaceMoats(const BattleInfo & battle);
-	void castOpeningSpells(const BattleInfo & battle);
-	void activateNextStack(const BattleInfo & battle);
-	void startNextRound(const BattleInfo & battle, bool isFirstRound);
+	void summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex);
+	void trySummonGuardians(const CBattleInfoCallback & battle, const CStack * stack);
+	void tryPlaceMoats(const CBattleInfoCallback & battle);
+	void castOpeningSpells(const CBattleInfoCallback & battle);
+	void activateNextStack(const CBattleInfoCallback & battle);
+	void startNextRound(const CBattleInfoCallback & battle, bool isFirstRound);
 
-	void stackEnchantedTrigger(const BattleInfo & battle, const CStack * stack);
-	void removeObstacle(const BattleInfo & battle, const CObstacleInstance & obstacle);
-	void stackTurnTrigger(const BattleInfo & battle, const CStack * stack);
-	void setActiveStack(const BattleInfo & battle, const CStack * stack);
+	void stackEnchantedTrigger(const CBattleInfoCallback & battle, const CStack * stack);
+	void removeObstacle(const CBattleInfoCallback & battle, const CObstacleInstance & obstacle);
+	void stackTurnTrigger(const CBattleInfoCallback & battle, const CStack * stack);
+	void setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack);
 
-	void makeStackDoNothing(const BattleInfo & battle, const CStack * next);
-	bool makeAutomaticAction(const BattleInfo & battle, const CStack * stack, BattleAction & ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack)
+	void makeStackDoNothing(const CBattleInfoCallback & battle, const CStack * next);
+	bool makeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack, BattleAction & ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack)
 
 public:
 	explicit BattleFlowProcessor(BattleProcessor * owner);
 	void setGameHandler(CGameHandler * newGameHandler);
 
-	void onBattleStarted(const BattleInfo & battle);
-	void onTacticsEnded(const BattleInfo & battle);
-	void onActionMade(const BattleInfo & battle, const BattleAction & ba);
+	void onBattleStarted(const CBattleInfoCallback & battle);
+	void onTacticsEnded(const CBattleInfoCallback & battle);
+	void onActionMade(const CBattleInfoCallback & battle, const BattleAction & ba);
 };

+ 13 - 12
server/battles/BattleProcessor.cpp

@@ -19,6 +19,7 @@
 #include "../queries/BattleQueries.h"
 
 #include "../../lib/TerrainHandler.h"
+#include "../../lib/battle/CBattleInfoCallback.h"
 #include "../../lib/battle/BattleInfo.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/mapping/CMap.h"
@@ -147,17 +148,17 @@ BattleID BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2]
 	return bs.battleID;
 }
 
-bool BattleProcessor::checkBattleStateChanges(const BattleInfo & battle)
+bool BattleProcessor::checkBattleStateChanges(const CBattleInfoCallback & battle)
 {
 	//check if drawbridge state need to be changes
-	if (gameHandler->battleGetSiegeLevel() > 0)
+	if (battle.battleGetSiegeLevel() > 0)
 		updateGateState(battle);
 
 	if (resultProcessor->battleIsEnding(battle))
 		return true;
 
 	//check if battle ended
-	if (auto result = gameHandler->battleIsFinished())
+	if (auto result = battle.battleIsFinished())
 	{
 		setBattleResult(battle, EBattleResult::NORMAL, *result);
 		return true;
@@ -166,7 +167,7 @@ bool BattleProcessor::checkBattleStateChanges(const BattleInfo & battle)
 	return false;
 }
 
-void BattleProcessor::updateGateState(const BattleInfo & battle)
+void BattleProcessor::updateGateState(const CBattleInfoCallback & battle)
 {
 	// GATE_BRIDGE - leftmost tile, located over moat
 	// GATE_OUTER - central tile, mostly covered by gate image
@@ -182,20 +183,20 @@ void BattleProcessor::updateGateState(const BattleInfo & battle)
 	// - if Force Field is cast here, bridge can't open (but can close, in any town)
 	// - deals moat damage to attacker if bridge is closed (fortress only)
 
-	bool hasForceFieldOnBridge = !gameHandler->battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), true).empty();
+	bool hasForceFieldOnBridge = !battle.battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), true).empty();
 	bool hasStackAtGateInner   = battle.battleGetUnitByPos(BattleHex(BattleHex::GATE_INNER), false) != nullptr;
 	bool hasStackAtGateOuter   = battle.battleGetUnitByPos(BattleHex(BattleHex::GATE_OUTER), false) != nullptr;
 	bool hasStackAtGateBridge  = battle.battleGetUnitByPos(BattleHex(BattleHex::GATE_BRIDGE), false) != nullptr;
-	bool hasWideMoat           = vstd::contains_if(gameHandler->battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
+	bool hasWideMoat           = vstd::contains_if(battle.battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
 	{
 		return obst->obstacleType == CObstacleInstance::MOAT;
 	});
 
 	BattleUpdateGateState db;
-	db.state = battle.si.gateState;
-	db.battleID = battle.battleID;
+	db.state = battle.battleGetGateState();
+	db.battleID = battle.getBattle()->getBattleID();
 
-	if (battle.si.wallState.at(EWallPart::GATE) == EWallState::DESTROYED)
+	if (battle.battleGetWallState(EWallPart::GATE) == EWallState::DESTROYED)
 	{
 		db.state = EGateState::DESTROYED;
 	}
@@ -219,7 +220,7 @@ void BattleProcessor::updateGateState(const BattleInfo & battle)
 			db.state = EGateState::CLOSED;
 	}
 
-	if (db.state != battle.si.gateState)
+	if (db.state != battle.battleGetGateState())
 		gameHandler->sendAndApply(&db);
 }
 
@@ -236,13 +237,13 @@ bool BattleProcessor::makePlayerBattleAction(const BattleID & battleID, PlayerCo
 	return result;
 }
 
-void BattleProcessor::setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide)
+void BattleProcessor::setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide)
 {
 	resultProcessor->setBattleResult(battle, resultType, victoriusSide);
 	resultProcessor->endBattle(battle);
 }
 
-bool BattleProcessor::makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction &ba)
+bool BattleProcessor::makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction &ba)
 {
 	return actionsProcessor->makeAutomaticBattleAction(battle, ba);
 }

+ 5 - 6
server/battles/BattleProcessor.h

@@ -17,7 +17,7 @@ class CGTownInstance;
 class CArmedInstance;
 class BattleAction;
 class int3;
-class BattleInfo;
+class CBattleInfoCallback;
 struct BattleResult;
 class BattleID;
 VCMI_LIB_NAMESPACE_END
@@ -40,16 +40,15 @@ class BattleProcessor : boost::noncopyable
 	std::unique_ptr<BattleFlowProcessor> flowProcessor;
 	std::unique_ptr<BattleResultProcessor> resultProcessor;
 
-	void onTacticsEnded();
-	void updateGateState(const BattleInfo & battle);
+	void updateGateState(const CBattleInfoCallback & battle);
 	void engageIntoBattle(PlayerColor player);
 
-	bool checkBattleStateChanges(const BattleInfo & battle);
+	bool checkBattleStateChanges(const CBattleInfoCallback & battle);
 	BattleID setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
 
-	bool makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba);
+	bool makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba);
 
-	void setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide);
+	void setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide);
 
 public:
 	explicit BattleProcessor(CGameHandler * gameHandler);

+ 73 - 45
server/battles/BattleResultProcessor.cpp

@@ -18,7 +18,9 @@
 #include "../../lib/ArtifactUtils.h"
 #include "../../lib/CStack.h"
 #include "../../lib/GameSettings.h"
-#include "../../lib/battle/BattleInfo.h"
+#include "../../lib/battle/CBattleInfoCallback.h"
+#include "../../lib/battle/IBattleState.h"
+#include "../../lib/battle/SideInBattle.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/serializer/Cast.h"
@@ -35,15 +37,19 @@ void BattleResultProcessor::setGameHandler(CGameHandler * newGameHandler)
 	gameHandler = newGameHandler;
 }
 
-CasualtiesAfterBattle::CasualtiesAfterBattle(const SideInBattle & battleSide, const BattleInfo * bat):
-	army(battleSide.armyObject)
+CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle, uint8_t sideInBattle):
+	army(battle.battleGetArmyObject(sideInBattle))
 {
 	heroWithDeadCommander = ObjectInstanceID();
 
-	PlayerColor color = battleSide.color;
+	PlayerColor color = battle.sideToPlayer(sideInBattle);
 
-	for(CStack * st : bat->stacks)
+	for(const CStack * stConst : battle.battleGetAllStacks(true))
 	{
+		// Use const cast - in order to call non-const "takeResurrected" for proper calculation of casualties
+		// TODO: better solution
+		CStack * st = const_cast<CStack*>(stConst);
+
 		if(st->summoned) //don't take into account temporary summoned stacks
 			continue;
 		if(st->unitOwner() != color) //remove only our stacks
@@ -181,11 +187,23 @@ FinishingBattleHelper::FinishingBattleHelper(std::shared_ptr<const CBattleQuery>
 	auto &result = *Query->result;
 	auto &info = *Query->bi;
 
-	winnerHero = result.winner != 0 ? info.sides[1].hero : info.sides[0].hero;
-	loserHero = result.winner != 0 ? info.sides[0].hero : info.sides[1].hero;
-	victor = info.sides[result.winner].color;
-	loser = info.sides[!result.winner].color;
+	if (result.winner == BattleSide::ATTACKER)
+	{
+		winnerHero = info.getSideHero(BattleSide::ATTACKER);
+		loserHero = info.getSideHero(BattleSide::DEFENDER);
+		victor = info.getSidePlayer(BattleSide::ATTACKER);
+		loser = info.getSidePlayer(BattleSide::DEFENDER);
+	}
+	else
+	{
+		winnerHero = info.getSideHero(BattleSide::DEFENDER);
+		loserHero = info.getSideHero(BattleSide::ATTACKER);
+		victor = info.getSidePlayer(BattleSide::DEFENDER);
+		loser = info.getSidePlayer(BattleSide::ATTACKER);
+	}
+
 	winnerSide = result.winner;
+
 	this->remainingBattleQueriesCount = remainingBattleQueriesCount;
 }
 
@@ -196,7 +214,7 @@ FinishingBattleHelper::FinishingBattleHelper()
 	remainingBattleQueriesCount = 0;
 }
 
-void BattleResultProcessor::endBattle(const BattleInfo & battle)
+void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle)
 {
 	auto const & giveExp = [](BattleResult &r)
 	{
@@ -215,9 +233,9 @@ void BattleResultProcessor::endBattle(const BattleInfo & battle)
 
 	LOG_TRACE(logGlobal);
 
-	auto * battleResult = battleResults.at(battle.battleID).get();
-	const auto * heroAttacker = battle.getSideHero(BattleSide::ATTACKER);
-	const auto * heroDefender = battle.getSideHero(BattleSide::DEFENDER);
+	auto * battleResult = battleResults.at(battle.getBattle()->getBattleID()).get();
+	const auto * heroAttacker = battle.battleGetFightingHero(BattleSide::ATTACKER);
+	const auto * heroDefender = battle.battleGetFightingHero(BattleSide::DEFENDER);
 
 	//Fill BattleResult structure with exp info
 	giveExp(*battleResult);
@@ -235,11 +253,11 @@ void BattleResultProcessor::endBattle(const BattleInfo & battle)
 	if(heroDefender)
 		battleResult->exp[1] = heroDefender->calculateXp(battleResult->exp[1]);
 
-	auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sides[0].color));
+	auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sideToPlayer(0)));
 	if (!battleQuery)
 	{
 		logGlobal->error("Cannot find battle query!");
-		gameHandler->complain("Player " + boost::lexical_cast<std::string>(battle.sides[0].color) + " has no battle query at the top!");
+		gameHandler->complain("Player " + boost::lexical_cast<std::string>(battle.sideToPlayer(0)) + " has no battle query at the top!");
 		return;
 	}
 
@@ -248,13 +266,13 @@ void BattleResultProcessor::endBattle(const BattleInfo & battle)
 	//Check how many battle gameHandler->queries were created (number of players blocked by battle)
 	const int queriedPlayers = battleQuery ? (int)boost::count(gameHandler->queries->allQueries(), battleQuery) : 0;
 
-	assert(finishingBattles.count(battle.battleID) == 0);
-	finishingBattles[battle.battleID] = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
+	assert(finishingBattles.count(battle.getBattle()->getBattleID()) == 0);
+	finishingBattles[battle.getBattle()->getBattleID()] = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
 
 	// in battles against neutrals, 1st player can ask to replay battle manually
-	if (!battle.sides[1].color.isValidPlayer())
+	if (!battle.sideToPlayer(1).isValidPlayer())
 	{
-		auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(gameHandler, &battle);
+		auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(gameHandler, battle.getBattle());
 		battleResult->queryID = battleDialogQuery->queryID;
 		gameHandler->queries->addQuery(battleDialogQuery);
 	}
@@ -275,21 +293,24 @@ void BattleResultProcessor::endBattle(const BattleInfo & battle)
 		endBattleConfirm(battle);
 }
 
-void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
+void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
 {
-	auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sides.at(0).color));
+	auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sideToPlayer(0)));
 	if(!battleQuery)
 	{
 		logGlobal->trace("No battle query, battle end was confirmed by another player");
 		return;
 	}
 
-	auto * battleResult = battleResults.at(battle.battleID).get();
-	auto * finishingBattle = finishingBattles.at(battle.battleID).get();
+	auto * battleResult = battleResults.at(battle.getBattle()->getBattleID()).get();
+	auto * finishingBattle = finishingBattles.at(battle.getBattle()->getBattleID()).get();
 
 	const EBattleResult result = battleResult->result;
 
-	CasualtiesAfterBattle cab1(battle.sides.at(0), &battle), cab2(battle.sides.at(1), &battle); //calculate casualties before deleting battle
+	//calculate casualties before deleting battle
+	CasualtiesAfterBattle cab1(battle, BattleSide::ATTACKER);
+	CasualtiesAfterBattle cab2(battle, BattleSide::DEFENDER);
+
 	ChangeSpells cs; //for Eagle Eye
 
 	if(!finishingBattle->isDraw() && finishingBattle->winnerHero)
@@ -297,7 +318,7 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
 		if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_LEVEL_LIMIT, -1))
 		{
 			double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_CHANCE, 0);
-			for(auto & spellId : battle.sides.at(!battleResult->winner).usedSpellsHistory)
+			for(auto & spellId : battle.getBattle()->getUsedSpells(battle.otherSide(battleResult->winner)))
 			{
 				auto spell = spellId.toSpell(VLC->spells());
 				if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && gameHandler->getRandomGenerator().nextInt(99) < eagleEyeChance)
@@ -365,7 +386,10 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
 				}
 			}
 		}
-		for (auto armySlot : battle.sides.at(!battleResult->winner).armyObject->stacks)
+
+		auto loser = battle.otherSide(battleResult->winner);
+
+		for (auto armySlot : battle.battleGetArmyObject(loser)->stacks)
 		{
 			auto artifactsWorn = armySlot.second->artifactsWorn;
 			for (auto artSlot : artifactsWorn)
@@ -466,10 +490,10 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
 		gameHandler->changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult->exp[finishingBattle->winnerSide]);
 
 	BattleResultAccepted raccepted;
-	raccepted.heroResult[0].army = const_cast<CArmedInstance*>(battle.sides.at(0).armyObject);
-	raccepted.heroResult[1].army = const_cast<CArmedInstance*>(battle.sides.at(1).armyObject);
-	raccepted.heroResult[0].hero = const_cast<CGHeroInstance*>(battle.sides.at(0).hero);
-	raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battle.sides.at(1).hero);
+	raccepted.heroResult[0].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(0));
+	raccepted.heroResult[1].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(1));
+	raccepted.heroResult[0].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(0));
+	raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(1));
 	raccepted.heroResult[0].exp = battleResult->exp[0];
 	raccepted.heroResult[1].exp = battleResult->exp[1];
 	raccepted.winnerSide = finishingBattle->winnerSide; 
@@ -479,15 +503,15 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
 	//--> continuation (battleAfterLevelUp) occurs after level-up gameHandler->queries are handled or on removing query
 }
 
-void BattleResultProcessor::battleAfterLevelUp(const BattleInfo & battle, const BattleResult & result)
+void BattleResultProcessor::battleAfterLevelUp(const CBattleInfoCallback & battle, const BattleResult & result)
 {
 	LOG_TRACE(logGlobal);
 
-	assert(finishingBattles.count(battle.battleID) != 0);
-	if(finishingBattles.count(battle.battleID) == 0)
+	assert(finishingBattles.count(battle.getBattle()->getBattleID()) != 0);
+	if(finishingBattles.count(battle.getBattle()->getBattleID()) == 0)
 		return;
 
-	auto & finishingBattle = finishingBattles[battle.battleID];
+	auto & finishingBattle = finishingBattles[battle.getBattle()->getBattleID()];
 
 	finishingBattle->remainingBattleQueriesCount--;
 	logGlobal->trace("Decremented gameHandler->queries count to %d", finishingBattle->remainingBattleQueriesCount);
@@ -517,8 +541,6 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleInfo & battle, const
 	resultsApplied.player2 = finishingBattle->loser;
 	gameHandler->sendAndApply(&resultsApplied);
 
-	gameHandler->setBattle(nullptr);
-
 	//handle victory/loss of engaged players
 	std::set<PlayerColor> playerColors = {finishingBattle->loser, finishingBattle->victor};
 	gameHandler->checkVictoryLossConditions(playerColors);
@@ -539,23 +561,29 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleInfo & battle, const
 			gameHandler->heroPool->onHeroEscaped(finishingBattle->victor, finishingBattle->winnerHero);
 	}
 
-	finishingBattles.erase(battle.battleID);
-	battleResults.erase(battle.battleID);
+	finishingBattles.erase(battle.getBattle()->getBattleID());
+	battleResults.erase(battle.getBattle()->getBattleID());
 }
 
-void BattleResultProcessor::setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide)
+void BattleResultProcessor::setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide)
 {
-	assert(battleResults.count(battle.battleID) == 0);
+	assert(battleResults.count(battle.getBattle()->getBattleID()) == 0);
 
-	battleResults[battle.battleID] = std::make_unique<BattleResult>();
+	battleResults[battle.getBattle()->getBattleID()] = std::make_unique<BattleResult>();
 
-	auto & battleResult = battleResults[battle.battleID];
+	auto & battleResult = battleResults[battle.getBattle()->getBattleID()];
 	battleResult->result = resultType;
 	battleResult->winner = victoriusSide; //surrendering side loses
-	battle.calculateCasualties(battleResult->casualties);
+
+	for(const auto & st : battle.battleGetAllStacks(true)) //setting casualties
+	{
+		si32 killed = st->getKilled();
+		if(killed > 0)
+			battleResult->casualties[st->unitSide()][st->creatureId()] += killed;
+	}
 }
 
-bool BattleResultProcessor::battleIsEnding(const BattleInfo & battle) const
+bool BattleResultProcessor::battleIsEnding(const CBattleInfoCallback & battle) const
 {
-	return battleResults.count(battle.battleID) != 0;
+	return battleResults.count(battle.getBattle()->getBattleID()) != 0;
 }

+ 6 - 6
server/battles/BattleResultProcessor.h

@@ -31,7 +31,7 @@ struct CasualtiesAfterBattle
 	TSummoned summoned;
 	ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations
 
-	CasualtiesAfterBattle(const SideInBattle & battleSide, const BattleInfo * bat);
+	CasualtiesAfterBattle(const CBattleInfoCallback & battle, uint8_t sideInBattle);
 	void updateArmy(CGameHandler * gh);
 };
 
@@ -71,10 +71,10 @@ public:
 	explicit BattleResultProcessor(BattleProcessor * owner);
 	void setGameHandler(CGameHandler * newGameHandler);
 
-	bool battleIsEnding(const BattleInfo & battle) const;
+	bool battleIsEnding(const CBattleInfoCallback & battle) const;
 
-	void setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide);
-	void endBattle(const BattleInfo & battle); //ends battle
-	void endBattleConfirm(const BattleInfo & battle);
-	void battleAfterLevelUp(const BattleInfo & battle, const BattleResult & result);
+	void setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide);
+	void endBattle(const CBattleInfoCallback & battle); //ends battle
+	void endBattleConfirm(const CBattleInfoCallback & battle);
+	void battleAfterLevelUp(const CBattleInfoCallback & battle, const BattleResult & result);
 };

+ 0 - 0
server/battles/ServerBattleCallback.cpp


+ 0 - 0
server/battles/ServerBattleCallback.h


+ 24 - 18
server/queries/BattleQueries.cpp

@@ -14,7 +14,7 @@
 #include "../CGameHandler.h"
 #include "../battles/BattleProcessor.h"
 
-#include "../../lib/battle/BattleInfo.h"
+#include "../../lib/battle/IBattleState.h"
 
 void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
 {
@@ -22,16 +22,15 @@ void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisi
 		objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
 }
 
-CBattleQuery::CBattleQuery(CGameHandler * owner, const BattleInfo * Bi):
-	CGhQuery(owner)
+CBattleQuery::CBattleQuery(CGameHandler * owner, const IBattleInfo * bi):
+	CGhQuery(owner),
+	bi(bi)
 {
-	belligerents[0] = Bi->sides[0].armyObject;
-	belligerents[1] = Bi->sides[1].armyObject;
+	belligerents[0] = bi->getSideArmy(0);
+	belligerents[1] = bi->getSideArmy(1);
 
-	bi = Bi;
-
-	for(auto & side : bi->sides)
-		addPlayer(side.color);
+	addPlayer(bi->getSidePlayer(0));
+	addPlayer(bi->getSidePlayer(1));
 }
 
 CBattleQuery::CBattleQuery(CGameHandler * owner):
@@ -49,16 +48,15 @@ bool CBattleQuery::blocksPack(const CPack * pack) const
 void CBattleQuery::onRemoval(PlayerColor color)
 {
 	if(result)
-		gh->battles->battleAfterLevelUp(bi->battleID, *result);
+		gh->battles->battleAfterLevelUp(bi->getBattleID(), *result);
 }
 
-CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi):
-	CDialogQuery(owner)
+CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const IBattleInfo * bi):
+	CDialogQuery(owner),
+	bi(bi)
 {
-	bi = Bi;
-
-	for(auto & side : bi->sides)
-		addPlayer(side.color);
+	addPlayer(bi->getSidePlayer(0));
+	addPlayer(bi->getSidePlayer(1));
 }
 
 void CBattleDialogQuery::onRemoval(PlayerColor color)
@@ -66,10 +64,18 @@ void CBattleDialogQuery::onRemoval(PlayerColor color)
 	assert(answer);
 	if(*answer == 1)
 	{
-		gh->startBattlePrimary(bi->sides[0].armyObject, bi->sides[1].armyObject, bi->tile, bi->sides[0].hero, bi->sides[1].hero, bi->creatureBank, bi->town);
+		gh->startBattlePrimary(
+			bi->getSideArmy(0),
+			bi->getSideArmy(1),
+			bi->getLocation(),
+			bi->getSideHero(0),
+			bi->getSideHero(1),
+			bi->isCreatureBank(),
+			bi->getDefendedTown()
+		);
 	}
 	else
 	{
-		gh->battles->endBattleConfirm(bi->battleID);
+		gh->battles->endBattleConfirm(bi->getBattleID());
 	}
 }

+ 8 - 4
server/queries/BattleQueries.h

@@ -13,17 +13,21 @@
 
 #include "../../lib/NetPacks.h"
 
+VCMI_LIB_NAMESPACE_BEGIN
+class IBattleInfo;
+VCMI_LIB_NAMESPACE_END
+
 class CBattleQuery : public CGhQuery
 {
 public:
 	std::array<const CArmedInstance *,2> belligerents;
 	std::array<int, 2> initialHeroMana;
 
-	const BattleInfo *bi;
+	const IBattleInfo *bi;
 	std::optional<BattleResult> result;
 
 	CBattleQuery(CGameHandler * owner);
-	CBattleQuery(CGameHandler * owner, const BattleInfo * Bi); //TODO
+	CBattleQuery(CGameHandler * owner, const IBattleInfo * Bi); //TODO
 	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
 	virtual bool blocksPack(const CPack *pack) const override;
 	virtual void onRemoval(PlayerColor color) override;
@@ -32,9 +36,9 @@ public:
 class CBattleDialogQuery : public CDialogQuery
 {
 public:
-	CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi);
+	CBattleDialogQuery(CGameHandler * owner, const IBattleInfo * Bi);
 
-	const BattleInfo * bi;
+	const IBattleInfo * bi;
 
 	virtual void onRemoval(PlayerColor color) override;
 };