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.Interactions; using Questionable.Controller.Steps.Movement; using Questionable.Controller.Steps.Shared; using Questionable.Model.Questing; using SmartNav; namespace Questionable.Navigation; internal sealed class SmartNavReRouteService(ReRoutePolicy reRoutePolicy, SmartNavTaskMapper taskMapper, Configuration configuration, ILogger logger) { private ETravelMode _travelMode; public void SetDestination(uint territoryId, Vector3 position, ETravelMode travelMode = ETravelMode.Auto) { _travelMode = travelMode; var (allowMount, allowFlight) = GetCapabilities(travelMode); reRoutePolicy.SetDestination(territoryId, position, allowMount, allowFlight); } public void ClearDestination() { _travelMode = ETravelMode.Auto; 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, null, _travelMode)) { 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)) { if (task is Dive.Task task2) { if (task2.SmartNavRouted) { goto IL_006c; } } else if (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) { goto IL_006c; } return false; } goto IL_006c; IL_006c: return true; } internal static (bool AllowMount, bool AllowFlight) GetCapabilities(ETravelMode travelMode) { return travelMode switch { ETravelMode.OnFoot => (AllowMount: false, AllowFlight: false), ETravelMode.GroundMount => (AllowMount: true, AllowFlight: false), _ => (AllowMount: true, AllowFlight: true), }; } }