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;
{
public const int VesperBayAetheryteTicket = 30362;
- internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+ internal sealed class Factory(IServiceProvider serviceProvider, ILogger<Factory> logger) : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
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<UnmountTask>();
if (step.GroundTarget == true)
{
public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
=> throw new InvalidOperationException();
+
+ private IEnumerable<ITask> 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<AetheryteShortcut.UseAetheryteShortcut>()
+ .With(null, EAetheryteLocation.Limsa, territoryId);
+ yield return serviceProvider.GetRequiredService<AethernetShortcut.UseAethernetShortcut>()
+ .With(EAetheryteLocation.Limsa, EAetheryteLocation.LimsaArcanist);
+ yield return serviceProvider.GetRequiredService<Move.MoveInternal>()
+ .With(territoryId, destination, dataId: npcId, sprint: false);
+ yield return serviceProvider.GetRequiredService<Interact.DoInteract>()
+ .With(npcId, true);
+ }
}
internal abstract class UseItemBase(ILogger logger) : ITask
{
private DateTime _continueAt;
- public QuestStep Step { get; set; } = null!;
+ public QuestStep? Step { get; set; }
public EAetheryteLocation TargetAetheryte { get; set; }
/// <summary>
/// </summary>
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;
{
_continueAt = DateTime.Now.AddSeconds(8);
ushort territoryType = clientState.TerritoryType;
- if (ExpectedTerritoryId == territoryType)
+ if (Step != null && ExpectedTerritoryId == territoryType)
{
if (Step.SkipIf.Contains(ESkipCondition.AetheryteShortcutIfInSameTerritory))
{
if (actualDistance > distance)
{
yield return serviceProvider.GetRequiredService<MoveInternal>()
- .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
if (actualDistance > distance)
{
yield return serviceProvider.GetRequiredService<MoveInternal>()
- .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);
}
}
}
}
- internal sealed class MoveInternal(MovementController movementController, ILogger<MoveInternal> logger) : ITask
+ internal sealed class MoveInternal(
+ MovementController movementController,
+ GameFunctions gameFunctions,
+ ILogger<MoveInternal> logger) : ITask
{
- public Action<MovementController> StartAction { get; set; } = null!;
+ public Action StartAction { get; set; } = null!;
public Vector3 Destination { get; set; }
- public ITask With(Vector3 destination, Action<MovementController> 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;
}