fix(B.6+B.7): arrival predicate uses safety margin INSIDE ACE's WithinUseRadius

Trace showed local arrival landing at 3.025 m from a target with
objDist=3.00. The previous arrival used ArrivalEpsilon=0.05 to
EXPAND the threshold (dist <= 3.05), so the body stopped right at
the boundary. ACE's server-side WithinUseRadius is strict
(dist <= radius), so 3.025 > 3.00 — ACE rejected the re-sent
action and looped back to MoveToObject. User had to manually
re-press R because auto-arrival kept landing just-outside-range.

Fix: walk INSIDE ACE's radius by a safety margin (0.3–0.5 m, capped
at 40 % of threshold so tight pickup radii like 0.6 m stay
reachable).

  arrivalThreshold = wire's distanceToObject or minDistance
  safetyMargin     = min(0.5, arrivalThreshold * 0.4)
  effectiveArrival = max(arrivalThreshold - safetyMargin, 0.1)

  Examples:
    objDist=3.00 (NPC) → walk to ≤2.50 m  (ACE happy at 3.0)
    objDist=2.00 (door) → walk to ≤1.50 m
    objDist=0.60 (item) → walk to ≤0.36 m
    objDist=0.50 (small) → walk to ≤0.30 m

Flee case (moveTowards=false) keeps its original predicate with
+ArrivalEpsilon — that's the boundary check semantics for fleeing
to a min-distance, not a max-distance use radius.
This commit is contained in:
Erik 2026-05-15 11:19:04 +02:00
parent a0fa3d68a7
commit 39ff3a5505

View file

@ -445,15 +445,24 @@ public sealed class PlayerMovementController
float dy = _autoWalkDestination.Y - pos.Y;
float dist = MathF.Sqrt(dx * dx + dy * dy);
// Arrival predicate (RemoteMoveToDriver convention; matches retail).
// Arrival predicate. CRITICAL: ACE's server-side WithinUseRadius
// is strict (dist <= radius), so arriving exactly at the radius
// boundary fails — ACE rejects the action and replies with
// another MoveToObject. To ensure the re-sent action lands
// INSIDE ACE's radius, we apply a safety margin that walks
// 0.30.5 m past the boundary (capped to 40 % of the threshold
// so tight pickup radii like 0.6 m stay reachable without
// collapsing to zero).
float arrivalThreshold = _autoWalkMoveTowards
? _autoWalkDistanceToObject
: _autoWalkMinDistance;
float safetyMargin = MathF.Min(0.5f, arrivalThreshold * 0.4f);
float effectiveArrival = MathF.Max(arrivalThreshold - safetyMargin, 0.1f);
bool arrived =
(_autoWalkMoveTowards
&& dist <= arrivalThreshold + RemoteMoveToDriver.ArrivalEpsilon)
&& dist <= effectiveArrival)
|| (!_autoWalkMoveTowards
&& dist >= arrivalThreshold - RemoteMoveToDriver.ArrivalEpsilon);
&& dist >= arrivalThreshold + RemoteMoveToDriver.ArrivalEpsilon);
if (arrived)
{
EndServerAutoWalk("arrived");