Compare commits

...

4 Commits

Author SHA1 Message Date
bash
3837425cad Revert "feat(Core/RPG): Path to elevated quest giver from POI marker"
This reverts commit 9ce874269d4a88cbada81bbfed69665400527240.
2026-05-16 20:22:20 +02:00
bash
9ce874269d feat(Core/RPG): Path to elevated quest giver from POI marker 2026-05-16 20:01:21 +02:00
bash
b703a83679 fix(Core/Travel): Trust travelnode waypoints when AC mmap rejects segments 2026-05-16 16:48:10 +02:00
bash
9f3a8a49f8 fix(Core/Travel): Ride active node plan to completion across threshold 2026-05-16 16:41:23 +02:00
2 changed files with 21 additions and 35 deletions

View File

@ -3221,34 +3221,26 @@ bool MovementAction::RefineWalkPoints(std::vector<G3D::Vector3>& walkPoints)
WorldPosition aPos(mapId, a.x, a.y, a.z); WorldPosition aPos(mapId, a.x, a.y, a.z);
WorldPosition bPos(mapId, b.x, b.y, b.z); WorldPosition bPos(mapId, b.x, b.y, b.z);
// Per-segment mmap query against the live navmesh. The // Per-segment mmap query: routes around geometry the offline
// travel-node graph stores offline-baked waypoints; if the // graph didn't account for, or returns empty if unreachable.
// straight line A->B crosses geometry the live navmesh has
// (mountain, ledge, model edit since offline gen), this
// returns either an mmap-routed path around it (NORMAL/
// INCOMPLETE) or empty (NOT_USING_PATH was rejected as
// "would walk through walls").
std::vector<WorldPosition> segPath = bPos.getPathStepFrom(aPos, bot); std::vector<WorldPosition> segPath = bPos.getPathStepFrom(aPos, bot);
if (segPath.empty()) // Trust the raw waypoint pair when mmap can't validate it —
// navmesh gaps/tile-edge artifacts on short segments shouldn't
// kill an active plan. Travelnode waypoints are authoritative.
bool const trustRaw = segPath.empty() ||
TravelPath::IsPathCheating(segPath, aPos.distance(bPos));
if (trustRaw)
{ {
// Live mmap refuses A->B. Caller should abort the plan if (i == 0)
// and let MoveFarTo's own probe re-derive a route. refined.emplace_back(a);
return false; refined.emplace_back(b);
continue;
} }
// Reject "pathfinder cheating" — same checks the offline gen // Include the first segment's start; skip subsequent starts
// applies to BuildPath. Catches cached segments where the // to avoid duplicating the prior segment's tail.
// live navmesh still produces a near-vertical hop or a
// 2-point straight line through geometry.
if (TravelPath::IsPathCheating(segPath, aPos.distance(bPos)))
{
return false;
}
// First segment: include its start point so the spline
// begins from the original A. Later segments: skip the first
// point — it duplicates the previous segment's tail.
size_t startK = (i == 0) ? 0 : 1; size_t startK = (i == 0) ? 0 : 1;
for (size_t k = startK; k < segPath.size(); ++k) for (size_t k = startK; k < segPath.size(); ++k)
refined.emplace_back(segPath[k].GetPositionX(), refined.emplace_back(segPath[k].GetPositionX(),

View File

@ -163,13 +163,12 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
// waypoint spline at dest. // waypoint spline at dest.
bool tryNodes = (dis >= nodeFirstDis && sPlayerbotAIConfig.enableTravelNodes); bool tryNodes = (dis >= nodeFirstDis && sPlayerbotAIConfig.enableTravelNodes);
// If a node plan is already active, ride it — but only if its // Ride an active plan to completion as long as its destination
// destination still matches the requested dest. Otherwise the // still matches. Remaining distance can drop below nodeFirstDis
// old plan (e.g. built toward a quest objective POI) would keep // mid-route (e.g. crossing a zone border near the target); killing
// driving the bot after the caller switched targets (e.g. to a // the plan here would replace remaining waypoints with raw mmap
// turn-in NPC). cmangos's ResolveMovePath dodges this by being // and often produce a u-turn. Clear only on dest change.
// stateless; we have a long-lived plan flag, so check explicitly. if (botAI->rpgInfo.HasActiveTravelPlan())
if (tryNodes && botAI->rpgInfo.HasActiveTravelPlan())
{ {
if (botAI->rpgInfo.travelPlan.destination.distance(dest) > 10.0f) if (botAI->rpgInfo.travelPlan.destination.distance(dest) > 10.0f)
botAI->rpgInfo.ClearTravel(); botAI->rpgInfo.ClearTravel();
@ -192,11 +191,6 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
} }
// Graph returned no plan — fall through to mmap probe. // Graph returned no plan — fall through to mmap probe.
} }
else if (botAI->rpgInfo.HasActiveTravelPlan())
{
// Move dropped below node-first threshold — drop any leftover plan.
botAI->rpgInfo.ClearTravel();
}
// 40-step chained mmap probe — fallback when the node graph // 40-step chained mmap probe — fallback when the node graph
// returned no plan (or for short moves below nodeFirstDis). // returned no plan (or for short moves below nodeFirstDis).