qstbak/Questionable/Questionable.Navigation/SmartNavRouteEnqueuer.cs
2026-08-19 13:19:57 +10:00

164 lines
4.9 KiB
C#

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<SmartNavRouteEnqueuer> 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<NavInstruction> 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<ITask> 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<ITask> list = new List<ITask>();
list.AddRange(taskMapper.MapInstructions(instructionBuilder.Build(routeResult, territoryId)));
return new global::_003C_003Ez__ReadOnlyList<ITask>(list);
}
}
return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(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);
}
}