using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using Dalamud.Plugin.Services; 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.Data; using Questionable.Model.Questing; using SmartNav; namespace Questionable.Navigation; internal sealed class SmartNavTaskMapper(IClientState clientState, TerritoryData territoryData) { private const float SurfaceExitRise = 3f; public IEnumerable MapInstructions(IReadOnlyList instructions, QuestStep? step = null, ETravelMode travelMode = ETravelMode.Auto) { for (int i = 0; i < instructions.Count; i++) { foreach (ITask item in Map(instructions[i], NextTravelInstruction(instructions, i), step)) { if (item is MoveTask task && !(instructions[i] is NavInstruction.Swim)) { yield return ApplyTravelMode(task, travelMode); } else { yield return item; } } } } private static NavInstruction? NextTravelInstruction(IReadOnlyList instructions, int i) { for (int j = i + 1; j < instructions.Count; j++) { NavInstruction navInstruction = instructions[j]; if ((!(navInstruction is NavInstruction.Land) && !(navInstruction is NavInstruction.Unmount) && !(navInstruction is NavInstruction.WaitForTerritory) && !(navInstruction is NavInstruction.WaitForNavmesh)) || 1 == 0) { return instructions[j]; } } return null; } private IEnumerable Map(NavInstruction instruction, NavInstruction? nextTravel, QuestStep? step) { NavInstruction.Teleport teleport = instruction as NavInstruction.Teleport; if ((object)teleport == null) { if (!(instruction is NavInstruction.AethernetHop aethernetHop)) { if (!(instruction is NavInstruction.Move move)) { if (!(instruction is NavInstruction.Swim swim)) { if (!(instruction is NavInstruction.DiveEntry dive)) { if (!(instruction is NavInstruction.Surface surface)) { if (!(instruction is NavInstruction.Land)) { if (!(instruction is NavInstruction.Unmount)) { NavInstruction.WaitForTerritory waitForTerritory = instruction as NavInstruction.WaitForTerritory; if ((object)waitForTerritory == null) { if (!(instruction is NavInstruction.WaitForNavmesh)) { if (!(instruction is NavInstruction.InteractWarp interactWarp)) { if (!(instruction is NavInstruction.UseTeleportTicket useTeleportTicket)) { if (!(instruction is NavInstruction.SubRegionTransport subRegionTransport)) { throw new InvalidOperationException($"Unmapped NavInstruction: {instruction}"); } yield return new SubRegionTransport.Task(subRegionTransport.From, subRegionTransport.To, subRegionTransport.Territory); } else { yield return new TicketTeleport.Task(useTeleportTicket.ItemId, useTeleportTicket.Territory); } } else { yield return new WarpInteract.Task(interactWarp.NpcDataId, interactWarp.WarpRowId, interactWarp.FromTerritory, interactWarp.ToTerritory); } } else { yield return new WaitNavmesh.Task(); } } else { yield return new WaitCondition.Task(() => waitForTerritory.Acceptable.Contains(clientState.TerritoryType), waitForTerritory.Description); } } else { yield return new Mount.UnmountTask(); } } else { yield return new LandTask(); } } else { ushort territory = surface.Territory; Vector3 position = surface.Position; position.Y = surface.Position.Y + 3f; Vector3 destination = position; bool? mount = true; string targetNodeId = surface.TargetNodeId; string approachNodeId = surface.ApproachNodeId; yield return new MoveTask(territory, destination, mount, MountRequired: false, DismountRequired: false, null, null, DisableNavmesh: true, null, Fly: true, Land: false, IgnoreDistanceToObject: false, RestartNavigation: true, EInteractionType.None, SmartNavRouted: true, AllowZoneTransition: false, targetNodeId, approachNodeId); } } else { yield return new Dive.Task { SmartNavRouted = true }; ushort territory2 = dive.Territory; Vector3 position = dive.Position; position.Y = dive.Position.Y - 25f; Vector3 destination2 = position; string approachNodeId = dive.TargetNodeId; string targetNodeId = dive.ApproachNodeId; yield return new MoveTask(territory2, destination2, null, MountRequired: false, DismountRequired: false, null, null, DisableNavmesh: true, null, Fly: true, Land: false, IgnoreDistanceToObject: false, RestartNavigation: true, EInteractionType.None, SmartNavRouted: true, AllowZoneTransition: false, approachNodeId, targetNodeId); } } else if (swim.IsFinal && step != null) { Vector3 destination3 = step.Position ?? swim.Position; yield return new MoveTask(step, destination3)with { Fly = true, Land = false, SmartNavRouted = true, RouteTargetNodeId = swim.TargetNodeId, RouteApproachNodeId = swim.ApproachNodeId }; } else { NavInstruction.Swim swim2 = swim; ushort territory3 = swim2.Territory; Vector3 position2 = swim2.Position; float? stopDistance = swim2.StopDistance; string targetNodeId = swim2.TargetNodeId; string approachNodeId = swim2.ApproachNodeId; yield return new MoveTask(territory3, position2, null, MountRequired: false, DismountRequired: false, stopDistance, null, DisableNavmesh: false, null, Fly: true, Land: false, IgnoreDistanceToObject: false, RestartNavigation: true, EInteractionType.None, SmartNavRouted: true, AllowZoneTransition: false, targetNodeId, approachNodeId); } } else if (move.IsFinal && step != null) { Vector3 destination4 = step.Position ?? move.Position; MoveTask moveTask = new MoveTask(step, destination4)with { Fly = (move.Fly || step.Fly == true) }; MoveTask moveTask2 = moveTask; bool flag = move.Fly || step.Land == true; if (!flag) { EArrivalMode arrivalMode = step.ArrivalMode; bool flag2 = (uint)(arrivalMode - 2) <= 1u; flag = flag2; } moveTask2.Land = flag; moveTask.SmartNavRouted = true; moveTask.RouteTargetNodeId = move.TargetNodeId; moveTask.RouteApproachNodeId = move.ApproachNodeId; yield return moveTask; if (step != null) { bool? fly = step.Fly; if (fly.HasValue && fly == true && (step.Land ?? false)) { yield return new LandTask(); } } if (step.ArrivalMode == EArrivalMode.StayMounted && !step.DismountRequired) { yield return new Mount.MountTask(step.TerritoryId, Mount.EMountIf.Always); } else if (step.ArrivalMode == EArrivalMode.Dismount && !step.MountRequired) { yield return new Mount.UnmountTask(); } } else { NavInstruction.Move move2 = move; ushort territory4 = move2.Territory; Vector3 position3 = move2.Position; bool? mount2 = move2.Mount; float? stopDistance2 = move2.StopDistance; uint? dataId = move2.DataId; bool disableNavmesh = move2.DisableNavmesh; bool flag = move2.Fly; bool flag2 = move2.LandAtTarget; bool allowZoneTransition = move2.AllowZoneTransition; string approachNodeId = move2.TargetNodeId; string targetNodeId = move2.ApproachNodeId; yield return new MoveTask(territory4, position3, mount2, MountRequired: false, DismountRequired: false, stopDistance2, dataId, disableNavmesh, null, flag, flag2, IgnoreDistanceToObject: false, RestartNavigation: true, EInteractionType.None, SmartNavRouted: true, allowZoneTransition, approachNodeId, targetNodeId); } } else { yield return new AethernetRide.Task(aethernetHop.From, aethernetHop.To); } yield break; } yield return new AetheryteTeleport.Task(teleport.Target, teleport.ExpectedTerritory); if (AetheryteTeleport.MoveAwayFromAetheryteExecutor.AppliesTo(teleport.Target) && (nextTravel as NavInstruction.AethernetHop)?.From != teleport.Target) { yield return new WaitCondition.Task(() => clientState.TerritoryType == teleport.ExpectedTerritory, "Wait(territory: " + territoryData.GetNameAndId(teleport.ExpectedTerritory) + ")"); yield return new AetheryteTeleport.MoveAwayFromAetheryte(teleport.Target); } } private static MoveTask ApplyTravelMode(MoveTask task, ETravelMode travelMode) { if (travelMode == ETravelMode.Auto) { return task; } if (task.MountRequired) { return task with { Mount = true, Fly = (travelMode == ETravelMode.Flying && task.Fly), Land = (travelMode == ETravelMode.Flying && task.Land) }; } if (task.DismountRequired) { return task with { Mount = false, Fly = false, Land = false }; } return travelMode switch { ETravelMode.OnFoot => task with { Mount = false, Fly = false, Land = false }, ETravelMode.GroundMount => task with { Mount = true, Fly = false, Land = false }, ETravelMode.Flying => task with { Mount = true }, _ => task, }; } }