using System.Collections.Generic; using System.Linq; using System.Numerics; using Microsoft.Extensions.Logging; using Questionable.Controller.CustomDelivery; using Questionable.Controller.Steps; using Questionable.Controller.Steps.Common; using Questionable.Controller.Steps.Movement; using Questionable.Controller.Steps.Shared; using SmartNav; namespace Questionable.Navigation; internal sealed class SmartNavReRouteService(ReRoutePolicy reRoutePolicy, SmartNavTaskMapper taskMapper, Configuration configuration, ILogger logger) { public void SetDestination(uint territoryId, Vector3 position) { reRoutePolicy.SetDestination(territoryId, position); } public void ClearDestination() { reRoutePolicy.ClearDestination(); } public bool HandleMovementFailure(TaskQueue taskQueue) { if (!configuration.Navigation.ReRouteOnFailure) { return false; } string failedTargetNodeId = null; string failedApproachNodeId = null; if (taskQueue.CurrentTaskExecutor?.CurrentTask is MoveTask moveTask) { string routeTargetNodeId = moveTask.RouteTargetNodeId; if (routeTargetNodeId != null) { failedTargetNodeId = routeTargetNodeId; failedApproachNodeId = moveTask.RouteApproachNodeId; } } List list = ExtractNonMovementTasks(taskQueue); if (taskQueue.CurrentTaskExecutor?.CurrentTask is DeliveryNpcApproachTask item) { list.Insert(0, item); } ReRouteDecision reRouteDecision = reRoutePolicy.HandleMovementFailure(failedTargetNodeId, failedApproachNodeId, list.Count > 0); if (!(reRouteDecision is ReRouteDecision.SkipMovement)) { if (reRouteDecision is ReRouteDecision.Replan replan) { taskQueue.Reset(); foreach (ITask item2 in taskMapper.MapInstructions(replan.Instructions)) { taskQueue.Enqueue(item2); } foreach (ITask item3 in list) { taskQueue.Enqueue(item3); } return true; } return false; } taskQueue.Reset(); foreach (ITask item4 in list) { taskQueue.Enqueue(item4); } logger.LogInformation("Re-route: skipping movement with {TaskCount} preserved tasks", list.Count); return true; } private static List ExtractNonMovementTasks(TaskQueue taskQueue) { return taskQueue.RemainingTasks.Where((ITask t) => !IsMovementTask(t)).ToList(); } private static bool IsMovementTask(ITask task) { if (task is MoveTask || task is LandTask || task is Mount.MountTask || task is AetheryteTeleport.Task || task is AethernetRide.Task || task is WaitNavmesh.Task || task is WaitCondition.Task || task is WarpInteract.Task || task is TaxiInteract.Task || task is SubRegionTransport.Task || task is TicketTeleport.Task) { return true; } return false; } }