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

CPathfinder: draft implementation of layers logic; not yet works

This commit is contained in:
ArseniyShestakov 2015-11-02 16:03:03 +03:00
parent 9c1c7d0caf
commit 400152caee
2 changed files with 110 additions and 68 deletions

View File

@ -100,40 +100,44 @@ void CPathfinder::calculatePaths()
addNeighbours(cp->coord); addNeighbours(cp->coord);
for(auto & neighbour : neighbours) for(auto & neighbour : neighbours)
{ {
dp = out.getNode(neighbour, EPathfindingLayer::LAND);
dt = &gs->map->getTile(neighbour); dt = &gs->map->getTile(neighbour);
useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1))
if(!isMovementPossible())
continue;
int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement);
int remains = movement - cost;
if(useEmbarkCost)
{ {
remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1); useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark
cost = movement - remains; dp = out.getNode(neighbour, i);
} if(cp->layer != i && isLayerTransitionPossible())
continue;
int turnAtNextTile = turn; if(!isMovementPossible())
if(remains < 0) continue;
{ int cost = gs->getMovementCost(hero, cp->coord, dp->coord, movement);
//occurs rarely, when hero with low movepoints tries to leave the road int remains = movement - cost;
turnAtNextTile++; if(useEmbarkCost)
int moveAtNextTile = maxMovePoints(cp); {
cost = gs->getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1);
remains = moveAtNextTile - cost; cost = movement - remains;
} }
if(isBetterWay(remains, turnAtNextTile)) int turnAtNextTile = turn;
{ if(remains < 0)
assert(dp != cp->theNodeBefore); //two tiles can't point to each other {
dp->moveRemains = remains; //occurs rarely, when hero with low movepoints tries to leave the road
dp->turns = turnAtNextTile; turnAtNextTile++;
dp->theNodeBefore = cp; int moveAtNextTile = maxMovePoints(cp);
cost = gs->getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :(
remains = moveAtNextTile - cost;
}
if(checkDestinationTile()) if(isBetterWay(remains, turnAtNextTile))
mq.push_back(dp); {
assert(dp != cp->theNodeBefore); //two tiles can't point to each other
dp->moveRemains = remains;
dp->turns = turnAtNextTile;
dp->theNodeBefore = cp;
if(checkDestinationTile())
mq.push_back(dp);
}
} }
} //neighbours loop } //neighbours loop
@ -143,7 +147,7 @@ void CPathfinder::calculatePaths()
addTeleportExits(); addTeleportExits();
for(auto & neighbour : neighbours) for(auto & neighbour : neighbours)
{ {
dp = out.getNode(neighbour, EPathfindingLayer::LAND); dp = out.getNode(neighbour, cp->layer);
if(isBetterWay(movement, turn)) if(isBetterWay(movement, turn))
{ {
dp->moveRemains = movement; dp->moveRemains = movement;
@ -220,38 +224,42 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes)
bool CPathfinder::isMovementPossible() bool CPathfinder::isMovementPossible()
{ {
if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED) switch (dp->layer)
return false;
Obj destTopVisObjID = dt->topVisitableId();
if(cp->land != dp->land) //hero can traverse land<->sea only in special circumstances
{ {
if(cp->land) //from land to sea -> embark or assault hero on boat case EPathfindingLayer::LAND:
{ if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED)
if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable
return false; return false;
if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard
return false;
if(destTopVisObjID == Obj::BOAT)
useEmbarkCost = 1;
}
else //disembark
{
//can disembark only on coastal tiles
if(!dt->isCoastal())
return false; return false;
//tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast break;
if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked)) case EPathfindingLayer::SAIL:
|| dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED)
return false;; return false;
if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard
return false;
useEmbarkCost = 2; break;
}
case EPathfindingLayer::AIR:
if(!options.useFlying)
return false;
if(!canMoveBetween(cp->coord, dp->coord))
return false;
break;
case EPathfindingLayer::WATER:
if(!options.useWaterWalking)
return false;
if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED)
return false;
if(isDestinationGuarded())
return false;
break;
} }
if(isSourceGuarded() && !isDestinationGuardian()) // Can step into tile of guard
return false;
return true; return true;
} }
@ -314,12 +322,9 @@ bool CPathfinder::isDestinationGuardian()
void CPathfinder::initializeGraph() void CPathfinder::initializeGraph()
{ {
int3 pos; auto addNode = [&](EPathfindingLayer layer, const TerrainTile *tinfo, int3 pos)
CGPathNode ****graph = out.nodes;
const TerrainTile *tinfo;
auto addNode = [&](EPathfindingLayer layer)
{ {
CGPathNode &node = graph[pos.x][pos.y][pos.z][layer]; CGPathNode &node = out.nodes[pos.x][pos.y][pos.z][layer];
node.accessible = evaluateAccessibility(pos, tinfo); node.accessible = evaluateAccessibility(pos, tinfo);
node.turns = 0xff; node.turns = 0xff;
node.moveRemains = 0; node.moveRemains = 0;
@ -329,6 +334,7 @@ void CPathfinder::initializeGraph()
node.layer = layer; node.layer = layer;
}; };
int3 pos;
for(pos.x=0; pos.x < out.sizes.x; ++pos.x) for(pos.x=0; pos.x < out.sizes.x; ++pos.x)
{ {
for(pos.y=0; pos.y < out.sizes.y; ++pos.y) for(pos.y=0; pos.y < out.sizes.y; ++pos.y)
@ -343,16 +349,16 @@ void CPathfinder::initializeGraph()
case ETerrainType::ROCK: case ETerrainType::ROCK:
break; break;
case ETerrainType::WATER: case ETerrainType::WATER:
addNode(EPathfindingLayer::SAIL); addNode(EPathfindingLayer::SAIL, tinfo, pos);
if(options.useFlying) // if(options.useFlying)
addNode(EPathfindingLayer::AIR); addNode(EPathfindingLayer::AIR, tinfo, pos);
if(options.useWaterWalking) // if(options.useWaterWalking)
addNode(EPathfindingLayer::WATER); addNode(EPathfindingLayer::WATER, tinfo, pos);
break; break;
default: default:
addNode(EPathfindingLayer::LAND); addNode(EPathfindingLayer::LAND, tinfo, pos);
if(options.useFlying) // if(options.useFlying)
addNode(EPathfindingLayer::AIR); addNode(EPathfindingLayer::AIR, tinfo, pos);
break; break;
} }
} }
@ -446,6 +452,40 @@ bool CPathfinder::canVisitObject() const
return cp->layer == EPathfindingLayer::LAND || cp->layer == EPathfindingLayer::SAIL; return cp->layer == EPathfindingLayer::LAND || cp->layer == EPathfindingLayer::SAIL;
} }
bool CPathfinder::isLayerTransitionPossible()
{
Obj destTopVisObjID = dt->topVisitableId();
if((cp->layer == EPathfindingLayer::AIR || EPathfindingLayer::WATER)
&& dp->layer != EPathfindingLayer::LAND)
{
return false;
}
else if(cp->layer == EPathfindingLayer::SAIL && dp->layer != EPathfindingLayer::LAND)
return false;
else if(cp->layer == EPathfindingLayer::SAIL && dp->layer == EPathfindingLayer::LAND)
{
if(!dt->isCoastal())
return false;
//tile must be accessible -> exception: unblocked blockvis tiles -> clear but guarded by nearby monster coast
if((dp->accessible != CGPathNode::ACCESSIBLE && (dp->accessible != CGPathNode::BLOCKVIS || dt->blocked))
|| dt->visitable) //TODO: passableness problem -> town says it's passable (thus accessible) but we obviously can't disembark onto town gate
return false;
useEmbarkCost = 2;
}
else if(cp->layer == EPathfindingLayer::LAND && dp->layer == EPathfindingLayer::SAIL)
{
if(dp->accessible == CGPathNode::ACCESSIBLE || destTopVisObjID < 0) //cannot enter empty water tile from land -> it has to be visitable
return false;
if(destTopVisObjID != Obj::HERO && destTopVisObjID != Obj::BOAT) //only boat or hero can be accessed from land
return false;
if(destTopVisObjID == Obj::BOAT)
useEmbarkCost = 1;
}
return true;
}
CGPathNode::CGPathNode() CGPathNode::CGPathNode()
:coord(-1,-1,-1) :coord(-1,-1,-1)
{ {

View File

@ -124,4 +124,6 @@ private:
bool addTeleportWhirlpool(const CGWhirlpool * obj) const; bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
bool canVisitObject() const; bool canVisitObject() const;
bool isLayerTransitionPossible();
}; };