1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

AI: move teleportation handling to pathfinder helper and neighbour finder

This commit is contained in:
Andrii Danylchenko 2018-08-01 23:21:14 +03:00
parent 6ac987794c
commit 24221f3fb4
2 changed files with 112 additions and 61 deletions

View File

@ -26,13 +26,16 @@ bool canSeeObj(const CGObjectInstance * obj)
}
CNeighbourFinder::CNeighbourFinder()
:neighbours(), neighbourTiles(), accessibleNeighbourTiles()
{
}
std::vector<CGPathNode *> & CNeighbourFinder::calculateNeighbours(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper, CNodeHelper * nodeHelper)
std::vector<CGPathNode *> CNeighbourFinder::calculateNeighbours(
CPathNodeInfo & source,
CPathfinderHelper * pathfinderHelper,
CNodeHelper * nodeHelper) const
{
addNeighbourTiles(source, pathfinderHelper);
std::vector<CGPathNode *> neighbours;
auto accessibleNeighbourTiles = getNeighbourTiles(source, pathfinderHelper);
for(auto & neighbour : accessibleNeighbourTiles)
{
@ -50,11 +53,27 @@ std::vector<CGPathNode *> & CNeighbourFinder::calculateNeighbours(CPathNodeInfo
return neighbours;
}
void CNeighbourFinder::addNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper)
std::vector<CGPathNode *> CNeighbourFinder::calculateTeleportations(
CPathNodeInfo & source,
CPathfinderHelper * pathfinderHelper,
CNodeHelper * nodeHelper) const
{
neighbourTiles.clear();
accessibleNeighbourTiles.clear();
neighbours.clear();
std::vector<CGPathNode *> neighbours;
auto accessibleExits = getTeleportExits(source, pathfinderHelper);
for(auto & neighbour : accessibleExits)
{
auto node = nodeHelper->getNode(neighbour, source.node->layer);
neighbours.push_back(node);
}
return neighbours;
}
std::vector<int3> CNeighbourFinder::getNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const
{
std::vector<int3> neighbourTiles;
pathfinderHelper->getNeighbours(
*source.tile,
@ -65,14 +84,13 @@ void CNeighbourFinder::addNeighbourTiles(CPathNodeInfo & source, CPathfinderHelp
if(source.isNodeObjectVisitable())
{
for(int3 tile : neighbourTiles)
vstd::erase_if(neighbourTiles, [&](int3 tile) -> bool
{
if(pathfinderHelper->canMoveBetween(tile, source.nodeObject->visitablePos()))
accessibleNeighbourTiles.push_back(tile);
}
return !pathfinderHelper->canMoveBetween(tile, source.nodeObject->visitablePos());
});
}
else
vstd::concatenate(accessibleNeighbourTiles, neighbourTiles);
return neighbourTiles;
}
class CPathfinderNodeHelper : public CNodeHelper
@ -154,8 +172,6 @@ CPathfinder::CPathfinder(
initializePatrol();
initializeGraph();
neighbourTiles.reserve(8);
neighbours.reserve(16);
}
void CPathfinder::calculatePaths()
@ -224,9 +240,13 @@ void CPathfinder::calculatePaths()
continue;
}
source.guarded = isSourceGuarded();
if(source.nodeObject)
source.objectRelations = gs->getPlayerRelations(hero->tempOwner, source.nodeObject->tempOwner);
//add accessible neighbouring nodes to the queue
auto neighbourNodes = neighbourFinder->calculateNeighbours(source, hlp.get(), nodeHelper.get());
for(auto & neighbour : neighbourNodes)
for(CGPathNode * neighbour : neighbourNodes)
{
destination.setNode(gs, neighbour);
@ -248,6 +268,8 @@ void CPathfinder::calculatePaths()
continue;
destination.guarded = isDestinationGuarded();
if(destination.nodeObject)
destination.objectRelations = gs->getPlayerRelations(hero->tempOwner, destination.nodeObject->tempOwner);
if(!isMovementToDestPossible())
continue;
@ -276,7 +298,7 @@ void CPathfinder::calculatePaths()
if(isBetterWay(remains, turnAtNextTile) &&
((source.node->turns == turnAtNextTile && remains) || passOneTurnLimitCheck()))
{
assert(dp != source.node->theNodeBefore); //two tiles can't point to each other
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;
@ -291,10 +313,15 @@ void CPathfinder::calculatePaths()
} //neighbours loop
//just add all passable teleport exits
addTeleportExits();
for(auto & neighbour : neighbours)
/// For now we disable teleports usage for patrol movement
/// VCAI not aware about patrol and may stuck while attempt to use teleport
if(!source.isNodeObjectVisitable() || patrolState == PATROL_RADIUS)
continue;
auto teleportationNodes = neighbourFinder->calculateTeleportations(source, hlp.get(), nodeHelper.get());
for(CGPathNode * teleportNode : teleportationNodes)
{
auto teleportNode = nodeHelper->getNode(neighbour, source.node->layer);
if(teleportNode->locked)
continue;
/// TODO: We may consider use invisible exits on FoW border in future
@ -320,50 +347,73 @@ void CPathfinder::calculatePaths()
} //queue loop
}
void CPathfinder::addTeleportExits()
std::vector<int3> CPathfinderHelper::getAllowedTeleportChannelExits(TeleportChannelID channelID) const
{
neighbours.clear();
/// For now we disable teleports usage for patrol movement
/// VCAI not aware about patrol and may stuck while attempt to use teleport
if(!source.isNodeObjectVisitable() || patrolState == PATROL_RADIUS)
return;
std::vector<int3> allowedExits;
const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(source.nodeObject);
if(hlp->isAllowedTeleportEntrance(objTeleport))
for(auto objId : getTeleportChannelExits(channelID, hero->tempOwner))
{
for(auto objId : getTeleportChannelExits(objTeleport->channel, hero->tempOwner))
auto obj = getObj(objId);
if(dynamic_cast<const CGWhirlpool *>(obj))
{
auto obj = getObj(objId);
if(dynamic_cast<const CGWhirlpool *>(obj))
auto pos = obj->getBlockedPos();
for(auto p : pos)
{
auto pos = obj->getBlockedPos();
for(auto p : pos)
{
if(gs->map->getTile(p).topVisitableId() == obj->ID)
neighbours.push_back(p);
}
if(gs->map->getTile(p).topVisitableId() == obj->ID)
allowedExits.push_back(p);
}
else if(CGTeleport::isExitPassable(gs, hero, obj))
neighbours.push_back(obj->visitablePos());
}
else if(CGTeleport::isExitPassable(gs, hero, obj))
allowedExits.push_back(obj->visitablePos());
}
return allowedExits;
}
std::vector<int3> CPathfinderHelper::getCastleGates(CPathNodeInfo & source) const
{
std::vector<int3> allowedExits;
auto towns = getPlayer(hero->tempOwner)->towns;
for(const auto & town : towns)
{
if(town->id != source.nodeObject->id && town->visitingHero == nullptr
&& town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO))
{
allowedExits.push_back(town->visitablePos());
}
}
if(options.useCastleGate
return allowedExits;
}
std::vector<int3> CNeighbourFinder::getTeleportExits(
CPathNodeInfo & source,
CPathfinderHelper * pathfinderHelper) const
{
std::vector<int3> teleportationExits;
const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(source.nodeObject);
if(pathfinderHelper->isAllowedTeleportEntrance(objTeleport))
{
for(auto exit : pathfinderHelper->getAllowedTeleportChannelExits(objTeleport->channel))
{
teleportationExits.push_back(exit);
}
}
else if(pathfinderHelper->options.useCastleGate
&& (source.nodeObject->ID == Obj::TOWN && source.nodeObject->subID == ETownType::INFERNO
&& getPlayerRelations(hero->tempOwner, source.nodeObject->tempOwner) != PlayerRelations::ENEMIES))
&& source.objectRelations != PlayerRelations::ENEMIES))
{
/// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo
/// This may be handy if we allow to use teleportation to friendly towns
auto towns = getPlayer(hero->tempOwner)->towns;
for(const auto & town : towns)
for(auto exit : pathfinderHelper->getCastleGates(source))
{
if(town->id != source.nodeObject->id && town->visitingHero == nullptr
&& town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO))
{
neighbours.push_back(town->visitablePos());
}
teleportationExits.push_back(exit);
}
}
return teleportationExits;
}
bool CPathfinder::isHeroPatrolLocked() const

View File

@ -103,6 +103,7 @@ struct DLL_LINKAGE CPathNodeInfo
const TerrainTile * tile;
int3 coord;
bool guarded;
PlayerRelations::PlayerRelations objectRelations;
CPathNodeInfo();
@ -146,18 +147,21 @@ public:
class CNeighbourFinder
{
protected:
CPathfinder * pathfinder;
std::vector<int3> neighbourTiles;
std::vector<int3> accessibleNeighbourTiles;
std::vector<CGPathNode *> neighbours;
public:
CNeighbourFinder();
virtual std::vector<CGPathNode *> & calculateNeighbours(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper, CNodeHelper * nodeHelper);
virtual std::vector<CGPathNode *> calculateNeighbours(
CPathNodeInfo & source,
CPathfinderHelper * pathfinderHelper,
CNodeHelper * nodeHelper) const;
virtual std::vector<CGPathNode *> calculateTeleportations(
CPathNodeInfo & source,
CPathfinderHelper * pathfinderHelper,
CNodeHelper * nodeHelper) const;
protected:
void addNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper);
std::vector<int3> getNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const;
std::vector<int3> getTeleportExits(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const;
};
struct DLL_LINKAGE PathfinderOptions
@ -256,14 +260,9 @@ private:
};
boost::heap::priority_queue<CGPathNode *, boost::heap::compare<NodeComparer> > pq;
std::vector<int3> neighbourTiles;
std::vector<int3> neighbours;
CPathNodeInfo source; //current (source) path node -> we took it from the queue
CDestinationNodeInfo destination; //destination node -> it's a neighbour of source that we consider
void addTeleportExits();
bool isHeroPatrolLocked() const;
bool isPatrolMovementAllowed(const int3 & dst) const;
@ -329,7 +328,9 @@ public:
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
int getMaxMovePoints(const EPathfindingLayer layer) const;
std::vector<int3> CPathfinderHelper::getCastleGates(CPathNodeInfo & source) const;
bool isAllowedTeleportEntrance(const CGTeleport * obj) const;
std::vector<int3> getAllowedTeleportChannelExits(TeleportChannelID channelID) const;
bool addTeleportTwoWay(const CGTeleport * obj) const;
bool addTeleportOneWay(const CGTeleport * obj) const;
bool addTeleportOneWayRandom(const CGTeleport * obj) const;