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;