Browse Source

AI: move teleportation handling to pathfinder helper and neighbour finder

Andrii Danylchenko 7 years ago
parent
commit
24221f3fb4
2 changed files with 112 additions and 61 deletions
  1. 98 48
      lib/CPathfinder.cpp
  2. 14 13
      lib/CPathfinder.h

+ 98 - 48
lib/CPathfinder.cpp

@@ -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

+ 14 - 13
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<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;