From 02b70a362be718e5689b62bf0d100c709a4e75be Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <timfelgentreff@gmail.com>
Date: Sun, 20 Feb 2022 08:39:10 +0100
Subject: [PATCH] port depot finder change from  Wyrmgus that searches from
 outside mine

---
 src/action/action_resource.cpp | 13 +++---
 src/ai/ai.cpp                  |  2 +-
 src/include/pathfinder.h       |  4 +-
 src/pathfinder/pathfinder.cpp  | 73 +++++++++++++++++++++++++---------
 src/unit/unit.cpp              |  4 +-
 src/unit/unit_find.cpp         | 14 ++-----
 6 files changed, 69 insertions(+), 41 deletions(-)

diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp
index 307ca55c9..4d69479cc 100644
--- a/src/action/action_resource.cpp
+++ b/src/action/action_resource.cpp
@@ -910,20 +910,19 @@ int COrder_Resource::StopGathering(CUnit &unit)
 
 	// Find and send to resource deposit.
 	CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource);
-	// There's a bug in the traversal that leads to workers "sometimes" not finding their way to the old depot
-	// TODO: once we figure out that bug, this workaround can probably be removed.
+	// There's a bug in the traversal that leads to workers "sometimes" not finding their way to the old depot.
 	// timfel: of course, maybe it's actually nice that workers drop out towards their last depot...
 	if (!depot && (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) && Depot && Depot->IsAlive()) {
+		CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource);
 		Assert(unit.Container);
-		DropOutNearest(unit, Depot->tilePos + depot->Type->GetHalfTileSize(), source);
-		// try to path again
-		depot = FindDeposit(unit, 1000, unit.CurrentResource);
+		DropOutNearest(unit, Depot->tilePos + Depot->Type->GetHalfTileSize(), source);
 	}
 	Depot = depot;
 	if (!depot || !unit.ResourcesHeld || this->Finished) {
 		if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) {
-			Assert(unit.Container);
-			DropOutOnSide(unit, LookingW, source);
+			if (unit.Container) {
+				DropOutOnSide(unit, LookingW, source);
+			}
 		}
 		CUnit *mine = this->Resource.Mine;
 
diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp
index 72aac91a6..274e25d36 100644
--- a/src/ai/ai.cpp
+++ b/src/ai/ai.cpp
@@ -926,7 +926,7 @@ void AiCanNotMove(CUnit &unit)
 	const int gh = unit.pathFinderData->input.GetGoalSize().y;
 
 	AiPlayer = unit.Player->Ai;
-	if (PlaceReachable(unit, goalPos, gw, gh, 0, 255)) {
+	if (PlaceReachable(unit, goalPos, gw, gh, 0, 255, false)) {
 		// Path probably closed by unit here
 		AiMoveUnitInTheWay(unit);
 	}
diff --git a/src/include/pathfinder.h b/src/include/pathfinder.h
index cb1071f3e..1bdcb86e9 100644
--- a/src/include/pathfinder.h
+++ b/src/include/pathfinder.h
@@ -219,13 +219,13 @@ extern void FreePathfinder();
 /// Returns the next element of the path
 extern int NextPathElement(CUnit &unit, short int *xdp, short int *ydp);
 /// Return path length to unit 'dst'.
-extern int UnitReachable(const CUnit &src, const CUnit &dst, int range);
+extern int UnitReachable(const CUnit &src, const CUnit &dst, int range, bool from_outside_container);
 /// Return path length to unit 'dst' or error code.
 extern int CalcPathLengthToUnit(const CUnit &src, const CUnit &dst,
 						  const int minrange, const int range);
 /// Can the unit 'src' reach the place x,y
 extern int PlaceReachable(const CUnit &src, const Vec2i &pos, int w, int h,
-						  int minrange, int maxrange);
+						  int minrange, int maxrange, bool from_outside_container);
 
 //
 // in astar.cpp
diff --git a/src/pathfinder/pathfinder.cpp b/src/pathfinder/pathfinder.cpp
index 46f47df25..a229396c2 100644
--- a/src/pathfinder/pathfinder.cpp
+++ b/src/pathfinder/pathfinder.cpp
@@ -183,33 +183,68 @@ void FreePathfinder()
 **
 **  @return          Distance to place.
 */
-int PlaceReachable(const CUnit &src, const Vec2i &goalPos, int w, int h, int minrange, int range)
+int PlaceReachable(const CUnit &src, const Vec2i &goalPos, int w, int h, int minrange, int range, bool from_outside_container)
 {
 	SetAStarFixedEnemyUnitsUnpassable(true); /// change Path Finder setting to don't count tiles with enemy units as passable
-	int i = AStarFindPath(src.tilePos, goalPos, w, h,
-						  src.Type->TileWidth, src.Type->TileHeight,
-						  minrange, range, NULL, 0, src);
+	int i;
+	Vec2i srcTilePos = src.tilePos;
+	int srcTW = src.Type->TileWidth;
+	int srcTH = src.Type->TileHeight;
+	if (!from_outside_container || !src.Container) {
+		i = AStarFindPath(srcTilePos, goalPos, w, h,
+						  srcTW, srcTH,
+						  minrange, range, nullptr, 0, src);
+	} else {
+		const CUnit *first_container = GetFirstContainer(src);
+
+        const Vec2i offset(1, 1);
+		int containerW = first_container->Type->TileWidth;
+		int containerH = first_container->Type->TileHeight;
+		Vec2i containerTilePos = first_container->tilePos;
+		// check top and bottom rows and left and right columns around the container
+		for (int x = -1; x <= containerW; x++) {
+			for (int y = -1; y <= containerH; y++) {
+				if (x >= 0 && x < containerW && y >= 0 && y < containerH) {
+					// inside the container, no need to check
+					continue;
+				}
+				Vec2i tile_pos = containerTilePos + Vec2i(x, y);
+				if (!Map.Info.IsPointOnMap(tile_pos)) {
+					continue;
+				}
+				if (!CanMoveToMask(tile_pos, src.Type->MovementMask)) {
+					//ignore tiles to which the unit cannot be dropped from its container
+					continue;
+				}
+
+				i = AStarFindPath(tile_pos, goalPos, w, h,
+					srcTW, srcTH,
+					minrange, range, nullptr, 0, src);
+
+				switch (i) {
+					case PF_FAILED:
+					case PF_UNREACHABLE:
+					case PF_WAIT:
+						continue;
+				}
+				goto finished;
+			}
+		}
+	}
+finished:
 	SetAStarFixedEnemyUnitsUnpassable(false); /// restore Path Finder setting
 	switch (i) {
 		case PF_FAILED:
 		case PF_UNREACHABLE:
-			i = 0;
-			break;
+		case PF_WAIT:
+			return 0;
 		case PF_REACHED:
 			/* since most of this function usage check return value as bool
-			 * then reached state should be track as true value */
-			i = 1;
-			break;
-		case PF_WAIT:
-			Assert(0);
-			i = 0;
-			break;
-		case PF_MOVE:
-			break;
+			* then reached state should be track as true value */
+			return std::max(i, 1);
 		default:
-			break;
+			return i;
 	}
-	return i;
 }
 
 /**
@@ -221,14 +256,14 @@ int PlaceReachable(const CUnit &src, const Vec2i &goalPos, int w, int h, int min
 **
 **  @return       Distance to place.
 */
-int UnitReachable(const CUnit &src, const CUnit &dst, int range)
+int UnitReachable(const CUnit &src, const CUnit &dst, int range, bool from_outside_container)
 {
 	//  Find a path to the goal.
 	if (src.Type->Building) {
 		return 0;
 	}
 	const int depth = PlaceReachable(src, dst.tilePos,
-									 dst.Type->TileWidth, dst.Type->TileHeight, 0, range);
+									 dst.Type->TileWidth, dst.Type->TileHeight, 0, range, from_outside_container);
 	if (depth <= 0) {
 		return 0;
 	}
diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp
index af8accb32..84eb4d0ad 100644
--- a/src/unit/unit.cpp
+++ b/src/unit/unit.cpp
@@ -2984,7 +2984,7 @@ static void HitUnit_AttackBack(CUnit &attacker, CUnit &target)
 			COrder_Attack &order = dynamic_cast<COrder_Attack &>(*target.CurrentOrder());
 			if (order.IsAutoTargeting() || target.Player->AiEnabled) {
 				if (attacker.IsVisibleAsGoal(*target.Player)) {
-					if (UnitReachable(target, attacker, target.Stats->Variables[ATTACKRANGE_INDEX].Max)) {
+					if (UnitReachable(target, attacker, target.Stats->Variables[ATTACKRANGE_INDEX].Max, false)) {
 						target.UnderAttack = underAttack; /// allow target to ignore non aggressive targets while searching attacker
 						order.OfferNewTarget(target, &attacker);
 					}
@@ -3018,7 +3018,7 @@ static void HitUnit_AttackBack(CUnit &attacker, CUnit &target)
 			const Vec2i posToAttack = (attacker.IsVisibleAsGoal(*target.Player)) 
 									? attacker.tilePos 
 									: GetRndPosInDirection(target.tilePos, attacker.tilePos, false, target.Type->ReactRangeComputer, 2);
-			if (!PlaceReachable(target, posToAttack, 1, 1, 0, target.Stats->Variables[ATTACKRANGE_INDEX].Max)) {
+			if (!PlaceReachable(target, posToAttack, 1, 1, 0, target.Stats->Variables[ATTACKRANGE_INDEX].Max, false)) {
 				return;
 			}
 			COrder *savedOrder = NULL;
diff --git a/src/unit/unit_find.cpp b/src/unit/unit_find.cpp
index 5cae1a2e8..b8b41e985 100644
--- a/src/unit/unit_find.cpp
+++ b/src/unit/unit_find.cpp
@@ -213,14 +213,8 @@ class BestDepotFinder
 					return;
 				}
 
-				// calck real travel distance
-				if (worker->Container != nullptr) {
-					UnmarkUnitFieldFlags(*first_container);
-				}
-				const int travel_distance = UnitReachable(*worker, *dest, 1);
-				if (worker->Container != nullptr) {
-					MarkUnitFieldFlags(*first_container);
-				}
+				// calc real travel distance
+				const int travel_distance = UnitReachable(*worker, *dest, 1, worker->Container != nullptr);
 				//
 				// Take this depot?
 				//
@@ -730,7 +724,7 @@ private:
 		// Unit in range ?
 		const int d = attacker->MapDistanceTo(*dest);
 
-		if (d > attackrange && !UnitReachable(*attacker, *dest, attackrange)) {
+		if (d > attackrange && !UnitReachable(*attacker, *dest, attackrange, false)) {
 			return INT_MAX;
 		}
 
@@ -934,7 +928,7 @@ public:
 
 				int attackrange = attacker->Stats->Variables[ATTACKRANGE_INDEX].Max;
 				if (d <= attackrange ||
-					(d <= range && UnitReachable(*attacker, *dest, attackrange))) {
+					(d <= range && UnitReachable(*attacker, *dest, attackrange, false))) {
 					++enemy_count;
 				} else {
 					dest->CacheLock = 1;