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:
parent
6ac987794c
commit
24221f3fb4
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user