|
@@ -17,7 +17,8 @@
|
|
|
#include "GameConstants.h"
|
|
|
#include "CStopWatch.h"
|
|
|
#include "CConfigHandler.h"
|
|
|
-#include "../lib/CPlayerState.h"
|
|
|
+#include "CPlayerState.h"
|
|
|
+#include "PathfinderUtil.h"
|
|
|
|
|
|
bool canSeeObj(const CGObjectInstance * obj)
|
|
|
{
|
|
@@ -25,6 +26,50 @@ bool canSeeObj(const CGObjectInstance * obj)
|
|
|
return obj != nullptr && obj->ID != Obj::EVENT;
|
|
|
}
|
|
|
|
|
|
+void NodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero)
|
|
|
+{
|
|
|
+ //TODO: fix this code duplication with AINodeStorage::initialize, problem is to keep `resetTile` inline
|
|
|
+
|
|
|
+ int3 pos;
|
|
|
+ const int3 sizes = gs->getMapSize();
|
|
|
+ const auto & fow = static_cast<const CGameInfoCallback *>(gs)->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)
|
|
|
+ const bool useFlying = options.useFlying;
|
|
|
+ const bool useWaterWalking = options.useWaterWalking;
|
|
|
+
|
|
|
+ for(pos.x=0; pos.x < sizes.x; ++pos.x)
|
|
|
+ {
|
|
|
+ for(pos.y=0; pos.y < sizes.y; ++pos.y)
|
|
|
+ {
|
|
|
+ for(pos.z=0; pos.z < sizes.z; ++pos.z)
|
|
|
+ {
|
|
|
+ const TerrainTile * tile = &gs->map->getTile(pos);
|
|
|
+ switch(tile->terType)
|
|
|
+ {
|
|
|
+ case ETerrainType::ROCK:
|
|
|
+ break;
|
|
|
+
|
|
|
+ case ETerrainType::WATER:
|
|
|
+ resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
|
|
|
+ if(useFlying)
|
|
|
+ resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
|
|
|
+ if(useWaterWalking)
|
|
|
+ resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
|
|
|
+ if(useFlying)
|
|
|
+ resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
std::vector<CGPathNode *> NodeStorage::calculateNeighbours(
|
|
|
const PathNodeInfo & source,
|
|
|
const PathfinderConfig * pathfinderConfig,
|
|
@@ -91,7 +136,7 @@ std::vector<int3> CPathfinderHelper::getNeighbourTiles(const PathNodeInfo & sour
|
|
|
return !canMoveBetween(tile, source.nodeObject->visitablePos());
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return neighbourTiles;
|
|
|
}
|
|
|
|
|
@@ -102,11 +147,6 @@ NodeStorage::NodeStorage(CPathsInfo & pathsInfo, const CGHeroInstance * hero)
|
|
|
out.hpos = hero->getPosition(false);
|
|
|
}
|
|
|
|
|
|
-CGPathNode * NodeStorage::getNode(const int3 & coord, const EPathfindingLayer layer)
|
|
|
-{
|
|
|
- return out.getNode(coord, layer);
|
|
|
-}
|
|
|
-
|
|
|
void NodeStorage::resetTile(
|
|
|
const int3 & tile,
|
|
|
EPathfindingLayer layer,
|
|
@@ -221,7 +261,7 @@ CPathfinder::CPathfinder(
|
|
|
std::shared_ptr<PathfinderConfig> config)
|
|
|
: CGameInfoCallback(_gs, boost::optional<PlayerColor>())
|
|
|
, hero(_hero)
|
|
|
- , FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap), patrolTiles({})
|
|
|
+ , patrolTiles({})
|
|
|
, config(config)
|
|
|
, source()
|
|
|
, destination()
|
|
@@ -301,7 +341,7 @@ void CPathfinder::calculatePaths()
|
|
|
destination.isGuardianTile = destination.guarded && isDestinationGuardian();
|
|
|
if(destination.nodeObject)
|
|
|
destination.objectRelations = gs->getPlayerRelations(hero->tempOwner, destination.nodeObject->tempOwner);
|
|
|
-
|
|
|
+
|
|
|
for(auto rule : config->rules)
|
|
|
{
|
|
|
rule->process(source, destination, config.get(), hlp.get());
|
|
@@ -312,7 +352,7 @@ void CPathfinder::calculatePaths()
|
|
|
|
|
|
if(!destination.blocked)
|
|
|
pq.push(destination.node);
|
|
|
-
|
|
|
+
|
|
|
} //neighbours loop
|
|
|
|
|
|
//just add all passable teleport exits
|
|
@@ -632,7 +672,7 @@ void MovementAfterDestinationRule::process(
|
|
|
}
|
|
|
|
|
|
PathfinderBlockingRule::BlockingReason MovementAfterDestinationRule::getBlockingReason(
|
|
|
- const PathNodeInfo & source,
|
|
|
+ const PathNodeInfo & source,
|
|
|
const CDestinationNodeInfo & destination,
|
|
|
const PathfinderConfig * config,
|
|
|
const CPathfinderHelper * pathfinderHelper) const
|
|
@@ -702,7 +742,9 @@ void DestinationActionRule::process(
|
|
|
{
|
|
|
if(destination.action != CGPathNode::ENodeAction::UNKNOWN)
|
|
|
{
|
|
|
+#ifdef VCMI_TRACE_PATHFINDER
|
|
|
logAi->trace("Accepted precalculated action at %s", destination.coord.toString());
|
|
|
+#endif
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -851,106 +893,8 @@ void CPathfinder::initializePatrol()
|
|
|
|
|
|
void CPathfinder::initializeGraph()
|
|
|
{
|
|
|
- auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo)
|
|
|
- {
|
|
|
- auto accessibility = evaluateAccessibility(pos, tinfo, layer);
|
|
|
-
|
|
|
- config->nodeStorage->resetTile(pos, layer, accessibility);
|
|
|
- };
|
|
|
-
|
|
|
- int3 pos;
|
|
|
- int3 sizes = gs->getMapSize();
|
|
|
- for(pos.x=0; pos.x < sizes.x; ++pos.x)
|
|
|
- {
|
|
|
- for(pos.y=0; pos.y < sizes.y; ++pos.y)
|
|
|
- {
|
|
|
- for(pos.z=0; pos.z < sizes.z; ++pos.z)
|
|
|
- {
|
|
|
- const TerrainTile * tinfo = &gs->map->getTile(pos);
|
|
|
- switch(tinfo->terType)
|
|
|
- {
|
|
|
- case ETerrainType::ROCK:
|
|
|
- break;
|
|
|
-
|
|
|
- case ETerrainType::WATER:
|
|
|
- updateNode(pos, ELayer::SAIL, tinfo);
|
|
|
- if(config->options.useFlying)
|
|
|
- updateNode(pos, ELayer::AIR, tinfo);
|
|
|
- if(config->options.useWaterWalking)
|
|
|
- updateNode(pos, ELayer::WATER, tinfo);
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- updateNode(pos, ELayer::LAND, tinfo);
|
|
|
- if(config->options.useFlying)
|
|
|
- updateNode(pos, ELayer::AIR, tinfo);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const
|
|
|
-{
|
|
|
- if(tinfo->terType == ETerrainType::ROCK || !FoW[pos.x][pos.y][pos.z])
|
|
|
- return CGPathNode::BLOCKED;
|
|
|
-
|
|
|
- switch(layer)
|
|
|
- {
|
|
|
- case ELayer::LAND:
|
|
|
- case ELayer::SAIL:
|
|
|
- if(tinfo->visitable)
|
|
|
- {
|
|
|
- if(tinfo->visitableObjects.front()->ID == Obj::SANCTUARY && tinfo->visitableObjects.back()->ID == Obj::HERO && tinfo->visitableObjects.back()->tempOwner != hero->tempOwner) //non-owned hero stands on Sanctuary
|
|
|
- {
|
|
|
- return CGPathNode::BLOCKED;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- for(const CGObjectInstance * obj : tinfo->visitableObjects)
|
|
|
- {
|
|
|
- if(obj->blockVisit)
|
|
|
- {
|
|
|
- return CGPathNode::BLOCKVIS;
|
|
|
- }
|
|
|
- else if(obj->passableFor(hero->tempOwner))
|
|
|
- {
|
|
|
- return CGPathNode::ACCESSIBLE;
|
|
|
- }
|
|
|
- else if(canSeeObj(obj))
|
|
|
- {
|
|
|
- return CGPathNode::VISITABLE;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else if(tinfo->blocked)
|
|
|
- {
|
|
|
- return CGPathNode::BLOCKED;
|
|
|
- }
|
|
|
- else if(gs->guardingCreaturePosition(pos).valid())
|
|
|
- {
|
|
|
- // Monster close by; blocked visit for battle
|
|
|
- return CGPathNode::BLOCKVIS;
|
|
|
- }
|
|
|
-
|
|
|
- break;
|
|
|
-
|
|
|
- case ELayer::WATER:
|
|
|
- if(tinfo->blocked || tinfo->terType != ETerrainType::WATER)
|
|
|
- return CGPathNode::BLOCKED;
|
|
|
-
|
|
|
- break;
|
|
|
-
|
|
|
- case ELayer::AIR:
|
|
|
- if(tinfo->blocked || tinfo->terType == ETerrainType::WATER)
|
|
|
- return CGPathNode::FLYABLE;
|
|
|
-
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return CGPathNode::ACCESSIBLE;
|
|
|
+ INodeStorage * nodeStorage = config->nodeStorage.get();
|
|
|
+ nodeStorage->initialize(config->options, gs, hero);
|
|
|
}
|
|
|
|
|
|
bool CPathfinderHelper::canMoveBetween(const int3 & a, const int3 & b) const
|
|
@@ -1055,10 +999,7 @@ TurnInfo::BonusCache::BonusCache(TBonusListPtr bl)
|
|
|
TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn)
|
|
|
: hero(Hero), maxMovePointsLand(-1), maxMovePointsWater(-1)
|
|
|
{
|
|
|
- std::stringstream cachingStr;
|
|
|
- cachingStr << "days_" << turn;
|
|
|
-
|
|
|
- bonuses = hero->getAllBonuses(Selector::days(turn), nullptr, nullptr, cachingStr.str());
|
|
|
+ bonuses = hero->getAllBonuses(Selector::days(turn), Selector::all, nullptr, "");
|
|
|
bonusCache = make_unique<BonusCache>(bonuses);
|
|
|
nativeTerrain = hero->getNativeTerrain();
|
|
|
}
|
|
@@ -1186,10 +1127,10 @@ int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const
|
|
|
}
|
|
|
|
|
|
void CPathfinderHelper::getNeighbours(
|
|
|
- const TerrainTile & srct,
|
|
|
+ const TerrainTile & srct,
|
|
|
const int3 & tile,
|
|
|
std::vector<int3> & vec,
|
|
|
- const boost::logic::tribool & onLand,
|
|
|
+ const boost::logic::tribool & onLand,
|
|
|
const bool limitCoastSailing) const
|
|
|
{
|
|
|
CMap * map = gs->map;
|
|
@@ -1237,10 +1178,10 @@ void CPathfinderHelper::getNeighbours(
|
|
|
|
|
|
int CPathfinderHelper::getMovementCost(
|
|
|
const int3 & src,
|
|
|
- const int3 & dst,
|
|
|
+ const int3 & dst,
|
|
|
const TerrainTile * ct,
|
|
|
const TerrainTile * dt,
|
|
|
- const int remainingMovePoints,
|
|
|
+ const int remainingMovePoints,
|
|
|
const bool checkLast) const
|
|
|
{
|
|
|
if(src == dst) //same tile
|
|
@@ -1309,40 +1250,6 @@ int CPathfinderHelper::getMovementCost(
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-CGPathNode::CGPathNode()
|
|
|
- : coord(int3(-1, -1, -1)), layer(ELayer::WRONG)
|
|
|
-{
|
|
|
- reset();
|
|
|
-}
|
|
|
-
|
|
|
-void CGPathNode::reset()
|
|
|
-{
|
|
|
- locked = false;
|
|
|
- accessible = NOT_SET;
|
|
|
- moveRemains = 0;
|
|
|
- turns = 255;
|
|
|
- theNodeBefore = nullptr;
|
|
|
- action = UNKNOWN;
|
|
|
-}
|
|
|
-
|
|
|
-void CGPathNode::update(const int3 & Coord, const ELayer Layer, const EAccessibility Accessible)
|
|
|
-{
|
|
|
- if(layer == ELayer::WRONG)
|
|
|
- {
|
|
|
- coord = Coord;
|
|
|
- layer = Layer;
|
|
|
- }
|
|
|
- else
|
|
|
- reset();
|
|
|
-
|
|
|
- accessible = Accessible;
|
|
|
-}
|
|
|
-
|
|
|
-bool CGPathNode::reachable() const
|
|
|
-{
|
|
|
- return turns < 255;
|
|
|
-}
|
|
|
-
|
|
|
int3 CGPath::startPos() const
|
|
|
{
|
|
|
return nodes[nodes.size()-1].coord;
|
|
@@ -1364,16 +1271,13 @@ void CGPath::convert(ui8 mode)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-CPathsInfo::CPathsInfo(const int3 & Sizes)
|
|
|
- : sizes(Sizes)
|
|
|
+CPathsInfo::CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_)
|
|
|
+ : sizes(Sizes), hero(hero_)
|
|
|
{
|
|
|
- hero = nullptr;
|
|
|
nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][ELayer::NUM_LAYERS]);
|
|
|
}
|
|
|
|
|
|
-CPathsInfo::~CPathsInfo()
|
|
|
-{
|
|
|
-}
|
|
|
+CPathsInfo::~CPathsInfo() = default;
|
|
|
|
|
|
const CGPathNode * CPathsInfo::getPathInfo(const int3 & tile) const
|
|
|
{
|
|
@@ -1381,14 +1285,11 @@ const CGPathNode * CPathsInfo::getPathInfo(const int3 & tile) const
|
|
|
assert(vstd::iswithin(tile.y, 0, sizes.y));
|
|
|
assert(vstd::iswithin(tile.z, 0, sizes.z));
|
|
|
|
|
|
- boost::unique_lock<boost::mutex> pathLock(pathMx);
|
|
|
return getNode(tile);
|
|
|
}
|
|
|
|
|
|
bool CPathsInfo::getPath(CGPath & out, const int3 & dst) const
|
|
|
{
|
|
|
- boost::unique_lock<boost::mutex> pathLock(pathMx);
|
|
|
-
|
|
|
out.nodes.clear();
|
|
|
const CGPathNode * curnode = getNode(dst);
|
|
|
if(!curnode->theNodeBefore)
|
|
@@ -1405,8 +1306,6 @@ bool CPathsInfo::getPath(CGPath & out, const int3 & dst) const
|
|
|
|
|
|
int CPathsInfo::getDistance(const int3 & tile) const
|
|
|
{
|
|
|
- boost::unique_lock<boost::mutex> pathLock(pathMx);
|
|
|
-
|
|
|
CGPath ret;
|
|
|
if(getPath(ret, tile))
|
|
|
return ret.nodes.size();
|
|
@@ -1423,11 +1322,6 @@ const CGPathNode * CPathsInfo::getNode(const int3 & coord) const
|
|
|
return &nodes[coord.x][coord.y][coord.z][ELayer::SAIL];
|
|
|
}
|
|
|
|
|
|
-CGPathNode * CPathsInfo::getNode(const int3 & coord, const ELayer layer)
|
|
|
-{
|
|
|
- return &nodes[coord.x][coord.y][coord.z][layer];
|
|
|
-}
|
|
|
-
|
|
|
PathNodeInfo::PathNodeInfo()
|
|
|
: node(nullptr), nodeObject(nullptr), tile(nullptr), coord(-1, -1, -1), guarded(false)
|
|
|
{
|