Ver Fonte

Pathfinder now uses IGameInfoCallback instead of CGameState

Ivan Savenko há 5 meses atrás
pai
commit
c0850f41b3

+ 11 - 11
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -112,7 +112,7 @@ AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes)
 
 AINodeStorage::~AINodeStorage() = default;
 
-void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
+void AINodeStorage::initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo)
 {
 	if(heroChainPass != EHeroChainPass::INITIAL)
 		return;
@@ -121,8 +121,8 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
 
 	//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
 	const PlayerColor fowPlayer = ai->playerID;
-	const auto & fow = static_cast<const IGameInfoCallback *>(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap;
-	const int3 sizes = gs->getMapSize();
+	const auto & fow = gameInfo.getPlayerTeam(fowPlayer)->fogOfWarMap;
+	const int3 sizes = gameInfo.getMapSize();
 
 	//Each thread gets different x, but an array of y located next to each other in memory
 
@@ -140,23 +140,23 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
 			{
 				for(pos.y = 0; pos.y < sizes.y; ++pos.y)
 				{
-					const TerrainTile & tile = gs->getMap().getTile(pos);
-					if (!tile.getTerrain()->isPassable())
+					const TerrainTile * tile = gameInfo.getTile(pos);
+					if (!tile->getTerrain()->isPassable())
 						continue;
 
-					if (tile.isWater())
+					if (tile->isWater())
 					{
-						resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
+						resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, *tile, fow, player, gameInfo));
 						if (useFlying)
-							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
+							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, *tile, fow, player, gameInfo));
 						if (useWaterWalking)
-							resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
+							resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, *tile, fow, player, gameInfo));
 					}
 					else
 					{
-						resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
+						resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, *tile, fow, player, gameInfo));
 						if (useFlying)
-							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
+							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, *tile, fow, player, gameInfo));
 					}
 				}
 			}

+ 1 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -186,7 +186,7 @@ public:
 	AINodeStorage(const Nullkiller * ai, const int3 & sizes);
 	~AINodeStorage();
 
-	void initialize(const PathfinderOptions & options, const CGameState * gs) override;
+	void initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo) override;
 
 	bool increaseHeroChainTurnLimit();
 	bool selectFirstActor();

+ 2 - 2
AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp

@@ -56,14 +56,14 @@ namespace AIPathfinding
 
 	AIPathfinderConfig::~AIPathfinderConfig() = default;
 
-	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, const CGameState & gs)
+	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo)
 	{
 		auto hero = aiNodeStorage->getHero(source.node);
 		auto & helper = pathfindingHelpers[hero];
 
 		if(!helper)
 		{
-			helper.reset(new CPathfinderHelper(gs, hero, options));
+			helper.reset(new CPathfinderHelper(gameInfo, hero, options));
 		}
 
 		return helper.get();

+ 1 - 1
AI/Nullkiller/Pathfinding/AIPathfinderConfig.h

@@ -35,7 +35,7 @@ namespace AIPathfinding
 
 		~AIPathfinderConfig();
 
-		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const CGameState & gs) override;
+		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo) override;
 	};
 }
 

+ 11 - 11
AI/VCAI/Pathfinding/AINodeStorage.cpp

@@ -29,11 +29,11 @@ AINodeStorage::AINodeStorage(const int3 & Sizes)
 
 AINodeStorage::~AINodeStorage() = default;
 
-void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
+void AINodeStorage::initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo)
 {
 	int3 pos;
-	const int3 sizes = gs->getMapSize();
-	const auto & fow = static_cast<const IGameInfoCallback *>(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap;
+	const int3 sizes = gameInfo.getMapSize();
+	const auto & fow = gameInfo.getPlayerTeam(hero->tempOwner)->fogOfWarMap;
 	const PlayerColor player = hero->tempOwner;
 
 	//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
@@ -46,23 +46,23 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
 		{
 			for(pos.y=0; pos.y < sizes.y; ++pos.y)
 			{
-				const TerrainTile & tile = gs->getMap().getTile(pos);
-				if(!tile.getTerrain()->isPassable())
+				const TerrainTile * tile = gameInfo.getTile(pos);
+				if(!tile->getTerrain()->isPassable())
 					continue;
 				
-				if(tile.getTerrain()->isWater())
+				if(tile->getTerrain()->isWater())
 				{
-					resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
+					resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, *tile, fow, player, gameInfo));
 					if(useFlying)
-						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
+						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, *tile, fow, player, gameInfo));
 					if(useWaterWalking)
-						resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
+						resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, *tile, fow, player, gameInfo));
 				}
 				else
 				{
-					resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
+					resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, *tile, fow, player, gameInfo));
 					if(useFlying)
-						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
+						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, *tile, fow, player, gameInfo));
 				}
 			}
 		}

+ 1 - 1
AI/VCAI/Pathfinding/AINodeStorage.h

@@ -83,7 +83,7 @@ public:
 	AINodeStorage(const int3 & sizes);
 	~AINodeStorage();
 
-	void initialize(const PathfinderOptions & options, const CGameState * gs) override;
+	void initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo) override;
 
 	std::vector<CGPathNode *> getInitialNodes() override;
 

+ 2 - 2
AI/VCAI/Pathfinding/AIPathfinderConfig.cpp

@@ -51,11 +51,11 @@ namespace AIPathfinding
 
 	AIPathfinderConfig::~AIPathfinderConfig() = default;
 
-	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, const CGameState & gs)
+	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo)
 	{
 		if(!helper)
 		{
-			helper.reset(new CPathfinderHelper(gs, hero, options));
+			helper.reset(new CPathfinderHelper(gameInfo, hero, options));
 		}
 
 		return helper.get();

+ 1 - 1
AI/VCAI/Pathfinding/AIPathfinderConfig.h

@@ -30,6 +30,6 @@ namespace AIPathfinding
 
 		~AIPathfinderConfig();
 
-		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const CGameState & gs) override;
+		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo) override;
 	};
 }

+ 7 - 0
lib/callback/CGameInfoCallback.cpp

@@ -973,6 +973,13 @@ void CGameInfoCallback::getAllowedSpells(std::vector<SpellID> & out, std::option
 	}
 }
 
+bool CGameInfoCallback::checkForVisitableDir(const int3 & src, const int3 & dst) const
+{
+	const CMap & map = gameState().getMap();
+	const TerrainTile * pom = &map.getTile(dst);
+	return map.checkForVisitableDir(src, pom, dst);
+}
+
 #if SCRIPTING_ENABLED
 scripting::Pool * CGameInfoCallback::getGlobalContextPool() const
 {

+ 3 - 2
lib/callback/CGameInfoCallback.h

@@ -75,8 +75,8 @@ public:
 	const IMarket * getMarket(ObjectInstanceID objid) const;
 
 	//map
-	int3 guardingCreaturePosition (int3 pos) const;
-	std::vector<const CGObjectInstance*> getGuardingCreatures (int3 pos) const;
+	int3 guardingCreaturePosition (int3 pos) const override;
+	std::vector<const CGObjectInstance*> getGuardingCreatures (int3 pos) const override;
 	bool isTileGuardedUnchecked(int3 tile) const;
 	const CMapHeader * getMapHeader()const override;
 	int3 getMapSize() const override;
@@ -86,6 +86,7 @@ public:
 	void getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
 	void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
 	EDiggingStatus getTileDigStatus(int3 tile, bool verbose = true) const override;
+	bool checkForVisitableDir(const int3 & src, const int3 & dst) const override;
 
 	//town
 	const CGTownInstance* getTown(ObjectInstanceID objid) const override;

+ 7 - 0
lib/callback/IGameInfoCallback.h

@@ -141,6 +141,13 @@ public:
 	/// Calculates pathfinding data into specified pathfinder config
 	virtual void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const = 0;
 
+	/// Returns position of creature that guards specified tile, or invalid tile if there are no guards
+	virtual int3 guardingCreaturePosition (int3 pos) const = 0;
+	/// Return true if src tile is visitable from dst tile
+	virtual bool checkForVisitableDir(const int3 & src, const int3 & dst) const = 0;
+	/// Returns all wandering monsters that guard specified tile
+	virtual std::vector<const CGObjectInstance*> getGuardingCreatures (int3 pos) const = 0;
+
 	/// Returns all tiles within specified range with specific tile visibility mode
 	virtual void getTilesInRange(std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const = 0;
 

+ 1 - 5
lib/gameState/CGameState.cpp

@@ -1190,11 +1190,7 @@ bool CGameState::isVisibleFor(const CGObjectInstance * obj, PlayerColor player)
 	return false;
 }
 
-bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const
-{
-	const TerrainTile * pom = &map->getTile(dst);
-	return map->checkForVisitableDir(src, pom, dst);
-}
+
 
 EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(const PlayerColor & player) const
 {

+ 0 - 1
lib/gameState/CGameState.h

@@ -100,7 +100,6 @@ public:
 	BattleField battleGetBattlefieldType(int3 tile, vstd::RNG & randomGenerator);
 
 	PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const override;
-	bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile
 	void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
 	std::vector<const CGObjectInstance*> guardingCreatures (int3 pos) const;
 

+ 6 - 6
lib/mapObjects/MiscObjects.cpp

@@ -322,10 +322,10 @@ bool CGTeleport::isConnected(const CGObjectInstance * src, const CGObjectInstanc
 	return isConnected(srcObj, dstObj);
 }
 
-bool CGTeleport::isExitPassable(const CGameState & gs, const CGHeroInstance * h, const CGObjectInstance * obj)
+bool CGTeleport::isExitPassable(const IGameInfoCallback & gameInfo, const CGHeroInstance * h, const CGObjectInstance * obj)
 {
-	ObjectInstanceID topObjectID = gs.getMap().getTile(obj->visitablePos()).topVisitableObj();
-	const CGObjectInstance * topObject = gs.getObjInstance(topObjectID);
+	ObjectInstanceID topObjectID = gameInfo.getTile(obj->visitablePos())->topVisitableObj();
+	const CGObjectInstance * topObject = gameInfo.getObjInstance(topObjectID);
 
 	if(topObject->ID == Obj::HERO)
 	{
@@ -333,7 +333,7 @@ bool CGTeleport::isExitPassable(const CGameState & gs, const CGHeroInstance * h,
 			return false;
 
 		// Check if it's friendly hero or not
-		if(gs.getPlayerRelations(h->tempOwner, topObject->tempOwner) != PlayerRelations::ENEMIES)
+		if(gameInfo.getPlayerRelations(h->tempOwner, topObject->tempOwner) != PlayerRelations::ENEMIES)
 		{
 			// Exchange between heroes only possible via subterranean gates
 			if(!dynamic_cast<const CGSubterraneanGate *>(obj))
@@ -343,11 +343,11 @@ bool CGTeleport::isExitPassable(const CGameState & gs, const CGHeroInstance * h,
 	return true;
 }
 
-std::vector<ObjectInstanceID> CGTeleport::getPassableExits(const CGameState & gs, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits)
+std::vector<ObjectInstanceID> CGTeleport::getPassableExits(const IGameInfoCallback & gameInfo, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits)
 {
 	vstd::erase_if(exits, [&](const ObjectInstanceID & exit) -> bool 
 	{
-		return !isExitPassable(gs, h, gs.getObj(exit));
+		return !isExitPassable(gameInfo, h, gameInfo.getObj(exit));
 	});
 	return exits;
 }

+ 2 - 2
lib/mapObjects/MiscObjects.h

@@ -220,8 +220,8 @@ public:
 	static bool isConnected(const CGTeleport * src, const CGTeleport * dst);
 	static bool isConnected(const CGObjectInstance * src, const CGObjectInstance * dst);
 	static void addToChannel(std::map<TeleportChannelID, std::shared_ptr<TeleportChannel> > &channelsList, const CGTeleport * obj);
-	static std::vector<ObjectInstanceID> getPassableExits(const CGameState & gs, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits);
-	static bool isExitPassable(const CGameState & gs, const CGHeroInstance * h, const CGObjectInstance * obj);
+	static std::vector<ObjectInstanceID> getPassableExits(const IGameInfoCallback & gameInfo, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits);
+	static bool isExitPassable(const IGameInfoCallback & gameInfo, const CGHeroInstance * h, const CGObjectInstance * obj);
 
 	template <typename Handler> void serialize(Handler &h)
 	{

+ 10 - 10
lib/pathfinder/CGPathNode.cpp

@@ -100,7 +100,7 @@ PathNodeInfo::PathNodeInfo()
 {
 }
 
-void PathNodeInfo::setNode(const CGameState & gs, CGPathNode * n)
+void PathNodeInfo::setNode(const IGameInfoCallback & gameInfo, CGPathNode * n)
 {
 	node = n;
 	guarded = false;
@@ -110,14 +110,14 @@ void PathNodeInfo::setNode(const CGameState & gs, CGPathNode * n)
 		assert(node->coord.isValid());
 
 		coord = node->coord;
-		tile = gs.getTile(coord);
+		tile = gameInfo.getTile(coord);
 		nodeObject = nullptr;
 		nodeHero = nullptr;
 
 		ObjectInstanceID topObjectID = tile->topVisitableObj();
 		if (topObjectID.hasValue())
 		{
-			nodeObject = gs.getObjInstance(topObjectID);
+			nodeObject = gameInfo.getObjInstance(topObjectID);
 
 			if (nodeObject->ID == Obj::HERO)
 			{
@@ -125,28 +125,28 @@ void PathNodeInfo::setNode(const CGameState & gs, CGPathNode * n)
 				ObjectInstanceID bottomObjectID = tile->topVisitableObj(true);
 
 				if (bottomObjectID.hasValue())
-					nodeObject = gs.getObjInstance(bottomObjectID);
+					nodeObject = gameInfo.getObjInstance(bottomObjectID);
 			}
 		}
 	}
 
 }
 
-void PathNodeInfo::updateInfo(CPathfinderHelper * hlp, const CGameState & gs)
+void PathNodeInfo::updateInfo(CPathfinderHelper * hlp, const IGameInfoCallback & gameInfo)
 {
-	if(gs.guardingCreaturePosition(node->coord).isValid() && !isInitialPosition)
+	if(gameInfo.guardingCreaturePosition(node->coord).isValid() && !isInitialPosition)
 	{
 		guarded = true;
 	}
 
 	if(nodeObject)
 	{
-		objectRelations = gs.getPlayerRelations(hlp->owner, nodeObject->tempOwner);
+		objectRelations = gameInfo.getPlayerRelations(hlp->owner, nodeObject->tempOwner);
 	}
 
 	if(nodeHero)
 	{
-		heroRelations = gs.getPlayerRelations(hlp->owner, nodeHero->tempOwner);
+		heroRelations = gameInfo.getPlayerRelations(hlp->owner, nodeHero->tempOwner);
 	}
 }
 
@@ -164,9 +164,9 @@ CDestinationNodeInfo::CDestinationNodeInfo():
 {
 }
 
-void CDestinationNodeInfo::setNode(const CGameState & gs, CGPathNode * n)
+void CDestinationNodeInfo::setNode(const IGameInfoCallback & gameInfo, CGPathNode * n)
 {
-	PathNodeInfo::setNode(gs, n);
+	PathNodeInfo::setNode(gameInfo, n);
 
 	blocked = false;
 	action = EPathNodeAction::UNKNOWN;

+ 3 - 3
lib/pathfinder/CGPathNode.h

@@ -219,9 +219,9 @@ struct DLL_LINKAGE PathNodeInfo
 
 	PathNodeInfo();
 
-	virtual void setNode(const CGameState & gs, CGPathNode * n);
+	virtual void setNode(const IGameInfoCallback & gameInfo, CGPathNode * n);
 
-	void updateInfo(CPathfinderHelper * hlp, const CGameState & gs);
+	void updateInfo(CPathfinderHelper * hlp, const IGameInfoCallback & gameInfo);
 
 	bool isNodeObjectVisitable() const;
 };
@@ -237,7 +237,7 @@ struct DLL_LINKAGE CDestinationNodeInfo : public PathNodeInfo
 
 	CDestinationNodeInfo();
 
-	void setNode(const CGameState & gs, CGPathNode * n) override;
+	void setNode(const IGameInfoCallback & gameInfo, CGPathNode * n) override;
 
 	virtual bool isBetterWay() const;
 };

+ 40 - 42
lib/pathfinder/CPathfinder.cpp

@@ -15,7 +15,6 @@
 #include "PathfindingRules.h"
 #include "TurnInfo.h"
 
-#include "../gameState/CGameState.h"
 #include "../IGameSettings.h"
 #include "../CPlayerState.h"
 #include "../TerrainHandler.h"
@@ -75,8 +74,8 @@ void CPathfinderHelper::calculateNeighbourTiles(NeighbourTilesVector & result, c
 	}
 }
 
-CPathfinder::CPathfinder(const CGameState & gamestate, std::shared_ptr<PathfinderConfig> config):
-	gamestate(gamestate),
+CPathfinder::CPathfinder(const IGameInfoCallback & gameInfo, std::shared_ptr<PathfinderConfig> config):
+	gameInfo(gameInfo),
 	config(std::move(config))
 {
 	initializeGraph();
@@ -112,14 +111,14 @@ void CPathfinder::calculatePaths()
 
 	for(auto * initialNode : initialNodes)
 	{
-		if(!gamestate.isInTheMap(initialNode->coord)/* || !gameState().getMap().isInTheMap(dest)*/) //check input
+		if(!gameInfo.isInTheMap(initialNode->coord)/* || !gameInfo.getMap().isInTheMap(dest)*/) //check input
 		{
-			logGlobal->error("CGameState::calculatePaths: Hero outside the gameState().map? How dare you...");
+			logGlobal->error("CgameInfo::calculatePaths: Hero outside the gameInfo.map? How dare you...");
 			throw std::runtime_error("Wrong checksum");
 		}
 
-		source.setNode(gamestate, initialNode);
-		auto * hlp = config->getOrCreatePathfinderHelper(source, gamestate);
+		source.setNode(gameInfo, initialNode);
+		auto * hlp = config->getOrCreatePathfinderHelper(source, gameInfo);
 
 		if(hlp->isHeroPatrolLocked())
 			continue;
@@ -134,14 +133,14 @@ void CPathfinder::calculatePaths()
 		counter++;
 		auto * node = topAndPop();
 
-		source.setNode(gamestate, node);
+		source.setNode(gameInfo, node);
 		source.node->locked = true;
 
 		int movement = source.node->moveRemains;
 		uint8_t turn = source.node->turns;
 		float cost = source.node->getCost();
 
-		auto * hlp = config->getOrCreatePathfinderHelper(source, gamestate);
+		auto * hlp = config->getOrCreatePathfinderHelper(source, gameInfo);
 
 		hlp->updateTurnInfo(turn);
 		if(movement == 0)
@@ -155,7 +154,7 @@ void CPathfinder::calculatePaths()
 		}
 
 		source.isInitialPosition = source.nodeHero == hlp->hero;
-		source.updateInfo(hlp, gamestate);
+		source.updateInfo(hlp, gameInfo);
 
 		//add accessible neighbouring nodes to the queue
 		for(EPathfindingLayer layer = EPathfindingLayer::LAND; layer < EPathfindingLayer::NUM_LAYERS; layer.advance(1))
@@ -170,8 +169,8 @@ void CPathfinder::calculatePaths()
 				if(neighbour->locked)
 					continue;
 
-				destination.setNode(gamestate, neighbour);
-				hlp = config->getOrCreatePathfinderHelper(destination, gamestate);
+				destination.setNode(gameInfo, neighbour);
+				hlp = config->getOrCreatePathfinderHelper(destination, gameInfo);
 
 				if(!hlp->isPatrolMovementAllowed(neighbour->coord))
 					continue;
@@ -183,7 +182,7 @@ void CPathfinder::calculatePaths()
 				destination.turn = turn;
 				destination.movementLeft = movement;
 				destination.cost = cost;
-				destination.updateInfo(hlp, gamestate);
+				destination.updateInfo(hlp, gameInfo);
 				destination.isGuardianTile = destination.guarded && isDestinationGuardian();
 
 				for(const auto & rule : config->rules)
@@ -201,7 +200,7 @@ void CPathfinder::calculatePaths()
 		}
 
 		//just add all passable teleport exits
-		hlp = config->getOrCreatePathfinderHelper(source, gamestate);
+		hlp = config->getOrCreatePathfinderHelper(source, gameInfo);
 
 		/// For now we disable teleports usage for patrol movement
 		/// VCAI not aware about patrol and may stuck while attempt to use teleport
@@ -221,7 +220,7 @@ void CPathfinder::calculatePaths()
 			if(teleportNode->accessible == EPathAccessibility::BLOCKED)
 				continue;
 
-			destination.setNode(gamestate, teleportNode);
+			destination.setNode(gameInfo, teleportNode);
 			destination.turn = turn;
 			destination.movementLeft = movement;
 			destination.cost = cost;
@@ -244,20 +243,20 @@ TeleporterTilesVector CPathfinderHelper::getAllowedTeleportChannelExits(const Te
 {
 	TeleporterTilesVector allowedExits;
 
-	for(const auto & objId : gameState().getTeleportChannelExits(channelID, hero->tempOwner))
+	for(const auto & objId : gameInfo.getTeleportChannelExits(channelID, hero->tempOwner))
 	{
-		const auto * obj = gameState().getObj(objId);
+		const auto * obj = gameInfo.getObj(objId);
 		if(dynamic_cast<const CGWhirlpool *>(obj))
 		{
 			auto pos = obj->getBlockedPos();
 			for(const auto & p : pos)
 			{
-				ObjectInstanceID topObject = gameState().getMap().getTile(p).topVisitableObj();
-				if(topObject.hasValue() && gameState().getObj(topObject)->ID == obj->ID)
+				ObjectInstanceID topObject = gameInfo.getTile(p)->topVisitableObj();
+				if(topObject.hasValue() && gameInfo.getObj(topObject)->ID == obj->ID)
 					allowedExits.push_back(p);
 			}
 		}
-		else if(obj && CGTeleport::isExitPassable(gameState(), hero, obj))
+		else if(obj && CGTeleport::isExitPassable(gameInfo, hero, obj))
 			allowedExits.push_back(obj->visitablePos());
 	}
 
@@ -268,7 +267,7 @@ TeleporterTilesVector CPathfinderHelper::getCastleGates(const PathNodeInfo & sou
 {
 	TeleporterTilesVector allowedExits;
 
-	for(const auto & town : gameState().getPlayerState(hero->tempOwner)->getTowns())
+	for(const auto & town : gameInfo.getPlayerState(hero->tempOwner)->getTowns())
 	{
 		if(town->id != source.nodeObject->id && town->getVisitingHero() == nullptr
 			&& town->hasBuilt(BuildingSubID::CASTLE_GATE))
@@ -391,19 +390,19 @@ EPathNodeAction CPathfinder::getTeleportDestAction() const
 
 bool CPathfinder::isDestinationGuardian() const
 {
-	return gamestate.guardingCreaturePosition(destination.node->coord) == destination.node->coord;
+	return gameInfo.guardingCreaturePosition(destination.node->coord) == destination.node->coord;
 }
 
 void CPathfinderHelper::initializePatrol()
 {
 	auto state = PATROL_NONE;
 
-	if(hero->patrol.patrolling && !gameState().getPlayerState(hero->tempOwner)->human)
+	if(hero->patrol.patrolling && !gameInfo.getPlayerState(hero->tempOwner)->human)
 	{
 		if(hero->patrol.patrolRadius)
 		{
 			state = PATROL_RADIUS;
-			gameState().getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadius, ETileVisibility::REVEALED, std::optional<PlayerColor>(), int3::DIST_MANHATTAN);
+			gameInfo.getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadius, ETileVisibility::REVEALED, std::optional<PlayerColor>(), int3::DIST_MANHATTAN);
 		}
 		else
 			state = PATROL_LOCKED;
@@ -415,17 +414,17 @@ void CPathfinderHelper::initializePatrol()
 void CPathfinder::initializeGraph()
 {
 	INodeStorage * nodeStorage = config->nodeStorage.get();
-	nodeStorage->initialize(config->options, &gamestate);
+	nodeStorage->initialize(config->options, gameInfo);
 }
 
 bool CPathfinderHelper::canMoveBetween(const int3 & a, const int3 & b) const
 {
-	return gameState().checkForVisitableDir(a, b);
+	return gameInfo.checkForVisitableDir(a, b);
 }
 
 bool CPathfinderHelper::isAllowedTeleportEntrance(const CGTeleport * obj) const
 {
-	if(!obj || !gameState().isTeleportEntrancePassable(obj, hero->tempOwner))
+	if(!obj || !gameInfo.isTeleportEntrancePassable(obj, hero->tempOwner))
 		return false;
 
 	const auto * whirlpool = dynamic_cast<const CGWhirlpool *>(obj);
@@ -442,14 +441,14 @@ bool CPathfinderHelper::isAllowedTeleportEntrance(const CGTeleport * obj) const
 
 bool CPathfinderHelper::addTeleportTwoWay(const CGTeleport * obj) const
 {
-	return options.useTeleportTwoWay && gameState().isTeleportChannelBidirectional(obj->channel, hero->tempOwner);
+	return options.useTeleportTwoWay && gameInfo.isTeleportChannelBidirectional(obj->channel, hero->tempOwner);
 }
 
 bool CPathfinderHelper::addTeleportOneWay(const CGTeleport * obj) const
 {
-	if(options.useTeleportOneWay && gameState().isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
+	if(options.useTeleportOneWay && gameInfo.isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
 	{
-		auto passableExits = CGTeleport::getPassableExits(gameState(), hero, gameState().getTeleportChannelExits(obj->channel, hero->tempOwner));
+		auto passableExits = CGTeleport::getPassableExits(gameInfo, hero, gameInfo.getTeleportChannelExits(obj->channel, hero->tempOwner));
 		if(passableExits.size() == 1)
 			return true;
 	}
@@ -458,9 +457,9 @@ bool CPathfinderHelper::addTeleportOneWay(const CGTeleport * obj) const
 
 bool CPathfinderHelper::addTeleportOneWayRandom(const CGTeleport * obj) const
 {
-	if(options.useTeleportOneWayRandom && gameState().isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
+	if(options.useTeleportOneWayRandom && gameInfo.isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
 	{
-		auto passableExits = CGTeleport::getPassableExits(gameState(), hero, gameState().getTeleportChannelExits(obj->channel, hero->tempOwner));
+		auto passableExits = CGTeleport::getPassableExits(gameInfo, hero, gameInfo.getTeleportChannelExits(obj->channel, hero->tempOwner));
 		if(passableExits.size() > 1)
 			return true;
 	}
@@ -495,11 +494,11 @@ bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const
 
 int CPathfinderHelper::getGuardiansCount(int3 tile) const
 {
-	return gameState().getGuardingCreatures(tile).size();
+	return gameInfo.getGuardingCreatures(tile).size();
 }
 
-CPathfinderHelper::CPathfinderHelper(const CGameState & gs, const CGHeroInstance * Hero, const PathfinderOptions & Options):
-	gs(gs),
+CPathfinderHelper::CPathfinderHelper(const IGameInfoCallback & gameInfo, const CGHeroInstance * Hero, const PathfinderOptions & Options):
+	gameInfo(gameInfo),
 	turn(-1),
 	owner(Hero->tempOwner),
 	hero(Hero),
@@ -573,7 +572,6 @@ void CPathfinderHelper::getNeighbours(
 	const boost::logic::tribool & onLand,
 	const bool limitCoastSailing) const
 {
-	const CMap * map = &gameState().getMap();
 	const TerrainType * sourceTerrain = sourceTile.getTerrain();
 
 	static constexpr std::array dirs = {
@@ -585,11 +583,11 @@ void CPathfinderHelper::getNeighbours(
 	for(const auto & dir : dirs)
 	{
 		const int3 destCoord = srcCoord + dir;
-		if(!map->isInTheMap(destCoord))
+		if(!gameInfo.isInTheMap(destCoord))
 			continue;
 
-		const TerrainTile & destTile = map->getTile(destCoord);
-		const TerrainType * destTerrain = destTile.getTerrain();
+		const TerrainTile * destTile = gameInfo.getTile(destCoord);
+		const TerrainType * destTerrain = destTile->getTerrain();
 		if(!destTerrain->isPassable())
 			continue;
 
@@ -598,7 +596,7 @@ void CPathfinderHelper::getNeighbours(
 		{
 			const int3 horizontalNeighbour = srcCoord + int3{dir.x, 0, 0};
 			const int3 verticalNeighbour = srcCoord + int3{0, dir.y, 0};
-			if(map->getTile(horizontalNeighbour).isLand() || map->getTile(verticalNeighbour).isLand())
+			if(gameInfo.getTile(horizontalNeighbour)->isLand() || gameInfo.getTile(verticalNeighbour)->isLand())
 				continue;
 		}
 
@@ -670,7 +668,7 @@ int CPathfinderHelper::getMovementCost(
 	}
 	else if(isAirLayer)
 	{
-		int baseCost = gameState().getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
+		int baseCost = gameInfo.getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
 		vstd::amin(movementCost, baseCost + ti->getFlyingMovementValue());
 	}
 	else if(isWaterLayer && ti->hasWaterWalking())
@@ -716,7 +714,7 @@ ui32 CPathfinderHelper::getTileMovementCost(const TerrainTile & dest, const Terr
 	//if hero can move without penalty - either all-native army, or creatures like Nomads in army
 	if(ti->hasNoTerrainPenalty(from.getTerrainID()))
 	{
-		int baseCost = gameState().getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
+		int baseCost = gameInfo.getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
 		return std::min(baseCost, costWithPathfinding);
 	}
 

+ 4 - 5
lib/pathfinder/CPathfinder.h

@@ -32,13 +32,13 @@ public:
 	friend class CPathfinderHelper;
 
 	CPathfinder(
-		const CGameState & _gs,
+		const IGameInfoCallback & gameInfo,
 		std::shared_ptr<PathfinderConfig> config);
 
 	void calculatePaths(); //calculates possible paths for hero, uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
 
 private:
-	const CGameState & gamestate;
+	const IGameInfoCallback & gameInfo;
 
 	using ELayer = EPathfindingLayer;
 
@@ -68,7 +68,7 @@ class DLL_LINKAGE CPathfinderHelper
 	/// returns base movement cost for movement between specific tiles. Does not accounts for diagonal movement or last tile exception
 	ui32 getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const;
 
-	const CGameState & gs;
+	const IGameInfoCallback & gameInfo;
 public:
 	enum EPatrolState
 	{
@@ -87,9 +87,8 @@ public:
 	bool canCastWaterWalk;
 	bool whirlpoolProtection;
 
-	CPathfinderHelper(const CGameState & gs, const CGHeroInstance * Hero, const PathfinderOptions & Options);
+	CPathfinderHelper(const IGameInfoCallback & gameInfo, const CGHeroInstance * Hero, const PathfinderOptions & Options);
 	virtual ~CPathfinderHelper();
-	const CGameState & gameState() const { return gs; }
 	void initializePatrol();
 	bool isHeroPatrolLocked() const;
 	bool canMoveFromNode(const PathNodeInfo & source) const;

+ 1 - 1
lib/pathfinder/INodeStorage.h

@@ -45,7 +45,7 @@ public:
 
 	virtual void commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) = 0;
 
-	virtual void initialize(const PathfinderOptions & options, const CGameState * gs) = 0;
+	virtual void initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo) = 0;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 11 - 11
lib/pathfinder/NodeStorage.cpp

@@ -21,14 +21,14 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-void NodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
+void NodeStorage::initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo)
 {
 	//TODO: fix this code duplication with AINodeStorage::initialize, problem is to keep `resetTile` inline
 
 	int3 pos;
 	const PlayerColor player = out.hero->tempOwner;
-	const int3 sizes = gs->getMapSize();
-	const auto & fow = static_cast<const IGameInfoCallback *>(gs)->getPlayerTeam(player)->fogOfWarMap;
+	const int3 sizes = gameInfo.getMapSize();
+	const auto & fow = gameInfo.getPlayerTeam(player)->fogOfWarMap;
 
 	//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
 	const bool useFlying = options.useFlying;
@@ -40,20 +40,20 @@ void NodeStorage::initialize(const PathfinderOptions & options, const CGameState
 		{
 			for(pos.y=0; pos.y < sizes.y; ++pos.y)
 			{
-				const TerrainTile & tile = gs->getMap().getTile(pos);
-				if(tile.isWater())
+				const TerrainTile * tile = gameInfo.getTile(pos);
+				if(tile->isWater())
 				{
-					resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
+					resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, *tile, fow, player, gameInfo));
 					if(useFlying)
-						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
+						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, *tile, fow, player, gameInfo));
 					if(useWaterWalking)
-						resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
+						resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, *tile, fow, player, gameInfo));
 				}
-				if(tile.isLand())
+				if(tile->isLand())
 				{
-					resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
+					resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, *tile, fow, player, gameInfo));
 					if(useFlying)
-						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
+						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, *tile, fow, player, gameInfo));
 				}
 			}
 		}

+ 1 - 1
lib/pathfinder/NodeStorage.h

@@ -31,7 +31,7 @@ public:
 		return out.getNode(coord, layer);
 	}
 
-	void initialize(const PathfinderOptions & options, const CGameState * gs) override;
+	void initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo) override;
 	virtual ~NodeStorage() = default;
 
 	std::vector<CGPathNode *> getInitialNodes() override;

+ 2 - 2
lib/pathfinder/PathfinderOptions.cpp

@@ -65,10 +65,10 @@ SingleHeroPathfinderConfig::SingleHeroPathfinderConfig(CPathsInfo & out, const I
 {
 }
 
-CPathfinderHelper * SingleHeroPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, const CGameState & gs)
+CPathfinderHelper * SingleHeroPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo)
 {
 	if (!pathfinderHelper)
-		pathfinderHelper = std::make_unique<CPathfinderHelper>(gs, hero, options);
+		pathfinderHelper = std::make_unique<CPathfinderHelper>(gameInfo, hero, options);
 
 	return pathfinderHelper.get();
 }

+ 2 - 2
lib/pathfinder/PathfinderOptions.h

@@ -101,7 +101,7 @@ public:
 		std::vector<std::shared_ptr<IPathfindingRule>> rules);
 	virtual ~PathfinderConfig() = default;
 
-	virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const CGameState & gs) = 0;
+	virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo) = 0;
 };
 
 class DLL_LINKAGE SingleHeroPathfinderConfig : public PathfinderConfig
@@ -114,7 +114,7 @@ public:
 	SingleHeroPathfinderConfig(CPathsInfo & out, const IGameInfoCallback & gs, const CGHeroInstance * hero);
 	virtual ~SingleHeroPathfinderConfig();
 
-	CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const CGameState & gs) override;
+	CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo) override;
 
 	static std::vector<std::shared_ptr<IPathfindingRule>> buildRuleSet();
 };

+ 5 - 5
lib/pathfinder/PathfinderUtil.h

@@ -23,7 +23,7 @@ namespace PathfinderUtil
 	using ELayer = EPathfindingLayer;
 
 	template<EPathfindingLayer::Type layer>
-	EPathAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile & tinfo, const FoW & fow, const PlayerColor player, const CGameState * gs)
+	EPathAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile & tinfo, const FoW & fow, const PlayerColor player, const IGameInfoCallback & gameInfo)
 	{
 		if(!fow[pos.z][pos.x][pos.y])
 			return EPathAccessibility::BLOCKED;
@@ -34,8 +34,8 @@ namespace PathfinderUtil
 		case ELayer::SAIL:
 			if(tinfo.visitable())
 			{
-				auto frontVisitable = gs->getObjInstance(tinfo.visitableObjects.front());
-				auto backVisitable = gs->getObjInstance(tinfo.visitableObjects.front());
+				auto frontVisitable = gameInfo.getObjInstance(tinfo.visitableObjects.front());
+				auto backVisitable = gameInfo.getObjInstance(tinfo.visitableObjects.front());
 
 				if(frontVisitable->ID == Obj::SANCTUARY && backVisitable->ID == Obj::HERO && backVisitable->getOwner() != player) //non-owned hero stands on Sanctuary
 				{
@@ -48,7 +48,7 @@ namespace PathfinderUtil
 
 					for(const auto objID : tinfo.visitableObjects)
 					{
-						auto obj = gs->getObjInstance(objID);
+						auto obj = gameInfo.getObjInstance(objID);
 
 						if(obj->isBlockedVisitable())
 							hasBlockedVisitable = true;
@@ -68,7 +68,7 @@ namespace PathfinderUtil
 			{
 				return EPathAccessibility::BLOCKED;
 			}
-			else if(gs->guardingCreaturePosition(pos).isValid())
+			else if(gameInfo.guardingCreaturePosition(pos).isValid())
 			{
 				// Monster close by; blocked visit for battle
 				return EPathAccessibility::GUARDED;

+ 4 - 0
test/mock/mock_IGameInfoCallback.h

@@ -75,6 +75,10 @@ public:
 	MOCK_CONST_METHOD2(isTeleportChannelBidirectional, bool(TeleportChannelID id, PlayerColor player));
 	MOCK_CONST_METHOD2(isTeleportChannelUnidirectional, bool(TeleportChannelID id, PlayerColor player));
 	MOCK_CONST_METHOD2(isTeleportEntrancePassable, bool(const CGTeleport * obj, PlayerColor player));
+	MOCK_CONST_METHOD1(guardingCreaturePosition, int3(int3 pos));
+	MOCK_CONST_METHOD2(checkForVisitableDir, bool(const int3 & src, const int3 & dst));
+	MOCK_CONST_METHOD1(getGuardingCreatures, std::vector<const CGObjectInstance *>(int3 pos));
+
 	MOCK_METHOD2(pickAllowedArtsSet, void(std::vector<ArtifactID> & out, vstd::RNG & rand));
 
 #if SCRIPTING_ENABLED