From 6bb205b15b81d5f66a9a16da7be73fcefe82434e Mon Sep 17 00:00:00 2001
From: ArseniyShestakov <ArseniyShestakov@users.noreply.github.com>
Date: Sat, 28 Nov 2015 20:34:50 +0300
Subject: [PATCH] CPathfinder: patrol support using getTilesInRange

For now we use getTilesInRange which isn't really correct for patrol, but good enough for first version.
---
 lib/CPathfinder.cpp          | 47 +++++++++++++++++++++++++++++++++---
 lib/CPathfinder.h            | 11 +++++++++
 lib/mapping/MapFormatH3M.cpp |  2 +-
 3 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp
index 8be71f212..8046f461d 100644
--- a/lib/CPathfinder.cpp
+++ b/lib/CPathfinder.cpp
@@ -37,7 +37,7 @@ CPathfinder::PathfinderOptions::PathfinderOptions()
 }
 
 CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero)
-	: CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap)
+	: CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap), patrolTiles({})
 {
 	assert(hero);
 	assert(hero == getHero(hero->id));
@@ -52,6 +52,7 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan
 
 	hlp = make_unique<CPathfinderHelper>(hero, options);
 
+	initializePatrol();
 	initializeGraph();
 	neighbourTiles.reserve(8);
 	neighbours.reserve(16);
@@ -95,8 +96,10 @@ void CPathfinder::calculatePaths()
 	CGPathNode * initialNode = out.getNode(out.hpos, hero->boat ? ELayer::SAIL : ELayer::LAND);
 	initialNode->turns = 0;
 	initialNode->moveRemains = hero->movement;
-	pq.push(initialNode);
+	if(isHeroPatrolLocked())
+		return;
 
+	pq.push(initialNode);
 	while(!pq.empty())
 	{
 		cp = pq.top();
@@ -119,6 +122,9 @@ void CPathfinder::calculatePaths()
 		addNeighbours();
 		for(auto & neighbour : neighbours)
 		{
+			if(!isPatrolMovementAllowed(neighbour))
+				continue;
+
 			dt = &gs->map->getTile(neighbour);
 			dtObj = dt->topVisitableObj();
 			for(ELayer i = ELayer::LAND; i <= ELayer::AIR; i.advance(1))
@@ -215,7 +221,9 @@ void CPathfinder::addNeighbours()
 void CPathfinder::addTeleportExits()
 {
 	neighbours.clear();
-	if(!isSourceVisitableObj())
+	/// For now we disable teleports usage for patrol movement
+	/// VCAI not aware about patrol and may stuck while attempt to use teleport
+	if(!isSourceVisitableObj() || patrolState == PATROL_RADIUS)
 		return;
 
 	const CGTeleport * objTeleport = dynamic_cast<const CGTeleport *>(ctObj);
@@ -256,6 +264,22 @@ void CPathfinder::addTeleportExits()
 	}
 }
 
+bool CPathfinder::isHeroPatrolLocked() const
+{
+	return patrolState == PATROL_LOCKED;
+}
+
+bool CPathfinder::isPatrolMovementAllowed(const int3 & dst) const
+{
+	if(patrolState == PATROL_RADIUS)
+	{
+		if(!vstd::contains(patrolTiles, dst))
+			return false;
+	}
+
+	return true;
+}
+
 bool CPathfinder::isLayerTransitionPossible(const ELayer destLayer) const
 {
 	/// No layer transition allowed when previous node action is BATTLE
@@ -564,6 +588,23 @@ bool CPathfinder::isDestinationGuardian() const
 	return gs->guardingCreaturePosition(cp->coord) == dp->coord;
 }
 
+void CPathfinder::initializePatrol()
+{
+	auto state = PATROL_NONE;
+	if(hero->patrol.patrolling && !getPlayer(hero->tempOwner)->human)
+	{
+		if(hero->patrol.patrolRadious)
+		{
+			state = PATROL_RADIUS;
+			gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadious);
+		}
+		else
+			state = PATROL_LOCKED;
+	}
+
+	patrolState = state;
+}
+
 void CPathfinder::initializeGraph()
 {
 	auto updateNode = [&](int3 pos, ELayer layer, const TerrainTile * tinfo)
diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h
index 29c5bd35e..e3e533864 100644
--- a/lib/CPathfinder.h
+++ b/lib/CPathfinder.h
@@ -159,6 +159,13 @@ private:
 	const std::vector<std::vector<std::vector<ui8> > > &FoW;
 	unique_ptr<CPathfinderHelper> hlp;
 
+	enum EPatrolState {
+		PATROL_NONE = 0,
+		PATROL_LOCKED = 1,
+		PATROL_RADIUS
+	} patrolState;
+	std::unordered_set<int3, ShashInt3> patrolTiles;
+
 	struct NodeComparer
 	{
 		bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const
@@ -185,6 +192,9 @@ private:
 	void addNeighbours();
 	void addTeleportExits();
 
+	bool isHeroPatrolLocked() const;
+	bool isPatrolMovementAllowed(const int3 & dst) const;
+
 	bool isLayerTransitionPossible(const ELayer dstLayer) const;
 	bool isLayerTransitionPossible() const;
 	bool isMovementToDestPossible() const;
@@ -198,6 +208,7 @@ private:
 	bool isDestinationGuarded(const bool ignoreAccessibility = true) const;
 	bool isDestinationGuardian() const;
 
+	void initializePatrol();
 	void initializeGraph();
 
 	CGPathNode::EAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile * tinfo, const ELayer layer) const;
diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp
index 869681360..859abc110 100644
--- a/lib/mapping/MapFormatH3M.cpp
+++ b/lib/mapping/MapFormatH3M.cpp
@@ -1658,7 +1658,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven, const i
 	else
 	{
 		nhi->patrol.patrolling = true;
-		nhi->patrol.initialPos = initialPos;
+		nhi->patrol.initialPos = CGHeroInstance::convertPosition(initialPos, false);
 	}
 
 	if(map->version > EMapFormat::ROE)