refactor(Core/Movement): DispatchPathPoints → DispatchMovement (TravelPath sig + transport sandwich)

This commit is contained in:
bash 2026-05-31 18:15:24 +02:00
parent d00ad8d327
commit dae09388ad
2 changed files with 53 additions and 27 deletions

View File

@ -338,7 +338,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// Path-aware funnel: ResolveMovePath → makeShortCut → // Path-aware funnel: ResolveMovePath → makeShortCut →
// UpcommingSpecialMovement/HandleSpecialMovement → ClipPath → // UpcommingSpecialMovement/HandleSpecialMovement → ClipPath →
// DispatchPathPoints. Matches the reference's MoveTo2 flow. // DispatchMovement. Matches the reference's MoveTo2 flow.
return MoveTo2(WorldPosition(mapId, x, y, z), return MoveTo2(WorldPosition(mapId, x, y, z),
idle, react, false, ignoreEnemyTargets, priority, lessDelay); idle, react, false, ignoreEnemyTargets, priority, lessDelay);
} }
@ -2993,12 +2993,7 @@ bool MovementAction::MoveTo2(WorldPosition endPos,
botAI->TellMasterNoFacing(tlog); botAI->TellMasterNoFacing(tlog);
} }
std::vector<WorldPosition> const& pts = path.getPointPath(); if (path.empty())
Movement::PointsArray points;
points.reserve(pts.size());
for (auto const& wp : pts)
points.emplace_back(wp.GetPositionX(), wp.GetPositionY(), wp.GetPositionZ());
if (points.empty())
return false; return false;
if (!bot->IsMounted() && !bot->IsInCombat() && if (!bot->IsMounted() && !bot->IsInCombat() &&
@ -3006,7 +3001,7 @@ bool MovementAction::MoveTo2(WorldPosition endPos,
botAI->DoSpecificAction("check mount state", Event(), true); botAI->DoSpecificAction("check mount state", Event(), true);
bool const dispatched = bool const dispatched =
DispatchPathPoints(endPos, points, "walk", priority, lessDelay); DispatchMovement(path, endPos, "walk", priority, lessDelay);
if (dispatched && !idle) if (dispatched && !idle)
ClearIdleState(); ClearIdleState();
@ -3014,12 +3009,20 @@ bool MovementAction::MoveTo2(WorldPosition endPos,
return dispatched; return dispatched;
} }
bool MovementAction::DispatchPathPoints(WorldPosition const& dest, bool MovementAction::DispatchMovement(TravelPath const& path,
Movement::PointsArray& points, WorldPosition const& dest,
char const* label, char const* label,
MovementPriority priority, MovementPriority priority,
bool lessDelay) bool lessDelay)
{ {
// Build the PointsArray from the TravelPath. Done here (not at the
// caller) so DispatchMovement can be invoked with a TravelPath
// directly, matching the reference's signature.
std::vector<WorldPosition> const& pts = path.getPointPath();
Movement::PointsArray points;
points.reserve(pts.size());
for (auto const& wp : pts)
points.emplace_back(wp.GetPositionX(), wp.GetPositionY(), wp.GetPositionZ());
if (points.empty()) if (points.empty())
return false; return false;
@ -3080,11 +3083,22 @@ bool MovementAction::DispatchPathPoints(WorldPosition const& dest,
if (bot->IsNonMeleeSpellCast(true)) if (bot->IsNonMeleeSpellCast(true))
bot->InterruptNonMeleeSpells(true); bot->InterruptNonMeleeSpells(true);
// Per-point terrain clamp. // Per-point terrain clamp with transport-passenger conversion
// sandwich: when on a transport, path coords are in transport-local
// space; UpdateAllowedPositionZ samples world terrain, so we convert
// local→world, snap, world→local. Without the sandwich, snapping a
// transport-relative point against world terrain produces garbage.
Transport* transport = bot->GetTransport();
for (auto& pt : points) for (auto& pt : points)
{
if (transport)
transport->CalculatePassengerPosition(pt.x, pt.y, pt.z);
bot->UpdateAllowedPositionZ(pt.x, pt.y, pt.z); bot->UpdateAllowedPositionZ(pt.x, pt.y, pt.z);
if (transport)
transport->CalculatePassengerOffset(pt.x, pt.y, pt.z);
}
// mm.Clear → MovePoint(last) → MoveSplinePath → WaitForReach. // mm.Clear → MovePoint(last) → MoveSplinePath.
MotionMaster* mm = bot->GetMotionMaster(); MotionMaster* mm = bot->GetMotionMaster();
mm->Clear(); mm->Clear();
@ -3102,7 +3116,8 @@ bool MovementAction::DispatchPathPoints(WorldPosition const& dest,
// WaitForReach equivalent: cache the dispatched target + duration on // WaitForReach equivalent: cache the dispatched target + duration on
// lastMove. Leave ~10y headroom on long paths so we re-evaluate // lastMove. Leave ~10y headroom on long paths so we re-evaluate
// before arrival. // before arrival. (Reference also calls WaitForReach here, which
// blocks the AI loop; we omit that — see header comment.)
float waitDist = totalDist > sPlayerbotAIConfig.reactDistance float waitDist = totalDist > sPlayerbotAIConfig.reactDistance
? std::max(totalDist - 10.0f, 0.0f) : totalDist; ? std::max(totalDist - 10.0f, 0.0f) : totalDist;
UnitMoveType const speedType = (moveMode == FORCED_MOVEMENT_WALK) ? MOVE_WALK : MOVE_RUN; UnitMoveType const speedType = (moveMode == FORCED_MOVEMENT_WALK) ? MOVE_WALK : MOVE_RUN;

View File

@ -65,7 +65,7 @@ protected:
// with makeShortCut, handles special head segments // with makeShortCut, handles special head segments
// (portal/area-trigger/transport/flight) via HandleSpecialMovement, // (portal/area-trigger/transport/flight) via HandleSpecialMovement,
// clips at hostile creatures via ClipPath (unless ignoreEnemyTargets), // clips at hostile creatures via ClipPath (unless ignoreEnemyTargets),
// and dispatches the resulting walk via DispatchPathPoints. // and dispatches the resulting walk via DispatchMovement.
// MoveTo(mapId,...) delegates here unless an intentional bypass // MoveTo(mapId,...) delegates here unless an intentional bypass
// (exact_waypoint / disableMoveSplinePath / flying / swimming / // (exact_waypoint / disableMoveSplinePath / flying / swimming /
// backwards) routes the move straight to DoMovePoint. // backwards) routes the move straight to DoMovePoint.
@ -75,13 +75,24 @@ protected:
MovementPriority priority = MovementPriority::MOVEMENT_NORMAL, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL,
bool lessDelay = false); bool lessDelay = false);
// Centralized walk dispatch. Applies inactive-bot teleport carve-out, // Centralized walk dispatch. Mirrors the reference's DispatchMovement
// masterWalking mode, pre-dispatch state cleanup (clear emote, stand, // shape: takes a TravelPath, builds the PointsArray internally,
// interrupt cast), per-point UpdateAllowedPositionZ, mm.Clear → // applies inactive-bot teleport carve-out, masterWalking mode,
// MovePoint(last) → MoveSplinePath, and a WaitForReach equivalent // pre-dispatch state cleanup (clear emote, stand, interrupt cast),
// that caches the destination + duration on lastMove. // transport-passenger coordinate sandwich
bool DispatchPathPoints(WorldPosition const& dest, // (CalculatePassengerPosition → UpdateAllowedPositionZ → Offset)
Movement::PointsArray& points, // around the per-point Z snap, mm.Clear → MovePoint(last) →
// MoveSplinePath. Caches the destination + duration on lastMove.
//
// Divergence from reference: reference ends with WaitForReach(size)
// which blocks the AI loop until the move completes. AC's combat
// callers (ReachCombatTo) currently funnel through MoveTo → MoveTo2
// → DispatchMovement; blocking the AI loop here would suspend combat
// re-evaluation for the full move duration. Until combat dispatch is
// restructured to bypass MoveTo2, the WaitForReach is deliberately
// omitted.
bool DispatchMovement(TravelPath const& path,
WorldPosition const& dest,
char const* label, char const* label,
MovementPriority priority = MovementPriority::MOVEMENT_NORMAL, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL,
bool lessDelay = false); bool lessDelay = false);