1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-17 01:32:21 +02:00

CPathfinder: separate neighbours code into getNeighbours

This is first step to make this code usable outside of calculatePaths.
This commit is contained in:
ArseniyShestakov
2015-10-10 16:09:40 +03:00
parent abe02247b5
commit f15065fdc5
2 changed files with 72 additions and 56 deletions

View File

@ -3300,6 +3300,60 @@ void CPathfinder::initializeGraph()
} }
} }
void CPathfinder::getNeighbours(const int3 &coord, bool noTeleportExcludes)
{
ct = &gs->map->getTile(coord);
// Will be needed for usage outside of calculatePaths
// if(!cp)
// cp = getNode(coord);
neighbours.clear();
auto isAllowedTeleportEntrance = [&](const CGTeleport * obj) -> bool
{
if(!gs->isTeleportEntrancePassable(obj, hero->tempOwner))
return false;
if(noTeleportExcludes)
return true;
auto whirlpool = dynamic_cast<const CGWhirlpool *>(obj);
if(whirlpool)
{
if(addTeleportWhirlpool(whirlpool))
return true;
}
else if(addTeleportTwoWay(obj) || addTeleportOneWay(obj) || addTeleportOneWayRandom(obj))
return true;
return false;
};
sTileObj = ct->topVisitableObj(coord == CGHeroInstance::convertPosition(hero->pos, false));
sTileTeleport = dynamic_cast<const CGTeleport *>(sTileObj);
if(isAllowedTeleportEntrance(sTileTeleport))
{
for(auto objId : gs->getTeleportChannelExits(sTileTeleport->channel, hero->tempOwner))
{
auto obj = getObj(objId);
if(CGTeleport::isExitPassable(gs, hero, obj))
neighbours.push_back(obj->visitablePos());
}
}
std::vector<int3> neighbour_tiles;
gs->getNeighbours(*ct, coord, neighbour_tiles, boost::logic::indeterminate, !cp->land);
if(sTileObj)
{
for(int3 neighbour_tile: neighbour_tiles)
{
if(canMoveBetween(neighbour_tile, sTileObj->visitablePos()))
neighbours.push_back(neighbour_tile);
}
}
else
vstd::concatenate(neighbours, neighbour_tiles);
}
void CPathfinder::calculatePaths() void CPathfinder::calculatePaths()
{ {
bool flying = hero->hasBonusOfType(Bonus::FLYING_MOVEMENT); bool flying = hero->hasBonusOfType(Bonus::FLYING_MOVEMENT);
@ -3330,8 +3384,6 @@ void CPathfinder::calculatePaths()
initialNode.moveRemains = hero->movement; initialNode.moveRemains = hero->movement;
mq.push_back(&initialNode); mq.push_back(&initialNode);
std::vector<int3> neighbours;
neighbours.reserve(16);
while(!mq.empty()) while(!mq.empty())
{ {
cp = mq.front(); cp = mq.front();
@ -3339,7 +3391,6 @@ void CPathfinder::calculatePaths()
const int3 sourceGuardPosition = gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z]; const int3 sourceGuardPosition = gs->map->guardingCreaturePositions[cp->coord.x][cp->coord.y][cp->coord.z];
bool guardedSource = (sourceGuardPosition != int3(-1, -1, -1) && cp->coord != src); bool guardedSource = (sourceGuardPosition != int3(-1, -1, -1) && cp->coord != src);
ct = &gs->map->getTile(cp->coord);
int movement = cp->moveRemains, turn = cp->turns; int movement = cp->moveRemains, turn = cp->turns;
if(!movement) if(!movement)
@ -3349,63 +3400,19 @@ void CPathfinder::calculatePaths()
} }
//add accessible neighbouring nodes to the queue //add accessible neighbouring nodes to the queue
neighbours.clear(); getNeighbours(cp->coord);
auto isAllowedTeleportEntrance = [&](const CGTeleport * obj) -> bool
{
if(!gs->isTeleportEntrancePassable(obj, hero->tempOwner))
return false;
auto whirlpool = dynamic_cast<const CGWhirlpool *>(obj);
if(whirlpool)
{
if(addTeleportWhirlpool(whirlpool))
return true;
}
else if(addTeleportTwoWay(obj) || addTeleportOneWay(obj) || addTeleportOneWayRandom(obj))
return true;
return false;
};
auto sObj = ct->topVisitableObj(cp->coord == CGHeroInstance::convertPosition(hero->pos, false));
auto cObj = dynamic_cast<const CGTeleport *>(sObj);
if(isAllowedTeleportEntrance(cObj))
{
for(auto objId : gs->getTeleportChannelExits(cObj->channel, hero->tempOwner))
{
auto obj = getObj(objId);
if(CGTeleport::isExitPassable(gs, hero, obj))
neighbours.push_back(obj->visitablePos());
}
}
std::vector<int3> neighbour_tiles;
gs->getNeighbours(*ct, cp->coord, neighbour_tiles, boost::logic::indeterminate, !cp->land);
if(sObj)
{
for(int3 neighbour_tile: neighbour_tiles)
{
if(canMoveBetween(neighbour_tile, sObj->visitablePos()))
neighbours.push_back(neighbour_tile);
}
}
else
vstd::concatenate(neighbours, neighbour_tiles);
for(auto & neighbour : neighbours) for(auto & neighbour : neighbours)
{ {
const int3 &n = neighbour; //current neighbor dp = getNode(neighbour);
dp = getNode(n); dt = &gs->map->getTile(neighbour);
dt = &gs->map->getTile(n);
destTopVisObjID = dt->topVisitableId(); destTopVisObjID = dt->topVisitableId();
useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark
const bool destIsGuardian = sourceGuardPosition == n; const bool destIsGuardian = sourceGuardPosition == neighbour;
auto dObj = dynamic_cast<const CGTeleport*>(dt->topVisitableObj()); dTileTeleport = dynamic_cast<const CGTeleport*>(dt->topVisitableObj());
if(!goodForLandSeaTransition() if(!goodForLandSeaTransition()
|| (!canMoveBetween(cp->coord, dp->coord) && !CGTeleport::isConnected(cObj, dObj)) || (!canMoveBetween(cp->coord, dp->coord) && !CGTeleport::isConnected(sTileTeleport, dTileTeleport))
|| dp->accessible == CGPathNode::BLOCKED) || dp->accessible == CGPathNode::BLOCKED)
{ {
continue; continue;
@ -3417,7 +3424,7 @@ void CPathfinder::calculatePaths()
int cost = gs->getMovementCost(hero, cp->coord, dp->coord, flying, movement); int cost = gs->getMovementCost(hero, cp->coord, dp->coord, flying, movement);
//special case -> moving from src Subterranean gate to dest gate -> it's free //special case -> moving from src Subterranean gate to dest gate -> it's free
if(CGTeleport::isConnected(cObj, dObj)) if(CGTeleport::isConnected(sTileTeleport, dTileTeleport))
cost = 0; cost = 0;
int remains = movement - cost; int remains = movement - cost;
@ -3460,9 +3467,9 @@ void CPathfinder::calculatePaths()
return true; // For now we'll walways allos transit for teleports return true; // For now we'll walways allos transit for teleports
if(useEmbarkCost && allowEmbarkAndDisembark) if(useEmbarkCost && allowEmbarkAndDisembark)
return true; return true;
if(gs->isTeleportEntrancePassable(dObj, hero->tempOwner)) if(gs->isTeleportEntrancePassable(dTileTeleport, hero->tempOwner))
return true; // Always add entry teleport with non-dummy channel return true; // Always add entry teleport with non-dummy channel
if(CGTeleport::isConnected(cObj, dObj)) if(CGTeleport::isConnected(sTileTeleport, dTileTeleport))
return true; // Always add exit points of teleport return true; // Always add exit points of teleport
if(guardedDst && !guardedSource) if(guardedDst && !guardedSource)
return true; // Can step into a hostile tile once return true; // Can step into a hostile tile once
@ -3573,6 +3580,8 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance
allowTeleportWhirlpool = false; allowTeleportWhirlpool = false;
if (CGWhirlpool::isProtected(hero)) if (CGWhirlpool::isProtected(hero))
allowTeleportWhirlpool = true; allowTeleportWhirlpool = true;
neighbours.reserve(16);
} }
CRandomGenerator & CGameState::getRandomGenerator() CRandomGenerator & CGameState::getRandomGenerator()

View File

@ -290,6 +290,7 @@ private:
std::list<CGPathNode*> mq; //BFS queue -> nodes to be checked std::list<CGPathNode*> mq; //BFS queue -> nodes to be checked
std::vector<int3> neighbours;
int3 curPos; int3 curPos;
CGPathNode *cp; //current (source) path node -> we took it from the queue CGPathNode *cp; //current (source) path node -> we took it from the queue
@ -298,11 +299,17 @@ private:
ui8 useEmbarkCost; //0 - usual movement; 1 - embark; 2 - disembark ui8 useEmbarkCost; //0 - usual movement; 1 - embark; 2 - disembark
Obj destTopVisObjID; Obj destTopVisObjID;
CGObjectInstance *sTileObj;
CGObjectInstance *dTileObj;
const CGTeleport *sTileTeleport;
const CGTeleport *dTileTeleport;
CGPathNode *getNode(const int3 &coord); CGPathNode *getNode(const int3 &coord);
void initializeGraph(); void initializeGraph();
bool goodForLandSeaTransition(); //checks if current move will be between sea<->land. If so, checks it legality (returns false if movement is not possible) and sets useEmbarkCost bool goodForLandSeaTransition(); //checks if current move will be between sea<->land. If so, checks it legality (returns false if movement is not possible) and sets useEmbarkCost
void getNeighbours(const int3 &coord, bool noTeleportExcludes = false);
CGPathNode::EAccessibility evaluateAccessibility(const TerrainTile *tinfo) const; CGPathNode::EAccessibility evaluateAccessibility(const TerrainTile *tinfo) const;
bool canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility) bool canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)