mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-15 13:33:36 +02:00
AI: pathfinder rework, rules refactored a bit.
This commit is contained in:
parent
24221f3fb4
commit
9ab44b950a
@ -30,18 +30,18 @@ CNeighbourFinder::CNeighbourFinder()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> CNeighbourFinder::calculateNeighbours(
|
std::vector<CGPathNode *> CNeighbourFinder::calculateNeighbours(
|
||||||
CPathNodeInfo & source,
|
CPathNodeInfo & source,
|
||||||
CPathfinderHelper * pathfinderHelper,
|
CPathfinderConfig * pathfinderConfig,
|
||||||
CNodeHelper * nodeHelper) const
|
CPathfinderHelper * pathfinderHelper) const
|
||||||
{
|
{
|
||||||
std::vector<CGPathNode *> neighbours;
|
std::vector<CGPathNode *> neighbours;
|
||||||
auto accessibleNeighbourTiles = getNeighbourTiles(source, pathfinderHelper);
|
auto accessibleNeighbourTiles = getNeighbourTiles(source, pathfinderConfig, pathfinderHelper);
|
||||||
|
|
||||||
for(auto & neighbour : accessibleNeighbourTiles)
|
for(auto & neighbour : accessibleNeighbourTiles)
|
||||||
{
|
{
|
||||||
for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1))
|
for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1))
|
||||||
{
|
{
|
||||||
auto node = nodeHelper->getNode(neighbour, i);
|
auto node = pathfinderConfig->nodeHelper->getNode(neighbour, i);
|
||||||
|
|
||||||
if(node->accessible == CGPathNode::NOT_SET)
|
if(node->accessible == CGPathNode::NOT_SET)
|
||||||
continue;
|
continue;
|
||||||
@ -54,16 +54,16 @@ std::vector<CGPathNode *> CNeighbourFinder::calculateNeighbours(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> CNeighbourFinder::calculateTeleportations(
|
std::vector<CGPathNode *> CNeighbourFinder::calculateTeleportations(
|
||||||
CPathNodeInfo & source,
|
CPathNodeInfo & source,
|
||||||
CPathfinderHelper * pathfinderHelper,
|
CPathfinderConfig * pathfinderConfig,
|
||||||
CNodeHelper * nodeHelper) const
|
CPathfinderHelper * pathfinderHelper) const
|
||||||
{
|
{
|
||||||
std::vector<CGPathNode *> neighbours;
|
std::vector<CGPathNode *> neighbours;
|
||||||
auto accessibleExits = getTeleportExits(source, pathfinderHelper);
|
auto accessibleExits = getTeleportExits(source, pathfinderConfig, pathfinderHelper);
|
||||||
|
|
||||||
for(auto & neighbour : accessibleExits)
|
for(auto & neighbour : accessibleExits)
|
||||||
{
|
{
|
||||||
auto node = nodeHelper->getNode(neighbour, source.node->layer);
|
auto node = pathfinderConfig->nodeHelper->getNode(neighbour, source.node->layer);
|
||||||
|
|
||||||
neighbours.push_back(node);
|
neighbours.push_back(node);
|
||||||
}
|
}
|
||||||
@ -71,7 +71,10 @@ std::vector<CGPathNode *> CNeighbourFinder::calculateTeleportations(
|
|||||||
return neighbours;
|
return neighbours;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int3> CNeighbourFinder::getNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const
|
std::vector<int3> CNeighbourFinder::getNeighbourTiles(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) const
|
||||||
{
|
{
|
||||||
std::vector<int3> neighbourTiles;
|
std::vector<int3> neighbourTiles;
|
||||||
|
|
||||||
@ -139,36 +142,92 @@ PathfinderOptions::PathfinderOptions()
|
|||||||
originalMovementRules = settings["pathfinder"]["originalMovementRules"].Bool();
|
originalMovementRules = settings["pathfinder"]["originalMovementRules"].Bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CMovementCostRule : public IPathfindingRule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void process(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) override
|
||||||
|
{
|
||||||
|
int turnAtNextTile = destination.turn, moveAtNextTile = destination.movementLeft;
|
||||||
|
int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile);
|
||||||
|
int remains = moveAtNextTile - cost;
|
||||||
|
if(remains < 0)
|
||||||
|
{
|
||||||
|
//occurs rarely, when hero with low movepoints tries to leave the road
|
||||||
|
pathfinderHelper->updateTurnInfo(++turnAtNextTile);
|
||||||
|
moveAtNextTile = pathfinderHelper->getMaxMovePoints(destination.node->layer);
|
||||||
|
cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
|
||||||
|
remains = moveAtNextTile - cost;
|
||||||
|
}
|
||||||
|
if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK)
|
||||||
|
{
|
||||||
|
/// FREE_SHIP_BOARDING bonus only remove additional penalty
|
||||||
|
/// land <-> sail transition still cost movement points as normal movement
|
||||||
|
remains = pathfinderHelper->movementPointsAfterEmbark(moveAtNextTile, cost, destination.action - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.turn = turnAtNextTile;
|
||||||
|
destination.movementLeft = remains;
|
||||||
|
|
||||||
|
if(destination.isBetterWay() &&
|
||||||
|
((source.node->turns == turnAtNextTile && remains) || pathfinderHelper->passOneTurnLimitCheck(source)))
|
||||||
|
{
|
||||||
|
assert(destination.node != source.node->theNodeBefore); //two tiles can't point to each other
|
||||||
|
destination.node->moveRemains = remains;
|
||||||
|
destination.node->turns = turnAtNextTile;
|
||||||
|
destination.node->theNodeBefore = source.node;
|
||||||
|
destination.node->action = destination.action;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.blocked = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CPathfinderConfig::CPathfinderConfig(
|
||||||
|
std::shared_ptr<CNodeHelper> nodeHelper,
|
||||||
|
std::shared_ptr<CNeighbourFinder> neighbourFinder,
|
||||||
|
std::vector<std::shared_ptr<IPathfindingRule>> rules)
|
||||||
|
: nodeHelper(nodeHelper), neighbourFinder(neighbourFinder), rules(rules), options()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
CPathfinder::CPathfinder(
|
CPathfinder::CPathfinder(
|
||||||
CPathsInfo & _out,
|
CPathsInfo & _out,
|
||||||
CGameState * _gs,
|
CGameState * _gs,
|
||||||
const CGHeroInstance * _hero)
|
const CGHeroInstance * _hero)
|
||||||
:CPathfinder(
|
: CPathfinder(
|
||||||
_gs,
|
_gs,
|
||||||
_hero,
|
_hero,
|
||||||
std::make_shared<CPathfinderNodeHelper>(_out, _hero),
|
std::make_shared<CPathfinderConfig>(
|
||||||
std::make_shared<CNeighbourFinder>())
|
std::make_shared<CPathfinderNodeHelper>(_out, _hero),
|
||||||
|
std::make_shared<CNeighbourFinder>(),
|
||||||
|
std::vector<std::shared_ptr<IPathfindingRule>>{
|
||||||
|
std::make_shared<CMovementCostRule>(),
|
||||||
|
std::make_shared<CMovementAfterDestinationRule>()
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CPathfinder::CPathfinder(
|
CPathfinder::CPathfinder(
|
||||||
CGameState * _gs,
|
CGameState * _gs,
|
||||||
const CGHeroInstance * _hero,
|
const CGHeroInstance * _hero,
|
||||||
std::shared_ptr<CNodeHelper> nodeHelper,
|
std::shared_ptr<CPathfinderConfig> config)
|
||||||
std::shared_ptr<CNeighbourFinder> neighbourFinder)
|
|
||||||
: CGameInfoCallback(_gs, boost::optional<PlayerColor>())
|
: CGameInfoCallback(_gs, boost::optional<PlayerColor>())
|
||||||
, hero(_hero)
|
, hero(_hero)
|
||||||
, FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap), patrolTiles({})
|
, FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap), patrolTiles({})
|
||||||
, nodeHelper(nodeHelper)
|
, config(config)
|
||||||
, neighbourFinder(neighbourFinder)
|
|
||||||
, source()
|
, source()
|
||||||
, destination()
|
, destination()
|
||||||
{
|
{
|
||||||
assert(hero);
|
assert(hero);
|
||||||
assert(hero == getHero(hero->id));
|
assert(hero == getHero(hero->id));
|
||||||
|
|
||||||
hlp = make_unique<CPathfinderHelper>(_gs, hero, options);
|
hlp = make_unique<CPathfinderHelper>(_gs, hero, config->options);
|
||||||
|
|
||||||
initializePatrol();
|
initializePatrol();
|
||||||
initializeGraph();
|
initializeGraph();
|
||||||
@ -176,40 +235,10 @@ CPathfinder::CPathfinder(
|
|||||||
|
|
||||||
void CPathfinder::calculatePaths()
|
void CPathfinder::calculatePaths()
|
||||||
{
|
{
|
||||||
auto passOneTurnLimitCheck = [&]() -> bool
|
|
||||||
{
|
|
||||||
if(!options.oneTurnSpecialLayersLimit)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if(source.node->layer == ELayer::WATER)
|
|
||||||
return false;
|
|
||||||
if(source.node->layer == ELayer::AIR)
|
|
||||||
{
|
|
||||||
if(options.originalMovementRules && source.node->accessible == CGPathNode::ACCESSIBLE)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto isBetterWay = [&](int remains, int turn) -> bool
|
|
||||||
{
|
|
||||||
if(destination.node->turns == 0xff) //we haven't been here before
|
|
||||||
return true;
|
|
||||||
else if(destination.node->turns > turn)
|
|
||||||
return true;
|
|
||||||
else if(destination.node->turns >= turn && destination.node->moveRemains < remains) //this route is faster
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
//logGlobal->info("Calculating paths for hero %s (adress %d) of player %d", hero->name, hero , hero->tempOwner);
|
//logGlobal->info("Calculating paths for hero %s (adress %d) of player %d", hero->name, hero , hero->tempOwner);
|
||||||
|
|
||||||
//initial tile - set cost on 0 and add to the queue
|
//initial tile - set cost on 0 and add to the queue
|
||||||
CGPathNode * initialNode = nodeHelper->getInitialNode();
|
CGPathNode * initialNode = config->nodeHelper->getInitialNode();
|
||||||
|
|
||||||
if(!isInTheMap(initialNode->coord)/* || !gs->map->isInTheMap(dest)*/) //check input
|
if(!isInTheMap(initialNode->coord)/* || !gs->map->isInTheMap(dest)*/) //check input
|
||||||
{
|
{
|
||||||
@ -236,7 +265,7 @@ void CPathfinder::calculatePaths()
|
|||||||
{
|
{
|
||||||
hlp->updateTurnInfo(++turn);
|
hlp->updateTurnInfo(++turn);
|
||||||
movement = hlp->getMaxMovePoints(source.node->layer);
|
movement = hlp->getMaxMovePoints(source.node->layer);
|
||||||
if(!passOneTurnLimitCheck())
|
if(!hlp->passOneTurnLimitCheck(source))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +274,7 @@ void CPathfinder::calculatePaths()
|
|||||||
source.objectRelations = gs->getPlayerRelations(hero->tempOwner, source.nodeObject->tempOwner);
|
source.objectRelations = gs->getPlayerRelations(hero->tempOwner, source.nodeObject->tempOwner);
|
||||||
|
|
||||||
//add accessible neighbouring nodes to the queue
|
//add accessible neighbouring nodes to the queue
|
||||||
auto neighbourNodes = neighbourFinder->calculateNeighbours(source, hlp.get(), nodeHelper.get());
|
auto neighbourNodes = config->neighbourFinder->calculateNeighbours(source, config.get(), hlp.get());
|
||||||
for(CGPathNode * neighbour : neighbourNodes)
|
for(CGPathNode * neighbour : neighbourNodes)
|
||||||
{
|
{
|
||||||
destination.setNode(gs, neighbour);
|
destination.setNode(gs, neighbour);
|
||||||
@ -275,41 +304,20 @@ void CPathfinder::calculatePaths()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
destination.action = getDestAction();
|
destination.action = getDestAction();
|
||||||
|
destination.turn = turn;
|
||||||
|
destination.movementLeft = movement;
|
||||||
|
|
||||||
|
for(auto rule : config->rules)
|
||||||
|
{
|
||||||
|
rule->process(source, destination, config.get(), hlp.get());
|
||||||
|
|
||||||
int turnAtNextTile = turn, moveAtNextTile = movement;
|
if(destination.blocked)
|
||||||
int cost = hlp->getMovementCost(source, destination, moveAtNextTile);
|
break;
|
||||||
int remains = moveAtNextTile - cost;
|
|
||||||
if(remains < 0)
|
|
||||||
{
|
|
||||||
//occurs rarely, when hero with low movepoints tries to leave the road
|
|
||||||
hlp->updateTurnInfo(++turnAtNextTile);
|
|
||||||
moveAtNextTile = hlp->getMaxMovePoints(destination.node->layer);
|
|
||||||
cost = hlp->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
|
|
||||||
remains = moveAtNextTile - cost;
|
|
||||||
}
|
|
||||||
if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK)
|
|
||||||
{
|
|
||||||
/// FREE_SHIP_BOARDING bonus only remove additional penalty
|
|
||||||
/// land <-> sail transition still cost movement points as normal movement
|
|
||||||
remains = hero->movementPointsAfterEmbark(moveAtNextTile, cost, destination.action - 1, hlp->getTurnInfo());
|
|
||||||
cost = moveAtNextTile - remains;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isBetterWay(remains, turnAtNextTile) &&
|
if(!destination.blocked && !destination.furtherProcessingImpossible)
|
||||||
((source.node->turns == turnAtNextTile && remains) || passOneTurnLimitCheck()))
|
pq.push(destination.node);
|
||||||
{
|
|
||||||
assert(destination.node != source.node->theNodeBefore); //two tiles can't point to each other
|
|
||||||
destination.node->moveRemains = remains;
|
|
||||||
destination.node->turns = turnAtNextTile;
|
|
||||||
destination.node->theNodeBefore = source.node;
|
|
||||||
destination.node->action = destination.action;
|
|
||||||
|
|
||||||
CMovementAfterDestinationRule rl = CMovementAfterDestinationRule();
|
|
||||||
rl.process(hlp.get(), source, destination);
|
|
||||||
|
|
||||||
if(!destination.furtherProcessingImpossible)
|
|
||||||
pq.push(destination.node);
|
|
||||||
}
|
|
||||||
} //neighbours loop
|
} //neighbours loop
|
||||||
|
|
||||||
//just add all passable teleport exits
|
//just add all passable teleport exits
|
||||||
@ -319,7 +327,7 @@ void CPathfinder::calculatePaths()
|
|||||||
if(!source.isNodeObjectVisitable() || patrolState == PATROL_RADIUS)
|
if(!source.isNodeObjectVisitable() || patrolState == PATROL_RADIUS)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto teleportationNodes = neighbourFinder->calculateTeleportations(source, hlp.get(), nodeHelper.get());
|
auto teleportationNodes = config->neighbourFinder->calculateTeleportations(source, config.get(), hlp.get());
|
||||||
for(CGPathNode * teleportNode : teleportationNodes)
|
for(CGPathNode * teleportNode : teleportationNodes)
|
||||||
{
|
{
|
||||||
if(teleportNode->locked)
|
if(teleportNode->locked)
|
||||||
@ -333,11 +341,13 @@ void CPathfinder::calculatePaths()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
destination.setNode(gs, teleportNode);
|
destination.setNode(gs, teleportNode);
|
||||||
|
destination.turn = turn;
|
||||||
|
destination.movementLeft = movement;
|
||||||
|
|
||||||
if(isBetterWay(movement, turn))
|
if(destination.isBetterWay())
|
||||||
{
|
{
|
||||||
destination.node->moveRemains = movement;
|
destination.node->moveRemains = destination.movementLeft;
|
||||||
destination.node->turns = turn;
|
destination.node->turns = destination.turn;
|
||||||
destination.node->theNodeBefore = source.node;
|
destination.node->theNodeBefore = source.node;
|
||||||
destination.node->action = getTeleportDestAction();
|
destination.node->action = getTeleportDestAction();
|
||||||
if(destination.node->action == CGPathNode::TELEPORT_NORMAL)
|
if(destination.node->action == CGPathNode::TELEPORT_NORMAL)
|
||||||
@ -389,6 +399,7 @@ std::vector<int3> CPathfinderHelper::getCastleGates(CPathNodeInfo & source) cons
|
|||||||
|
|
||||||
std::vector<int3> CNeighbourFinder::getTeleportExits(
|
std::vector<int3> CNeighbourFinder::getTeleportExits(
|
||||||
CPathNodeInfo & source,
|
CPathNodeInfo & source,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
CPathfinderHelper * pathfinderHelper) const
|
CPathfinderHelper * pathfinderHelper) const
|
||||||
{
|
{
|
||||||
std::vector<int3> teleportationExits;
|
std::vector<int3> teleportationExits;
|
||||||
@ -443,7 +454,7 @@ bool CPathfinder::isLayerTransitionPossible(const ELayer destLayer) const
|
|||||||
case ELayer::LAND:
|
case ELayer::LAND:
|
||||||
if(destLayer == ELayer::AIR)
|
if(destLayer == ELayer::AIR)
|
||||||
{
|
{
|
||||||
if(!options.lightweightFlyingMode || isSourceInitialPosition())
|
if(!config->options.lightweightFlyingMode || isSourceInitialPosition())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if(destLayer == ELayer::SAIL)
|
else if(destLayer == ELayer::SAIL)
|
||||||
@ -503,7 +514,7 @@ bool CPathfinder::isLayerTransitionPossible() const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ELayer::AIR:
|
case ELayer::AIR:
|
||||||
if(options.originalMovementRules)
|
if(config->options.originalMovementRules)
|
||||||
{
|
{
|
||||||
if((source.node->accessible != CGPathNode::ACCESSIBLE &&
|
if((source.node->accessible != CGPathNode::ACCESSIBLE &&
|
||||||
source.node->accessible != CGPathNode::VISITABLE) &&
|
source.node->accessible != CGPathNode::VISITABLE) &&
|
||||||
@ -547,7 +558,7 @@ bool CPathfinder::isMovementToDestPossible() const
|
|||||||
return false;
|
return false;
|
||||||
if(isSourceGuarded())
|
if(isSourceGuarded())
|
||||||
{
|
{
|
||||||
if(!(options.originalMovementRules && source.node->layer == ELayer::AIR) &&
|
if(!(config->options.originalMovementRules && source.node->layer == ELayer::AIR) &&
|
||||||
!isDestinationGuardian()) // Can step into tile of guard
|
!isDestinationGuardian()) // Can step into tile of guard
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -594,7 +605,11 @@ bool CPathfinder::isMovementToDestPossible() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMovementAfterDestinationRule::process(CPathfinderHelper * pathfinderHelper, CPathNodeInfo & source, CDestinationNodeInfo & destination)
|
void CMovementAfterDestinationRule::process(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * config,
|
||||||
|
CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
switch(destination.action)
|
switch(destination.action)
|
||||||
{
|
{
|
||||||
@ -707,12 +722,12 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const
|
|||||||
}
|
}
|
||||||
else if(isDestinationGuardian())
|
else if(isDestinationGuardian())
|
||||||
action = CGPathNode::BATTLE;
|
action = CGPathNode::BATTLE;
|
||||||
else if(destination.nodeObject->blockVisit && !(options.useCastleGate && destination.nodeObject->ID == Obj::TOWN))
|
else if(destination.nodeObject->blockVisit && !(config->options.useCastleGate && destination.nodeObject->ID == Obj::TOWN))
|
||||||
action = CGPathNode::BLOCKING_VISIT;
|
action = CGPathNode::BLOCKING_VISIT;
|
||||||
|
|
||||||
if(action == CGPathNode::NORMAL)
|
if(action == CGPathNode::NORMAL)
|
||||||
{
|
{
|
||||||
if(options.originalMovementRules && isDestinationGuarded())
|
if(config->options.originalMovementRules && isDestinationGuarded())
|
||||||
action = CGPathNode::BATTLE;
|
action = CGPathNode::BATTLE;
|
||||||
else
|
else
|
||||||
action = CGPathNode::VISIT;
|
action = CGPathNode::VISIT;
|
||||||
@ -744,7 +759,7 @@ CGPathNode::ENodeAction CPathfinder::getTeleportDestAction() const
|
|||||||
|
|
||||||
bool CPathfinder::isSourceInitialPosition() const
|
bool CPathfinder::isSourceInitialPosition() const
|
||||||
{
|
{
|
||||||
return source.node->coord == nodeHelper->getInitialNode()->coord;
|
return source.node->coord == config->nodeHelper->getInitialNode()->coord;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::isSourceGuarded() const
|
bool CPathfinder::isSourceGuarded() const
|
||||||
@ -795,7 +810,7 @@ void CPathfinder::initializeGraph()
|
|||||||
{
|
{
|
||||||
auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo)
|
auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo)
|
||||||
{
|
{
|
||||||
auto node = nodeHelper->getNode(pos, layer);
|
auto node = config->nodeHelper->getNode(pos, layer);
|
||||||
auto accessibility = evaluateAccessibility(pos, tinfo, layer);
|
auto accessibility = evaluateAccessibility(pos, tinfo, layer);
|
||||||
node->update(pos, layer, accessibility);
|
node->update(pos, layer, accessibility);
|
||||||
};
|
};
|
||||||
@ -816,15 +831,15 @@ void CPathfinder::initializeGraph()
|
|||||||
|
|
||||||
case ETerrainType::WATER:
|
case ETerrainType::WATER:
|
||||||
updateNode(pos, ELayer::SAIL, tinfo);
|
updateNode(pos, ELayer::SAIL, tinfo);
|
||||||
if(options.useFlying)
|
if(config->options.useFlying)
|
||||||
updateNode(pos, ELayer::AIR, tinfo);
|
updateNode(pos, ELayer::AIR, tinfo);
|
||||||
if(options.useWaterWalking)
|
if(config->options.useWaterWalking)
|
||||||
updateNode(pos, ELayer::WATER, tinfo);
|
updateNode(pos, ELayer::WATER, tinfo);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
updateNode(pos, ELayer::LAND, tinfo);
|
updateNode(pos, ELayer::LAND, tinfo);
|
||||||
if(options.useFlying)
|
if(config->options.useFlying)
|
||||||
updateNode(pos, ELayer::AIR, tinfo);
|
updateNode(pos, ELayer::AIR, tinfo);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -949,6 +964,35 @@ bool CPathfinderHelper::addTeleportWhirlpool(const CGWhirlpool * obj) const
|
|||||||
return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
|
return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CPathfinderHelper::getHeroMaxMovementPoints(EPathfindingLayer layer)
|
||||||
|
{
|
||||||
|
return hero->maxMovePoints(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CPathfinderHelper::movementPointsAfterEmbark(int movement, int turn, int action)
|
||||||
|
{
|
||||||
|
return hero->movementPointsAfterEmbark(movement, turn, action, getTurnInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPathfinderHelper::passOneTurnLimitCheck(CPathNodeInfo & source)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(!options.oneTurnSpecialLayersLimit)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(source.node->layer == EPathfindingLayer::WATER)
|
||||||
|
return false;
|
||||||
|
if(source.node->layer == EPathfindingLayer::AIR)
|
||||||
|
{
|
||||||
|
if(options.originalMovementRules && source.node->accessible == CGPathNode::ACCESSIBLE)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
TurnInfo::BonusCache::BonusCache(TBonusListPtr bl)
|
TurnInfo::BonusCache::BonusCache(TBonusListPtr bl)
|
||||||
{
|
{
|
||||||
noTerrainPenalty.reserve(ETerrainType::ROCK);
|
noTerrainPenalty.reserve(ETerrainType::ROCK);
|
||||||
@ -1365,6 +1409,19 @@ void CDestinationNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool exclude
|
|||||||
action = CGPathNode::ENodeAction::UNKNOWN;
|
action = CGPathNode::ENodeAction::UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CDestinationNodeInfo::isBetterWay()
|
||||||
|
{
|
||||||
|
if(node->turns == 0xff) //we haven't been here before
|
||||||
|
return true;
|
||||||
|
else if(node->turns > turn)
|
||||||
|
return true;
|
||||||
|
else if(node->turns >= turn && node->moveRemains < movementLeft) //this route is faster
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool CPathNodeInfo::isNodeObjectVisitable() const
|
bool CPathNodeInfo::isNodeObjectVisitable() const
|
||||||
{
|
{
|
||||||
/// Hero can't visit objects while walking on water or flying
|
/// Hero can't visit objects while walking on water or flying
|
||||||
|
@ -115,12 +115,16 @@ struct DLL_LINKAGE CPathNodeInfo
|
|||||||
struct DLL_LINKAGE CDestinationNodeInfo : public CPathNodeInfo
|
struct DLL_LINKAGE CDestinationNodeInfo : public CPathNodeInfo
|
||||||
{
|
{
|
||||||
CGPathNode::ENodeAction action;
|
CGPathNode::ENodeAction action;
|
||||||
|
int turn;
|
||||||
|
int movementLeft;
|
||||||
bool furtherProcessingImpossible;
|
bool furtherProcessingImpossible;
|
||||||
bool blocked;
|
bool blocked;
|
||||||
|
|
||||||
CDestinationNodeInfo();
|
CDestinationNodeInfo();
|
||||||
|
|
||||||
virtual void setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject = false) override;
|
virtual void setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject = false) override;
|
||||||
|
|
||||||
|
virtual bool isBetterWay();
|
||||||
};
|
};
|
||||||
|
|
||||||
class CNodeHelper
|
class CNodeHelper
|
||||||
@ -132,17 +136,26 @@ public:
|
|||||||
|
|
||||||
class CPathfinderHelper;
|
class CPathfinderHelper;
|
||||||
class CPathfinder;
|
class CPathfinder;
|
||||||
|
class CPathfinderConfig;
|
||||||
|
|
||||||
class IPathfindingRule
|
class IPathfindingRule
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void process(CPathfinderHelper * pathfinderHelper, CPathNodeInfo & source, CDestinationNodeInfo & destination) = 0;
|
virtual void process(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CMovementAfterDestinationRule : public IPathfindingRule
|
class CMovementAfterDestinationRule : public IPathfindingRule
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void process(CPathfinderHelper * pathfinderHelper, CPathNodeInfo & source, CDestinationNodeInfo & destination) override;
|
virtual void process(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CNeighbourFinder
|
class CNeighbourFinder
|
||||||
@ -151,17 +164,23 @@ public:
|
|||||||
CNeighbourFinder();
|
CNeighbourFinder();
|
||||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
virtual std::vector<CGPathNode *> calculateNeighbours(
|
||||||
CPathNodeInfo & source,
|
CPathNodeInfo & source,
|
||||||
CPathfinderHelper * pathfinderHelper,
|
CPathfinderConfig * pathfinderConfig,
|
||||||
CNodeHelper * nodeHelper) const;
|
CPathfinderHelper * pathfinderHelper) const;
|
||||||
|
|
||||||
virtual std::vector<CGPathNode *> calculateTeleportations(
|
virtual std::vector<CGPathNode *> calculateTeleportations(
|
||||||
CPathNodeInfo & source,
|
CPathNodeInfo & source,
|
||||||
CPathfinderHelper * pathfinderHelper,
|
CPathfinderConfig * pathfinderConfig,
|
||||||
CNodeHelper * nodeHelper) const;
|
CPathfinderHelper * pathfinderHelper) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<int3> getNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const;
|
std::vector<int3> getNeighbourTiles(
|
||||||
std::vector<int3> getTeleportExits(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const;
|
CPathNodeInfo & source,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) const;
|
||||||
|
|
||||||
|
std::vector<int3> getTeleportExits(CPathNodeInfo & source,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DLL_LINKAGE PathfinderOptions
|
struct DLL_LINKAGE PathfinderOptions
|
||||||
@ -215,6 +234,20 @@ struct DLL_LINKAGE PathfinderOptions
|
|||||||
PathfinderOptions();
|
PathfinderOptions();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CPathfinderConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::shared_ptr<CNodeHelper> nodeHelper;
|
||||||
|
std::shared_ptr<CNeighbourFinder> neighbourFinder;
|
||||||
|
std::vector<std::shared_ptr<IPathfindingRule>> rules;
|
||||||
|
PathfinderOptions options;
|
||||||
|
|
||||||
|
CPathfinderConfig(
|
||||||
|
std::shared_ptr<CNodeHelper> nodeHelper,
|
||||||
|
std::shared_ptr<CNeighbourFinder> neighbourFinder,
|
||||||
|
std::vector<std::shared_ptr<IPathfindingRule>> rules);
|
||||||
|
};
|
||||||
|
|
||||||
class CPathfinder : private CGameInfoCallback
|
class CPathfinder : private CGameInfoCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -224,20 +257,17 @@ public:
|
|||||||
CPathfinder(
|
CPathfinder(
|
||||||
CGameState * _gs,
|
CGameState * _gs,
|
||||||
const CGHeroInstance * _hero,
|
const CGHeroInstance * _hero,
|
||||||
std::shared_ptr<CNodeHelper> nodeHelper,
|
std::shared_ptr<CPathfinderConfig> config);
|
||||||
std::shared_ptr<CNeighbourFinder> neighbourFinder);
|
|
||||||
|
|
||||||
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
|
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:
|
private:
|
||||||
typedef EPathfindingLayer ELayer;
|
typedef EPathfindingLayer ELayer;
|
||||||
|
|
||||||
PathfinderOptions options;
|
|
||||||
const CGHeroInstance * hero;
|
const CGHeroInstance * hero;
|
||||||
const std::vector<std::vector<std::vector<ui8> > > &FoW;
|
const std::vector<std::vector<std::vector<ui8> > > &FoW;
|
||||||
std::unique_ptr<CPathfinderHelper> hlp;
|
std::unique_ptr<CPathfinderHelper> hlp;
|
||||||
std::shared_ptr<CNodeHelper> nodeHelper;
|
std::shared_ptr<CPathfinderConfig> config;
|
||||||
std::shared_ptr<CNeighbourFinder> neighbourFinder;
|
|
||||||
|
|
||||||
enum EPatrolState {
|
enum EPatrolState {
|
||||||
PATROL_NONE = 0,
|
PATROL_NONE = 0,
|
||||||
@ -362,4 +392,8 @@ public:
|
|||||||
checkLast
|
checkLast
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getHeroMaxMovementPoints(EPathfindingLayer layer);
|
||||||
|
int movementPointsAfterEmbark(int movement, int cost, int action);
|
||||||
|
bool passOneTurnLimitCheck(CPathNodeInfo & source);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user