From 6e6af668f28e7038d409f3bbb6446a05e71c8e37 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Fri, 20 Jun 2025 00:14:20 -0700 Subject: [PATCH 01/29] Initial Commit : Possible pathfinding changes --- src/map.cpp | 65 ++++++++++++++++++++++++++++------------------------- src/map.h | 38 ++++++++++++++++++++----------- src/otpch.h | 1 + 3 files changed, 60 insertions(+), 44 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index ce07509f35..8bd3272240 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -699,6 +699,7 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s iterations++; if (iterations >= 120) { + nodes.clear(); return false; } @@ -752,16 +753,17 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s neighborNode->g = g; neighborNode->f = newf; neighborNode->parent = n; - nodes.addNode(neighborNode); } else { // Does not exist in the open/closed list, create a new node - nodes.createNewNode(n, pos.x, pos.y, g, newf); + nodes.createNode(n, pos.x, pos.y, g, newf); } } n = nodes.getBestNode(); } + nodes.clear(); + if (!found) { return false; } @@ -804,47 +806,48 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s } // AStarNodes - AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() { - // Create our first node to check. - AStarNode* firstNode = new AStarNode; - firstNode->parent = nullptr; - firstNode->x = x; - firstNode->y = y; - firstNode->g = 0; - firstNode->f = 0; - - // Add node to node vector and map - nodes.reserve(50); - nodes.emplace_back(firstNode); - nodeMap[x][y] = firstNode; + nodes.reserve(static_cast(x * y)); + createNode(nullptr, x, y, 0, 0); } -void AStarNodes::createNewNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f) +void AStarNodes::clear() { - AStarNode* newNode = new AStarNode; - newNode->parent = parent; - newNode->x = x; - newNode->y = y; - newNode->g = g; - newNode->f = f; + nodes.clear(); + std::priority_queue, NodeCompare>().swap(openSet); + visited.clear(); + nodeMap.clear(); +} - nodes.emplace_back(newNode); - nodeMap[x][y] = newNode; +AStarNode* AStarNodes::createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f) +{ + uint32_t key = hashCoord(x, y); + nodes.emplace_back(AStarNode{parent, x, y, g, f}); + AStarNode* node = &nodes.back(); + nodeMap[key] = node; + openSet.push(node); + return node; } AStarNode* AStarNodes::getBestNode() { - if (nodes.size() == 0) { - return nullptr; + while (!openSet.empty()) { + AStarNode* node = openSet.top(); + openSet.pop(); + uint32_t key = hashCoord(node->x, node->y); + if (visited.find(key) == visited.end()) { + visited.insert(key); + return node; + } } + return nullptr; +} - std::nth_element(nodes.begin(), nodes.end() - 1, nodes.end(), - [](AStarNode* left, AStarNode* right) { return left->f > right->f; }); - AStarNode* retNode = nodes.back(); - nodes.pop_back(); - return retNode; +AStarNode* AStarNodes::getNodeByPosition(uint16_t x, uint16_t y) +{ + auto it = nodeMap.find(hashCoord(x, y)); + return (it != nodeMap.end()) ? it->second : nullptr; } uint16_t AStarNodes::getMapWalkCost(AStarNode* node, const Position& neighborPos) diff --git a/src/map.h b/src/map.h index 386b4180e5..0c656e7298 100644 --- a/src/map.h +++ b/src/map.h @@ -14,35 +14,47 @@ class Creature; static constexpr int32_t MAP_MAX_LAYERS = 16; + +static constexpr uint16_t MAP_NORMALWALKCOST = 10; +static constexpr uint16_t MAP_DIAGONALWALKCOST = 25; + struct FindPathParams; struct AStarNode { - AStarNode* parent; - uint16_t g; - uint16_t f; - uint16_t x, y; + AStarNode* parent = nullptr; + uint16_t x, y = 0; + uint16_t g, f = 0; }; -static constexpr uint16_t MAP_NORMALWALKCOST = 10; -static constexpr uint16_t MAP_DIAGONALWALKCOST = 25; +inline uint32_t hashCoord(uint16_t x, uint16_t y) { return (static_cast(x) << 16) | y; } class AStarNodes { public: - AStarNodes(uint16_t x, uint16_t y); - - void createNewNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f); - void addNode(AStarNode* node) { nodes.emplace_back(node); }; + AStarNodes(uint16_t width, uint16_t height); + void clear(); + AStarNode* createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f); AStarNode* getBestNode(); - AStarNode* getNodeByPosition(uint16_t x, uint16_t y) { return nodeMap[x][y]; }; + AStarNode* getNodeByPosition(uint16_t x, uint16_t y); static uint16_t getMapWalkCost(AStarNode* node, const Position& neighborPos); static uint16_t getTileWalkCost(const Creature& creature, const Tile* tile); private: - std::vector nodes; - std::map> nodeMap; + std::vector nodes; + std::unordered_map nodeMap; + std::unordered_set visited; + + struct NodeCompare + { + bool operator()(AStarNode* a, AStarNode* b) const + { + return a->f > b->f; // Min-heap based on f score + } + }; + + std::priority_queue, NodeCompare> openSet; }; using SpectatorCache = std::map; diff --git a/src/otpch.h b/src/otpch.h index 6ba4055536..88d7efb69a 100644 --- a/src/otpch.h +++ b/src/otpch.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include From 9034f850aa5cb141aa9ef54f1e41033f008a90a5 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Fri, 20 Jun 2025 07:41:42 -0700 Subject: [PATCH 02/29] Forgot width height change on these lines. --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index 8bd3272240..671cf5d65f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -689,7 +689,7 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s static constexpr std::array, 8> allNeighbors = { {{-1, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}}}; - AStarNodes nodes(pos.x, pos.y); + AStarNodes nodes(maxDistanceX, maxDistanceY); AStarNode* found = nullptr; int32_t bestMatch = 0; From 67ad7fb8ebcac5e8e62cb302848416f97a4901c3 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Fri, 20 Jun 2025 07:49:25 -0700 Subject: [PATCH 03/29] I always do this... Fix width height again --- src/map.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index 671cf5d65f..99a3561b27 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -689,7 +689,9 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s static constexpr std::array, 8> allNeighbors = { {{-1, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}}}; - AStarNodes nodes(maxDistanceX, maxDistanceY); + uint16_t width = startPos.getDistanceX(targetPos); + uint16_t height = startPos.getDistanceY(targetPos); + AStarNodes nodes(width, height); AStarNode* found = nullptr; int32_t bestMatch = 0; From 2649772e7626290f19f633c630bffe1f44ba26c7 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Fri, 20 Jun 2025 08:48:25 -0700 Subject: [PATCH 04/29] Revert height/width changes. This is required for hash purposes. --- src/map.cpp | 12 ++++++------ src/map.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 99a3561b27..44847f2d75 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -661,7 +661,6 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s const FrozenPathingConditionCall& pathCondition, const FindPathParams& fpp) const { Position pos = creature.getPosition(); - Position endPos; const Position startPos = pos; // We can't walk, no need to create path. @@ -674,24 +673,25 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s return false; } + uint16_t distanceX = startPos.getDistanceX(targetPos); + uint16_t distanceY = startPos.getDistanceY(targetPos); // We are next to our target. Let dance step decide. - if (fpp.maxTargetDist <= 1 && startPos.getDistanceX(targetPos) <= 1 && startPos.getDistanceY(targetPos) <= 1) { + if (fpp.maxTargetDist <= 1 && distanceX <= 1 && distanceY <= 1) { return true; } // Don't update path. The target is too far away. int32_t maxDistanceX = fpp.maxSearchDist ? fpp.maxSearchDist : Map::maxClientViewportX + 1; int32_t maxDistanceY = fpp.maxSearchDist ? fpp.maxSearchDist : Map::maxClientViewportY + 1; - if (startPos.getDistanceX(targetPos) > maxDistanceX || startPos.getDistanceY(targetPos) > maxDistanceY) { + if (distanceX > maxDistanceX || distanceY > maxDistanceY) { return false; } static constexpr std::array, 8> allNeighbors = { {{-1, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}}}; - uint16_t width = startPos.getDistanceX(targetPos); - uint16_t height = startPos.getDistanceY(targetPos); - AStarNodes nodes(width, height); + Position endPos; + AStarNodes nodes(pos.x , pos.y); AStarNode* found = nullptr; int32_t bestMatch = 0; diff --git a/src/map.h b/src/map.h index 0c656e7298..069fa07d12 100644 --- a/src/map.h +++ b/src/map.h @@ -31,7 +31,7 @@ inline uint32_t hashCoord(uint16_t x, uint16_t y) { return (static_cast Date: Fri, 20 Jun 2025 13:00:36 -0700 Subject: [PATCH 05/29] Fixed problem requiring large reserve --- src/map.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index 44847f2d75..6ba2cd238d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -810,7 +810,9 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s // AStarNodes AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() { - nodes.reserve(static_cast(x * y)); + // Needs to be large enough to never resize 250 should be plenty + // If you want paths larger than 20-30 sqm this must be increased. + nodes.reserve(250); createNode(nullptr, x, y, 0, 0); } From 2581e09ec60e29ef9638ac5748b2532607a56293 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Fri, 20 Jun 2025 21:15:48 -0700 Subject: [PATCH 06/29] Final optimization --- src/map.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/map.cpp b/src/map.cpp index 6ba2cd238d..0e3edd44df 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -691,6 +691,7 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s {{-1, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}}}; Position endPos; + bool sightClear = isSightClear(startPos, targetPos, true); AStarNodes nodes(pos.x , pos.y); AStarNode* found = nullptr; @@ -730,6 +731,43 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s continue; } + // If sight is clear we can ignore a lot of nodes. + if (sightClear) { + int32_t startX = startPos.getX(); + int32_t startY = startPos.getY(); + int32_t targetX = targetPos.getX(); + int32_t targetY = targetPos.getY(); + + // We don't need nodes behind us. + // We also do not need nodes on a different x or y if target and start is same x/y. + if (startX > targetX && pos.x > startX) { + continue; + } else if (startX == targetX && pos.x != startX) { + continue; + } else if (startX < targetX && pos.x < startX) { + continue; + } + if (startY > targetY && pos.y > startY) { + continue; + } else if (startY == targetY && pos.y != startY) { + continue; + } else if (startY < targetY && pos.y < startY) { + continue; + } + + // We don't need nodes past the targetPos + if (startX > targetX && pos.x < targetX) { + continue; + } else if (startX < targetX && pos.x > targetX) { + continue; + } + if (startY > targetY && pos.y < targetY) { + continue; + } else if (startY < targetY && pos.y > targetY) { + continue; + } + } + const Tile* tile; AStarNode* neighborNode = nodes.getNodeByPosition(pos.x, pos.y); if (neighborNode) { From 3573e7e45e761737894ae25521d43b24c61609f9 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Fri, 20 Jun 2025 22:38:30 -0700 Subject: [PATCH 07/29] Hueristic Optimization --- src/map.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 0e3edd44df..20b843e0b3 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -651,10 +651,9 @@ const Tile* Map::canWalkTo(const Creature& creature, const Position& pos) const uint16_t calculateHeuristic(const Position& p1, const Position& p2) { - uint16_t dx = p1.getX() - p2.getX(); - uint16_t dy = p1.getY() - p2.getY(); - - return dx * dx + dy * dy; + uint16_t dx = std::abs(p1.getX() - p2.getX()); + uint16_t dy = std::abs(p1.getY() - p2.getY()); + return 10 * (dx + dy) - 6 * std::min(dx, dy); // Octile distance } bool Map::getPathMatching(const Creature& creature, const Position& targetPos, std::vector& dirList, From 7ccb04b80f83b7eb14883ba3e11d884a2feac9f4 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Sat, 21 Jun 2025 10:48:46 -0700 Subject: [PATCH 08/29] We cant use sightclear without some changes unfortunatley. --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index 20b843e0b3..6a328159a1 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -690,7 +690,7 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s {{-1, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}}}; Position endPos; - bool sightClear = isSightClear(startPos, targetPos, true); + bool sightClear = false; AStarNodes nodes(pos.x , pos.y); AStarNode* found = nullptr; From bc7f1414e08e23cd3b3b1051d112d008c489f1cc Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Sat, 21 Jun 2025 11:45:18 -0700 Subject: [PATCH 09/29] Final change --- src/map.cpp | 45 +++++---------------------------------------- 1 file changed, 5 insertions(+), 40 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 6a328159a1..4ee79d27e2 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -653,7 +653,7 @@ uint16_t calculateHeuristic(const Position& p1, const Position& p2) { uint16_t dx = std::abs(p1.getX() - p2.getX()); uint16_t dy = std::abs(p1.getY() - p2.getY()); - return 10 * (dx + dy) - 6 * std::min(dx, dy); // Octile distance + return 10 * (dx + dy); } bool Map::getPathMatching(const Creature& creature, const Position& targetPos, std::vector& dirList, @@ -690,7 +690,6 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s {{-1, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}}}; Position endPos; - bool sightClear = false; AStarNodes nodes(pos.x , pos.y); AStarNode* found = nullptr; @@ -730,43 +729,6 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s continue; } - // If sight is clear we can ignore a lot of nodes. - if (sightClear) { - int32_t startX = startPos.getX(); - int32_t startY = startPos.getY(); - int32_t targetX = targetPos.getX(); - int32_t targetY = targetPos.getY(); - - // We don't need nodes behind us. - // We also do not need nodes on a different x or y if target and start is same x/y. - if (startX > targetX && pos.x > startX) { - continue; - } else if (startX == targetX && pos.x != startX) { - continue; - } else if (startX < targetX && pos.x < startX) { - continue; - } - if (startY > targetY && pos.y > startY) { - continue; - } else if (startY == targetY && pos.y != startY) { - continue; - } else if (startY < targetY && pos.y < startY) { - continue; - } - - // We don't need nodes past the targetPos - if (startX > targetX && pos.x < targetX) { - continue; - } else if (startX < targetX && pos.x > targetX) { - continue; - } - if (startY > targetY && pos.y < targetY) { - continue; - } else if (startY < targetY && pos.y > targetY) { - continue; - } - } - const Tile* tile; AStarNode* neighborNode = nodes.getNodeByPosition(pos.x, pos.y); if (neighborNode) { @@ -849,7 +811,10 @@ AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() { // Needs to be large enough to never resize 250 should be plenty // If you want paths larger than 20-30 sqm this must be increased. - nodes.reserve(250); + uint8_t reserveSize = 250; + nodes.reserve(reserveSize); + nodeMap.reserve(reserveSize); + visited.reserve(reserveSize); createNode(nullptr, x, y, 0, 0); } From 4dfbda96dd458187659b078d8cbabd16e023eccc Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Sat, 21 Jun 2025 14:52:48 -0700 Subject: [PATCH 10/29] Remove predefined values --- src/map.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map.h b/src/map.h index 069fa07d12..7234905f68 100644 --- a/src/map.h +++ b/src/map.h @@ -21,9 +21,9 @@ static constexpr uint16_t MAP_DIAGONALWALKCOST = 25; struct FindPathParams; struct AStarNode { - AStarNode* parent = nullptr; - uint16_t x, y = 0; - uint16_t g, f = 0; + AStarNode* parent; + uint16_t x, y; + uint16_t g, f; }; inline uint32_t hashCoord(uint16_t x, uint16_t y) { return (static_cast(x) << 16) | y; } From 239512ed73aeb5f16089655d13af1b627e3e62e5 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Sat, 21 Jun 2025 16:01:52 -0700 Subject: [PATCH 11/29] Add required changes for sightLine check optimization --- src/map.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++-------- src/map.h | 7 ++--- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 4ee79d27e2..781faad937 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -498,7 +498,8 @@ bool Map::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool return !checkLineOfSight || isSightClear(fromPos, toPos, sameFloor); } -bool Map::isTileClear(uint16_t x, uint16_t y, uint8_t z, bool blockFloor /*= false*/) const +bool Map::isTileClear(uint16_t x, uint16_t y, uint8_t z, bool blockFloor /*= false*/, + bool pathfinding /*= false*/) const { const Tile* tile = getTile(x, y, z); if (!tile) { @@ -509,12 +510,18 @@ bool Map::isTileClear(uint16_t x, uint16_t y, uint8_t z, bool blockFloor /*= fal return false; } + if (pathfinding) { + return !tile->hasProperty(CONST_PROP_BLOCKPROJECTILE) && !tile->hasProperty(CONST_PROP_BLOCKPATH) && + !tile->hasProperty(CONST_PROP_BLOCKSOLID) && !tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKPATH) && + !tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID); + } + return !tile->hasProperty(CONST_PROP_BLOCKPROJECTILE); } namespace { -bool checkSteepLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t z) +bool checkSteepLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t z, bool pathfinding /*= false*/) { float dx = x1 - x0; float slope = (dx == 0) ? 1 : (y1 - y0) / dx; @@ -522,7 +529,7 @@ bool checkSteepLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t for (uint16_t x = x0 + 1; x < x1; ++x) { // 0.1 is necessary to avoid loss of precision during calculation - if (!g_game.map.isTileClear(std::floor(yi + 0.1), x, z)) { + if (!g_game.map.isTileClear(std::floor(yi + 0.1), x, z, false, pathfinding)) { return false; } yi += slope; @@ -531,7 +538,7 @@ bool checkSteepLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t return true; } -bool checkSlightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t z) +bool checkSlightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t z, bool pathfinding /*= false*/) { float dx = x1 - x0; float slope = (dx == 0) ? 1 : (y1 - y0) / dx; @@ -539,7 +546,7 @@ bool checkSlightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t for (uint16_t x = x0 + 1; x < x1; ++x) { // 0.1 is necessary to avoid loss of precision during calculation - if (!g_game.map.isTileClear(x, std::floor(yi + 0.1), z)) { + if (!g_game.map.isTileClear(x, std::floor(yi + 0.1), z, false, pathfinding)) { return false; } yi += slope; @@ -550,7 +557,8 @@ bool checkSlightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t } // namespace -bool Map::checkSightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t z) const +bool Map::checkSightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t z, + bool pathfinding /*= false*/) const { if (x0 == x1 && y0 == y1) { return true; @@ -558,19 +566,20 @@ bool Map::checkSightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uin if (std::abs(y1 - y0) > std::abs(x1 - x0)) { if (y1 > y0) { - return checkSteepLine(y0, x0, y1, x1, z); + return checkSteepLine(y0, x0, y1, x1, z, pathfinding); } - return checkSteepLine(y1, x1, y0, x0, z); + return checkSteepLine(y1, x1, y0, x0, z, pathfinding); } if (x0 > x1) { - return checkSlightLine(x1, y1, x0, y0, z); + return checkSlightLine(x1, y1, x0, y0, z, pathfinding); } - return checkSlightLine(x0, y0, x1, y1, z); + return checkSlightLine(x0, y0, x1, y1, z, pathfinding); } -bool Map::isSightClear(const Position& fromPos, const Position& toPos, bool sameFloor /*= false*/) const +bool Map::isSightClear(const Position& fromPos, const Position& toPos, bool sameFloor /*= false*/, + bool pathfinding /*= false*/) const { // target is on the same floor if (fromPos.z == toPos.z) { @@ -579,6 +588,12 @@ bool Map::isSightClear(const Position& fromPos, const Position& toPos, bool same return true; } + // Check for additional tile properties when pathfinding + if (pathfinding) { + bool sightClear = checkSightLine(fromPos.x, fromPos.y, toPos.x, toPos.y, fromPos.z, true); + return sightClear; + } + // sight is clear or sameFloor is enabled bool sightClear = checkSightLine(fromPos.x, fromPos.y, toPos.x, toPos.y, fromPos.z); if (sightClear || sameFloor) { @@ -689,6 +704,8 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s static constexpr std::array, 8> allNeighbors = { {{-1, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}}}; + bool sightClear = isSightClear(startPos, targetPos, true, true); + Position endPos; AStarNodes nodes(pos.x , pos.y); @@ -729,6 +746,43 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s continue; } + // If sight is clear we can ignore a lot of nodes. + if (sightClear) { + int32_t startX = startPos.getX(); + int32_t startY = startPos.getY(); + int32_t targetX = targetPos.getX(); + int32_t targetY = targetPos.getY(); + + // We don't need nodes behind us. + // We also do not need nodes on a different x or y if target and start is same x/y. + if (startX > targetX && pos.x > startX) { + continue; + } else if (startX == targetX && pos.x != startX) { + continue; + } else if (startX < targetX && pos.x < startX) { + continue; + } + if (startY > targetY && pos.y > startY) { + continue; + } else if (startY == targetY && pos.y != startY) { + continue; + } else if (startY < targetY && pos.y < startY) { + continue; + } + + // We don't need nodes past the targetPos + if (startX > targetX && pos.x < targetX) { + continue; + } else if (startX < targetX && pos.x > targetX) { + continue; + } + if (startY > targetY && pos.y < targetY) { + continue; + } else if (startY < targetY && pos.y > targetY) { + continue; + } + } + const Tile* tile; AStarNode* neighborNode = nodes.getNodeByPosition(pos.x, pos.y); if (neighborNode) { diff --git a/src/map.h b/src/map.h index 7234905f68..b49d66d679 100644 --- a/src/map.h +++ b/src/map.h @@ -235,7 +235,7 @@ class Map * \param blockFloor counts the ground tile as an obstacle * \returns The result if there is an obstacle or not */ - bool isTileClear(uint16_t x, uint16_t y, uint8_t z, bool blockFloor = false) const; + bool isTileClear(uint16_t x, uint16_t y, uint8_t z, bool blockFloor = false, bool pathfinding = false) const; /** * Checks if path is clear from fromPos to toPos @@ -244,8 +244,9 @@ class Map *Destination point \param sameFloor checks if the destination is on same *floor \returns The result if there is no obstacles */ - bool isSightClear(const Position& fromPos, const Position& toPos, bool sameFloor = false) const; - bool checkSightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t z) const; + bool isSightClear(const Position& fromPos, const Position& toPos, bool sameFloor = false, + bool pathfinding = false) const; + bool checkSightLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t z, bool pathfinding = false) const; const Tile* canWalkTo(const Creature& creature, const Position& pos) const; From cdef821e4b5d804a300605a726311400f956274a Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Sun, 22 Jun 2025 16:08:53 -0700 Subject: [PATCH 12/29] A little change to the heuristic --- src/map.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 781faad937..3fda01db4a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -664,11 +664,11 @@ const Tile* Map::canWalkTo(const Creature& creature, const Position& pos) const return tile; } -uint16_t calculateHeuristic(const Position& p1, const Position& p2) +static uint16_t calculateHeuristic(const Position& p1, const Position& p2) { uint16_t dx = std::abs(p1.getX() - p2.getX()); uint16_t dy = std::abs(p1.getY() - p2.getY()); - return 10 * (dx + dy); + return MAP_NORMALWALKCOST * (dx + dy) + (MAP_DIAGONALWALKCOST - 2 * 10) * std::min(dx, dy); } bool Map::getPathMatching(const Creature& creature, const Position& targetPos, std::vector& dirList, @@ -865,7 +865,7 @@ AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() { // Needs to be large enough to never resize 250 should be plenty // If you want paths larger than 20-30 sqm this must be increased. - uint8_t reserveSize = 250; + uint8_t reserveSize = 150; nodes.reserve(reserveSize); nodeMap.reserve(reserveSize); visited.reserve(reserveSize); From 3753196acf99a8ac61d17a7dfd6c869585e8380d Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Tue, 24 Jun 2025 04:12:16 -0700 Subject: [PATCH 13/29] Heuristic Change --- src/map.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index 3fda01db4a..449a7c50f5 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -668,7 +668,7 @@ static uint16_t calculateHeuristic(const Position& p1, const Position& p2) { uint16_t dx = std::abs(p1.getX() - p2.getX()); uint16_t dy = std::abs(p1.getY() - p2.getY()); - return MAP_NORMALWALKCOST * (dx + dy) + (MAP_DIAGONALWALKCOST - 2 * 10) * std::min(dx, dy); + return 10 * (dx + dy); } bool Map::getPathMatching(const Creature& creature, const Position& targetPos, std::vector& dirList, @@ -740,6 +740,9 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s if (fpp.maxSearchDist != 0 && (startPos.getDistanceX(pos) > fpp.maxSearchDist || startPos.getDistanceY(pos) > fpp.maxSearchDist)) { continue; + } else if ((startPos.getDistanceX(pos) + startPos.getDistanceY(pos) > + Map::maxViewportX + Map::maxViewportY)) { + break; } if (fpp.keepDistance && !pathCondition.isInRange(startPos, pos, fpp)) { From 030a580029d7dc654b04f4169294a39d96f78e31 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Tue, 24 Jun 2025 04:48:52 -0700 Subject: [PATCH 14/29] format --- src/game.cpp | 10 +++++----- src/map.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index cc4ac2b524..8551140117 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5730,12 +5730,12 @@ Guild_ptr Game::getGuild(uint32_t id) const return it->second; } -void Game::addGuild(Guild_ptr guild) +void Game::addGuild(Guild_ptr guild) { - if (!guild) { - return; - } - + if (!guild) { + return; + } + guilds[guild->getId()] = guild; } diff --git a/src/map.cpp b/src/map.cpp index 449a7c50f5..a6d173dfa1 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -707,7 +707,7 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s bool sightClear = isSightClear(startPos, targetPos, true, true); Position endPos; - AStarNodes nodes(pos.x , pos.y); + AStarNodes nodes(pos.x, pos.y); AStarNode* found = nullptr; int32_t bestMatch = 0; From 1e7a3a514b41e990dfda4ddc9318c88763486b7e Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Tue, 24 Jun 2025 05:53:36 -0700 Subject: [PATCH 15/29] Move node reserve size to a place that makes sense --- src/map.cpp | 9 +++------ src/map.h | 2 +- src/otpch.h | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index a6d173dfa1..2e29e31ac2 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -866,12 +866,9 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s // AStarNodes AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() { - // Needs to be large enough to never resize 250 should be plenty - // If you want paths larger than 20-30 sqm this must be increased. - uint8_t reserveSize = 150; - nodes.reserve(reserveSize); - nodeMap.reserve(reserveSize); - visited.reserve(reserveSize); + nodes.reserve(Map::nodeReserveSize); + nodeMap.reserve(Map::nodeReserveSize); + visited.reserve(Map::nodeReserveSize); createNode(nullptr, x, y, 0, 0); } diff --git a/src/map.h b/src/map.h index b49d66d679..f658e59859 100644 --- a/src/map.h +++ b/src/map.h @@ -14,7 +14,6 @@ class Creature; static constexpr int32_t MAP_MAX_LAYERS = 16; - static constexpr uint16_t MAP_NORMALWALKCOST = 10; static constexpr uint16_t MAP_DIAGONALWALKCOST = 25; @@ -162,6 +161,7 @@ class Map static constexpr int32_t maxViewportY = 11; // min value: maxClientViewportY + 1 static constexpr int32_t maxClientViewportX = 8; static constexpr int32_t maxClientViewportY = 6; + static constexpr int16_t nodeReserveSize = 150; // Increase if maxViewport is increased. uint32_t clean() const; diff --git a/src/otpch.h b/src/otpch.h index 88d7efb69a..472a37b447 100644 --- a/src/otpch.h +++ b/src/otpch.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,6 @@ #include #include #include -#include #include #include #include From cff19d51f9d143a9b806b03c06d4df611db4c90e Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Tue, 24 Jun 2025 05:58:08 -0700 Subject: [PATCH 16/29] Allow nodeReserveSize to determine path size completly --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index 2e29e31ac2..bf00e2cbcc 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -716,7 +716,7 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s while (n) { iterations++; - if (iterations >= 120) { + if (iterations >= Map::nodeReserveSize) { nodes.clear(); return false; } From 87e74a212c3cd212ffe4fdddb863cca53719779a Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Tue, 24 Jun 2025 06:37:21 -0700 Subject: [PATCH 17/29] Allow maxViewPort to handle nodeReserveSize automatically --- src/map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.h b/src/map.h index f658e59859..f830a3d2d4 100644 --- a/src/map.h +++ b/src/map.h @@ -161,7 +161,7 @@ class Map static constexpr int32_t maxViewportY = 11; // min value: maxClientViewportY + 1 static constexpr int32_t maxClientViewportX = 8; static constexpr int32_t maxClientViewportY = 6; - static constexpr int16_t nodeReserveSize = 150; // Increase if maxViewport is increased. + static constexpr int16_t nodeReserveSize = (maxViewportX + maxViewportY) * 7; uint32_t clean() const; From 96774fb0cc4821b6ee7beacb15f2d3b9b6a59ed7 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Tue, 24 Jun 2025 17:10:01 -0700 Subject: [PATCH 18/29] Remove magic 10 --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index bf00e2cbcc..f5b1576e81 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -668,7 +668,7 @@ static uint16_t calculateHeuristic(const Position& p1, const Position& p2) { uint16_t dx = std::abs(p1.getX() - p2.getX()); uint16_t dy = std::abs(p1.getY() - p2.getY()); - return 10 * (dx + dy); + return MAP_NORMALWALKCOST * (dx + dy); } bool Map::getPathMatching(const Creature& creature, const Position& targetPos, std::vector& dirList, From 0ce7cb5e8874e7313536e01c2bb63e08663e98b1 Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Wed, 25 Jun 2025 08:05:11 -0700 Subject: [PATCH 19/29] Fix fleeing monster slow walk when player isn't moving --- src/map.cpp | 9 ++++----- src/monster.cpp | 9 ++++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index f5b1576e81..e89115940b 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -737,12 +737,11 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s pos.x = x + allNeighbors[i].first; pos.y = y + allNeighbors[i].second; - if (fpp.maxSearchDist != 0 && - (startPos.getDistanceX(pos) > fpp.maxSearchDist || startPos.getDistanceY(pos) > fpp.maxSearchDist)) { + int32_t startDistanceToNode = startPos.getDistanceX(pos) + startPos.getDistanceY(pos); + if (fpp.maxSearchDist != 0 && startDistanceToNode > fpp.maxSearchDist) { + continue; + } else if (fpp.maxSearchDist == 0 && (startDistanceToNode > (Map::maxViewportX + Map::maxViewportY))) { continue; - } else if ((startPos.getDistanceX(pos) + startPos.getDistanceY(pos) > - Map::maxViewportX + Map::maxViewportY)) { - break; } if (fpp.keepDistance && !pathCondition.isInRange(startPos, pos, fpp)) { diff --git a/src/monster.cpp b/src/monster.cpp index 4766f43bc7..23cedb9b1b 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1085,7 +1085,14 @@ bool Monster::walkToSpawn() return true; } -void Monster::onWalk() { Creature::onWalk(); } +void Monster::onWalk() +{ + Creature::onWalk(); + + if (isFleeing()) { + forceUpdatePath(); + } +} void Monster::onWalkComplete() { From aff3db1f0d71b5279ae25ca0898426c02a39ca4e Mon Sep 17 00:00:00 2001 From: gesior Date: Sat, 28 Jun 2025 19:37:07 +0200 Subject: [PATCH 20/29] Allocate AStarNode on stack; clear nodes in destructor --- src/map.cpp | 16 ++++++---------- src/map.h | 4 ++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index e89115940b..8937ec8bf5 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -717,7 +717,6 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s iterations++; if (iterations >= Map::nodeReserveSize) { - nodes.clear(); return false; } @@ -819,8 +818,6 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s n = nodes.getBestNode(); } - nodes.clear(); - if (!found) { return false; } @@ -871,19 +868,18 @@ AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() createNode(nullptr, x, y, 0, 0); } -void AStarNodes::clear() +AStarNodes::~AStarNodes() { - nodes.clear(); - std::priority_queue, NodeCompare>().swap(openSet); - visited.clear(); - nodeMap.clear(); + for (auto* node : nodes) { + delete node; + } } AStarNode* AStarNodes::createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f) { uint32_t key = hashCoord(x, y); - nodes.emplace_back(AStarNode{parent, x, y, g, f}); - AStarNode* node = &nodes.back(); + nodes.emplace_back(new AStarNode(parent, x, y, g, f)); + AStarNode* node = nodes.back(); nodeMap[key] = node; openSet.push(node); return node; diff --git a/src/map.h b/src/map.h index f830a3d2d4..a3f8f71195 100644 --- a/src/map.h +++ b/src/map.h @@ -31,7 +31,7 @@ class AStarNodes { public: AStarNodes(uint16_t x, uint16_t y); - void clear(); + ~AStarNodes(); AStarNode* createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f); AStarNode* getBestNode(); @@ -41,7 +41,7 @@ class AStarNodes static uint16_t getTileWalkCost(const Creature& creature, const Tile* tile); private: - std::vector nodes; + std::vector nodes; std::unordered_map nodeMap; std::unordered_set visited; From 63daa791fec95240f1d728f02aac9835f3593650 Mon Sep 17 00:00:00 2001 From: Nathan Heinz Date: Sat, 28 Jun 2025 17:54:04 -0700 Subject: [PATCH 21/29] Update monster.cpp --- src/monster.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/monster.cpp b/src/monster.cpp index 23cedb9b1b..b79fb265c9 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1089,8 +1089,11 @@ void Monster::onWalk() { Creature::onWalk(); - if (isFleeing()) { - forceUpdatePath(); + if (attackedCreature || followCreature && isFleeing()) { + if (lastPathUpdate - OTSYS_TIME() > 0) { + g_dispatcher.addTask(createTask([id = getID()]() { g_game.updateCreatureWalk(id); })); + lastPathUpdate = OTSYS_TIME() + getNumber(ConfigManager::PATHFINDING_DELAY); + } } } From 43f8b41881716af65f6a3072e828d1961cd4915b Mon Sep 17 00:00:00 2001 From: Nathan Heinz Date: Sun, 29 Jun 2025 07:57:24 -0700 Subject: [PATCH 22/29] Update monster.cpp --- src/monster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/monster.cpp b/src/monster.cpp index b79fb265c9..0cd91f67c6 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1089,7 +1089,7 @@ void Monster::onWalk() { Creature::onWalk(); - if (attackedCreature || followCreature && isFleeing()) { + if ((attackedCreature || followCreature) && isFleeing()) { if (lastPathUpdate - OTSYS_TIME() > 0) { g_dispatcher.addTask(createTask([id = getID()]() { g_game.updateCreatureWalk(id); })); lastPathUpdate = OTSYS_TIME() + getNumber(ConfigManager::PATHFINDING_DELAY); From 319cad73941b64cbee5471bbda1209753bec7922 Mon Sep 17 00:00:00 2001 From: Nathan Heinz Date: Mon, 30 Jun 2025 01:13:57 -0700 Subject: [PATCH 23/29] Update map.h --- src/map.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.h b/src/map.h index a3f8f71195..f830a3d2d4 100644 --- a/src/map.h +++ b/src/map.h @@ -31,7 +31,7 @@ class AStarNodes { public: AStarNodes(uint16_t x, uint16_t y); - ~AStarNodes(); + void clear(); AStarNode* createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f); AStarNode* getBestNode(); @@ -41,7 +41,7 @@ class AStarNodes static uint16_t getTileWalkCost(const Creature& creature, const Tile* tile); private: - std::vector nodes; + std::vector nodes; std::unordered_map nodeMap; std::unordered_set visited; From 947f2920a95acb5032ad1562cbbc6db0378352be Mon Sep 17 00:00:00 2001 From: Nathan Heinz Date: Mon, 30 Jun 2025 01:15:00 -0700 Subject: [PATCH 24/29] Update map.cpp --- src/map.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 8937ec8bf5..294c99361d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -717,6 +717,7 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s iterations++; if (iterations >= Map::nodeReserveSize) { + nodes.clear(); return false; } @@ -819,6 +820,7 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s } if (!found) { + nodes.clear(); return false; } @@ -856,6 +858,8 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s found = found->parent; } + + nodes.clear(); return true; } @@ -868,18 +872,21 @@ AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() createNode(nullptr, x, y, 0, 0); } -AStarNodes::~AStarNodes() +void AStarNodes::clear() { - for (auto* node : nodes) { - delete node; + nodes.clear(); + visited.clear(); + nodeMap.clear(); + while (!openSet.empty()) { + openSet.pop(); } } AStarNode* AStarNodes::createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f) { uint32_t key = hashCoord(x, y); - nodes.emplace_back(new AStarNode(parent, x, y, g, f)); - AStarNode* node = nodes.back(); + nodes.emplace_back(AStarNode{parent, x, y, g, f}); + AStarNode* node = &nodes.back(); nodeMap[key] = node; openSet.push(node); return node; From c68fe320ef4c4829879f8a56df07688a88f9577b Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Mon, 30 Jun 2025 01:49:41 -0700 Subject: [PATCH 25/29] Increase alloc size. --- src/map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.h b/src/map.h index f830a3d2d4..df2f483f28 100644 --- a/src/map.h +++ b/src/map.h @@ -161,7 +161,7 @@ class Map static constexpr int32_t maxViewportY = 11; // min value: maxClientViewportY + 1 static constexpr int32_t maxClientViewportX = 8; static constexpr int32_t maxClientViewportY = 6; - static constexpr int16_t nodeReserveSize = (maxViewportX + maxViewportY) * 7; + static constexpr int16_t nodeReserveSize = (maxViewportX + maxViewportY) * 12; uint32_t clean() const; From 8e66a44a05e53c804fb30095fff9afdb35068f9b Mon Sep 17 00:00:00 2001 From: gesior Date: Mon, 30 Jun 2025 10:57:51 +0200 Subject: [PATCH 26/29] Limit number of nodes to reserved nodes --- src/map.cpp | 20 +++++++------------- src/map.h | 3 +-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 294c99361d..30fd46d4b5 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -717,7 +717,6 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s iterations++; if (iterations >= Map::nodeReserveSize) { - nodes.clear(); return false; } @@ -812,7 +811,10 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s neighborNode->parent = n; } else { // Does not exist in the open/closed list, create a new node - nodes.createNode(n, pos.x, pos.y, g, newf); + if (!nodes.createNode(n, pos.x, pos.y, g, newf)) { + // Limit of nodes reached + return false; + } } } @@ -820,7 +822,6 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s } if (!found) { - nodes.clear(); return false; } @@ -859,7 +860,6 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s found = found->parent; } - nodes.clear(); return true; } @@ -872,18 +872,12 @@ AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() createNode(nullptr, x, y, 0, 0); } -void AStarNodes::clear() +AStarNode* AStarNodes::createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f) { - nodes.clear(); - visited.clear(); - nodeMap.clear(); - while (!openSet.empty()) { - openSet.pop(); + if (nodes.size() == Map::nodeReserveSize) { + return nullptr; } -} -AStarNode* AStarNodes::createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f) -{ uint32_t key = hashCoord(x, y); nodes.emplace_back(AStarNode{parent, x, y, g, f}); AStarNode* node = &nodes.back(); diff --git a/src/map.h b/src/map.h index df2f483f28..e435c4022d 100644 --- a/src/map.h +++ b/src/map.h @@ -31,7 +31,6 @@ class AStarNodes { public: AStarNodes(uint16_t x, uint16_t y); - void clear(); AStarNode* createNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f); AStarNode* getBestNode(); @@ -161,7 +160,7 @@ class Map static constexpr int32_t maxViewportY = 11; // min value: maxClientViewportY + 1 static constexpr int32_t maxClientViewportX = 8; static constexpr int32_t maxClientViewportY = 6; - static constexpr int16_t nodeReserveSize = (maxViewportX + maxViewportY) * 12; + static constexpr int16_t nodeReserveSize = (maxViewportX + maxViewportY) * 7; uint32_t clean() const; From 4eeaba3739345e5f4939392d1013fcd3be9e016f Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Mon, 30 Jun 2025 03:19:07 -0700 Subject: [PATCH 27/29] Adjust iterations and reserve size --- src/map.cpp | 4 ++-- src/map.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 30fd46d4b5..f87b4e0884 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -711,12 +711,12 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s AStarNode* found = nullptr; int32_t bestMatch = 0; - uint8_t iterations = 0; + uint16_t iterations = 0; AStarNode* n = nodes.getBestNode(); while (n) { iterations++; - if (iterations >= Map::nodeReserveSize) { + if (iterations >= Map::maxViewportX * Map::maxViewportY) { return false; } diff --git a/src/map.h b/src/map.h index e435c4022d..208ae01812 100644 --- a/src/map.h +++ b/src/map.h @@ -160,7 +160,7 @@ class Map static constexpr int32_t maxViewportY = 11; // min value: maxClientViewportY + 1 static constexpr int32_t maxClientViewportX = 8; static constexpr int32_t maxClientViewportY = 6; - static constexpr int16_t nodeReserveSize = (maxViewportX + maxViewportY) * 7; + static constexpr int16_t nodeReserveSize = static_cast((maxViewportX * maxViewportY * 3) / 2); uint32_t clean() const; From 9664797d5bb15e9a7213d53e4e4a886cd20a2ebc Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Mon, 30 Jun 2025 18:14:41 -0700 Subject: [PATCH 28/29] Small fixes --- src/map.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index f87b4e0884..c1fb465967 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -687,16 +687,16 @@ bool Map::getPathMatching(const Creature& creature, const Position& targetPos, s return false; } - uint16_t distanceX = startPos.getDistanceX(targetPos); - uint16_t distanceY = startPos.getDistanceY(targetPos); + int32_t distanceX = startPos.getDistanceX(targetPos); + int32_t distanceY = startPos.getDistanceY(targetPos); // We are next to our target. Let dance step decide. if (fpp.maxTargetDist <= 1 && distanceX <= 1 && distanceY <= 1) { return true; } // Don't update path. The target is too far away. - int32_t maxDistanceX = fpp.maxSearchDist ? fpp.maxSearchDist : Map::maxClientViewportX + 1; - int32_t maxDistanceY = fpp.maxSearchDist ? fpp.maxSearchDist : Map::maxClientViewportY + 1; + int32_t maxDistanceX = fpp.maxSearchDist ? fpp.maxSearchDist : Map::maxViewportX + 1; + int32_t maxDistanceY = fpp.maxSearchDist ? fpp.maxSearchDist : Map::maxViewportY + 1; if (distanceX > maxDistanceX || distanceY > maxDistanceY) { return false; } From 6dd995381ea25bbc206553fef046c482e835682c Mon Sep 17 00:00:00 2001 From: NRH-AA Date: Mon, 30 Jun 2025 18:16:27 -0700 Subject: [PATCH 29/29] Monster.cpp change --- src/monster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/monster.cpp b/src/monster.cpp index 0cd91f67c6..0805bfe213 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1090,7 +1090,7 @@ void Monster::onWalk() Creature::onWalk(); if ((attackedCreature || followCreature) && isFleeing()) { - if (lastPathUpdate - OTSYS_TIME() > 0) { + if (lastPathUpdate > OTSYS_TIME()) { g_dispatcher.addTask(createTask([id = getID()]() { g_game.updateCreatureWalk(id); })); lastPathUpdate = OTSYS_TIME() + getNumber(ConfigManager::PATHFINDING_DELAY); }