1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-06 09:09:40 +02:00

Pathfinder now uses IGameInfoCallback instead of CGameState

This commit is contained in:
Ivan Savenko
2025-05-15 21:23:54 +03:00
parent fe2f5f9217
commit c0850f41b3
26 changed files with 139 additions and 128 deletions

View File

@@ -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));
}
}
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;
};
}

View File

@@ -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));
}
}
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;
};
}

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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));
}
}
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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();
};

View File

@@ -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;

View File

@@ -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