mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-26 03:52:01 +02:00
AI: pathfinder extensibility - add one more rule for movement to destination and some refactoring
This commit is contained in:
parent
7150fc9f71
commit
eb17313f7f
@ -147,51 +147,47 @@ PathfinderOptions::PathfinderOptions()
|
|||||||
originalMovementRules = settings["pathfinder"]["originalMovementRules"].Bool();
|
originalMovementRules = settings["pathfinder"]["originalMovementRules"].Bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
class CMovementCostRule : public IPathfindingRule
|
void CMovementCostRule::process(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
public:
|
int turnAtNextTile = destination.turn, moveAtNextTile = destination.movementLeft;
|
||||||
virtual void process(
|
int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile);
|
||||||
CPathNodeInfo & source,
|
int remains = moveAtNextTile - cost;
|
||||||
CDestinationNodeInfo & destination,
|
if(remains < 0)
|
||||||
CPathfinderConfig * pathfinderConfig,
|
|
||||||
CPathfinderHelper * pathfinderHelper) override
|
|
||||||
{
|
{
|
||||||
int turnAtNextTile = destination.turn, moveAtNextTile = destination.movementLeft;
|
//occurs rarely, when hero with low movepoints tries to leave the road
|
||||||
int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile);
|
pathfinderHelper->updateTurnInfo(++turnAtNextTile);
|
||||||
int remains = moveAtNextTile - cost;
|
moveAtNextTile = pathfinderHelper->getMaxMovePoints(destination.node->layer);
|
||||||
if(remains < 0)
|
cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
|
||||||
{
|
remains = moveAtNextTile - cost;
|
||||||
//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;
|
|
||||||
}
|
}
|
||||||
};
|
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(
|
CPathfinderConfig::CPathfinderConfig(
|
||||||
std::shared_ptr<CNodeStorage> nodeStorage,
|
std::shared_ptr<CNodeStorage> nodeStorage,
|
||||||
@ -212,6 +208,7 @@ CPathfinder::CPathfinder(
|
|||||||
std::make_shared<CPathfinderNodeStorage>(_out, _hero),
|
std::make_shared<CPathfinderNodeStorage>(_out, _hero),
|
||||||
std::make_shared<CNeighbourFinder>(),
|
std::make_shared<CNeighbourFinder>(),
|
||||||
std::vector<std::shared_ptr<IPathfindingRule>>{
|
std::vector<std::shared_ptr<IPathfindingRule>>{
|
||||||
|
std::make_shared<CMovementToDestinationRule>(),
|
||||||
std::make_shared<CMovementCostRule>(),
|
std::make_shared<CMovementCostRule>(),
|
||||||
std::make_shared<CMovementAfterDestinationRule>()
|
std::make_shared<CMovementAfterDestinationRule>()
|
||||||
}))
|
}))
|
||||||
@ -305,9 +302,6 @@ void CPathfinder::calculatePaths()
|
|||||||
if(destination.nodeObject)
|
if(destination.nodeObject)
|
||||||
destination.objectRelations = gs->getPlayerRelations(hero->tempOwner, destination.nodeObject->tempOwner);
|
destination.objectRelations = gs->getPlayerRelations(hero->tempOwner, destination.nodeObject->tempOwner);
|
||||||
|
|
||||||
if(!isMovementToDestPossible())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
destination.action = getDestAction();
|
destination.action = getDestAction();
|
||||||
destination.turn = turn;
|
destination.turn = turn;
|
||||||
destination.movementLeft = movement;
|
destination.movementLeft = movement;
|
||||||
@ -320,7 +314,7 @@ void CPathfinder::calculatePaths()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!destination.blocked && !destination.furtherProcessingImpossible)
|
if(!destination.blocked)
|
||||||
pq.push(destination.node);
|
pq.push(destination.node);
|
||||||
|
|
||||||
} //neighbours loop
|
} //neighbours loop
|
||||||
@ -551,66 +545,94 @@ bool CPathfinder::isLayerTransitionPossible() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::isMovementToDestPossible() const
|
CPathfinderBlockingRule::BlockingReason CMovementToDestinationRule::getBlockingReason(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(destination.node->accessible == CGPathNode::BLOCKED)
|
if(destination.node->accessible == CGPathNode::BLOCKED)
|
||||||
return false;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
|
|
||||||
switch(destination.node->layer)
|
switch(destination.node->layer)
|
||||||
{
|
{
|
||||||
case ELayer::LAND:
|
case EPathfindingLayer::LAND:
|
||||||
if(!hlp->canMoveBetween(source.node->coord, destination.node->coord))
|
if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord))
|
||||||
return false;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
if(isSourceGuarded())
|
|
||||||
|
if(source.guarded)
|
||||||
{
|
{
|
||||||
if(!(config->options.originalMovementRules && source.node->layer == ELayer::AIR) &&
|
if(!(pathfinderConfig->options.originalMovementRules && source.node->layer == EPathfindingLayer::AIR) &&
|
||||||
!isDestinationGuardian()) // Can step into tile of guard
|
!destination.guarded) // Can step into tile of guard
|
||||||
{
|
{
|
||||||
return false;
|
return BlockingReason::SOURCE_GUARDED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ELayer::SAIL:
|
case EPathfindingLayer::SAIL:
|
||||||
if(!hlp->canMoveBetween(source.node->coord, destination.node->coord))
|
if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord))
|
||||||
return false;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
if(isSourceGuarded())
|
|
||||||
|
if(source.guarded)
|
||||||
{
|
{
|
||||||
// Hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
|
// Hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
|
||||||
if(source.node->action != CGPathNode::EMBARK && !isDestinationGuardian())
|
if(source.node->action != CGPathNode::EMBARK && !destination.guarded)
|
||||||
return false;
|
return BlockingReason::SOURCE_GUARDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(source.node->layer == ELayer::LAND)
|
if(source.node->layer == EPathfindingLayer::LAND)
|
||||||
{
|
{
|
||||||
if(!destination.isNodeObjectVisitable())
|
if(!destination.isNodeObjectVisitable())
|
||||||
return false;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
|
|
||||||
if(destination.nodeObject->ID != Obj::BOAT && destination.nodeObject->ID != Obj::HERO)
|
if(destination.nodeObject->ID != Obj::BOAT && destination.nodeObject->ID != Obj::HERO)
|
||||||
return false;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
}
|
}
|
||||||
else if(destination.isNodeObjectVisitable() && destination.nodeObject->ID == Obj::BOAT)
|
else if(destination.isNodeObjectVisitable() && destination.nodeObject->ID == Obj::BOAT)
|
||||||
{
|
{
|
||||||
/// Hero in boat can't visit empty boats
|
/// Hero in boat can't visit empty boats
|
||||||
return false;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ELayer::WATER:
|
case EPathfindingLayer::WATER:
|
||||||
if(!hlp->canMoveBetween(source.node->coord, destination.node->coord) || destination.node->accessible != CGPathNode::ACCESSIBLE)
|
if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord)
|
||||||
return false;
|
|| destination.node->accessible != CGPathNode::ACCESSIBLE)
|
||||||
if(isDestinationGuarded())
|
{
|
||||||
return false;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(destination.guarded)
|
||||||
|
return BlockingReason::DESTINATION_GUARDED;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return BlockingReason::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CMovementAfterDestinationRule::process(
|
void CMovementAfterDestinationRule::process(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * config,
|
||||||
|
CPathfinderHelper * pathfinderHelper)
|
||||||
|
{
|
||||||
|
auto blocker = getBlockingReason(source, destination, config, pathfinderHelper);
|
||||||
|
|
||||||
|
if(blocker == BlockingReason::DESTINATION_GUARDED && destination.action == CGPathNode::ENodeAction::BATTLE)
|
||||||
|
{
|
||||||
|
return; // allow bypass guarded tile but only in direction of guard, a bit UI related thing
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.blocked = blocker != BlockingReason::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPathfinderBlockingRule::BlockingReason CMovementAfterDestinationRule::getBlockingReason(
|
||||||
CPathNodeInfo & source,
|
CPathNodeInfo & source,
|
||||||
CDestinationNodeInfo & destination,
|
CDestinationNodeInfo & destination,
|
||||||
CPathfinderConfig * config,
|
CPathfinderConfig * config,
|
||||||
@ -629,43 +651,48 @@ void CMovementAfterDestinationRule::process(
|
|||||||
{
|
{
|
||||||
/// For now we'll always allow transit over teleporters
|
/// For now we'll always allow transit over teleporters
|
||||||
/// Transit over whirlpools only allowed when hero protected
|
/// Transit over whirlpools only allowed when hero protected
|
||||||
return;
|
return BlockingReason::NONE;
|
||||||
}
|
}
|
||||||
else if(destination.nodeObject->ID == Obj::GARRISON
|
else if(destination.nodeObject->ID == Obj::GARRISON
|
||||||
|| destination.nodeObject->ID == Obj::GARRISON2
|
|| destination.nodeObject->ID == Obj::GARRISON2
|
||||||
|| destination.nodeObject->ID == Obj::BORDER_GATE)
|
|| destination.nodeObject->ID == Obj::BORDER_GATE)
|
||||||
{
|
{
|
||||||
/// Transit via unguarded garrisons is always possible
|
/// Transit via unguarded garrisons is always possible
|
||||||
return;
|
return BlockingReason::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
return BlockingReason::DESTINATION_VISIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CGPathNode::BLOCKING_VISIT:
|
||||||
|
return destination.guarded
|
||||||
|
? BlockingReason::DESTINATION_GUARDED
|
||||||
|
: BlockingReason::DESTINATION_BLOCKVIS;
|
||||||
|
|
||||||
case CGPathNode::NORMAL:
|
case CGPathNode::NORMAL:
|
||||||
return;
|
return BlockingReason::NONE;
|
||||||
|
|
||||||
case CGPathNode::EMBARK:
|
case CGPathNode::EMBARK:
|
||||||
if(pathfinderHelper->options.useEmbarkAndDisembark)
|
if(pathfinderHelper->options.useEmbarkAndDisembark)
|
||||||
return;
|
return BlockingReason::NONE;
|
||||||
|
|
||||||
break;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
|
|
||||||
case CGPathNode::DISEMBARK:
|
case CGPathNode::DISEMBARK:
|
||||||
if(pathfinderHelper->options.useEmbarkAndDisembark && !destination.guarded)
|
if(pathfinderHelper->options.useEmbarkAndDisembark && !destination.guarded)
|
||||||
return;
|
return BlockingReason::NONE;
|
||||||
|
|
||||||
break;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
|
|
||||||
case CGPathNode::BATTLE:
|
case CGPathNode::BATTLE:
|
||||||
/// Movement after BATTLE action only possible from guarded tile to guardian tile
|
/// Movement after BATTLE action only possible from guarded tile to guardian tile
|
||||||
if(destination.guarded)
|
if(destination.guarded)
|
||||||
return;
|
return BlockingReason::NONE;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
destination.furtherProcessingImpossible = true;
|
return BlockingReason::DESTINATION_BLOCKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGPathNode::ENodeAction CPathfinder::getDestAction() const
|
CGPathNode::ENodeAction CPathfinder::getDestAction() const
|
||||||
@ -1401,7 +1428,7 @@ void CPathNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObje
|
|||||||
}
|
}
|
||||||
|
|
||||||
CDestinationNodeInfo::CDestinationNodeInfo()
|
CDestinationNodeInfo::CDestinationNodeInfo()
|
||||||
: CPathNodeInfo(), blocked(false), furtherProcessingImpossible(false), action(CGPathNode::ENodeAction::UNKNOWN)
|
: CPathNodeInfo(), blocked(false), action(CGPathNode::ENodeAction::UNKNOWN)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1410,7 +1437,6 @@ void CDestinationNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool exclude
|
|||||||
CPathNodeInfo::setNode(gs, n, excludeTopObject);
|
CPathNodeInfo::setNode(gs, n, excludeTopObject);
|
||||||
|
|
||||||
blocked = false;
|
blocked = false;
|
||||||
furtherProcessingImpossible = false;
|
|
||||||
action = CGPathNode::ENodeAction::UNKNOWN;
|
action = CGPathNode::ENodeAction::UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,6 @@ struct DLL_LINKAGE CDestinationNodeInfo : public CPathNodeInfo
|
|||||||
CGPathNode::ENodeAction action;
|
CGPathNode::ENodeAction action;
|
||||||
int turn;
|
int turn;
|
||||||
int movementLeft;
|
int movementLeft;
|
||||||
bool furtherProcessingImpossible;
|
|
||||||
bool blocked;
|
bool blocked;
|
||||||
|
|
||||||
CDestinationNodeInfo();
|
CDestinationNodeInfo();
|
||||||
@ -149,7 +148,7 @@ public:
|
|||||||
CPathfinderHelper * pathfinderHelper) = 0;
|
CPathfinderHelper * pathfinderHelper) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CMovementAfterDestinationRule : public IPathfindingRule
|
class CMovementCostRule : public IPathfindingRule
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void process(
|
virtual void process(
|
||||||
@ -159,6 +158,66 @@ public:
|
|||||||
CPathfinderHelper * pathfinderHelper) override;
|
CPathfinderHelper * pathfinderHelper) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CPathfinderBlockingRule : public IPathfindingRule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void process(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) override
|
||||||
|
{
|
||||||
|
auto blockingReason = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
||||||
|
|
||||||
|
destination.blocked = blockingReason != BlockingReason::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class BlockingReason
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
SOURCE_GUARDED = 1,
|
||||||
|
DESTINATION_GUARDED = 2,
|
||||||
|
SOURCE_BLOCKED = 3,
|
||||||
|
DESTINATION_BLOCKED = 4,
|
||||||
|
DESTINATION_BLOCKVIS = 5,
|
||||||
|
DESTINATION_VISIT = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual BlockingReason getBlockingReason(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CMovementAfterDestinationRule : public CPathfinderBlockingRule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void process(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual BlockingReason getBlockingReason(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CMovementToDestinationRule : public CPathfinderBlockingRule
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual BlockingReason getBlockingReason(
|
||||||
|
CPathNodeInfo & source,
|
||||||
|
CDestinationNodeInfo & destination,
|
||||||
|
CPathfinderConfig * pathfinderConfig,
|
||||||
|
CPathfinderHelper * pathfinderHelper) override;
|
||||||
|
};
|
||||||
|
|
||||||
class CNeighbourFinder
|
class CNeighbourFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -299,7 +358,6 @@ private:
|
|||||||
|
|
||||||
bool isLayerTransitionPossible(const ELayer dstLayer) const;
|
bool isLayerTransitionPossible(const ELayer dstLayer) const;
|
||||||
bool isLayerTransitionPossible() const;
|
bool isLayerTransitionPossible() const;
|
||||||
bool isMovementToDestPossible() const;
|
|
||||||
CGPathNode::ENodeAction getDestAction() const;
|
CGPathNode::ENodeAction getDestAction() const;
|
||||||
CGPathNode::ENodeAction getTeleportDestAction() const;
|
CGPathNode::ENodeAction getTeleportDestAction() const;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user