mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
AI: first rule extracted for pathfinder
This commit is contained in:
parent
9af0032f84
commit
6ac987794c
@ -597,3 +597,14 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos)
|
||||||
|
{
|
||||||
|
auto pathInfo = cb->getPathsInfo(hero)->getPathInfo(pos);
|
||||||
|
uint32_t totalMovementPoints = pathInfo->turns * hero->maxMovePoints(true) + hero->movement;
|
||||||
|
|
||||||
|
if(totalMovementPoints < pathInfo->moveRemains) // should not be but who knows
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return totalMovementPoints - pathInfo->moveRemains;
|
||||||
|
}
|
@ -176,6 +176,7 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
|
|||||||
ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGTownInstance * t);
|
ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGTownInstance * t);
|
||||||
ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t);
|
ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t);
|
||||||
int3 whereToExplore(HeroPtr h);
|
int3 whereToExplore(HeroPtr h);
|
||||||
|
uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos);
|
||||||
|
|
||||||
class CDistanceSorter
|
class CDistanceSorter
|
||||||
{
|
{
|
||||||
|
@ -25,20 +25,20 @@ bool canSeeObj(const CGObjectInstance * obj)
|
|||||||
return obj != nullptr && obj->ID != Obj::EVENT;
|
return obj != nullptr && obj->ID != Obj::EVENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
CNeighbourFinder::CNeighbourFinder(CPathfinder * pathfinder)
|
CNeighbourFinder::CNeighbourFinder()
|
||||||
:pathfinder(pathfinder), neighbours(), neighbourTiles(), accessibleNeighbourTiles()
|
:neighbours(), neighbourTiles(), accessibleNeighbourTiles()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> & CNeighbourFinder::calculateNeighbours()
|
std::vector<CGPathNode *> & CNeighbourFinder::calculateNeighbours(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper, CNodeHelper * nodeHelper)
|
||||||
{
|
{
|
||||||
addNeighbourTiles();
|
addNeighbourTiles(source, 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 = pathfinder->nodeHelper->getNode(neighbour, i);
|
auto node = nodeHelper->getNode(neighbour, i);
|
||||||
|
|
||||||
if(node->accessible == CGPathNode::NOT_SET)
|
if(node->accessible == CGPathNode::NOT_SET)
|
||||||
continue;
|
continue;
|
||||||
@ -50,19 +50,24 @@ std::vector<CGPathNode *> & CNeighbourFinder::calculateNeighbours()
|
|||||||
return neighbours;
|
return neighbours;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNeighbourFinder::addNeighbourTiles()
|
void CNeighbourFinder::addNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
neighbourTiles.clear();
|
neighbourTiles.clear();
|
||||||
accessibleNeighbourTiles.clear();
|
accessibleNeighbourTiles.clear();
|
||||||
neighbours.clear();
|
neighbours.clear();
|
||||||
|
|
||||||
pathfinder->populateNeighbourTiles(neighbourTiles);
|
pathfinderHelper->getNeighbours(
|
||||||
|
*source.tile,
|
||||||
|
source.node->coord,
|
||||||
|
neighbourTiles,
|
||||||
|
boost::logic::indeterminate,
|
||||||
|
source.node->layer == EPathfindingLayer::SAIL);
|
||||||
|
|
||||||
if(pathfinder->source.isNodeObjectVisitable())
|
if(source.isNodeObjectVisitable())
|
||||||
{
|
{
|
||||||
for(int3 tile : neighbourTiles)
|
for(int3 tile : neighbourTiles)
|
||||||
{
|
{
|
||||||
if(pathfinder->canMoveBetween(tile, pathfinder->source.nodeObject->visitablePos()))
|
if(pathfinderHelper->canMoveBetween(tile, source.nodeObject->visitablePos()))
|
||||||
accessibleNeighbourTiles.push_back(tile);
|
accessibleNeighbourTiles.push_back(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,7 +104,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CPathfinder::PathfinderOptions::PathfinderOptions()
|
PathfinderOptions::PathfinderOptions()
|
||||||
{
|
{
|
||||||
useFlying = settings["pathfinder"]["layers"]["flying"].Bool();
|
useFlying = settings["pathfinder"]["layers"]["flying"].Bool();
|
||||||
useWaterWalking = settings["pathfinder"]["layers"]["waterWalking"].Bool();
|
useWaterWalking = settings["pathfinder"]["layers"]["waterWalking"].Bool();
|
||||||
@ -125,7 +130,7 @@ CPathfinder::CPathfinder(
|
|||||||
_gs,
|
_gs,
|
||||||
_hero,
|
_hero,
|
||||||
std::make_shared<CPathfinderNodeHelper>(_out, _hero),
|
std::make_shared<CPathfinderNodeHelper>(_out, _hero),
|
||||||
std::make_shared<CNeighbourFinder>(this))
|
std::make_shared<CNeighbourFinder>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,9 +150,7 @@ CPathfinder::CPathfinder(
|
|||||||
assert(hero);
|
assert(hero);
|
||||||
assert(hero == getHero(hero->id));
|
assert(hero == getHero(hero->id));
|
||||||
|
|
||||||
destAction = CGPathNode::UNKNOWN;
|
hlp = make_unique<CPathfinderHelper>(_gs, hero, options);
|
||||||
|
|
||||||
hlp = make_unique<CPathfinderHelper>(hero, options);
|
|
||||||
|
|
||||||
initializePatrol();
|
initializePatrol();
|
||||||
initializeGraph();
|
initializeGraph();
|
||||||
@ -222,7 +225,7 @@ void CPathfinder::calculatePaths()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//add accessible neighbouring nodes to the queue
|
//add accessible neighbouring nodes to the queue
|
||||||
auto neighbourNodes = neighbourFinder->calculateNeighbours();
|
auto neighbourNodes = neighbourFinder->calculateNeighbours(source, hlp.get(), nodeHelper.get());
|
||||||
for(auto & neighbour : neighbourNodes)
|
for(auto & neighbour : neighbourNodes)
|
||||||
{
|
{
|
||||||
destination.setNode(gs, neighbour);
|
destination.setNode(gs, neighbour);
|
||||||
@ -244,26 +247,29 @@ void CPathfinder::calculatePaths()
|
|||||||
if(source.node->layer != destination.node->layer && !isLayerTransitionPossible())
|
if(source.node->layer != destination.node->layer && !isLayerTransitionPossible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
destination.guarded = isDestinationGuarded();
|
||||||
|
|
||||||
if(!isMovementToDestPossible())
|
if(!isMovementToDestPossible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
destAction = getDestAction();
|
destination.action = getDestAction();
|
||||||
|
|
||||||
int turnAtNextTile = turn, moveAtNextTile = movement;
|
int turnAtNextTile = turn, moveAtNextTile = movement;
|
||||||
int cost = CPathfinderHelper::getMovementCost(hero, source, destination, moveAtNextTile, hlp->getTurnInfo());
|
int cost = hlp->getMovementCost(source, destination, moveAtNextTile);
|
||||||
int remains = moveAtNextTile - cost;
|
int remains = moveAtNextTile - cost;
|
||||||
if(remains < 0)
|
if(remains < 0)
|
||||||
{
|
{
|
||||||
//occurs rarely, when hero with low movepoints tries to leave the road
|
//occurs rarely, when hero with low movepoints tries to leave the road
|
||||||
hlp->updateTurnInfo(++turnAtNextTile);
|
hlp->updateTurnInfo(++turnAtNextTile);
|
||||||
moveAtNextTile = hlp->getMaxMovePoints(destination.node->layer);
|
moveAtNextTile = hlp->getMaxMovePoints(destination.node->layer);
|
||||||
cost = CPathfinderHelper::getMovementCost(hero, source, destination, moveAtNextTile, hlp->getTurnInfo()); //cost must be updated, movement points changed :(
|
cost = hlp->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
|
||||||
remains = moveAtNextTile - cost;
|
remains = moveAtNextTile - cost;
|
||||||
}
|
}
|
||||||
if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK)
|
if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK)
|
||||||
{
|
{
|
||||||
/// FREE_SHIP_BOARDING bonus only remove additional penalty
|
/// FREE_SHIP_BOARDING bonus only remove additional penalty
|
||||||
/// land <-> sail transition still cost movement points as normal movement
|
/// land <-> sail transition still cost movement points as normal movement
|
||||||
remains = hero->movementPointsAfterEmbark(moveAtNextTile, cost, destAction - 1, hlp->getTurnInfo());
|
remains = hero->movementPointsAfterEmbark(moveAtNextTile, cost, destination.action - 1, hlp->getTurnInfo());
|
||||||
cost = moveAtNextTile - remains;
|
cost = moveAtNextTile - remains;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,10 +280,11 @@ void CPathfinder::calculatePaths()
|
|||||||
destination.node->moveRemains = remains;
|
destination.node->moveRemains = remains;
|
||||||
destination.node->turns = turnAtNextTile;
|
destination.node->turns = turnAtNextTile;
|
||||||
destination.node->theNodeBefore = source.node;
|
destination.node->theNodeBefore = source.node;
|
||||||
destination.node->action = destAction;
|
destination.node->action = destination.action;
|
||||||
|
|
||||||
|
CMovementAfterDestinationRule rl = CMovementAfterDestinationRule();
|
||||||
|
rl.process(hlp.get(), source, destination);
|
||||||
|
|
||||||
// TODO: move this method to a separete module kind of IPathfinderRule.process(). And all above.
|
|
||||||
checkMovementAfterDestPossible();
|
|
||||||
if(!destination.furtherProcessingImpossible)
|
if(!destination.furtherProcessingImpossible)
|
||||||
pq.push(destination.node);
|
pq.push(destination.node);
|
||||||
}
|
}
|
||||||
@ -313,17 +320,6 @@ void CPathfinder::calculatePaths()
|
|||||||
} //queue loop
|
} //queue loop
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPathfinder::populateNeighbourTiles(std::vector<int3> & neighbourTiles)
|
|
||||||
{
|
|
||||||
CPathfinderHelper::getNeighbours(
|
|
||||||
gs->map,
|
|
||||||
*source.tile,
|
|
||||||
source.node->coord,
|
|
||||||
neighbourTiles,
|
|
||||||
boost::logic::indeterminate,
|
|
||||||
source.node->layer == EPathfindingLayer::SAIL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPathfinder::addTeleportExits()
|
void CPathfinder::addTeleportExits()
|
||||||
{
|
{
|
||||||
neighbours.clear();
|
neighbours.clear();
|
||||||
@ -333,7 +329,7 @@ void CPathfinder::addTeleportExits()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(source.nodeObject);
|
const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(source.nodeObject);
|
||||||
if(isAllowedTeleportEntrance(objTeleport))
|
if(hlp->isAllowedTeleportEntrance(objTeleport))
|
||||||
{
|
{
|
||||||
for(auto objId : getTeleportChannelExits(objTeleport->channel, hero->tempOwner))
|
for(auto objId : getTeleportChannelExits(objTeleport->channel, hero->tempOwner))
|
||||||
{
|
{
|
||||||
@ -497,7 +493,7 @@ bool CPathfinder::isMovementToDestPossible() const
|
|||||||
switch(destination.node->layer)
|
switch(destination.node->layer)
|
||||||
{
|
{
|
||||||
case ELayer::LAND:
|
case ELayer::LAND:
|
||||||
if(!canMoveBetween(source.node->coord, destination.node->coord))
|
if(!hlp->canMoveBetween(source.node->coord, destination.node->coord))
|
||||||
return false;
|
return false;
|
||||||
if(isSourceGuarded())
|
if(isSourceGuarded())
|
||||||
{
|
{
|
||||||
@ -511,7 +507,7 @@ bool CPathfinder::isMovementToDestPossible() const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ELayer::SAIL:
|
case ELayer::SAIL:
|
||||||
if(!canMoveBetween(source.node->coord, destination.node->coord))
|
if(!hlp->canMoveBetween(source.node->coord, destination.node->coord))
|
||||||
return false;
|
return false;
|
||||||
if(isSourceGuarded())
|
if(isSourceGuarded())
|
||||||
{
|
{
|
||||||
@ -537,7 +533,7 @@ bool CPathfinder::isMovementToDestPossible() const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ELayer::WATER:
|
case ELayer::WATER:
|
||||||
if(!canMoveBetween(source.node->coord, destination.node->coord) || destination.node->accessible != CGPathNode::ACCESSIBLE)
|
if(!hlp->canMoveBetween(source.node->coord, destination.node->coord) || destination.node->accessible != CGPathNode::ACCESSIBLE)
|
||||||
return false;
|
return false;
|
||||||
if(isDestinationGuarded())
|
if(isDestinationGuarded())
|
||||||
return false;
|
return false;
|
||||||
@ -548,9 +544,9 @@ bool CPathfinder::isMovementToDestPossible() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPathfinder::checkMovementAfterDestPossible()
|
void CMovementAfterDestinationRule::process(CPathfinderHelper * pathfinderHelper, CPathNodeInfo & source, CDestinationNodeInfo & destination)
|
||||||
{
|
{
|
||||||
switch(destAction)
|
switch(destination.action)
|
||||||
{
|
{
|
||||||
/// TODO: Investigate what kind of limitation is possible to apply on movement from visitable tiles
|
/// TODO: Investigate what kind of limitation is possible to apply on movement from visitable tiles
|
||||||
/// Likely in many cases we don't need to add visitable tile to queue when hero don't fly
|
/// Likely in many cases we don't need to add visitable tile to queue when hero don't fly
|
||||||
@ -559,14 +555,14 @@ void CPathfinder::checkMovementAfterDestPossible()
|
|||||||
/// For now we only add visitable tile into queue when it's teleporter that allow transit
|
/// For now we only add visitable tile into queue when it's teleporter that allow transit
|
||||||
/// Movement from visitable tile when hero is standing on it is possible into any layer
|
/// Movement from visitable tile when hero is standing on it is possible into any layer
|
||||||
const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(destination.nodeObject);
|
const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(destination.nodeObject);
|
||||||
if(isAllowedTeleportEntrance(objTeleport))
|
if(pathfinderHelper->isAllowedTeleportEntrance(objTeleport))
|
||||||
{
|
{
|
||||||
/// 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;
|
||||||
}
|
}
|
||||||
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
|
||||||
@ -580,20 +576,20 @@ void CPathfinder::checkMovementAfterDestPossible()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case CGPathNode::EMBARK:
|
case CGPathNode::EMBARK:
|
||||||
if(options.useEmbarkAndDisembark)
|
if(pathfinderHelper->options.useEmbarkAndDisembark)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CGPathNode::DISEMBARK:
|
case CGPathNode::DISEMBARK:
|
||||||
if(options.useEmbarkAndDisembark && !isDestinationGuarded())
|
if(pathfinderHelper->options.useEmbarkAndDisembark && !destination.guarded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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(isDestinationGuarded())
|
if(destination.guarded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -643,7 +639,7 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const
|
|||||||
{
|
{
|
||||||
if(destination.nodeObject->passableFor(hero->tempOwner))
|
if(destination.nodeObject->passableFor(hero->tempOwner))
|
||||||
{
|
{
|
||||||
if(isDestinationGuarded(true))
|
if(isDestinationGuarded())
|
||||||
action = CGPathNode::BATTLE;
|
action = CGPathNode::BATTLE;
|
||||||
}
|
}
|
||||||
else if(objRel == PlayerRelations::ENEMIES)
|
else if(objRel == PlayerRelations::ENEMIES)
|
||||||
@ -653,7 +649,7 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const
|
|||||||
{
|
{
|
||||||
if(destination.nodeObject->passableFor(hero->tempOwner))
|
if(destination.nodeObject->passableFor(hero->tempOwner))
|
||||||
{
|
{
|
||||||
if(isDestinationGuarded(true))
|
if(isDestinationGuarded())
|
||||||
action = CGPathNode::BATTLE;
|
action = CGPathNode::BATTLE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -716,17 +712,11 @@ bool CPathfinder::isSourceGuarded() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::isDestinationGuarded(const bool ignoreAccessibility) const
|
bool CPathfinder::isDestinationGuarded() const
|
||||||
{
|
{
|
||||||
/// isDestinationGuarded is exception needed for garrisons.
|
/// isDestinationGuarded is exception needed for garrisons.
|
||||||
/// When monster standing behind garrison it's visitable and guarded at the same time.
|
/// When monster standing behind garrison it's visitable and guarded at the same time.
|
||||||
if(gs->guardingCreaturePosition(destination.node->coord).valid()
|
return gs->guardingCreaturePosition(destination.node->coord).valid();
|
||||||
&& (ignoreAccessibility || destination.node->accessible == CGPathNode::BLOCKVIS))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::isDestinationGuardian() const
|
bool CPathfinder::isDestinationGuardian() const
|
||||||
@ -855,12 +845,12 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const int3 & pos,
|
|||||||
return CGPathNode::ACCESSIBLE;
|
return CGPathNode::ACCESSIBLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::canMoveBetween(const int3 & a, const int3 & b) const
|
bool CPathfinderHelper::canMoveBetween(const int3 & a, const int3 & b) const
|
||||||
{
|
{
|
||||||
return gs->checkForVisitableDir(a, b);
|
return gs->checkForVisitableDir(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::isAllowedTeleportEntrance(const CGTeleport * obj) const
|
bool CPathfinderHelper::isAllowedTeleportEntrance(const CGTeleport * obj) const
|
||||||
{
|
{
|
||||||
if(!obj || !isTeleportEntrancePassable(obj, hero->tempOwner))
|
if(!obj || !isTeleportEntrancePassable(obj, hero->tempOwner))
|
||||||
return false;
|
return false;
|
||||||
@ -877,12 +867,12 @@ bool CPathfinder::isAllowedTeleportEntrance(const CGTeleport * obj) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::addTeleportTwoWay(const CGTeleport * obj) const
|
bool CPathfinderHelper::addTeleportTwoWay(const CGTeleport * obj) const
|
||||||
{
|
{
|
||||||
return options.useTeleportTwoWay && isTeleportChannelBidirectional(obj->channel, hero->tempOwner);
|
return options.useTeleportTwoWay && isTeleportChannelBidirectional(obj->channel, hero->tempOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::addTeleportOneWay(const CGTeleport * obj) const
|
bool CPathfinderHelper::addTeleportOneWay(const CGTeleport * obj) const
|
||||||
{
|
{
|
||||||
if(options.useTeleportOneWay && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
|
if(options.useTeleportOneWay && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
|
||||||
{
|
{
|
||||||
@ -893,7 +883,7 @@ bool CPathfinder::addTeleportOneWay(const CGTeleport * obj) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::addTeleportOneWayRandom(const CGTeleport * obj) const
|
bool CPathfinderHelper::addTeleportOneWayRandom(const CGTeleport * obj) const
|
||||||
{
|
{
|
||||||
if(options.useTeleportOneWayRandom && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
|
if(options.useTeleportOneWayRandom && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
|
||||||
{
|
{
|
||||||
@ -904,9 +894,9 @@ bool CPathfinder::addTeleportOneWayRandom(const CGTeleport * obj) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const
|
bool CPathfinderHelper::addTeleportWhirlpool(const CGWhirlpool * obj) const
|
||||||
{
|
{
|
||||||
return options.useTeleportWhirlpool && hlp->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
|
return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
TurnInfo::BonusCache::BonusCache(TBonusListPtr bl)
|
TurnInfo::BonusCache::BonusCache(TBonusListPtr bl)
|
||||||
@ -997,8 +987,8 @@ int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
|
|||||||
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero, const CPathfinder::PathfinderOptions & Options)
|
CPathfinderHelper::CPathfinderHelper(CGameState * gs, const CGHeroInstance * Hero, const PathfinderOptions & Options)
|
||||||
: turn(-1), hero(Hero), options(Options)
|
: CGameInfoCallback(gs, boost::optional<PlayerColor>()), turn(-1), hero(Hero), options(Options)
|
||||||
{
|
{
|
||||||
turnsInfo.reserve(16);
|
turnsInfo.reserve(16);
|
||||||
updateTurnInfo();
|
updateTurnInfo();
|
||||||
@ -1058,8 +1048,10 @@ int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const
|
|||||||
return turnsInfo[turn]->getMaxMovePoints(layer);
|
return turnsInfo[turn]->getMaxMovePoints(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPathfinderHelper::getNeighbours(const CMap * map, const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing)
|
void CPathfinderHelper::getNeighbours(const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing)
|
||||||
{
|
{
|
||||||
|
CMap * map = gs->map;
|
||||||
|
|
||||||
static const int3 dirs[] = {
|
static const int3 dirs[] = {
|
||||||
int3(-1, +1, +0), int3(0, +1, +0), int3(+1, +1, +0),
|
int3(-1, +1, +0), int3(0, +1, +0), int3(+1, +1, +0),
|
||||||
int3(-1, +0, +0), /* source pos */ int3(+1, +0, +0),
|
int3(-1, +0, +0), /* source pos */ int3(+1, +0, +0),
|
||||||
@ -1101,28 +1093,23 @@ void CPathfinderHelper::getNeighbours(const CMap * map, const TerrainTile & srct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & src, const int3 & dst, const TerrainTile * ct, const TerrainTile * dt, const int remainingMovePoints, const TurnInfo * ti, const bool checkLast)
|
int CPathfinderHelper::getMovementCost(const int3 & src, const int3 & dst, const TerrainTile * ct, const TerrainTile * dt, const int remainingMovePoints, const bool checkLast)
|
||||||
{
|
{
|
||||||
if(src == dst) //same tile
|
if(src == dst) //same tile
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bool localTi = false;
|
auto ti = getTurnInfo();
|
||||||
if(!ti)
|
|
||||||
{
|
|
||||||
localTi = true;
|
|
||||||
ti = new TurnInfo(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ct == nullptr || dt == nullptr)
|
if(ct == nullptr || dt == nullptr)
|
||||||
{
|
{
|
||||||
ct = h->cb->getTile(src);
|
ct = hero->cb->getTile(src);
|
||||||
dt = h->cb->getTile(dst);
|
dt = hero->cb->getTile(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: by the original game rules hero shouldn't be affected by terrain penalty while flying.
|
/// TODO: by the original game rules hero shouldn't be affected by terrain penalty while flying.
|
||||||
/// Also flying movement only has penalty when player moving over blocked tiles.
|
/// Also flying movement only has penalty when player moving over blocked tiles.
|
||||||
/// So if you only have base flying with 40% penalty you can still ignore terrain penalty while having zero flying penalty.
|
/// So if you only have base flying with 40% penalty you can still ignore terrain penalty while having zero flying penalty.
|
||||||
int ret = h->getTileCost(*dt, *ct, ti);
|
int ret = hero->getTileCost(*dt, *ct, ti);
|
||||||
/// Unfortunately this can't be implemented yet as server don't know when player flying and when he's not.
|
/// Unfortunately this can't be implemented yet as server don't know when player flying and when he's not.
|
||||||
/// Difference in cost calculation on client and server is much worse than incorrect cost.
|
/// Difference in cost calculation on client and server is much worse than incorrect cost.
|
||||||
/// So this one is waiting till server going to use pathfinder rules for path validation.
|
/// So this one is waiting till server going to use pathfinder rules for path validation.
|
||||||
@ -1133,9 +1120,9 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr
|
|||||||
}
|
}
|
||||||
else if(dt->terType == ETerrainType::WATER)
|
else if(dt->terType == ETerrainType::WATER)
|
||||||
{
|
{
|
||||||
if(h->boat && ct->hasFavorableWinds() && dt->hasFavorableWinds())
|
if(hero->boat && ct->hasFavorableWinds() && dt->hasFavorableWinds())
|
||||||
ret *= 0.666;
|
ret *= 0.666;
|
||||||
else if(!h->boat && ti->hasBonusOfType(Bonus::WATER_WALKING))
|
else if(!hero->boat && ti->hasBonusOfType(Bonus::WATER_WALKING))
|
||||||
{
|
{
|
||||||
ret *= (100.0 + ti->valOfBonuses(Bonus::WATER_WALKING)) / 100.0;
|
ret *= (100.0 + ti->valOfBonuses(Bonus::WATER_WALKING)) / 100.0;
|
||||||
}
|
}
|
||||||
@ -1148,9 +1135,6 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr
|
|||||||
//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
|
//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
|
||||||
if(ret > remainingMovePoints && remainingMovePoints >= old)
|
if(ret > remainingMovePoints && remainingMovePoints >= old)
|
||||||
{
|
{
|
||||||
if(localTi)
|
|
||||||
delete ti;
|
|
||||||
|
|
||||||
return remainingMovePoints;
|
return remainingMovePoints;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1162,32 +1146,21 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr
|
|||||||
{
|
{
|
||||||
std::vector<int3> vec;
|
std::vector<int3> vec;
|
||||||
vec.reserve(8); //optimization
|
vec.reserve(8); //optimization
|
||||||
getNeighbours(h->cb->gameState()->map, *dt, dst, vec, ct->terType != ETerrainType::WATER, true);
|
getNeighbours(*dt, dst, vec, ct->terType != ETerrainType::WATER, true);
|
||||||
for(auto & elem : vec)
|
for(auto & elem : vec)
|
||||||
{
|
{
|
||||||
int fcost = getMovementCost(h, dst, elem, nullptr, nullptr, left, ti, false);
|
int fcost = getMovementCost(dst, elem, nullptr, nullptr, left, false);
|
||||||
if(fcost <= left)
|
if(fcost <= left)
|
||||||
{
|
{
|
||||||
if(localTi)
|
|
||||||
delete ti;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = remainingMovePoints;
|
ret = remainingMovePoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(localTi)
|
|
||||||
delete ti;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & dst)
|
|
||||||
{
|
|
||||||
return getMovementCost(h, h->visitablePos(), dst, nullptr, nullptr, h->movement);
|
|
||||||
}
|
|
||||||
|
|
||||||
CGPathNode::CGPathNode()
|
CGPathNode::CGPathNode()
|
||||||
: coord(int3(-1, -1, -1)), layer(ELayer::WRONG)
|
: coord(int3(-1, -1, -1)), layer(ELayer::WRONG)
|
||||||
{
|
{
|
||||||
@ -1308,7 +1281,7 @@ CGPathNode * CPathsInfo::getNode(const int3 & coord, const ELayer layer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CPathNodeInfo::CPathNodeInfo()
|
CPathNodeInfo::CPathNodeInfo()
|
||||||
: node(nullptr), nodeObject(nullptr), tile(nullptr), coord(-1, -1, -1), blocked(false), furtherProcessingImpossible(false)
|
: node(nullptr), nodeObject(nullptr), tile(nullptr), coord(-1, -1, -1), guarded(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1325,8 +1298,21 @@ void CPathNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObje
|
|||||||
nodeObject = tile->topVisitableObj(excludeTopObject);
|
nodeObject = tile->topVisitableObj(excludeTopObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guarded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDestinationNodeInfo::CDestinationNodeInfo()
|
||||||
|
: CPathNodeInfo(), blocked(false), furtherProcessingImpossible(false), action(CGPathNode::ENodeAction::UNKNOWN)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDestinationNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject)
|
||||||
|
{
|
||||||
|
CPathNodeInfo::setNode(gs, n, excludeTopObject);
|
||||||
|
|
||||||
blocked = false;
|
blocked = false;
|
||||||
furtherProcessingImpossible = false;
|
furtherProcessingImpossible = false;
|
||||||
|
action = CGPathNode::ENodeAction::UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathNodeInfo::isNodeObjectVisitable() const
|
bool CPathNodeInfo::isNodeObjectVisitable() const
|
||||||
|
@ -102,16 +102,26 @@ struct DLL_LINKAGE CPathNodeInfo
|
|||||||
const CGObjectInstance * nodeObject;
|
const CGObjectInstance * nodeObject;
|
||||||
const TerrainTile * tile;
|
const TerrainTile * tile;
|
||||||
int3 coord;
|
int3 coord;
|
||||||
bool blocked;
|
bool guarded;
|
||||||
bool furtherProcessingImpossible;
|
|
||||||
|
|
||||||
CPathNodeInfo();
|
CPathNodeInfo();
|
||||||
|
|
||||||
void setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject = false);
|
virtual void setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject = false);
|
||||||
|
|
||||||
bool isNodeObjectVisitable() const;
|
bool isNodeObjectVisitable() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DLL_LINKAGE CDestinationNodeInfo : public CPathNodeInfo
|
||||||
|
{
|
||||||
|
CGPathNode::ENodeAction action;
|
||||||
|
bool furtherProcessingImpossible;
|
||||||
|
bool blocked;
|
||||||
|
|
||||||
|
CDestinationNodeInfo();
|
||||||
|
|
||||||
|
virtual void setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject = false) override;
|
||||||
|
};
|
||||||
|
|
||||||
class CNodeHelper
|
class CNodeHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -119,8 +129,21 @@ public:
|
|||||||
virtual CGPathNode * getInitialNode() = 0;
|
virtual CGPathNode * getInitialNode() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CPathfinderHelper;
|
||||||
class CPathfinder;
|
class CPathfinder;
|
||||||
|
|
||||||
|
class IPathfindingRule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void process(CPathfinderHelper * pathfinderHelper, CPathNodeInfo & source, CDestinationNodeInfo & destination) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CMovementAfterDestinationRule : public IPathfindingRule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void process(CPathfinderHelper * pathfinderHelper, CPathNodeInfo & source, CDestinationNodeInfo & destination) override;
|
||||||
|
};
|
||||||
|
|
||||||
class CNeighbourFinder
|
class CNeighbourFinder
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@ -130,11 +153,62 @@ protected:
|
|||||||
std::vector<CGPathNode *> neighbours;
|
std::vector<CGPathNode *> neighbours;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CNeighbourFinder(CPathfinder * pathfinder);
|
CNeighbourFinder();
|
||||||
virtual std::vector<CGPathNode *> & calculateNeighbours();
|
virtual std::vector<CGPathNode *> & calculateNeighbours(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper, CNodeHelper * nodeHelper);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void addNeighbourTiles();
|
void addNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DLL_LINKAGE PathfinderOptions
|
||||||
|
{
|
||||||
|
bool useFlying;
|
||||||
|
bool useWaterWalking;
|
||||||
|
bool useEmbarkAndDisembark;
|
||||||
|
bool useTeleportTwoWay; // Two-way monoliths and Subterranean Gate
|
||||||
|
bool useTeleportOneWay; // One-way monoliths with one known exit only
|
||||||
|
bool useTeleportOneWayRandom; // One-way monoliths with more than one known exit
|
||||||
|
bool useTeleportWhirlpool; // Force enabled if hero protected or unaffected (have one stack of one creature)
|
||||||
|
|
||||||
|
/// TODO: Find out with client and server code, merge with normal teleporters.
|
||||||
|
/// Likely proper implementation would require some refactoring of CGTeleport.
|
||||||
|
/// So for now this is unfinished and disabled by default.
|
||||||
|
bool useCastleGate;
|
||||||
|
|
||||||
|
/// If true transition into air layer only possible from initial node.
|
||||||
|
/// This is drastically decrease path calculation complexity (and time).
|
||||||
|
/// Downside is less MP effective paths calculation.
|
||||||
|
///
|
||||||
|
/// TODO: If this option end up useful for slow devices it's can be improved:
|
||||||
|
/// - Allow transition into air layer not only from initial position, but also from teleporters.
|
||||||
|
/// Movement into air can be also allowed when hero disembarked.
|
||||||
|
/// - Other idea is to allow transition into air within certain radius of N tiles around hero.
|
||||||
|
/// Patrol support need similar functionality so it's won't be ton of useless code.
|
||||||
|
/// Such limitation could be useful as it's can be scaled depend on device performance.
|
||||||
|
bool lightweightFlyingMode;
|
||||||
|
|
||||||
|
/// This option enable one turn limitation for flying and water walking.
|
||||||
|
/// So if we're out of MP while cp is blocked or water tile we won't add dest tile to queue.
|
||||||
|
///
|
||||||
|
/// Following imitation is default H3 mechanics, but someone may want to disable it in mods.
|
||||||
|
/// After all this limit should benefit performance on maps with tons of water or blocked tiles.
|
||||||
|
///
|
||||||
|
/// TODO:
|
||||||
|
/// - Behavior when option is disabled not implemented and will lead to crashes.
|
||||||
|
bool oneTurnSpecialLayersLimit;
|
||||||
|
|
||||||
|
/// VCMI have different movement rules to solve flaws original engine has.
|
||||||
|
/// If this option enabled you'll able to do following things in fly:
|
||||||
|
/// - Move from blocked tiles to visitable one
|
||||||
|
/// - Move from guarded tiles to blockvis tiles without being attacked
|
||||||
|
/// - Move from guarded tiles to guarded visitable tiles with being attacked after
|
||||||
|
/// TODO:
|
||||||
|
/// - Option should also allow same tile land <-> air layer transitions.
|
||||||
|
/// Current implementation only allow go into (from) air layer only to neighbour tiles.
|
||||||
|
/// I find it's reasonable limitation, but it's will make some movements more expensive than in H3.
|
||||||
|
bool originalMovementRules;
|
||||||
|
|
||||||
|
PathfinderOptions();
|
||||||
};
|
};
|
||||||
|
|
||||||
class CPathfinder : private CGameInfoCallback
|
class CPathfinder : private CGameInfoCallback
|
||||||
@ -151,60 +225,10 @@ public:
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
struct PathfinderOptions
|
PathfinderOptions options;
|
||||||
{
|
|
||||||
bool useFlying;
|
|
||||||
bool useWaterWalking;
|
|
||||||
bool useEmbarkAndDisembark;
|
|
||||||
bool useTeleportTwoWay; // Two-way monoliths and Subterranean Gate
|
|
||||||
bool useTeleportOneWay; // One-way monoliths with one known exit only
|
|
||||||
bool useTeleportOneWayRandom; // One-way monoliths with more than one known exit
|
|
||||||
bool useTeleportWhirlpool; // Force enabled if hero protected or unaffected (have one stack of one creature)
|
|
||||||
|
|
||||||
/// TODO: Find out with client and server code, merge with normal teleporters.
|
|
||||||
/// Likely proper implementation would require some refactoring of CGTeleport.
|
|
||||||
/// So for now this is unfinished and disabled by default.
|
|
||||||
bool useCastleGate;
|
|
||||||
|
|
||||||
/// If true transition into air layer only possible from initial node.
|
|
||||||
/// This is drastically decrease path calculation complexity (and time).
|
|
||||||
/// Downside is less MP effective paths calculation.
|
|
||||||
///
|
|
||||||
/// TODO: If this option end up useful for slow devices it's can be improved:
|
|
||||||
/// - Allow transition into air layer not only from initial position, but also from teleporters.
|
|
||||||
/// Movement into air can be also allowed when hero disembarked.
|
|
||||||
/// - Other idea is to allow transition into air within certain radius of N tiles around hero.
|
|
||||||
/// Patrol support need similar functionality so it's won't be ton of useless code.
|
|
||||||
/// Such limitation could be useful as it's can be scaled depend on device performance.
|
|
||||||
bool lightweightFlyingMode;
|
|
||||||
|
|
||||||
/// This option enable one turn limitation for flying and water walking.
|
|
||||||
/// So if we're out of MP while cp is blocked or water tile we won't add dest tile to queue.
|
|
||||||
///
|
|
||||||
/// Following imitation is default H3 mechanics, but someone may want to disable it in mods.
|
|
||||||
/// After all this limit should benefit performance on maps with tons of water or blocked tiles.
|
|
||||||
///
|
|
||||||
/// TODO:
|
|
||||||
/// - Behavior when option is disabled not implemented and will lead to crashes.
|
|
||||||
bool oneTurnSpecialLayersLimit;
|
|
||||||
|
|
||||||
/// VCMI have different movement rules to solve flaws original engine has.
|
|
||||||
/// If this option enabled you'll able to do following things in fly:
|
|
||||||
/// - Move from blocked tiles to visitable one
|
|
||||||
/// - Move from guarded tiles to blockvis tiles without being attacked
|
|
||||||
/// - Move from guarded tiles to guarded visitable tiles with being attacked after
|
|
||||||
/// TODO:
|
|
||||||
/// - Option should also allow same tile land <-> air layer transitions.
|
|
||||||
/// Current implementation only allow go into (from) air layer only to neighbour tiles.
|
|
||||||
/// I find it's reasonable limitation, but it's will make some movements more expensive than in H3.
|
|
||||||
bool originalMovementRules;
|
|
||||||
|
|
||||||
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;
|
||||||
@ -236,10 +260,8 @@ public:
|
|||||||
std::vector<int3> neighbours;
|
std::vector<int3> neighbours;
|
||||||
|
|
||||||
CPathNodeInfo source; //current (source) path node -> we took it from the queue
|
CPathNodeInfo source; //current (source) path node -> we took it from the queue
|
||||||
CPathNodeInfo destination; //destination node -> it's a neighbour of source that we consider
|
CDestinationNodeInfo destination; //destination node -> it's a neighbour of source that we consider
|
||||||
CGPathNode::ENodeAction destAction;
|
|
||||||
|
|
||||||
void populateNeighbourTiles(std::vector<int3> & neighbourTiles);
|
|
||||||
void addTeleportExits();
|
void addTeleportExits();
|
||||||
|
|
||||||
bool isHeroPatrolLocked() const;
|
bool isHeroPatrolLocked() const;
|
||||||
@ -248,27 +270,18 @@ public:
|
|||||||
bool isLayerTransitionPossible(const ELayer dstLayer) const;
|
bool isLayerTransitionPossible(const ELayer dstLayer) const;
|
||||||
bool isLayerTransitionPossible() const;
|
bool isLayerTransitionPossible() const;
|
||||||
bool isMovementToDestPossible() const;
|
bool isMovementToDestPossible() const;
|
||||||
void checkMovementAfterDestPossible();
|
|
||||||
CGPathNode::ENodeAction getDestAction() const;
|
CGPathNode::ENodeAction getDestAction() const;
|
||||||
CGPathNode::ENodeAction getTeleportDestAction() const;
|
CGPathNode::ENodeAction getTeleportDestAction() const;
|
||||||
|
|
||||||
bool isSourceInitialPosition() const;
|
bool isSourceInitialPosition() const;
|
||||||
bool isSourceGuarded() const;
|
bool isSourceGuarded() const;
|
||||||
bool isDestinationGuarded(const bool ignoreAccessibility = true) const;
|
bool isDestinationGuarded() const;
|
||||||
bool isDestinationGuardian() const;
|
bool isDestinationGuardian() const;
|
||||||
|
|
||||||
void initializePatrol();
|
void initializePatrol();
|
||||||
void initializeGraph();
|
void initializeGraph();
|
||||||
|
|
||||||
CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const;
|
CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) 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 isAllowedTeleportEntrance(const CGTeleport * obj) const;
|
|
||||||
bool addTeleportTwoWay(const CGTeleport * obj) const;
|
|
||||||
bool addTeleportOneWay(const CGTeleport * obj) const;
|
|
||||||
bool addTeleportOneWayRandom(const CGTeleport * obj) const;
|
|
||||||
bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DLL_LINKAGE TurnInfo
|
struct DLL_LINKAGE TurnInfo
|
||||||
@ -300,10 +313,15 @@ struct DLL_LINKAGE TurnInfo
|
|||||||
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CPathfinderHelper
|
class DLL_LINKAGE CPathfinderHelper : private CGameInfoCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CPathfinderHelper(const CGHeroInstance * Hero, const CPathfinder::PathfinderOptions & Options);
|
int turn;
|
||||||
|
const CGHeroInstance * hero;
|
||||||
|
std::vector<TurnInfo *> turnsInfo;
|
||||||
|
const PathfinderOptions & options;
|
||||||
|
|
||||||
|
CPathfinderHelper(CGameState * gs, const CGHeroInstance * Hero, const PathfinderOptions & Options);
|
||||||
~CPathfinderHelper();
|
~CPathfinderHelper();
|
||||||
void updateTurnInfo(const int turn = 0);
|
void updateTurnInfo(const int turn = 0);
|
||||||
bool isLayerAvailable(const EPathfindingLayer layer) const;
|
bool isLayerAvailable(const EPathfindingLayer layer) const;
|
||||||
@ -311,43 +329,36 @@ public:
|
|||||||
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
|
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
|
||||||
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
int getMaxMovePoints(const EPathfindingLayer layer) const;
|
||||||
|
|
||||||
static void getNeighbours(const CMap * map, const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing);
|
bool isAllowedTeleportEntrance(const CGTeleport * obj) const;
|
||||||
|
bool addTeleportTwoWay(const CGTeleport * obj) const;
|
||||||
|
bool addTeleportOneWay(const CGTeleport * obj) const;
|
||||||
|
bool addTeleportOneWayRandom(const CGTeleport * obj) const;
|
||||||
|
bool addTeleportWhirlpool(const CGWhirlpool * obj) 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)
|
||||||
|
|
||||||
|
void getNeighbours(const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing);
|
||||||
|
|
||||||
static int getMovementCost(
|
int getMovementCost(
|
||||||
const CGHeroInstance * h,
|
|
||||||
const int3 & src,
|
const int3 & src,
|
||||||
const int3 & dst,
|
const int3 & dst,
|
||||||
const TerrainTile * ct,
|
const TerrainTile * ct,
|
||||||
const TerrainTile * dt,
|
const TerrainTile * dt,
|
||||||
const int remainingMovePoints =- 1,
|
const int remainingMovePoints =- 1,
|
||||||
const TurnInfo * ti = nullptr,
|
|
||||||
const bool checkLast = true);
|
const bool checkLast = true);
|
||||||
|
|
||||||
static int getMovementCost(
|
int getMovementCost(
|
||||||
const CGHeroInstance * h,
|
|
||||||
const CPathNodeInfo & src,
|
const CPathNodeInfo & src,
|
||||||
const CPathNodeInfo & dst,
|
const CPathNodeInfo & dst,
|
||||||
const int remainingMovePoints = -1,
|
const int remainingMovePoints = -1,
|
||||||
const TurnInfo * ti = nullptr,
|
|
||||||
const bool checkLast = true)
|
const bool checkLast = true)
|
||||||
{
|
{
|
||||||
return getMovementCost(
|
return getMovementCost(
|
||||||
h,
|
|
||||||
src.coord,
|
src.coord,
|
||||||
dst.coord,
|
dst.coord,
|
||||||
src.tile,
|
src.tile,
|
||||||
dst.tile,
|
dst.tile,
|
||||||
remainingMovePoints,
|
remainingMovePoints,
|
||||||
ti,
|
|
||||||
checkLast
|
checkLast
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getMovementCost(const CGHeroInstance * h, const int3 & dst);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int turn;
|
|
||||||
const CGHeroInstance * hero;
|
|
||||||
std::vector<TurnInfo *> turnsInfo;
|
|
||||||
const CPathfinder::PathfinderOptions & options;
|
|
||||||
};
|
};
|
||||||
|
@ -2158,10 +2158,14 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
|
|||||||
tmh.movePoints = h->movement;
|
tmh.movePoints = h->movement;
|
||||||
|
|
||||||
//check if destination tile is available
|
//check if destination tile is available
|
||||||
auto ti = make_unique<TurnInfo>(h);
|
auto pathfinderHelper = make_unique<CPathfinderHelper>(gs, h, PathfinderOptions());
|
||||||
const bool canFly = ti->hasBonusOfType(Bonus::FLYING_MOVEMENT);
|
|
||||||
const bool canWalkOnSea = ti->hasBonusOfType(Bonus::WATER_WALKING);
|
pathfinderHelper->updateTurnInfo(0);
|
||||||
const int cost = CPathfinderHelper::getMovementCost(h, h->getPosition(), hmpos, nullptr, nullptr, h->movement, ti.get());
|
auto ti = pathfinderHelper->getTurnInfo();
|
||||||
|
|
||||||
|
const bool canFly = pathfinderHelper->hasBonusOfType(Bonus::FLYING_MOVEMENT);
|
||||||
|
const bool canWalkOnSea = pathfinderHelper->hasBonusOfType(Bonus::WATER_WALKING);
|
||||||
|
const int cost = pathfinderHelper->getMovementCost(h->getPosition(), hmpos, nullptr, nullptr, h->movement);
|
||||||
|
|
||||||
//it's a rock or blocked and not visitable tile
|
//it's a rock or blocked and not visitable tile
|
||||||
//OR hero is on land and dest is water and (there is not present only one object - boat)
|
//OR hero is on land and dest is water and (there is not present only one object - boat)
|
||||||
@ -2253,14 +2257,14 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
|
|||||||
|
|
||||||
if (!transit && embarking)
|
if (!transit && embarking)
|
||||||
{
|
{
|
||||||
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false, ti.get());
|
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false, ti);
|
||||||
return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE);
|
return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE);
|
||||||
// In H3 embark ignore guards
|
// In H3 embark ignore guards
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disembarking)
|
if (disembarking)
|
||||||
{
|
{
|
||||||
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true, ti.get());
|
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true, ti);
|
||||||
return doMove(TryMoveHero::DISEMBARK, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE);
|
return doMove(TryMoveHero::DISEMBARK, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user