using System; using System.Numerics; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Questionable.Controller.GameUi; using Questionable.Functions; namespace Questionable.Controller.Steps.Shared; internal static class TaxiInteract { internal sealed record Task(uint NpcDataId, uint DestStandRowId, string DestPlaceName, ushort SourceTerritoryId, ushort DestTerritoryId) : ITask { public bool ShouldRedoOnInterrupt() { return true; } public override string ToString() { return $"TaxiInteract({NpcDataId}, dest={DestPlaceName}, {SourceTerritoryId}->{DestTerritoryId})"; } } internal sealed class DoTaxiInteract(GameFunctions gameFunctions, InteractionUiController interactionUiController, ICondition condition, IClientState clientState, IObjectTable objectTable, ILogger logger) : TaskExecutor(), IConditionChangeAware, ITaskExecutor { private enum ETaxiPhase { None, Unmounting, Interacting, Riding } private ETaxiPhase _phase; private Vector3 _startPosition; private long _interactingStartedAt; private bool _wasRiding; protected override bool Start() { _phase = ETaxiPhase.None; _wasRiding = false; _startPosition = objectTable.LocalPlayer?.Position ?? Vector3.Zero; interactionUiController.SmartNavTaxiDestPlaceName = base.Task.DestPlaceName; IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.NpcDataId); if (gameObject == null) { logger.LogDebug("TaxiInteract: NPC with DataId {DataId} not found yet, waiting", base.Task.NpcDataId); return true; } if (condition[ConditionFlag.Mounted]) { logger.LogDebug("TaxiInteract: Unmounting before taxi interaction"); gameFunctions.Unmount(); _phase = ETaxiPhase.Unmounting; return true; } if (gameFunctions.InteractWith(gameObject)) { logger.LogDebug("TaxiInteract: Interacting with NPC {DataId} for taxi to {Dest}", base.Task.NpcDataId, base.Task.DestPlaceName); _phase = ETaxiPhase.Interacting; _interactingStartedAt = Environment.TickCount64; } return true; } public override ETaskResult Update() { switch (_phase) { case ETaxiPhase.Unmounting: { if (condition[ConditionFlag.Mounted]) { return ETaskResult.StillRunning; } IGameObject gameObject2 = gameFunctions.FindObjectByDataId(base.Task.NpcDataId); if (gameObject2 == null) { logger.LogDebug("TaxiInteract: NPC not found after unmount, waiting"); _phase = ETaxiPhase.None; return ETaskResult.StillRunning; } if (gameFunctions.InteractWith(gameObject2)) { logger.LogDebug("TaxiInteract: Interacting with NPC {DataId} after unmount", base.Task.NpcDataId); _phase = ETaxiPhase.Interacting; _interactingStartedAt = Environment.TickCount64; } return ETaskResult.StillRunning; } case ETaxiPhase.Interacting: if (condition[ConditionFlag.Mounted] && (condition[ConditionFlag.Occupied] || condition[ConditionFlag.OccupiedInEvent])) { logger.LogDebug("TaxiInteract: Taxi ride started"); _wasRiding = true; _phase = ETaxiPhase.Riding; return ETaskResult.StillRunning; } if (Environment.TickCount64 - _interactingStartedAt > 30000) { logger.LogWarning("TaxiInteract: Timed out waiting for taxi ride to start, retrying step"); ClearContext(); return ETaskResult.RetryStep; } return ETaskResult.StillRunning; case ETaxiPhase.Riding: if (clientState.TerritoryType != base.Task.SourceTerritoryId) { logger.LogDebug("TaxiInteract: Territory changed to {Territory}, taxi ride complete", clientState.TerritoryType); ClearContext(); return ETaskResult.TaskComplete; } if ((!condition[ConditionFlag.Mounted] || (!condition[ConditionFlag.Occupied] && !condition[ConditionFlag.OccupiedInEvent])) && _wasRiding) { logger.LogDebug("TaxiInteract: Taxi ride ended (dismounted)"); ClearContext(); return ETaskResult.TaskComplete; } return ETaskResult.StillRunning; case ETaxiPhase.None: { IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.NpcDataId); if (gameObject == null) { return ETaskResult.StillRunning; } _startPosition = objectTable.LocalPlayer?.Position ?? _startPosition; if (condition[ConditionFlag.Mounted]) { gameFunctions.Unmount(); _phase = ETaxiPhase.Unmounting; return ETaskResult.StillRunning; } if (gameFunctions.InteractWith(gameObject)) { logger.LogDebug("TaxiInteract: Interacting with NPC {DataId}", base.Task.NpcDataId); _phase = ETaxiPhase.Interacting; _interactingStartedAt = Environment.TickCount64; } return ETaskResult.StillRunning; } default: return ETaskResult.StillRunning; } } public void OnConditionChange(ConditionFlag flag, bool value) { bool flag2 = _phase == ETaxiPhase.Interacting; if (flag2) { bool flag3 = (uint)(flag - 31) <= 1u; flag2 = flag3; } if (flag2 && value) { logger.LogDebug("TaxiInteract: Interaction confirmed via condition flag"); } } public override bool ShouldInterruptOnDamage() { return false; } private void ClearContext() { interactionUiController.SmartNavTaxiDestPlaceName = null; } } }