diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index 97ade2e76..791d8081a 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -1060,7 +1060,14 @@ bool MovementAction::IsWaitingForLastMove(MovementPriority priority) if (priority > lastMove.priority) return false; - // heuristic 5s + // Stale delay bypass: if the bot has actually stopped moving (spline + // finalized + no movement flags), the delay is meaningless — common + // after combat ends with MOVEMENT_COMBAT priority still recorded, + // which would otherwise silently gate every non-combat MoveFarTo / + // MoveRandomNear request until the leftover delay expires. + if (bot->movespline->Finalized() && !bot->isMoving()) + return false; + if (lastMove.lastdelayTime + lastMove.msTime > getMSTime()) return true; diff --git a/src/Mgr/Travel/TravelNode.cpp b/src/Mgr/Travel/TravelNode.cpp index 9fa611764..0ed50bd76 100644 --- a/src/Mgr/Travel/TravelNode.cpp +++ b/src/Mgr/Travel/TravelNode.cpp @@ -1280,17 +1280,37 @@ bool TravelNodeMap::GetFullPath(TravelPlan& plan, plan.Reset(); plan.destination = destination; - // mmap-probe first: if a 40-step probe reaches dest, skip the - // graph entirely — a direct walk beats a node hop. + // mmap-probe first: if a 40-step probe makes meaningful progress, + // prefer it over the graph. Loosened from "reaches within spellDistance" + // because the strict gate falls through to graph routing whenever the + // probe stops a few yards short of the destination (e.g., bot can't + // reach the exact GO position, or destination is inside an area the + // probe can't fully enter). Graph paths come from DB-cached walk + // edges baked at offline generation time and can route through + // terrain that current mmaps treat as unwalkable. + // + // Accept the probe if EITHER: + // (a) it reaches within 30y of destination, OR + // (b) it makes >50% progress and got at least 30y total if (botPos.GetMapId() == destination.GetMapId()) { std::vector probe = destination.getPathFromPath({botPos}, bot, 40); - if (probe.size() >= 2 && destination.isPathTo(probe, sPlayerbotAIConfig.spellDistance)) + if (probe.size() >= 2) { - plan.steps.addPoint(botPos, PathNodeType::NODE_PREPATH); - for (size_t i = 1; i < probe.size(); ++i) - plan.steps.addPoint(probe[i], PathNodeType::NODE_PATH); - return true; + float const totalDist = botPos.distance(destination); + float const probeEndToDest = destination.distance(probe.back()); + float const probeProgress = totalDist - probeEndToDest; + + bool const closeEnough = probeEndToDest < 30.0f; + bool const meaningfulProgress = probeProgress > totalDist * 0.5f && probeProgress > 30.0f; + + if (closeEnough || meaningfulProgress) + { + plan.steps.addPoint(botPos, PathNodeType::NODE_PREPATH); + for (size_t i = 1; i < probe.size(); ++i) + plan.steps.addPoint(probe[i], PathNodeType::NODE_PATH); + return true; + } } }