using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Numerics; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; using Questionable.Controller.Steps.Common; using Questionable.Controller.Utils; using Questionable.Data; using Questionable.External; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; namespace Questionable.Controller.Steps.Shared; internal static class WaitAtEnd { internal sealed class Factory(IClientState clientState, ICondition condition, TerritoryData territoryData, AutoDutyIpc autoDutyIpc, BossModIpc bossModIpc) : ITaskFactory { public IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) { if (step.CompletionQuestVariablesFlags.Count == 6 && QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags)) { WaitForCompletionFlags waitForCompletionFlags = new WaitForCompletionFlags((QuestId)quest.Id, step); WaitDelay waitDelay = new WaitDelay(); return new global::_003C_003Ez__ReadOnlyArray(new ITask[3] { waitForCompletionFlags, waitDelay, Next(quest, sequence) }); } ITask task; switch (step.InteractionType) { case EInteractionType.Combat: { if (step.EnemySpawnType == EEnemySpawnType.FinishCombatIfAny) { return new global::_003C_003Ez__ReadOnlySingleElementList(Next(quest, sequence)); } WaitCondition.Task task2 = new WaitCondition.Task(() => !condition[ConditionFlag.InCombat], "Wait(not in combat)"); return new global::_003C_003Ez__ReadOnlyArray(new ITask[4] { new WaitDelay(), task2, new WaitDelay(), Next(quest, sequence) }); } case EInteractionType.WaitForManualProgress: case EInteractionType.Snipe: case EInteractionType.Instruction: return new global::_003C_003Ez__ReadOnlySingleElementList(new WaitNextStepOrSequence()); case EInteractionType.Duty: if (autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions)) { break; } goto IL_019d; case EInteractionType.SinglePlayerDuty: if (bossModIpc.IsConfiguredToRunSoloInstance(quest.Id, step.SinglePlayerDutyOptions)) { break; } goto IL_019d; case EInteractionType.WalkTo: case EInteractionType.Jump: return new global::_003C_003Ez__ReadOnlySingleElementList(Next(quest, sequence)); case EInteractionType.WaitForObjectAtPosition: ArgumentNullException.ThrowIfNull(step.DataId, "step.DataId"); ArgumentNullException.ThrowIfNull(step.Position, "step.Position"); return new global::_003C_003Ez__ReadOnlyArray(new ITask[3] { new WaitObjectAtPosition(step.DataId.Value, step.Position.Value, step.NpcWaitDistance ?? 0.5f), new WaitDelay(), Next(quest, sequence) }); case EInteractionType.Interact: if (!step.TargetTerritoryId.HasValue) { break; } goto IL_0284; case EInteractionType.UseItem: if (!step.TargetTerritoryId.HasValue) { break; } goto IL_0284; case EInteractionType.AcceptQuest: { WaitQuestAccepted waitQuestAccepted = new WaitQuestAccepted(step.PickUpQuestId ?? quest.Id); WaitDelay waitDelay3 = new WaitDelay(); if (step.PickUpQuestId != null) { return new global::_003C_003Ez__ReadOnlyArray(new ITask[3] { waitQuestAccepted, waitDelay3, Next(quest, sequence) }); } return new global::_003C_003Ez__ReadOnlyArray(new ITask[2] { waitQuestAccepted, waitDelay3 }); } case EInteractionType.CompleteQuest: { WaitQuestCompleted waitQuestCompleted = new WaitQuestCompleted(step.TurnInQuestId ?? quest.Id); WaitDelay waitDelay2 = new WaitDelay(); if (step.TurnInQuestId != null) { return new global::_003C_003Ez__ReadOnlyArray(new ITask[3] { waitQuestCompleted, waitDelay2, Next(quest, sequence) }); } return new global::_003C_003Ez__ReadOnlyArray(new ITask[2] { waitQuestCompleted, waitDelay2 }); } IL_019d: return new global::_003C_003Ez__ReadOnlySingleElementList(new EndAutomation()); IL_0284: if (step.TerritoryId != step.TargetTerritoryId) { task = new WaitCondition.Task(() => clientState.TerritoryType == step.TargetTerritoryId, "Wait(tp to territory: " + territoryData.GetNameAndId(step.TargetTerritoryId.Value) + ")"); } else { Vector3 lastPosition = step.Position ?? clientState.LocalPlayer?.Position ?? Vector3.Zero; task = new WaitCondition.Task(delegate { Vector3? vector = clientState.LocalPlayer?.Position; return vector.HasValue && (lastPosition - vector.Value).Length() > 2f; }, "Wait(tp away from " + lastPosition.ToString("G", CultureInfo.InvariantCulture) + ")"); } return new global::_003C_003Ez__ReadOnlyArray(new ITask[3] { task, new WaitDelay(), Next(quest, sequence) }); } return new global::_003C_003Ez__ReadOnlyArray(new ITask[2] { new WaitDelay(), Next(quest, sequence) }); } private static NextStep Next(Quest quest, QuestSequence sequence) { return new NextStep(quest.Id, sequence.Sequence); } } internal sealed record WaitDelay(TimeSpan Delay) : ITask { public WaitDelay() : this(TimeSpan.FromSeconds(1L)) { } public bool ShouldRedoOnInterrupt() { return true; } public override string ToString() { return $"Wait(seconds: {Delay.TotalSeconds})"; } } internal sealed class WaitDelayExecutor : AbstractDelayedTaskExecutor { protected override bool StartInternal() { base.Delay = base.Task.Delay; return true; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed class WaitNextStepOrSequence : ITask { public override string ToString() { return "Wait(next step or sequence)"; } } internal sealed class WaitNextStepOrSequenceExecutor : TaskExecutor { protected override bool Start() { return true; } public override ETaskResult Update() { return ETaskResult.StillRunning; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record WaitForCompletionFlags(QuestId Quest, QuestStep Step) : ITask { public override string ToString() { return "Wait(QW: " + string.Join(", ", Step.CompletionQuestVariablesFlags.Select((QuestWorkValue x) => x?.ToString() ?? "-")) + ")"; } } internal sealed class WaitForCompletionFlagsExecutor(QuestFunctions questFunctions) : TaskExecutor() { protected override bool Start() { return true; } public override ETaskResult Update() { QuestProgressInfo questProgressInfo = questFunctions.GetQuestProgressInfo(base.Task.Quest); if (questProgressInfo == null || !QuestWorkUtils.MatchesQuestWork(base.Task.Step.CompletionQuestVariablesFlags, questProgressInfo)) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record WaitObjectAtPosition(uint DataId, Vector3 Destination, float Distance) : ITask { public override string ToString() { return $"WaitObj({DataId} at {Destination.ToString("G", CultureInfo.InvariantCulture)} < {Distance})"; } } internal sealed class WaitObjectAtPositionExecutor(GameFunctions gameFunctions) : TaskExecutor() { protected override bool Start() { return true; } public override ETaskResult Update() { if (!gameFunctions.IsObjectAtPosition(base.Task.DataId, base.Task.Destination, base.Task.Distance)) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record WaitQuestAccepted(ElementId ElementId) : ITask { public override string ToString() { return $"WaitQuestAccepted({ElementId})"; } } internal sealed class WaitQuestAcceptedExecutor(QuestFunctions questFunctions) : TaskExecutor() { protected override bool Start() { return true; } public override ETaskResult Update() { if (!questFunctions.IsQuestAccepted(base.Task.ElementId)) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record WaitQuestCompleted(ElementId ElementId) : ITask { public override string ToString() { return $"WaitQuestComplete({ElementId})"; } } internal sealed class WaitQuestCompletedExecutor(QuestFunctions questFunctions) : TaskExecutor() { protected override bool Start() { return true; } public override ETaskResult Update() { if (!questFunctions.IsQuestComplete(base.Task.ElementId)) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record NextStep(ElementId ElementId, int Sequence) : ILastTask, ITask { public override string ToString() { return "NextStep"; } } internal sealed class NextStepExecutor : TaskExecutor { protected override bool Start() { return true; } public override ETaskResult Update() { return ETaskResult.NextStep; } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed class EndAutomation : ILastTask, ITask { public ElementId ElementId { get { throw new InvalidOperationException(); } } public int Sequence { get { throw new InvalidOperationException(); } } public override string ToString() { return "EndAutomation"; } } internal sealed class EndAutomationExecutor : TaskExecutor { protected override bool Start() { return true; } public override ETaskResult Update() { return ETaskResult.End; } public override bool ShouldInterruptOnDamage() { return false; } } }