Add vnav stuck check
authorLiza Carvelli <liza@carvel.li>
Sun, 9 Mar 2025 19:18:06 +0000 (20:18 +0100)
committerLiza Carvelli <liza@carvel.li>
Sun, 9 Mar 2025 19:18:06 +0000 (20:18 +0100)
Questionable/Controller/MovementController.cs

index 338bea9..87934ee 100644 (file)
@@ -120,7 +120,7 @@ internal sealed class MovementController : IDisposable
                 if (!Destination.IsFlying)
                 {
                     (navPoints, bool recalculateNavmesh) = _movementOverrideController.AdjustPath(navPoints);
-                    if (recalculateNavmesh && Destination.NavmeshCalculations < 10)
+                    if (recalculateNavmesh && Destination.ShouldRecalculateNavmesh())
                     {
                         Destination.NavmeshCalculations++;
                         Destination.PartialRoute.AddRange(navPoints);
@@ -167,6 +167,7 @@ internal sealed class MovementController : IDisposable
             {
                 _logger.LogInformation("Flying but swimming, restarting as non-flying path...");
                 Restart(Destination);
+                return;
             }
             else if (Destination is { IsFlying: true } && !_condition[ConditionFlag.Mounted])
             {
@@ -179,7 +180,7 @@ internal sealed class MovementController : IDisposable
             if (Destination.MovementType == EMovementType.Landing)
             {
                 if (!_condition[ConditionFlag.InFlight])
-                    Stop();
+                Stop();
             }
             else if ((localPlayerPosition - Destination.Position).Length() < Destination.StopDistance)
             {
@@ -228,28 +229,66 @@ internal sealed class MovementController : IDisposable
                 else
                     Stop();
             }
-            else if (!Destination.IsFlying && !_condition[ConditionFlag.Mounted] &&
-                     !_gameFunctions.HasStatusPreventingSprint() && Destination.CanSprint)
+            else
             {
                 List<Vector3> navPoints = _navmeshIpc.GetWaypoints();
                 Vector3? start = _clientState.LocalPlayer?.Position;
                 if (start != null)
                 {
-                    float actualDistance = 0;
-                    foreach (Vector3 end in navPoints)
+                    if (Destination.ShouldRecalculateNavmesh())
                     {
-                        actualDistance += (start.Value - end).Length();
-                        start = end;
+                        var nextWaypoint = navPoints.FirstOrDefault();
+                        if (nextWaypoint != default)
+                        {
+                            var distance = Vector2.Distance(new Vector2(start.Value.X, start.Value.Z),
+                                new Vector2(nextWaypoint.X, nextWaypoint.Z));
+                            if (Destination.LastWaypoint == null ||
+                                (Destination.LastWaypoint.Position - nextWaypoint).Length() > 0.1f)
+                            {
+                                Destination.LastWaypoint = new LastWaypointData(nextWaypoint)
+                                {
+                                    Distance2DAtLastUpdate = distance,
+                                    UpdatedAt = Environment.TickCount64,
+                                };
+                            }
+                            else if (Environment.TickCount64 - Destination.LastWaypoint.UpdatedAt > 500)
+                            {
+                                // check whether we've made any progress of any kind
+                                if (Math.Abs(distance - Destination.LastWaypoint.Distance2DAtLastUpdate) < 0.5f)
+                                {
+                                    int calculations = Destination.NavmeshCalculations;
+                                    _logger.LogWarning("Recalculating navmesh (n = {Calculations})", calculations);
+                                    Restart(Destination);
+                                    Destination.NavmeshCalculations = calculations + 1;
+                                }
+                                else
+                                {
+                                    Destination.LastWaypoint.Distance2DAtLastUpdate = distance;
+                                    Destination.LastWaypoint.UpdatedAt = Environment.TickCount64;
+                                }
+                            }
+                        }
                     }
 
-                    unsafe
+                    if (!Destination.IsFlying && !_condition[ConditionFlag.Mounted] &&
+                        !_gameFunctions.HasStatusPreventingSprint() && Destination.CanSprint)
                     {
-                        // 70 is ~10 seconds of sprint
-                        if (actualDistance > 100f &&
-                            ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 4) == 0)
+                        float actualDistance = 0;
+                        foreach (Vector3 end in navPoints)
                         {
-                            _logger.LogInformation("Triggering Sprint");
-                            ActionManager.Instance()->UseAction(ActionType.GeneralAction, 4);
+                            actualDistance += (start.Value - end).Length();
+                            start = end;
+                        }
+
+                        unsafe
+                        {
+                            // 70 is ~10 seconds of sprint
+                            if (actualDistance > 100f &&
+                                ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 4) == 0)
+                            {
+                                _logger.LogInformation("Triggering Sprint");
+                                ActionManager.Instance()->UseAction(ActionType.GeneralAction, 4);
+                            }
                         }
                     }
                 }
@@ -393,6 +432,15 @@ internal sealed class MovementController : IDisposable
     {
         public int NavmeshCalculations { get; set; }
         public List<Vector3> PartialRoute { get; } = [];
+        public LastWaypointData? LastWaypoint { get; set; }
+
+        public bool ShouldRecalculateNavmesh() => NavmeshCalculations < 10;
+    }
+
+    public sealed record LastWaypointData(Vector3 Position)
+    {
+        public long UpdatedAt { get; set; }
+        public double Distance2DAtLastUpdate { get; set; }
     }
 
     public sealed class PathfindingFailedException : Exception