Rework task factories; finish up Artisan crafting
authorLiza Carvelli <liza@carvel.li>
Sat, 17 Aug 2024 18:07:27 +0000 (20:07 +0200)
committerLiza Carvelli <liza@carvel.li>
Sat, 17 Aug 2024 18:07:27 +0000 (20:07 +0200)
30 files changed:
QuestPaths/6.x - Endwalker/Allied Societies/Loporrits/Dailies/4689_Money Makes the Moon Go Round.json
Questionable/Controller/Steps/Common/NextQuest.cs
Questionable/Controller/Steps/Gathering/TurnInDelivery.cs
Questionable/Controller/Steps/ITaskFactory.cs
Questionable/Controller/Steps/Interactions/Action.cs
Questionable/Controller/Steps/Interactions/AetherCurrent.cs
Questionable/Controller/Steps/Interactions/AethernetShard.cs
Questionable/Controller/Steps/Interactions/Aetheryte.cs
Questionable/Controller/Steps/Interactions/Dive.cs
Questionable/Controller/Steps/Interactions/Duty.cs
Questionable/Controller/Steps/Interactions/Emote.cs
Questionable/Controller/Steps/Interactions/EquipItem.cs
Questionable/Controller/Steps/Interactions/EquipRecommended.cs
Questionable/Controller/Steps/Interactions/Interact.cs
Questionable/Controller/Steps/Interactions/Jump.cs
Questionable/Controller/Steps/Interactions/Say.cs
Questionable/Controller/Steps/Interactions/UseItem.cs
Questionable/Controller/Steps/Leves/InitiateLeve.cs
Questionable/Controller/Steps/Shared/AethernetShortcut.cs
Questionable/Controller/Steps/Shared/AetheryteShortcut.cs
Questionable/Controller/Steps/Shared/Craft.cs
Questionable/Controller/Steps/Shared/GatheringRequiredItems.cs
Questionable/Controller/Steps/Shared/Move.cs
Questionable/Controller/Steps/Shared/SkipCondition.cs
Questionable/Controller/Steps/Shared/StepDisabled.cs
Questionable/Controller/Steps/Shared/WaitAtEnd.cs
Questionable/Controller/Steps/Shared/WaitAtStart.cs
Questionable/Controller/Steps/SimpleTaskFactory.cs [new file with mode: 0644]
Questionable/External/ArtisanIpc.cs
Questionable/Functions/GameFunctions.cs

index 10d5d1e857112bc79b98bd4325b4f299eba26abd..b1d281b47a10c234fc27889f22f7f34e829b6600 100644 (file)
@@ -57,7 +57,8 @@
             "Z": -273.68756
           },
           "TerritoryId": 959,
-          "InteractionType": "WalkTo"
+          "InteractionType": "WalkTo",
+          "Fly": true
         },
         {
           "DataId": 1044403,
index 4efe08ae8679876883d00172fcc0949c13080141..f338c88cc73703555179113d4610ac474764aa1e 100644 (file)
@@ -9,9 +9,9 @@ namespace Questionable.Controller.Steps.Common;
 
 internal static class NextQuest
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.CompleteQuest)
                 return null;
index f3a4ed511d401d87e0a10f0c0681fd8084afbcd4..23abcb3ca128ef9d4c029fce0a3051f9d9b84edc 100644 (file)
@@ -13,9 +13,9 @@ namespace Questionable.Controller.Steps.Gathering;
 
 internal static class TurnInDelivery
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (quest.Id is not SatisfactionSupplyNpcId || sequence.Sequence != 1)
                 return null;
index 287f029f8fef2cb0fc10b868c8f59605262dbd58..19d5de0b01b19677cf3c1188d932c35a2881c3ad 100644 (file)
@@ -6,12 +6,5 @@ namespace Questionable.Controller.Steps;
 
 internal interface ITaskFactory
 {
-    ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step);
-
-    IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
-    {
-        var task = CreateTask(quest, sequence, step);
-        if (task != null)
-            yield return task;
-    }
+    IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step);
 }
index cf60b6c70412bda5109fae2085c0018aa5f152a1..802d2d8c43ac9a23a214a23f63173eba2f7ca859 100644 (file)
@@ -33,9 +33,6 @@ internal static class Action
                 return [unmount, task];
             }
         }
-
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new InvalidOperationException();
     }
 
     internal sealed class UseOnObject(GameFunctions gameFunctions, ILogger<UseOnObject> logger) : ITask
index 7df86e784b44f997fff7105a8f1d624548c2f1a6..d43872885eb24ab81995a377e6718c13afa6846a 100644 (file)
@@ -11,9 +11,9 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class AetherCurrent
 {
-    internal sealed class Factory(IServiceProvider serviceProvider, AetherCurrentData aetherCurrentData, IChatGui chatGui) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider, AetherCurrentData aetherCurrentData, IChatGui chatGui) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.AttuneAetherCurrent)
                 return null;
index c1e14010f3d8e89bebf161fd9fa7def5c3c727a0..06079acb41b34187753e01efca068c483bb74d5c 100644 (file)
@@ -11,9 +11,9 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class AethernetShard
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.AttuneAethernetShard)
                 return null;
index 4a47b7442fdd7cd661069b3f70b619f77ce52868..6103374972435a7cdebc0010ebf70c7ae2a1d616 100644 (file)
@@ -10,9 +10,9 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class Aetheryte
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.AttuneAetheryte)
                 return null;
index 3976eb73f958fa4fd50ea4d82fa98e70e67dec2b..3b8712f49b5feb38c9da5d8f9f75ef9577f05209 100644 (file)
@@ -18,9 +18,9 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class Dive
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.Dive)
                 return null;
index 35a73dd976d238054c9a35968c7bcca2f7efd4f1..ed80dd4cc3440e7275666622acd6567c92d31049 100644 (file)
@@ -10,9 +10,9 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class Duty
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.Duty)
                 return null;
index fafa0fd836c2ae9ba316128b1fca385b13d460cf..35cd64b7cedd89b30a1759fbdcd3faa76297168a 100644 (file)
@@ -36,9 +36,6 @@ internal static class Emote
                 return [unmount, task];
             }
         }
-
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new InvalidOperationException();
     }
 
     internal sealed class UseOnObject(ChatFunctions chatFunctions) : AbstractDelayedTask
index 84952fe7add66c47d3cf59f8989cadb9662c4d4c..abd72c7cb926281edc0ea127333038648a074736 100644 (file)
@@ -16,9 +16,9 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class EquipItem
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.EquipItem)
                 return null;
index c97ec7dc8734b321f3805c042dd614bba93004dd..e94f819fa7076b5456da8d843c1e8fd46c984687 100644 (file)
@@ -10,9 +10,9 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class EquipRecommended
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.EquipRecommended)
                 return null;
@@ -21,9 +21,9 @@ internal static class EquipRecommended
         }
     }
 
-    internal sealed class BeforeDutyOrInstance(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class BeforeDutyOrInstance(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.Duty &&
                 step.InteractionType != EInteractionType.SinglePlayerDuty &&
index 9fc57638414104e434bc22cdef2edaf3b6331d64..50aae41a4db60fbf6a996db1a234452e9f337550 100644 (file)
@@ -42,9 +42,6 @@ internal static class Interact
                 .With(step.DataId.Value, quest, step.InteractionType,
                     step.TargetTerritoryId != null || quest.Id is SatisfactionSupplyNpcId);
         }
-
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new InvalidOperationException();
     }
 
     internal sealed class DoInteract(GameFunctions gameFunctions, ICondition condition, ILogger<DoInteract> logger)
index 0b0b099d7d0aa884a328b6e6356367689f87fe6d..dd2dc92aafb93b5704426a0b5d35fbde9ee6fe2b 100644 (file)
@@ -11,9 +11,9 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class Jump
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.Jump)
                 return null;
index ee900af35b0748ec1aa72eaac471e5a079e8fd79..f203d42ff3ec6d73474447806a33ac611ce985f3 100644 (file)
@@ -28,9 +28,6 @@ internal static class Say
             var task = serviceProvider.GetRequiredService<UseChat>().With(excelString);
             return [unmount, task];
         }
-
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new InvalidOperationException();
     }
 
     internal sealed class UseChat(ChatFunctions chatFunctions) : AbstractDelayedTask
index d32bb0a44c431537e40bae8d4ea2111702a64005..ead2209882572a6248ef1fc373160b3c4b2edbee 100644 (file)
@@ -100,9 +100,6 @@ internal static class UseItem
             }
         }
 
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new InvalidOperationException();
-
         private IEnumerable<ITask> CreateVesperBayFallbackTask()
         {
             logger.LogWarning("No vesper bay aetheryte tickets in inventory, navigating via ferry in Limsa instead");
index 42cd8a4846ab75360bc48e0c1ee77f0069dc057b..545324a422118ccd11896eb4352ec3df149667a3 100644 (file)
@@ -30,9 +30,6 @@ internal static class InitiateLeve
             yield return serviceProvider.GetRequiredService<SelectDifficulty>();
             yield return new WaitConditionTask(() => condition[ConditionFlag.BoundByDuty], "Wait(BoundByDuty)");
         }
-
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new NotImplementedException();
     }
 
     internal sealed unsafe class SkipInitiateIfActive : ITask
index 1c45596db0cc28b8b642fe272f54a1d594e5651f..d550cb6611aa6bce6033e363e81fdbf76fb69014 100644 (file)
@@ -20,9 +20,9 @@ namespace Questionable.Controller.Steps.Shared;
 
 internal static class AethernetShortcut
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.AethernetShortcut == null)
                 return null;
index 9da8a019bb3b170479dbbd13affbf5e1c2b6fa36..59cd75f3ca2a3e2b3ebb7a2572fe37e2f2da3f2c 100644 (file)
@@ -17,9 +17,9 @@ internal static class AetheryteShortcut
 {
     internal sealed class Factory(
         IServiceProvider serviceProvider,
-        AetheryteData aetheryteData) : ITaskFactory
+        AetheryteData aetheryteData) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.AetheryteShortcut == null)
                 return null;
index ef46af8d953b949bfa63d96fcbb9c79417cec561..2aeff8642f7531931d024f6d65ced2f3bf774773 100644 (file)
@@ -1,9 +1,15 @@
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Client.Game;
+using FFXIVClientStructs.FFXIV.Client.UI.Agent;
+using FFXIVClientStructs.FFXIV.Component.GUI;
 using LLib.GameData;
 using Lumina.Excel.GeneratedSheets;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Questionable.Controller.Steps.Common;
 using Questionable.External;
 using Questionable.Model.Questing;
 using Quest = Questionable.Model.Quest;
@@ -14,19 +20,27 @@ internal static class Craft
 {
     internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.InteractionType != EInteractionType.Craft)
-                return null;
+                return [];
 
             ArgumentNullException.ThrowIfNull(step.ItemId);
             ArgumentNullException.ThrowIfNull(step.ItemCount);
-            return serviceProvider.GetRequiredService<DoCraft>()
-                .With(step.ItemId.Value, step.ItemCount.Value);
+            return
+            [
+                serviceProvider.GetRequiredService<UnmountTask>(),
+                serviceProvider.GetRequiredService<DoCraft>()
+                    .With(step.ItemId.Value, step.ItemCount.Value)
+            ];
         }
     }
 
-    internal sealed class DoCraft(IDataManager dataManager, IClientState clientState, ArtisanIpc artisanIpc) : ITask
+    internal sealed class DoCraft(
+        IDataManager dataManager,
+        IClientState clientState,
+        ArtisanIpc artisanIpc,
+        ILogger<DoCraft> logger) : ITask
     {
         private uint _itemId;
         private int _itemCount;
@@ -40,11 +54,17 @@ internal static class Craft
 
         public bool Start()
         {
+            if (HasRequestedItems())
+            {
+                logger.LogInformation("Already own {ItemCount}x {ItemId}", _itemCount, _itemId);
+                return false;
+            }
+
             RecipeLookup? recipeLookup = dataManager.GetExcelSheet<RecipeLookup>()!.GetRow(_itemId);
             if (recipeLookup == null)
                 throw new TaskException($"Item {_itemId} is not craftable");
 
-            uint recipeId = ((EClassJob)clientState.LocalPlayer!.ClassJob.Id) switch
+            uint recipeId = (EClassJob)clientState.LocalPlayer!.ClassJob.Id switch
             {
                 EClassJob.Carpenter => recipeLookup.CRP.Row,
                 EClassJob.Blacksmith => recipeLookup.BSM.Row,
@@ -59,7 +79,8 @@ internal static class Craft
 
             if (recipeId == 0)
             {
-                recipeId = new[]{
+                recipeId = new[]
+                    {
                         recipeLookup.CRP.Row,
                         recipeLookup.BSM.Row,
                         recipeLookup.ARM.Row,
@@ -75,15 +96,49 @@ internal static class Craft
             if (recipeId == 0)
                 throw new TaskException($"Unable to determine recipe for item {_itemId}");
 
-            if (!artisanIpc.CraftItem((ushort)recipeId, _itemCount))
+            int remainingItemCount = _itemCount - GetOwnedItemCount();
+            logger.LogInformation(
+                "Starting craft for item {ItemId} with recipe {RecipeId} for {RemainingItemCount} items",
+                _itemId, recipeId, remainingItemCount);
+            if (!artisanIpc.CraftItem((ushort)recipeId, remainingItemCount))
                 throw new TaskException($"Failed to start Artisan craft for recipe {recipeId}");
 
             return true;
         }
 
-        public ETaskResult Update()
+        public unsafe ETaskResult Update()
         {
+            if (HasRequestedItems() && !artisanIpc.IsCrafting())
+            {
+                AgentRecipeNote* agentRecipeNote = AgentRecipeNote.Instance();
+                if (agentRecipeNote != null && agentRecipeNote->IsAgentActive())
+                {
+                    uint addonId = agentRecipeNote->GetAddonId();
+                    if (addonId == 0)
+                        return ETaskResult.StillRunning;
+
+                    AtkUnitBase* addon = AtkStage.Instance()->RaptureAtkUnitManager->GetAddonById((ushort)addonId);
+                    if (addon != null)
+                    {
+                        logger.LogInformation("Closing crafting window");
+                        addon->Close(true);
+                        return ETaskResult.TaskComplete;
+                    }
+                }
+            }
+
             return ETaskResult.StillRunning;
         }
+
+        private bool HasRequestedItems() => GetOwnedItemCount() >= _itemCount;
+
+        private unsafe int GetOwnedItemCount()
+        {
+            InventoryManager* inventoryManager = InventoryManager.Instance();
+            return inventoryManager->GetInventoryItemCount(_itemId, isHq: false, checkEquipped: false)
+                   + inventoryManager->GetInventoryItemCount(_itemId, isHq: true, checkEquipped: false);
+        }
+
+        public override string ToString() => $"Craft {_itemCount}x {_itemId} (with Artisan)";
     }
 }
index a58c288e871f293c1579952f4a68a57e169a14fa..9fdf9033878fc371b835118a67e823a43fdad19e 100644 (file)
@@ -105,9 +105,6 @@ internal static class GatheringRequiredItems
                        minCollectability: (short)requiredGatheredItems.Collectability) >=
                    requiredGatheredItems.ItemCount;
         }
-
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new NotImplementedException();
     }
 
     internal sealed class StartGathering(GatheringController gatheringController) : ITask
index 83b0f932d9da0e9f927fbcbdbd506f77f4c6a91f..256badeff0cd32cbf15988a6394cc4facc744a57 100644 (file)
@@ -59,9 +59,6 @@ internal static class Move
 
             return [];
         }
-
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new InvalidOperationException();
     }
 
     internal sealed class MoveBuilder(
index f750cc8bbc42184f92b73395ad5592a7f013607e..eed128c5b2e19edcef02e0cf09d7f7c3313c2b92 100644 (file)
@@ -20,9 +20,9 @@ namespace Questionable.Controller.Steps.Shared;
 
 internal static class SkipCondition
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             var skipConditions = step.SkipConditions?.StepIf;
             if (skipConditions is { Never: true })
index 609f3143e337e6fb25f972e5fba4c8245f493ad8..430a8981fffcd47c4ca0834423068f27778438df 100644 (file)
@@ -8,9 +8,9 @@ namespace Questionable.Controller.Steps.Shared;
 
 internal static class StepDisabled
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (!step.Disabled)
                 return null;
index 97e53f7d317574f45cd7aa5cfe3d2cd22e84dbdd..a1d29c4232e555edfab6375306d436837ba9a00e 100644 (file)
@@ -136,9 +136,6 @@ internal static class WaitAtEnd
             }
         }
 
-        public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
-            => throw new InvalidOperationException();
-
         private static NextStep Next(Quest quest, QuestSequence sequence)
         {
             return new NextStep(quest.Id, sequence.Sequence);
index b39e301dde277ef35671bff8b9e23c8fef55a700..1e7944c571d59295aa0134fcce6d413d24921f19 100644 (file)
@@ -8,9 +8,9 @@ namespace Questionable.Controller.Steps.Shared;
 
 internal static class WaitAtStart
 {
-    internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory
+    internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory
     {
-        public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
+        public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             if (step.DelaySecondsAtStart == null)
                 return null;
diff --git a/Questionable/Controller/Steps/SimpleTaskFactory.cs b/Questionable/Controller/Steps/SimpleTaskFactory.cs
new file mode 100644 (file)
index 0000000..624886b
--- /dev/null
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using Questionable.Model;
+using Questionable.Model.Questing;
+
+namespace Questionable.Controller.Steps;
+
+internal abstract class SimpleTaskFactory : ITaskFactory
+{
+    public abstract ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step);
+
+    public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
+    {
+        var task = CreateTask(quest, sequence, step);
+        if (task != null)
+            yield return task;
+    }
+}
index b64d5137df1e5798f65242a6ebac0c3aae253a03..742d9ab128655962f257836a9cc06f2d99be7550 100644 (file)
@@ -9,11 +9,13 @@ internal sealed class ArtisanIpc
 {
     private readonly ILogger<ArtisanIpc> _logger;
     private readonly ICallGateSubscriber<ushort, int, object> _craftItem;
+    private readonly ICallGateSubscriber<bool> _getEnduranceStatus;
 
     public ArtisanIpc(IDalamudPluginInterface pluginInterface, ILogger<ArtisanIpc> logger)
     {
         _logger = logger;
         _craftItem = pluginInterface.GetIpcSubscriber<ushort, int, object>("Artisan.CraftItem");
+        _getEnduranceStatus = pluginInterface.GetIpcSubscriber<bool>("Artisan.GetEnduranceStatus");
     }
 
     public bool CraftItem(ushort recipeId, int quantity)
@@ -31,4 +33,20 @@ internal sealed class ArtisanIpc
             return false;
         }
     }
+
+    /// <summary>
+    /// This ignores crafting lists, but we can't create/use those.
+    /// </summary>
+    public bool IsCrafting()
+    {
+        try
+        {
+            return _getEnduranceStatus.InvokeFunc();
+        }
+        catch (IpcError e)
+        {
+            _logger.LogError(e, "Unable to check for Artisan endurance status");
+            return false;
+        }
+    }
 }
index e1c20069863157432fe17e275aed8234ee02b015..32b35ce7b74dd27da6112803a2fa2796f77a80ba 100644 (file)
@@ -401,6 +401,15 @@ internal sealed unsafe class GameFunctions
         if (IsLoadingScreenVisible())
             return true;
 
+        if (_condition[ConditionFlag.Crafting])
+        {
+            if (!AgentRecipeNote.Instance()->IsAgentActive())
+                return true;
+
+            if (!_condition[ConditionFlag.PreparingToCraft])
+                return true;
+        }
+
         return _condition[ConditionFlag.Occupied] || _condition[ConditionFlag.Occupied30] ||
                _condition[ConditionFlag.Occupied33] || _condition[ConditionFlag.Occupied38] ||
                _condition[ConditionFlag.Occupied39] || _condition[ConditionFlag.OccupiedInEvent] ||