using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using Microsoft.Extensions.Logging; using Questionable.Controller.Steps; using Questionable.Controller.Steps.Movement; using SmartNav; namespace Questionable.Navigation; internal sealed class SmartNavRouteEnqueuer(NavRouter navRouter, PlayerNavStateBuilder playerNavStateBuilder, RouteInstructionBuilder instructionBuilder, SmartNavTaskMapper taskMapper, SmartNavReRouteService reRouteService, ILogger logger) { public void Enqueue(TaskQueue taskQueue, uint territoryId, Vector3 position, float? fallbackStopDistance = null) { if (!TryEnqueueRouted(taskQueue, territoryId, position)) { taskQueue.Enqueue(new MoveTask((ushort)territoryId, position, null, MountRequired: false, DismountRequired: false, fallbackStopDistance)); reRouteService.SetDestination(territoryId, position); } } public bool TryEnqueueRouted(TaskQueue taskQueue, uint territoryId, Vector3 position) { PlayerNavState playerNavState = playerNavStateBuilder.Build(); if (playerNavState == null) { return false; } RouteResult routeResult = navRouter.FindRoute(playerNavState, territoryId, position); if (routeResult.Segments.Count == 0) { return false; } logger.LogDebug("SmartNav routing with {Count} segments", routeResult.Segments.Count); foreach (ITask item in taskMapper.MapInstructions(instructionBuilder.Build(routeResult, territoryId))) { taskQueue.Enqueue(item); } reRouteService.SetDestination(territoryId, position); return true; } public bool TryEnqueueToTerritory(TaskQueue taskQueue, uint territoryId, Vector3 position) { PlayerNavState playerNavState = playerNavStateBuilder.Build(); if (playerNavState == null) { return false; } RouteResult routeResult = navRouter.FindRoute(playerNavState, territoryId, position); if (routeResult.Segments.Count == 0) { return false; } List list = instructionBuilder.Build(routeResult, territoryId).TakeWhile((NavInstruction x) => !(x is NavInstruction.Move move) || !move.IsFinal).ToList(); if (list.Count == 0) { return false; } logger.LogDebug("SmartNav routing to territory {TerritoryId} with {Count} segments; leaving final movement to the caller", territoryId, routeResult.Segments.Count); foreach (ITask item in taskMapper.MapInstructions(list)) { taskQueue.Enqueue(item); } reRouteService.SetDestination(territoryId, position); return true; } public IReadOnlyList BuildRoutedTasks(uint territoryId, Vector3 position) { reRouteService.SetDestination(territoryId, position); PlayerNavState playerNavState = playerNavStateBuilder.Build(); if (playerNavState != null) { RouteResult routeResult = navRouter.FindRoute(playerNavState, territoryId, position); if (routeResult.Segments.Count > 0) { logger.LogDebug("SmartNav routing with {Count} segments", routeResult.Segments.Count); List list = new List(); list.AddRange(taskMapper.MapInstructions(instructionBuilder.Build(routeResult, territoryId))); return new global::_003C_003Ez__ReadOnlyList(list); } } return new global::_003C_003Ez__ReadOnlySingleElementList(new MoveTask((ushort)territoryId, position)); } public bool RouteStartsWithTeleport(uint territoryId, Vector3 position) { PlayerNavState playerNavState = playerNavStateBuilder.Build(); if (playerNavState == null) { return false; } RouteResult routeResult = navRouter.FindRoute(playerNavState, territoryId, position); if (routeResult.Segments.Count == 0) { return false; } foreach (NavInstruction item in instructionBuilder.Build(routeResult, territoryId)) { bool? flag = StartsWithTeleport(item); if (flag.HasValue) { return flag == true; } } return false; static bool? StartsWithTeleport(NavInstruction instruction) { if (instruction is NavInstruction.Teleport) { return true; } if (instruction is NavInstruction.UseTeleportTicket) { return true; } if (instruction is NavInstruction.Move) { return false; } if (instruction is NavInstruction.AethernetHop) { return false; } if (instruction is NavInstruction.InteractWarp) { return false; } if (instruction is NavInstruction.SubRegionTransport) { return false; } if (instruction is NavInstruction.Land) { return null; } if (instruction is NavInstruction.Unmount) { return null; } if (instruction is NavInstruction.WaitForTerritory) { return null; } if (!(instruction is NavInstruction.WaitForNavmesh)) { throw new InvalidOperationException($"Unclassified NavInstruction: {instruction}"); } return null; } } public void ClearDestination() { reRouteService.ClearDestination(); } public void SetDestination(uint territoryId, Vector3 position) { reRouteService.SetDestination(territoryId, position); } }