using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Dalamud.Game.ClientState.Objects.Types; using FFXIVClientStructs.FFXIV.Client.Game; using Microsoft.Extensions.Logging; using Questionable.Controller.Steps.Common; using Questionable.Controller.Utils; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; namespace Questionable.Controller.Steps.Interactions; internal static class Action { internal sealed class Factory : ITaskFactory { public IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) { if (step.InteractionType != EInteractionType.Action) { return Array.Empty(); } ArgumentNullException.ThrowIfNull(step.Action, "step.Action"); ITask task = OnObject(step.DataId, quest, step.Action.Value, step.CompletionQuestVariablesFlags); if (step.Action.Value.RequiresMount()) { return new global::_003C_003Ez__ReadOnlySingleElementList(task); } return new global::_003C_003Ez__ReadOnlyArray(new ITask[2] { new Mount.UnmountTask(), task }); } public static ITask OnObject(uint? dataId, Quest quest, EAction action, List? completionQuestVariablesFlags) { if ((uint)(action - 2265) <= 2u) { ArgumentNullException.ThrowIfNull(dataId, "dataId"); return new UseMudraOnObject(dataId.Value, action); } return new UseOnObject(dataId, quest, action, completionQuestVariablesFlags); } } internal sealed record UseOnObject(uint? DataId, Quest? Quest, EAction Action, List? CompletionQuestVariablesFlags) : ITask { public bool ShouldRedoOnInterrupt() { return true; } public override string ToString() { return $"Action({Action})"; } } internal sealed class UseOnObjectExecutor(GameFunctions gameFunctions, QuestFunctions questFunctions, ILogger logger) : TaskExecutor() { private bool _usedAction; private DateTime _continueAt = DateTime.MinValue; protected override bool Start() { if (base.Task.DataId.HasValue) { IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId.Value); if (gameObject == null) { logger.LogWarning("No game object with dataId {DataId}", base.Task.DataId); return false; } if (gameObject.IsTargetable) { if (base.Task.Action == EAction.Diagnosis && gameFunctions.HasStatus(EStatus.Eukrasia) && GameFunctions.RemoveStatus(EStatus.Eukrasia)) { _continueAt = DateTime.Now.AddSeconds(2.0); return true; } _usedAction = gameFunctions.UseAction(gameObject, base.Task.Action); _continueAt = DateTime.Now.AddSeconds(0.5); return true; } return true; } _usedAction = gameFunctions.UseAction(base.Task.Action); _continueAt = DateTime.Now.AddSeconds(0.5); return true; } public override ETaskResult Update() { if (DateTime.Now <= _continueAt) { return ETaskResult.StillRunning; } if (!_usedAction) { if (base.Task.DataId.HasValue) { IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId.Value); if (gameObject == null || !gameObject.IsTargetable) { return ETaskResult.StillRunning; } _usedAction = gameFunctions.UseAction(gameObject, base.Task.Action); _continueAt = DateTime.Now.AddSeconds(0.5); } else { _usedAction = gameFunctions.UseAction(base.Task.Action); _continueAt = DateTime.Now.AddSeconds(0.5); } return ETaskResult.StillRunning; } if (base.Task.Quest != null && base.Task.CompletionQuestVariablesFlags != null && QuestWorkUtils.HasCompletionFlags(base.Task.CompletionQuestVariablesFlags)) { QuestProgressInfo questProgressInfo = questFunctions.GetQuestProgressInfo(base.Task.Quest.Id); if (questProgressInfo == null || !QuestWorkUtils.MatchesQuestWork(base.Task.CompletionQuestVariablesFlags, questProgressInfo)) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return true; } } internal sealed record UseMudraOnObject(uint DataId, EAction Action) : ITask { public override string ToString() { return $"Mudra({Action})"; } } internal sealed class UseMudraOnObjectExecutor(GameFunctions gameFunctions, ILogger logger) : TaskExecutor() { private static readonly ReadOnlyDictionary> Combos = new Dictionary> { { EAction.FumaShuriken, new Dictionary { { EAction.Ninjutsu, EAction.Ten } } }, { EAction.Raiton, new Dictionary { { EAction.Ninjutsu, EAction.Ten }, { EAction.FumaShuriken, EAction.Chi } } }, { EAction.Katon, new Dictionary { { EAction.Ninjutsu, EAction.Chi }, { EAction.FumaShuriken, EAction.Ten } } } }.AsReadOnly(); private DateTime _continueAt = DateTime.MinValue; protected override bool Start() { return true; } public unsafe override ETaskResult Update() { if (DateTime.Now < _continueAt) { return ETaskResult.StillRunning; } EAction adjustedActionId = (EAction)ActionManager.Instance()->GetAdjustedActionId(2260u); if (adjustedActionId == EAction.RabbitMedium) { _continueAt = DateTime.Now.AddSeconds(1.0); return ETaskResult.StillRunning; } IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId); if (gameObject == null || !gameObject.IsTargetable) { return ETaskResult.StillRunning; } if (adjustedActionId == base.Task.Action) { _continueAt = DateTime.Now.AddSeconds(0.25); if (!gameFunctions.UseAction(gameObject, base.Task.Action)) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } if (Combos.TryGetValue(base.Task.Action, out Dictionary value)) { if (value.TryGetValue(adjustedActionId, out var value2)) { _continueAt = DateTime.Now.AddSeconds(0.25); gameFunctions.UseAction(value2); return ETaskResult.StillRunning; } _continueAt = DateTime.Now.AddSeconds(0.25); return ETaskResult.StillRunning; } logger.LogError("Unable to find relevant combo for {Action}", base.Task.Action); return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record TriggerStatusIfMissing(EStatus Status, EAction Action) : ITask { public override string ToString() { return $"TriggerStatus({Status})"; } } internal sealed class TriggerStatusIfMissingExecutor(GameFunctions gameFunctions) : TaskExecutor() { protected override bool Start() { if (gameFunctions.HasStatus(base.Task.Status)) { return false; } gameFunctions.UseAction(base.Task.Action); return true; } public override ETaskResult Update() { if (!gameFunctions.HasStatus(base.Task.Status)) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } } }