From 24221f3fb4ae38a85e0717906c1720f2bd2b4295 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Wed, 1 Aug 2018 23:21:14 +0300 Subject: [PATCH] AI: move teleportation handling to pathfinder helper and neighbour finder --- lib/CPathfinder.cpp | 146 +++++++++++++++++++++++++++++--------------- lib/CPathfinder.h | 27 ++++---- 2 files changed, 112 insertions(+), 61 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index dc643505d..f6e0c3757 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -26,13 +26,16 @@ bool canSeeObj(const CGObjectInstance * obj) } CNeighbourFinder::CNeighbourFinder() - :neighbours(), neighbourTiles(), accessibleNeighbourTiles() { } -std::vector & CNeighbourFinder::calculateNeighbours(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper, CNodeHelper * nodeHelper) +std::vector CNeighbourFinder::calculateNeighbours( + CPathNodeInfo & source, + CPathfinderHelper * pathfinderHelper, + CNodeHelper * nodeHelper) const { - addNeighbourTiles(source, pathfinderHelper); + std::vector neighbours; + auto accessibleNeighbourTiles = getNeighbourTiles(source, pathfinderHelper); for(auto & neighbour : accessibleNeighbourTiles) { @@ -50,11 +53,27 @@ std::vector & CNeighbourFinder::calculateNeighbours(CPathNodeInfo return neighbours; } -void CNeighbourFinder::addNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) +std::vector CNeighbourFinder::calculateTeleportations( + CPathNodeInfo & source, + CPathfinderHelper * pathfinderHelper, + CNodeHelper * nodeHelper) const { - neighbourTiles.clear(); - accessibleNeighbourTiles.clear(); - neighbours.clear(); + std::vector 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 CNeighbourFinder::getNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const +{ + std::vector 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 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 allowedExits; - const CGTeleport * objTeleport = dynamic_cast(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(obj)) { - auto obj = getObj(objId); - if(dynamic_cast(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 CPathfinderHelper::getCastleGates(CPathNodeInfo & source) const +{ + std::vector 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 CNeighbourFinder::getTeleportExits( + CPathNodeInfo & source, + CPathfinderHelper * pathfinderHelper) const +{ + std::vector teleportationExits; + + const CGTeleport * objTeleport = dynamic_cast(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 diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 9a7eae125..1f98e4c2f 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -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 neighbourTiles; - std::vector accessibleNeighbourTiles; - std::vector neighbours; - public: CNeighbourFinder(); - virtual std::vector & calculateNeighbours(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper, CNodeHelper * nodeHelper); + virtual std::vector calculateNeighbours( + CPathNodeInfo & source, + CPathfinderHelper * pathfinderHelper, + CNodeHelper * nodeHelper) const; + + virtual std::vector calculateTeleportations( + CPathNodeInfo & source, + CPathfinderHelper * pathfinderHelper, + CNodeHelper * nodeHelper) const; protected: - void addNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper); + std::vector getNeighbourTiles(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const; + std::vector getTeleportExits(CPathNodeInfo & source, CPathfinderHelper * pathfinderHelper) const; }; struct DLL_LINKAGE PathfinderOptions @@ -256,14 +260,9 @@ private: }; boost::heap::priority_queue > pq; - std::vector neighbourTiles; - std::vector 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 CPathfinderHelper::getCastleGates(CPathNodeInfo & source) const; bool isAllowedTeleportEntrance(const CGTeleport * obj) const; + std::vector getAllowedTeleportChannelExits(TeleportChannelID channelID) const; bool addTeleportTwoWay(const CGTeleport * obj) const; bool addTeleportOneWay(const CGTeleport * obj) const; bool addTeleportOneWayRandom(const CGTeleport * obj) const;