using System; using System.Collections.Generic; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; using LLib.Gear; using Questionable.Controller.Steps.Common; using Questionable.Controller.Steps.Shared; using Questionable.Controller.Utils; using Questionable.Data; using Questionable.External; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; namespace Questionable.Controller.Steps.Interactions; internal static class Duty { internal sealed class Factory(AutoDutyIpc autoDutyIpc) : ITaskFactory { public IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) { if (step.InteractionType != EInteractionType.Duty) { yield break; } ArgumentNullException.ThrowIfNull(step.DutyOptions, "step.DutyOptions"); uint contentFinderConditionId; int dutyMode; if (autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions)) { contentFinderConditionId = step.DutyOptions.ContentFinderConditionId; ElementId id = quest.Id; if (id is QuestId) { ushort value = id.Value; if (value >= 357 && value <= 360) { dutyMode = 2; goto IL_00b2; } } dutyMode = 1; goto IL_00b2; } if (!step.DutyOptions.LowPriority) { yield return new OpenDutyFinderTask(step.DutyOptions.ContentFinderConditionId); } yield break; IL_00b2: yield return new StartAutoDutyTask(contentFinderConditionId, (AutoDutyIpc.DutyMode)dutyMode); yield return new WaitAutoDutyTask(step.DutyOptions.ContentFinderConditionId); if (!QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags)) { yield return new WaitAtEnd.WaitNextStepOrSequence(); } } } internal sealed record StartAutoDutyTask(uint ContentFinderConditionId, AutoDutyIpc.DutyMode DutyMode) : ITask { public override string ToString() { return $"StartAutoDuty({ContentFinderConditionId}, {DutyMode})"; } } internal sealed class StartAutoDutyExecutor(GearStatsCalculator gearStatsCalculator, AutoDutyIpc autoDutyIpc, TerritoryData territoryData, IClientState clientState, IChatGui chatGui, SendNotification.Executor sendNotificationExecutor) : TaskExecutor(), IStoppableTaskExecutor, ITaskExecutor { protected unsafe override bool Start() { if (!territoryData.TryGetContentFinderCondition(base.Task.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData)) { throw new TaskException("Failed to get territory ID for content finder condition"); } InventoryManager* intPtr = InventoryManager.Instance(); if (intPtr == null) { throw new TaskException("Inventory unavailable"); } InventoryContainer* inventoryContainer = intPtr->GetInventoryContainer(InventoryType.EquippedItems); if (inventoryContainer == null) { throw new TaskException("Equipped items unavailable"); } short num = gearStatsCalculator.CalculateAverageItemLevel(inventoryContainer); if (contentFinderConditionData.RequiredItemLevel > num) { string text = $"Could not use AutoDuty to queue for {contentFinderConditionData.Name}, required item level: {contentFinderConditionData.RequiredItemLevel}, current item level: {num}."; if (!sendNotificationExecutor.Start(new SendNotification.Task(EInteractionType.Duty, text))) { chatGui.PrintError(text, "Questionable", 576); } return false; } autoDutyIpc.StartInstance(base.Task.ContentFinderConditionId, base.Task.DutyMode); return true; } public override ETaskResult Update() { if (!territoryData.TryGetContentFinderCondition(base.Task.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData)) { throw new TaskException("Failed to get territory ID for content finder condition"); } if (clientState.TerritoryType != contentFinderConditionData.TerritoryId) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } public void StopNow() { autoDutyIpc.Stop(); } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record WaitAutoDutyTask(uint ContentFinderConditionId) : ITask { public override string ToString() { return $"Wait(AutoDuty, left instance {ContentFinderConditionId})"; } } internal sealed class WaitAutoDutyExecutor(AutoDutyIpc autoDutyIpc, TerritoryData territoryData, IClientState clientState) : TaskExecutor(), IStoppableTaskExecutor, ITaskExecutor { protected override bool Start() { return true; } public override ETaskResult Update() { if (!territoryData.TryGetContentFinderCondition(base.Task.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData)) { throw new TaskException("Failed to get territory ID for content finder condition"); } if (clientState.TerritoryType == contentFinderConditionData.TerritoryId || !autoDutyIpc.IsStopped()) { return ETaskResult.StillRunning; } return ETaskResult.TaskComplete; } public void StopNow() { autoDutyIpc.Stop(); } public override bool ShouldInterruptOnDamage() { return false; } } internal sealed record OpenDutyFinderTask(uint ContentFinderConditionId) : ITask { public override string ToString() { return $"OpenDutyFinder({ContentFinderConditionId})"; } } internal sealed class OpenDutyFinderExecutor(GameFunctions gameFunctions, ICondition condition) : TaskExecutor() { protected override bool Start() { if (condition[ConditionFlag.InDutyQueue]) { return false; } gameFunctions.OpenDutyFinder(base.Task.ContentFinderConditionId); return true; } public override ETaskResult Update() { return ETaskResult.TaskComplete; } public override bool ShouldInterruptOnDamage() { return false; } } }