using System; using System.Collections.Generic; using Dalamud.Game.ClientState.Objects.Types; using Microsoft.Extensions.Logging; using Questionable.Functions; using Questionable.Model.Questing; namespace Questionable.Controller.Steps.Interactions; internal static class FateFarming { internal sealed record WaitForFateTargets(IReadOnlyList Targets) : ITask { public override string ToString() { return $"WaitForFateTargets({Targets.Count} targets)"; } } internal sealed class WaitForFateTargetsExecutor(GameFunctions gameFunctions, ILogger logger) : TaskExecutor() { private DateTime _nextPollAt = DateTime.MinValue; protected override bool Start() { logger.LogInformation("Waiting for FATE targets to appear ({Count} targets)", base.Task.Targets.Count); return true; } public override ETaskResult Update() { if (DateTime.Now < _nextPollAt) { return ETaskResult.StillRunning; } foreach (FateActionTarget target in base.Task.Targets) { IGameObject gameObject = gameFunctions.FindObjectByDataId(target.DataId); if (gameObject != null && gameObject.IsTargetable) { logger.LogInformation("FATE target {DataId} is now targetable", target.DataId); return ETaskResult.TaskComplete; } } _nextPollAt = DateTime.Now.AddSeconds(1.0); return ETaskResult.StillRunning; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record SyncFateLevel : ITask { public override string ToString() { return "SyncFateLevel"; } } internal sealed class SyncFateLevelExecutor(GameFunctions gameFunctions, ILogger logger) : TaskExecutor() { protected override bool Start() { logger.LogInformation("Syncing to FATE level"); return true; } public override ETaskResult Update() { ushort currentFateId = gameFunctions.GetCurrentFateId(); if (currentFateId == 0) { logger.LogDebug("No active FATE to sync to, skipping"); return ETaskResult.TaskComplete; } gameFunctions.SyncToFate(currentFateId); return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record FateActionLoop(IReadOnlyList Targets) : ITask { public bool ShouldRedoOnInterrupt() { return true; } public override string ToString() { return $"FateActionLoop({Targets.Count} targets)"; } } internal sealed class FateActionLoopExecutor(GameFunctions gameFunctions, ILogger logger) : TaskExecutor() { private DateTime _nextActionAt = DateTime.MinValue; private bool _fateWasActive; protected override bool Start() { logger.LogInformation("Starting FATE action loop with {Count} targets", base.Task.Targets.Count); _fateWasActive = gameFunctions.GetCurrentFateId() != 0; return true; } public override ETaskResult Update() { if (DateTime.Now < _nextActionAt) { return ETaskResult.StillRunning; } bool flag = gameFunctions.GetCurrentFateId() != 0; if (_fateWasActive && !flag) { bool flag2 = false; foreach (FateActionTarget target in base.Task.Targets) { IGameObject gameObject = gameFunctions.FindObjectByDataId(target.DataId); if (gameObject != null && gameObject.IsTargetable) { flag2 = true; break; } } if (!flag2) { logger.LogInformation("FATE completed (was active, now inactive, no targetable NPCs)"); return ETaskResult.TaskComplete; } } _fateWasActive = flag; foreach (FateActionTarget target2 in base.Task.Targets) { IGameObject gameObject2 = gameFunctions.FindObjectByDataId(target2.DataId); if (gameObject2 != null && gameObject2.IsTargetable) { bool flag3 = gameFunctions.UseAction(gameObject2, target2.Action); _nextActionAt = (flag3 ? DateTime.Now.AddSeconds(2.5) : DateTime.Now.AddSeconds(0.5)); return ETaskResult.StillRunning; } } _nextActionAt = DateTime.Now.AddSeconds(0.25); return ETaskResult.StillRunning; } public override bool ShouldInterruptOnDamage() { return false; } } }