diff --git a/CMakeLists.txt b/CMakeLists.txt index 21c3d4dfc8..bad49a032e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,4 +130,4 @@ if(NOT SKIP_GIT) endif() ### END Git Version ### -target_precompile_headers(tfs PUBLIC src/otpch.h) +target_precompile_headers(tfs PUBLIC src/otpch.h) \ No newline at end of file diff --git a/config.lua.dist b/config.lua.dist index 8c0498d196..96c1221bd3 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -37,6 +37,13 @@ replaceKickOnLogin = true maxPacketsPerSecond = 25 enableTwoFactorAuth = true +-- Pathfinding +-- pathfindingInterval handles how often paths are force drawn +-- pathfindingDelay delays any recently drawn paths from drawing again +-- pathfindingDelay does not delay pathfindingInterval +pathfindingInterval = 200 +pathfindingDelay = 300 + -- Deaths -- NOTE: Leave deathLosePercent as -1 if you want to use the default -- death penalty formula. For the old formula, set it to 10. For diff --git a/src/configmanager.cpp b/src/configmanager.cpp index f3ec1170f0..976558d6cf 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -292,6 +292,8 @@ bool ConfigManager::load() integer[QUEST_TRACKER_PREMIUM_LIMIT] = getGlobalNumber(L, "questTrackerPremiumLimit", 15); integer[STAMINA_REGEN_MINUTE] = getGlobalNumber(L, "timeToRegenMinuteStamina", 3 * 60); integer[STAMINA_REGEN_PREMIUM] = getGlobalNumber(L, "timeToRegenMinutePremiumStamina", 6 * 60); + integer[PATHFINDING_INTERVAL] = getGlobalNumber(L, "pathfindingInterval", 200); + integer[PATHFINDING_DELAY] = getGlobalNumber(L, "pathfindingDelay", 300); expStages = loadXMLStages(); if (expStages.empty()) { diff --git a/src/configmanager.h b/src/configmanager.h index 8268afc107..5c0ab0b0fa 100644 --- a/src/configmanager.h +++ b/src/configmanager.h @@ -121,6 +121,8 @@ enum integer_config_t QUEST_TRACKER_PREMIUM_LIMIT, STAMINA_REGEN_MINUTE, STAMINA_REGEN_PREMIUM, + PATHFINDING_INTERVAL, + PATHFINDING_DELAY, LAST_INTEGER_CONFIG /* this must be the last one */ }; diff --git a/src/creature.cpp b/src/creature.cpp index 09cf1047e8..dcb872a3e4 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -135,20 +135,6 @@ void Creature::onThink(uint32_t interval) blockTicks = 0; } - if (followCreature) { - walkUpdateTicks += interval; - if (forceUpdateFollowPath || walkUpdateTicks >= 2000) { - walkUpdateTicks = 0; - forceUpdateFollowPath = false; - isUpdatingPath = true; - } - } - - if (isUpdatingPath) { - isUpdatingPath = false; - goToFollowCreature(); - } - // scripting event - onThink const CreatureEventList& thinkEvents = getCreatureEvents(CREATURE_EVENT_THINK); for (CreatureEvent* thinkEvent : thinkEvents) { @@ -156,6 +142,16 @@ void Creature::onThink(uint32_t interval) } } +void Creature::forceUpdatePath() +{ + if (!attackedCreature && !followCreature) { + return; + } + + lastPathUpdate = OTSYS_TIME() + getNumber(ConfigManager::PATHFINDING_DELAY); + g_dispatcher.addTask(createTask([id = getID()]() { g_game.updateCreatureWalk(id); })); +} + void Creature::onAttacking(uint32_t interval) { if (!attackedCreature) { @@ -190,8 +186,6 @@ void Creature::onWalk() player->sendCancelMessage(ret); player->sendCancelWalk(); } - - forceUpdateFollowPath = true; } } else { stopEventWalk(); @@ -202,6 +196,9 @@ void Creature::onWalk() } } + removeFollowers(); + updateFollowersPaths(); + if (cancelNextWalk) { listWalkDir.clear(); onWalkAborted(); @@ -212,6 +209,13 @@ void Creature::onWalk() eventWalk = 0; addEventWalk(); } + + if (attackedCreature || followCreature) { + if (lastPathUpdate - OTSYS_TIME() > 0) { + g_dispatcher.addTask(createTask([id = getID()]() { g_game.updateCreatureWalk(id); })); + lastPathUpdate = OTSYS_TIME() + getNumber(ConfigManager::PATHFINDING_DELAY); + } + } } void Creature::onWalk(Direction& dir) @@ -415,10 +419,6 @@ void Creature::onCreatureMove(Creature* creature, const Tile* newTile, const Pos } if (creature == followCreature || (creature == this && followCreature)) { - if (hasFollowPath) { - isUpdatingPath = true; - } - if (newPos.z != oldPos.z || !canSee(followCreature->getPosition())) { onCreatureDisappear(followCreature, false); } @@ -733,6 +733,7 @@ void Creature::setAttackedCreature(Creature* creature) } attackedCreature = creature; + creature->addFollower(this); onAttackedCreature(attackedCreature); attackedCreature->onAttacked(); @@ -763,7 +764,7 @@ void Creature::getPathSearchParams(const Creature*, FindPathParams& fpp) const { fpp.fullPathSearch = !hasFollowPath; fpp.clearSight = true; - fpp.maxSearchDist = 12; + fpp.maxSearchDist = Map::maxViewportX + Map::maxViewportY; fpp.minTargetDist = 1; fpp.maxTargetDist = 1; } @@ -780,6 +781,8 @@ void Creature::setFollowCreature(Creature* creature) } followCreature = creature; + creature->addFollower(this); + hasFollowPath = false; onFollowCreature(creature); } @@ -804,13 +807,64 @@ void Creature::onFollowCreature(const Creature*) listWalkDir.clear(); onWalkAborted(); } +} - hasFollowPath = false; - forceUpdateFollowPath = false; - isUpdatingPath = true; +void Creature::onUnfollowCreature() { hasFollowPath = false; } + +// Pathfinding Events +bool Creature::isFollower(Creature* creature) +{ + auto it = std::find(followers.begin(), followers.end(), creature); + return it != followers.end(); +} + +void Creature::addFollower(Creature* creature) +{ + if (!isFollower(creature)) { + followers.push_back(creature); + } } -void Creature::onUnfollowCreature() { isUpdatingPath = false; } +void Creature::removeFollowers() +{ + const Position& position = getPosition(); + + followers.erase(std::remove_if(followers.begin(), followers.end(), + [&position](Creature* creature) { + const Position& followerPosition = creature->getPosition(); + uint16_t distance = position.getDistanceX(followerPosition) + + position.getDistanceY(followerPosition); + return distance >= Map::maxViewportX + Map::maxViewportY || + position.z != followerPosition.z; + }), + followers.end()); +} + +void Creature::updateFollowersPaths() +{ + if (followers.empty()) { + return; + } + + const Position& thisPosition = getPosition(); + for (const auto& follower : followers) { + if (follower != nullptr) { + const Position& followerPosition = follower->getPosition(); + + if (follower->lastPathUpdate - OTSYS_TIME() > 0) { + continue; + } + + if (thisPosition.getDistanceX(followerPosition) >= Map::maxViewportX || + thisPosition.getDistanceY(followerPosition) >= Map::maxViewportY) { + continue; + } + + g_dispatcher.addTask(createTask([id = follower->getID()]() { g_game.updateCreatureWalk(id); })); + follower->lastPathUpdate = OTSYS_TIME() + getNumber(ConfigManager::PATHFINDING_DELAY); + } + } +} double Creature::getDamageRatio(Creature* attacker) const { @@ -1437,7 +1491,7 @@ bool Creature::isInvisible() const bool Creature::getPathTo(const Position& targetPos, std::vector& dirList, const FindPathParams& fpp) const { - return g_game.map.getPathMatching(*this, dirList, FrozenPathingConditionCall(targetPos), fpp); + return g_game.map.getPathMatching(*this, targetPos, dirList, FrozenPathingConditionCall(targetPos), fpp); } bool Creature::getPathTo(const Position& targetPos, std::vector& dirList, int32_t minTargetDist, diff --git a/src/creature.h b/src/creature.h index 3e3a8c2117..c16ac6c48f 100644 --- a/src/creature.h +++ b/src/creature.h @@ -55,6 +55,7 @@ struct FindPathParams static constexpr int32_t EVENT_CREATURECOUNT = 10; static constexpr int32_t EVENT_CREATURE_THINK_INTERVAL = 1000; static constexpr int32_t EVENT_CHECK_CREATURE_INTERVAL = (EVENT_CREATURE_THINK_INTERVAL / EVENT_CREATURECOUNT); + static constexpr uint32_t CREATURE_ID_MIN = 0x10000000; static constexpr uint32_t CREATURE_ID_MAX = std::numeric_limits::max(); @@ -199,6 +200,14 @@ class Creature : virtual public Thing virtual void onFollowCreature(const Creature*); virtual void onUnfollowCreature(); + // Pathfinding functions + bool isFollower(Creature* creature); + void addFollower(Creature* creature); + void removeFollowers(); + + // Pathfinding events + void updateFollowersPaths(); + // combat functions Creature* getAttackedCreature() { return attackedCreature; } virtual void setAttackedCreature(Creature* creature); @@ -286,6 +295,7 @@ class Creature : virtual public Thing void setCreatureLight(LightInfo lightInfo); virtual void onThink(uint32_t interval); + virtual void forceUpdatePath(); void onAttacking(uint32_t interval); virtual void onWalk(); virtual bool getNextStep(Direction& dir, uint32_t& flags); @@ -385,8 +395,10 @@ class Creature : virtual public Thing Creature* attackedCreature = nullptr; Creature* master = nullptr; Creature* followCreature = nullptr; + std::vector followers; uint64_t lastStep = 0; + int64_t lastPathUpdate = 0; uint32_t referenceCounter = 0; uint32_t id = 0; uint32_t scriptEventsBitField = 0; @@ -413,14 +425,12 @@ class Creature : virtual public Thing Skulls_t skull = SKULL_NONE; bool isInternalRemoved = false; - bool isUpdatingPath = false; bool creatureCheck = false; bool inCheckCreaturesVector = false; bool skillLoss = true; bool lootDrop = true; bool cancelNextWalk = false; bool hasFollowPath = false; - bool forceUpdateFollowPath = false; bool hiddenHealth = false; bool canUseDefense = true; bool movementBlocked = false; diff --git a/src/game.cpp b/src/game.cpp index 9a871adb52..58f0afaf3c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -66,6 +66,8 @@ void Game::start(ServiceManager* manager) serviceManager = manager; g_scheduler.addEvent(createSchedulerTask(EVENT_CREATURE_THINK_INTERVAL, [this]() { checkCreatures(0); })); + g_scheduler.addEvent( + createSchedulerTask(getNumber(ConfigManager::PATHFINDING_INTERVAL), [this]() { updateCreaturesPath(0); })); g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, [this]() { checkDecay(); })); } @@ -3884,6 +3886,19 @@ void Game::checkCreatures(size_t index) cleanup(); } +void Game::updateCreaturesPath(size_t index) +{ + g_scheduler.addEvent(createSchedulerTask(getNumber(ConfigManager::PATHFINDING_INTERVAL), + [=, this]() { updateCreaturesPath((index + 1) % EVENT_CREATURECOUNT); })); + + auto& checkCreatureList = checkCreatureLists[index]; + for (Creature* creature : checkCreatureList) { + if (!creature->isDead()) { + creature->forceUpdatePath(); + } + } +} + void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta) { int32_t varSpeed = creature->getSpeed() - creature->getBaseSpeed(); diff --git a/src/game.h b/src/game.h index 1ad03f1454..3d8981f823 100644 --- a/src/game.h +++ b/src/game.h @@ -424,6 +424,7 @@ class Game void updateCreatureWalk(uint32_t creatureId); void checkCreatureAttack(uint32_t creatureId); void checkCreatures(size_t index); + void updateCreaturesPath(size_t index); bool combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* target, bool checkDefense, bool checkArmor, bool field, bool ignoreResistances = false); diff --git a/src/map.cpp b/src/map.cpp index 89a993d634..ce07509f35 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -649,37 +649,61 @@ const Tile* Map::canWalkTo(const Creature& creature, const Position& pos) const return tile; } -bool Map::getPathMatching(const Creature& creature, std::vector& dirList, +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; +} + +bool Map::getPathMatching(const Creature& creature, const Position& targetPos, std::vector& dirList, const FrozenPathingConditionCall& pathCondition, const FindPathParams& fpp) const { Position pos = creature.getPosition(); Position endPos; + const Position startPos = pos; - AStarNodes nodes(pos.x, pos.y); + // We can't walk, no need to create path. + if (creature.getSpeed() <= 0) { + return false; + } - int32_t bestMatch = 0; + // We can't get paths up or down floors. + if (startPos.getZ() != targetPos.getZ()) { + return false; + } - static int_fast32_t dirNeighbors[8][5][2] = { - {{-1, 0}, {0, 1}, {1, 0}, {1, 1}, {-1, 1}}, {{-1, 0}, {0, 1}, {0, -1}, {-1, -1}, {-1, 1}}, - {{-1, 0}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}}, {{0, 1}, {1, 0}, {0, -1}, {1, -1}, {1, 1}}, - {{1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}}, {{-1, 0}, {0, -1}, {-1, -1}, {1, -1}, {-1, 1}}, - {{0, 1}, {1, 0}, {1, -1}, {1, 1}, {-1, 1}}, {{-1, 0}, {0, 1}, {-1, -1}, {1, 1}, {-1, 1}}}; - static int_fast32_t allNeighbors[8][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}, {-1, -1}, {1, -1}, {1, 1}, {-1, 1}}; + // We are next to our target. Let dance step decide. + if (fpp.maxTargetDist <= 1 && startPos.getDistanceX(targetPos) <= 1 && startPos.getDistanceY(targetPos) <= 1) { + return true; + } - const Position startPos = pos; + // 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) { + return false; + } + + 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); AStarNode* found = nullptr; - while (fpp.maxSearchDist != 0 || nodes.getClosedNodes() < 100) { - AStarNode* n = nodes.getBestNode(); - if (!n) { - if (found) { - break; - } + int32_t bestMatch = 0; + uint8_t iterations = 0; + AStarNode* n = nodes.getBestNode(); + while (n) { + iterations++; + + if (iterations >= 120) { return false; } - const int_fast32_t x = n->x; - const int_fast32_t y = n->y; + const int32_t x = n->x; + const int32_t y = n->y; pos.x = x; pos.y = y; if (pathCondition(startPos, pos, fpp, bestMatch)) { @@ -690,44 +714,9 @@ bool Map::getPathMatching(const Creature& creature, std::vector& dirL } } - uint_fast32_t dirCount; - int_fast32_t* neighbors; - if (n->parent) { - const int_fast32_t offset_x = n->parent->x - x; - const int_fast32_t offset_y = n->parent->y - y; - if (offset_y == 0) { - if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_WEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_EAST]; - } - } else if (!fpp.allowDiagonal || offset_x == 0) { - if (offset_y == -1) { - neighbors = *dirNeighbors[DIRECTION_NORTH]; - } else { - neighbors = *dirNeighbors[DIRECTION_SOUTH]; - } - } else if (offset_y == -1) { - if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_NORTHWEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_NORTHEAST]; - } - } else if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_SOUTHWEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_SOUTHEAST]; - } - dirCount = fpp.allowDiagonal ? 5 : 3; - } else { - dirCount = 8; - neighbors = *allNeighbors; - } - - const int_fast32_t f = n->f; - for (uint_fast32_t i = 0; i < dirCount; ++i) { - pos.x = x + *neighbors++; - pos.y = y + *neighbors++; + for (uint8_t i = 0; i < 8; ++i) { + 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)) { @@ -749,10 +738,10 @@ bool Map::getPathMatching(const Creature& creature, std::vector& dirL } } - // The cost (g) for this neighbor - const int_fast32_t cost = AStarNodes::getMapWalkCost(n, pos); - const int_fast32_t extraCost = AStarNodes::getTileWalkCost(creature, tile); - const int_fast32_t newf = f + cost + extraCost; + // The cost to walk to this neighbor + const uint16_t g = n->g + AStarNodes::getMapWalkCost(n, pos) + AStarNodes::getTileWalkCost(creature, tile); + const uint16_t h = calculateHeuristic(pos, targetPos); + const uint16_t newf = h + g; if (neighborNode) { if (neighborNode->f <= newf) { @@ -760,22 +749,17 @@ bool Map::getPathMatching(const Creature& creature, std::vector& dirL continue; } + neighborNode->g = g; neighborNode->f = newf; neighborNode->parent = n; - nodes.openNode(neighborNode); + nodes.addNode(neighborNode); } else { // Does not exist in the open/closed list, create a new node - neighborNode = nodes.createOpenNode(n, pos.x, pos.y, newf); - if (!neighborNode) { - if (found) { - break; - } - return false; - } + nodes.createNewNode(n, pos.x, pos.y, g, newf); } } - nodes.closeNode(n); + n = nodes.getBestNode(); } if (!found) { @@ -821,89 +805,49 @@ bool Map::getPathMatching(const Creature& creature, std::vector& dirL // AStarNodes -AStarNodes::AStarNodes(uint32_t x, uint32_t y) : nodes(), openNodes() +AStarNodes::AStarNodes(uint16_t x, uint16_t y) : nodes(), nodeMap() { - curNode = 1; - closedNodes = 0; - openNodes[0] = true; - - AStarNode& startNode = nodes[0]; - startNode.parent = nullptr; - startNode.x = x; - startNode.y = y; - startNode.f = 0; - nodeTable[(x << 16) | y] = nodes; + // 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; } -AStarNode* AStarNodes::createOpenNode(AStarNode* parent, uint32_t x, uint32_t y, int_fast32_t f) +void AStarNodes::createNewNode(AStarNode* parent, uint16_t x, uint16_t y, uint16_t g, uint16_t f) { - if (curNode >= MAX_NODES) { - return nullptr; - } - - size_t retNode = curNode++; - openNodes[retNode] = true; + AStarNode* newNode = new AStarNode; + newNode->parent = parent; + newNode->x = x; + newNode->y = y; + newNode->g = g; + newNode->f = f; - AStarNode* node = nodes + retNode; - nodeTable[(x << 16) | y] = node; - node->parent = parent; - node->x = x; - node->y = y; - node->f = f; - return node; + nodes.emplace_back(newNode); + nodeMap[x][y] = newNode; } AStarNode* AStarNodes::getBestNode() { - if (curNode == 0) { + if (nodes.size() == 0) { return nullptr; } - int32_t best_node_f = std::numeric_limits::max(); - int32_t best_node = -1; - for (size_t i = 0; i < curNode; i++) { - if (openNodes[i] && nodes[i].f < best_node_f) { - best_node_f = nodes[i].f; - best_node = i; - } - } - - if (best_node >= 0) { - return nodes + best_node; - } - return nullptr; -} - -void AStarNodes::closeNode(AStarNode* node) -{ - size_t index = node - nodes; - assert(index < MAX_NODES); - openNodes[index] = false; - ++closedNodes; -} - -void AStarNodes::openNode(AStarNode* node) -{ - size_t index = node - nodes; - assert(index < MAX_NODES); - if (!openNodes[index]) { - openNodes[index] = true; - --closedNodes; - } -} - -int_fast32_t AStarNodes::getClosedNodes() const { return closedNodes; } - -AStarNode* AStarNodes::getNodeByPosition(uint32_t x, uint32_t y) -{ - auto it = nodeTable.find((x << 16) | y); - if (it == nodeTable.end()) { - return nullptr; - } - return it->second; + 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; } -int_fast32_t AStarNodes::getMapWalkCost(AStarNode* node, const Position& neighborPos) +uint16_t AStarNodes::getMapWalkCost(AStarNode* node, const Position& neighborPos) { if (std::abs(node->x - neighborPos.x) == std::abs(node->y - neighborPos.y)) { // diagonal movement extra cost @@ -912,9 +856,9 @@ int_fast32_t AStarNodes::getMapWalkCost(AStarNode* node, const Position& neighbo return MAP_NORMALWALKCOST; } -int_fast32_t AStarNodes::getTileWalkCost(const Creature& creature, const Tile* tile) +uint16_t AStarNodes::getTileWalkCost(const Creature& creature, const Tile* tile) { - int_fast32_t cost = 0; + uint16_t cost = 0; if (tile->getTopVisibleCreature(&creature)) { // destroy creature cost cost += MAP_NORMALWALKCOST * 3; diff --git a/src/map.h b/src/map.h index d907f97be3..386b4180e5 100644 --- a/src/map.h +++ b/src/map.h @@ -18,36 +18,31 @@ struct FindPathParams; struct AStarNode { AStarNode* parent; - int_fast32_t f; + uint16_t g; + uint16_t f; uint16_t x, y; }; -static constexpr int32_t MAX_NODES = 512; - -static constexpr int32_t MAP_NORMALWALKCOST = 10; -static constexpr int32_t MAP_DIAGONALWALKCOST = 25; +static constexpr uint16_t MAP_NORMALWALKCOST = 10; +static constexpr uint16_t MAP_DIAGONALWALKCOST = 25; class AStarNodes { public: - AStarNodes(uint32_t x, uint32_t y); + 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); }; - AStarNode* createOpenNode(AStarNode* parent, uint32_t x, uint32_t y, int_fast32_t f); AStarNode* getBestNode(); - void closeNode(AStarNode* node); - void openNode(AStarNode* node); - int_fast32_t getClosedNodes() const; - AStarNode* getNodeByPosition(uint32_t x, uint32_t y); + AStarNode* getNodeByPosition(uint16_t x, uint16_t y) { return nodeMap[x][y]; }; - static int_fast32_t getMapWalkCost(AStarNode* node, const Position& neighborPos); - static int_fast32_t getTileWalkCost(const Creature& creature, const Tile* tile); + static uint16_t getMapWalkCost(AStarNode* node, const Position& neighborPos); + static uint16_t getTileWalkCost(const Creature& creature, const Tile* tile); private: - AStarNode nodes[MAX_NODES]; - bool openNodes[MAX_NODES]; - std::unordered_map nodeTable; - size_t curNode; - int_fast32_t closedNodes; + std::vector nodes; + std::map> nodeMap; }; using SpectatorCache = std::map; @@ -242,7 +237,7 @@ class Map const Tile* canWalkTo(const Creature& creature, const Position& pos) const; - bool getPathMatching(const Creature& creature, std::vector& dirList, + bool getPathMatching(const Creature& creature, const Position& targetPos, std::vector& dirList, const FrozenPathingConditionCall& pathCondition, const FindPathParams& fpp) const; std::map waypoints; diff --git a/src/monster.cpp b/src/monster.cpp index f50bec1e5a..4766f43bc7 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -864,7 +864,7 @@ void Monster::doAttacking(uint32_t interval) } // ensure ranged creatures turn to player - if (!lookUpdated && lastMeleeAttack == 0) { + if (!lookUpdated && lastMeleeAttack == 0 && !isFleeing()) { updateLookDirection(); } diff --git a/src/player.cpp b/src/player.cpp index 356d04b837..45bd80406f 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1334,7 +1334,6 @@ void Player::onCreatureMove(Creature* creature, const Tile* newTile, const Posit Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); if (hasFollowPath && (creature == followCreature || (creature == this && followCreature))) { - isUpdatingPath = false; g_dispatcher.addTask([id = getID()]() { g_game.updateCreatureWalk(id); }); }