diff --git a/Questionable/Controller/Steps/Interactions/UseItem.cs b/Questionable/Controller/Steps/Interactions/UseItem.cs index 05dbd72eb..497ae6173 100644 --- a/Questionable/Controller/Steps/Interactions/UseItem.cs +++ b/Questionable/Controller/Steps/Interactions/UseItem.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; +using System.Numerics; using FFXIVClientStructs.FFXIV.Client.Game; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Questionable.Controller.Steps.Common; +using Questionable.Controller.Steps.Shared; using Questionable.Model; using Questionable.Model.V1; +using AethernetShortcut = Questionable.Controller.Steps.Shared.AethernetShortcut; namespace Questionable.Controller.Steps.Interactions; @@ -13,7 +16,7 @@ internal static class UseItem { public const int VesperBayAetheryteTicket = 30362; - internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory + internal sealed class Factory(IServiceProvider serviceProvider, ILogger logger) : ITaskFactory { public IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) { @@ -22,6 +25,16 @@ internal static class UseItem ArgumentNullException.ThrowIfNull(step.ItemId); + if (step.ItemId == VesperBayAetheryteTicket) + { + unsafe + { + InventoryManager* inventoryManager = InventoryManager.Instance(); + if (inventoryManager->GetInventoryItemCount(step.ItemId.Value) == 0) + return CreateVesperBayFallbackTask(); + } + } + var unmount = serviceProvider.GetRequiredService(); if (step.GroundTarget == true) { @@ -47,6 +60,23 @@ internal static class UseItem public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step) => throw new InvalidOperationException(); + + private IEnumerable CreateVesperBayFallbackTask() + { + logger.LogWarning("No vesper bay aetheryte tickets in inventory, navigating via ferry in Limsa instead"); + + uint npcId = 1003540; + ushort territoryId = 129; + Vector3 destination = new(-360.9217f, 8f, 38.92566f); + yield return serviceProvider.GetRequiredService() + .With(null, EAetheryteLocation.Limsa, territoryId); + yield return serviceProvider.GetRequiredService() + .With(EAetheryteLocation.Limsa, EAetheryteLocation.LimsaArcanist); + yield return serviceProvider.GetRequiredService() + .With(territoryId, destination, dataId: npcId, sprint: false); + yield return serviceProvider.GetRequiredService() + .With(npcId, true); + } } internal abstract class UseItemBase(ILogger logger) : ITask diff --git a/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs b/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs index 2f645ea60..f897fe643 100644 --- a/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs +++ b/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs @@ -45,7 +45,7 @@ internal static class AetheryteShortcut { private DateTime _continueAt; - public QuestStep Step { get; set; } = null!; + public QuestStep? Step { get; set; } public EAetheryteLocation TargetAetheryte { get; set; } /// @@ -54,7 +54,7 @@ internal static class AetheryteShortcut /// public ushort ExpectedTerritoryId { get; set; } - public ITask With(QuestStep step, EAetheryteLocation targetAetheryte, ushort expectedTerritoryId) + public ITask With(QuestStep? step, EAetheryteLocation targetAetheryte, ushort expectedTerritoryId) { Step = step; TargetAetheryte = targetAetheryte; @@ -66,7 +66,7 @@ internal static class AetheryteShortcut { _continueAt = DateTime.Now.AddSeconds(8); ushort territoryType = clientState.TerritoryType; - if (ExpectedTerritoryId == territoryType) + if (Step != null && ExpectedTerritoryId == territoryType) { if (Step.SkipIf.Contains(ESkipCondition.AetheryteShortcutIfInSameTerritory)) { diff --git a/Questionable/Controller/Steps/Shared/Move.cs b/Questionable/Controller/Steps/Shared/Move.cs index d6da1491d..75fd691a4 100644 --- a/Questionable/Controller/Steps/Shared/Move.cs +++ b/Questionable/Controller/Steps/Shared/Move.cs @@ -99,15 +99,7 @@ internal static class Move if (actualDistance > distance) { yield return serviceProvider.GetRequiredService() - .With(Destination, m => - { - m.NavigateTo(EMovementType.Quest, Step.DataId, Destination, - fly: Step.Fly == true && gameFunctions.IsFlyingUnlocked(Step.TerritoryId), - sprint: Step.Sprint != false, - stopDistance: distance, - ignoreDistanceToObject: Step.IgnoreDistanceToObject == true, - land: Step.Land == true); - }); + .With(Step, Destination); } } else @@ -116,14 +108,7 @@ internal static class Move if (actualDistance > distance) { yield return serviceProvider.GetRequiredService() - .With(Destination, m => - { - m.NavigateTo(EMovementType.Quest, Step.DataId, [Destination], - fly: Step.Fly == true && gameFunctions.IsFlyingUnlockedInCurrentZone(), - sprint: Step.Sprint != false, - stopDistance: distance, - land: Step.Land == true); - }); + .With(Step, Destination); } } @@ -132,22 +117,68 @@ internal static class Move } } - internal sealed class MoveInternal(MovementController movementController, ILogger logger) : ITask + internal sealed class MoveInternal( + MovementController movementController, + GameFunctions gameFunctions, + ILogger logger) : ITask { - public Action StartAction { get; set; } = null!; + public Action StartAction { get; set; } = null!; public Vector3 Destination { get; set; } - public ITask With(Vector3 destination, Action startAction) + public ITask With(QuestStep step, Vector3 destination) + { + return With( + territoryId: step.TerritoryId, + destination: destination, + stopDistance: step.StopDistance, + dataId: step.DataId, + disableNavMesh: step.DisableNavmesh, + sprint: step.Sprint != false, + fly: step.Fly == true, + land: step.Land == true, + ignoreDistanceToObject: step.IgnoreDistanceToObject == true); + } + + public ITask With(ushort territoryId, Vector3 destination, float? stopDistance = null, uint? dataId = null, + bool disableNavMesh = false, bool sprint = true, bool fly = false, bool land = false, + bool ignoreDistanceToObject = false) { Destination = destination; - StartAction = startAction; + + if (!gameFunctions.IsFlyingUnlocked(territoryId)) + { + fly = false; + land = false; + } + + if (!disableNavMesh) + { + StartAction = () => + movementController.NavigateTo(EMovementType.Quest, dataId, Destination, + fly: fly, + sprint: sprint, + stopDistance: stopDistance, + ignoreDistanceToObject: ignoreDistanceToObject, + land: land); + } + else + { + StartAction = () => + movementController.NavigateTo(EMovementType.Quest, dataId, [Destination], + fly: fly, + sprint: sprint, + stopDistance: stopDistance, + ignoreDistanceToObject: ignoreDistanceToObject, + land: land); + } + return this; } public bool Start() { logger.LogInformation("Moving to {Destination}", Destination.ToString("G", CultureInfo.InvariantCulture)); - StartAction(movementController); + StartAction(); return true; }