mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
fix(Core/Movement): MoveFarTo clears lastMove on collapse + drops AC single-point branch; DispatchPathPoints mirrors reference dispatch order (Clear -> MovePoint(last) -> MoveSplinePath)
This commit is contained in:
parent
c194daa8a1
commit
180f45899c
@ -100,11 +100,14 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Trim leading waypoints behind the bot, bridge with mmap probe if
|
// Trim leading waypoints behind the bot, bridge with mmap probe if
|
||||||
// the new head requires it. May empty the path (collapsed) — let
|
// the new head requires it. May empty the path (collapsed) — clear
|
||||||
// the next tick rebuild from a fresh start.
|
// the cached path explicitly so the next tick re-resolves cleanly.
|
||||||
path.makeShortCut(botPos, sPlayerbotAIConfig.reactDistance, bot);
|
path.makeShortCut(botPos, sPlayerbotAIConfig.reactDistance, bot);
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
|
{
|
||||||
|
lastMove.setPath(path);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Special head segment (portal / area-trigger / transport / flight)?
|
// Special head segment (portal / area-trigger / transport / flight)?
|
||||||
// UpcommingSpecialMovement cuts the path so the head is the special;
|
// UpcommingSpecialMovement cuts the path so the head is the special;
|
||||||
@ -150,24 +153,17 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
|||||||
botAI->TellMasterNoFacing(tlog);
|
botAI->TellMasterNoFacing(tlog);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk dispatch.
|
// Walk dispatch — DispatchPathPoints handles any path size (single
|
||||||
|
// point falls through to a MovePoint dispatch matching reference's
|
||||||
|
// DispatchMovement). No size<2 branch on this side.
|
||||||
std::vector<WorldPosition> const& pts = path.getPointPath();
|
std::vector<WorldPosition> const& pts = path.getPointPath();
|
||||||
Movement::PointsArray points;
|
Movement::PointsArray points;
|
||||||
points.reserve(pts.size());
|
points.reserve(pts.size());
|
||||||
for (auto const& wp : pts)
|
for (auto const& wp : pts)
|
||||||
points.emplace_back(wp.GetPositionX(), wp.GetPositionY(), wp.GetPositionZ());
|
points.emplace_back(wp.GetPositionX(), wp.GetPositionY(), wp.GetPositionZ());
|
||||||
|
|
||||||
if (points.size() < 2)
|
if (points.empty())
|
||||||
{
|
return false;
|
||||||
// Single-point fallback path (cmangos pattern: ResolveMovePath
|
|
||||||
// emits a single dest point if nothing else worked). Hand it
|
|
||||||
// to the engine's MovePoint via MoveTo.
|
|
||||||
EmitDebugMove("MoveFar", "single-point",
|
|
||||||
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ());
|
|
||||||
return MoveTo(dest.GetMapId(), dest.GetPositionX(),
|
|
||||||
dest.GetPositionY(), dest.GetPositionZ(),
|
|
||||||
false, false, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bot->IsMounted() && !bot->IsInCombat() &&
|
if (!bot->IsMounted() && !bot->IsInCombat() &&
|
||||||
bot->IsOutdoors() && bot->IsAlive())
|
bot->IsOutdoors() && bot->IsAlive())
|
||||||
@ -180,31 +176,20 @@ bool NewRpgBaseAction::DispatchPathPoints(WorldPosition const& dest,
|
|||||||
Movement::PointsArray& points,
|
Movement::PointsArray& points,
|
||||||
char const* label)
|
char const* label)
|
||||||
{
|
{
|
||||||
if (points.size() < 2)
|
if (points.empty())
|
||||||
return false;
|
|
||||||
|
|
||||||
// MoveFarTo runs makeShortCut + setPath upstream now, so no need
|
|
||||||
// for the local prefix-trim or lastMove.setPath here.
|
|
||||||
|
|
||||||
for (auto& pt : points)
|
|
||||||
bot->UpdateAllowedPositionZ(pt.x, pt.y, pt.z);
|
|
||||||
|
|
||||||
// ClipPath now runs at MoveFarTo level on the TravelPath before the
|
|
||||||
// points array is built. No per-dispatch clip here.
|
|
||||||
|
|
||||||
if (points.size() < 2)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
LastMovement& lastMove = AI_VALUE(LastMovement&, "last movement");
|
||||||
G3D::Vector3 const& last = points.back();
|
G3D::Vector3 const& last = points.back();
|
||||||
|
|
||||||
float totalDist = 0.f;
|
float totalDist = 0.f;
|
||||||
for (size_t i = 1; i < points.size(); ++i)
|
for (size_t i = 1; i < points.size(); ++i)
|
||||||
totalDist += (points[i] - points[i - 1]).length();
|
totalDist += (points[i] - points[i - 1]).length();
|
||||||
|
|
||||||
LastMovement& lastMove = AI_VALUE(LastMovement&, "last movement");
|
|
||||||
|
|
||||||
// Skip cosmetic walking for random bots with no nearby player —
|
// Skip cosmetic walking for random bots with no nearby player —
|
||||||
// teleport to the path tail and schedule a cooldown instead.
|
// teleport to the path tail and schedule a cooldown instead.
|
||||||
|
// (Reference equivalent: long-distance teleport in MoveTo2 gated
|
||||||
|
// on !detailedMove && !HasPlayerNearby. Our gate is IsRandomBot.)
|
||||||
if (sRandomPlayerbotMgr.IsRandomBot(bot))
|
if (sRandomPlayerbotMgr.IsRandomBot(bot))
|
||||||
{
|
{
|
||||||
WorldPosition tail(dest.GetMapId(), last.x, last.y, last.z);
|
WorldPosition tail(dest.GetMapId(), last.x, last.y, last.z);
|
||||||
@ -228,8 +213,11 @@ bool NewRpgBaseAction::DispatchPathPoints(WorldPosition const& dest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match master's walk pace when they're walking and within 5y.
|
// Match master's walk pace when they're walking and within 5y.
|
||||||
|
// Reference picks FORCED_MOVEMENT_FLIGHT if bot IsFlying.
|
||||||
ForcedMovement moveMode = FORCED_MOVEMENT_RUN;
|
ForcedMovement moveMode = FORCED_MOVEMENT_RUN;
|
||||||
if (Player* master = botAI->GetMaster())
|
if (bot->IsFlying())
|
||||||
|
moveMode = FORCED_MOVEMENT_FLIGHT;
|
||||||
|
else if (Player* master = botAI->GetMaster())
|
||||||
{
|
{
|
||||||
if (bot->IsFriendlyTo(master) && master->IsWalking() &&
|
if (bot->IsFriendlyTo(master) && master->IsWalking() &&
|
||||||
bot->GetExactDist2d(master) < 5.0f)
|
bot->GetExactDist2d(master) < 5.0f)
|
||||||
@ -238,19 +226,48 @@ bool NewRpgBaseAction::DispatchPathPoints(WorldPosition const& dest,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear emote/sit/cast so the spline can begin cleanly.
|
bool const generatePath = !bot->IsFlying() && !bot->isSwimming();
|
||||||
|
|
||||||
|
// Pre-dispatch normalization: clear looping emote, stand, interrupt
|
||||||
|
// non-melee cast. Reference does this at MoveTo2 level before
|
||||||
|
// DispatchMovement; we do it here at the equivalent point in the
|
||||||
|
// flow (immediately before the dispatch).
|
||||||
bot->ClearEmoteState();
|
bot->ClearEmoteState();
|
||||||
if (!bot->IsStandState())
|
if (!bot->IsStandState())
|
||||||
bot->SetStandState(UNIT_STAND_STATE_STAND);
|
bot->SetStandState(UNIT_STAND_STATE_STAND);
|
||||||
if (bot->IsNonMeleeSpellCast(true))
|
if (bot->IsNonMeleeSpellCast(true))
|
||||||
bot->InterruptNonMeleeSpells(true);
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
|
||||||
bot->GetMotionMaster()->Clear();
|
// Per-point terrain clamp (reference does this on the converted
|
||||||
bot->GetMotionMaster()->MoveSplinePath(&points, moveMode);
|
// pointPath). Skip transport-passenger conversion — our transport
|
||||||
|
// guard upstream prevents reaching here while on transport.
|
||||||
|
for (auto& pt : points)
|
||||||
|
bot->UpdateAllowedPositionZ(pt.x, pt.y, pt.z);
|
||||||
|
|
||||||
|
// Reference DispatchMovement: mm.Clear → MovePoint(last) (for
|
||||||
|
// non-free-flying or when generatePath disabled) → MovePath(all) →
|
||||||
|
// WaitForReach. The MovePoint primes the motion master with a
|
||||||
|
// single-target goal so even if MoveSplinePath fails to register a
|
||||||
|
// ≥2-point spline, the bot still has something to walk to.
|
||||||
|
MotionMaster* mm = bot->GetMotionMaster();
|
||||||
|
mm->Clear();
|
||||||
|
|
||||||
|
if (!generatePath || !bot->IsFreeFlying())
|
||||||
|
{
|
||||||
|
float const flySpeed = bot->IsFlying() ? bot->GetSpeed(MOVE_FLIGHT) : 0.0f;
|
||||||
|
mm->MovePoint(0, last.x, last.y, last.z, moveMode,
|
||||||
|
flySpeed, 0.0f, generatePath, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (points.size() >= 2)
|
||||||
|
mm->MoveSplinePath(&points, moveMode);
|
||||||
|
|
||||||
EmitDebugMove("MoveFar", label, last.x, last.y, last.z);
|
EmitDebugMove("MoveFar", label, last.x, last.y, last.z);
|
||||||
|
|
||||||
// WaitForReach: leave ~10y headroom on long paths.
|
// WaitForReach equivalent: cache the dispatched target + duration
|
||||||
|
// on lastMove so the action chain knows the bot is mid-move.
|
||||||
|
// Leave ~10y headroom on long paths so we re-evaluate before
|
||||||
|
// arrival (matches reference's WaitForReach with a small slack).
|
||||||
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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user