Ixal gathering fixes
authorLiza Carvelli <liza@carvel.li>
Sun, 23 Mar 2025 15:07:33 +0000 (16:07 +0100)
committerLiza Carvelli <liza@carvel.li>
Sun, 23 Mar 2025 15:07:33 +0000 (16:07 +0100)
GatheringPaths/2.x - A Realm Reborn/Coerthas Central Highlands/208_Whitebrim_MIN.json
GatheringPaths/2.x - A Realm Reborn/Mor Dhona/206_The Tangle_MIN.json
GatheringPaths/2.x - A Realm Reborn/Mor Dhona/244_The Tangle_BTN.json
Questionable.Model/Questing/EAction.cs
Questionable/Controller/Steps/Gathering/DoGather.cs
Questionable/Controller/Steps/QuestCleanUp.cs
Questionable/QuestionablePlugin.cs

index 667379c570a6f51b4c2adf1915cc9a62d8e46b9f..30bc81e4c32a8a902bfc2637a75215a4598432db 100644 (file)
@@ -1,17 +1,22 @@
 {
   "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
   "Author": "Redacted",
-  "ExtraQuestItems": [
-    2001426,
-    2001427
-  ],
   "Steps": [
     {
       "TerritoryId": 155,
       "InteractionType": "None",
-      "AetheryteShortcut": "Coerthas Central Highlands - Camp Dragonhead"
+      "AetheryteShortcut": "Coerthas Central Highlands - Camp Dragonhead",
+      "SkipConditions": {
+        "AetheryteShortcutIf": {
+          "InSameTerritory": true
+        }
+      }
     }
   ],
+  "ExtraQuestItems": [
+    2001426,
+    2001427
+  ],
   "Groups": [
     {
       "Nodes": [
                 "Y": 305.9586,
                 "Z": -236.1029
               },
-              "MinimumAngle": -300,
-              "MaximumAngle": -130
+              "MinimumAngle": -270,
+              "MaximumAngle": -155,
+              "MinimumDistance": 1,
+              "MaximumDistance": 1.8
             }
           ]
         }
@@ -58,8 +65,8 @@
                 "Y": 321.804,
                 "Z": -231.1056
               },
-              "MinimumAngle": -300,
-              "MaximumAngle": -40
+              "MinimumAngle": -285,
+              "MaximumAngle": -60
             }
           ]
         }
@@ -76,8 +83,8 @@
                 "Y": 309.1797,
                 "Z": -201.3309
               },
-              "MinimumAngle": 80,
-              "MaximumAngle": 260
+              "MinimumAngle": 105,
+              "MaximumAngle": 220
             },
             {
               "Position": {
@@ -85,8 +92,8 @@
                 "Y": 305.774,
                 "Z": -206.6072
               },
-              "MinimumAngle": -240,
-              "MaximumAngle": -20
+              "MinimumAngle": -220,
+              "MaximumAngle": -30
             }
           ]
         }
                 "X": 325.9026,
                 "Y": 306.1053,
                 "Z": -278.1509
-              }
+              },
+              "MinimumAngle": -350,
+              "MaximumAngle": -55
             },
             {
               "Position": {
                 "X": 289.1882,
                 "Y": 302.4524,
                 "Z": -261.3322
-              }
+              },
+              "MinimumAngle": -240,
+              "MaximumAngle": 20
             }
           ]
         }
       ]
     }
   ]
-}
\ No newline at end of file
+}
index 36d3abebe1f59a02693f79cd9b33576b31d06007..90e8e7203e49b91677a7415b8df32149630c7b2e 100644 (file)
@@ -1,17 +1,22 @@
 {
   "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
-  "ExtraQuestItems": [
-    2001415,
-    2001416
-  ],
   "Author": "Redacted",
   "Steps": [
     {
       "TerritoryId": 156,
       "InteractionType": "None",
-      "AetheryteShortcut": "Mor Dhona"
+      "AetheryteShortcut": "Mor Dhona",
+      "SkipConditions": {
+        "AetheryteShortcutIf": {
+          "InSameTerritory": true
+        }
+      }
     }
   ],
+  "ExtraQuestItems": [
+    2001415,
+    2001416
+  ],
   "Groups": [
     {
       "Nodes": [
@@ -24,8 +29,8 @@
                 "Y": -15.49291,
                 "Z": -326.9827
               },
-              "MinimumAngle": -295,
-              "MaximumAngle": -100
+              "MinimumAngle": -250,
+              "MaximumAngle": -115
             },
             {
               "Position": {
@@ -33,8 +38,8 @@
                 "Y": -15.7935,
                 "Z": -321.7334
               },
-              "MinimumAngle": -235,
-              "MaximumAngle": -60
+              "MinimumAngle": -190,
+              "MaximumAngle": -100
             }
           ]
         }
                 "Y": -13.83647,
                 "Z": -329.9288
               },
-              "MinimumAngle": -90,
-              "MaximumAngle": 100
+              "MinimumAngle": -55,
+              "MaximumAngle": 70,
+              "MinimumDistance": 1.4,
+              "MaximumDistance": 3
             },
             {
               "Position": {
@@ -60,8 +67,8 @@
                 "Y": -11.39756,
                 "Z": -323.1493
               },
-              "MinimumAngle": -90,
-              "MaximumAngle": 125
+              "MinimumAngle": -65,
+              "MaximumAngle": 95
             }
           ]
         }
@@ -78,8 +85,8 @@
                 "Y": -8.5558,
                 "Z": -326.8167
               },
-              "MinimumAngle": -160,
-              "MaximumAngle": 40
+              "MinimumAngle": -135,
+              "MaximumAngle": 5
             },
             {
               "Position": {
                 "Y": -6.345774,
                 "Z": -325.4775
               },
-              "MinimumAngle": 95,
-              "MaximumAngle": 290
+              "MinimumAngle": 135,
+              "MaximumAngle": 250,
+              "MinimumDistance": 1.4,
+              "MaximumDistance": 3
             }
           ]
         }
                 "Y": -5.395673,
                 "Z": -317.087
               },
-              "MinimumAngle": 145,
-              "MaximumAngle": 305
+              "MinimumAngle": 180,
+              "MaximumAngle": 275
             },
             {
               "Position": {
                 "Y": -5.140871,
                 "Z": -324.9326
               },
-              "MinimumAngle": -55,
-              "MaximumAngle": 110
+              "MinimumAngle": -10,
+              "MaximumAngle": 60,
+              "MinimumDistance": 1.8,
+              "MaximumDistance": 3
             }
           ]
         }
       ]
     }
   ]
-}
\ No newline at end of file
+}
index 4267cb0c2b39b511d54c2af3482339371f709078..501297ecb5b9fdf3fb7d66f33533ade22f5d3b2d 100644 (file)
@@ -1,17 +1,22 @@
 {
   "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
   "Author": "Redacted",
-  "ExtraQuestItems": [
-    2001412,
-    2001413
-  ],
   "Steps": [
     {
       "TerritoryId": 156,
       "InteractionType": "None",
-      "AetheryteShortcut": "Mor Dhona"
+      "AetheryteShortcut": "Mor Dhona",
+      "SkipConditions": {
+        "AetheryteShortcutIf": {
+          "InSameTerritory": true
+        }
+      }
     }
   ],
+  "ExtraQuestItems": [
+    2001412,
+    2001413
+  ],
   "Groups": [
     {
       "Nodes": [
@@ -24,8 +29,8 @@
                 "Y": -15.86567,
                 "Z": -363.9182
               },
-              "MinimumAngle": -185,
-              "MaximumAngle": -10
+              "MinimumAngle": -145,
+              "MaximumAngle": -35
             },
             {
               "Position": {
@@ -33,8 +38,8 @@
                 "Y": -16.00947,
                 "Z": -351.6216
               },
-              "MinimumAngle": -160,
-              "MaximumAngle": 45
+              "MinimumAngle": -125,
+              "MaximumAngle": 30
             }
           ]
         }
@@ -51,8 +56,8 @@
                 "Y": -16.46771,
                 "Z": -338.7833
               },
-              "MinimumAngle": -115,
-              "MaximumAngle": 100
+              "MinimumAngle": -90,
+              "MaximumAngle": 75
             },
             {
               "Position": {
@@ -60,8 +65,8 @@
                 "Y": -16.07329,
                 "Z": -337.0371
               },
-              "MinimumAngle": -80,
-              "MaximumAngle": 130
+              "MinimumAngle": -65,
+              "MaximumAngle": 105
             }
           ]
         },
@@ -74,7 +79,7 @@
                 "Y": -16.18071,
                 "Z": -338.7262
               },
-              "MinimumAngle": -50,
+              "MinimumAngle": -35,
               "MaximumAngle": 140
             }
           ]
@@ -92,8 +97,8 @@
                 "Y": -15.12936,
                 "Z": -346.8752
               },
-              "MinimumAngle": -240,
-              "MaximumAngle": -60
+              "MinimumAngle": -195,
+              "MaximumAngle": -110
             },
             {
               "Position": {
                 "Y": -16.14678,
                 "Z": -363.7992
               },
-              "MinimumAngle": -50,
-              "MaximumAngle": 175
+              "MinimumAngle": -20,
+              "MaximumAngle": 145
             }
           ]
         }
       ]
     }
   ]
-}
\ No newline at end of file
+}
index 7ee4787c981f4009f651011f1eb90503d74f782e..c80da8bb1fb1a1c3fc6e48fa3648485a2af45faf 100644 (file)
@@ -57,11 +57,13 @@ public enum EAction
     Wasshoi = 11499,
 
     CollectMiner = 240,
+    LuckOfTheMountaineer = 4081,
     ScourMiner = 22182,
     MeticulousMiner = 22184,
     ScrutinyMiner = 22185,
 
     CollectBotanist = 815,
+    LuckOfThePioneer = 4095,
     ScourBotanist = 22186,
     MeticulousBotanist = 22188,
     ScrutinyBotanist = 22189,
index bf4ab4aa91d86d34249aaaef09940af62f0f45f4..e9abd7d06319e41e4b970f510d5beb5a04b53358 100644 (file)
@@ -38,6 +38,7 @@ internal static class DoGather
         ILogger<GatherExecutor> logger) : TaskExecutor<Task>
     {
         private bool _wasGathering;
+        private bool _usedLuck;
         private SlotInfo? _slotToGather;
         private Queue<EAction>? _actionQueue;
 
@@ -99,10 +100,26 @@ internal static class DoGather
                             }
 
                             _actionQueue = GetNextActions(nodeCondition, slots);
-                            if (_actionQueue.Count == 0)
+                            if (_actionQueue == null)
                             {
-                                var slot = _slotToGather ?? slots.Single(x => x.ItemId == Task.Request.ItemId);
-                                addonGathering->FireCallbackInt(slot.Index);
+                                logger.LogInformation("Skipping the rest of gathering...");
+                                addonGathering->FireCallbackInt(-1);
+                                return ETaskResult.TaskComplete;
+                            }
+                            else if (_actionQueue.Count == 0)
+                            {
+                                var slot = _slotToGather ?? slots.SingleOrDefault(x => x.ItemId == Task.Request.ItemId) ?? slots.MinBy(x => x.ItemId);
+                                if (slot?.ItemId is >= 2 and <= 19)
+                                {
+                                    InventoryManager* inventoryManager = InventoryManager.Instance();
+                                    if (inventoryManager->GetInventoryItemCount(slot.ItemId) == 9999)
+                                        slot = null;
+                                }
+
+                                if (slot != null)
+                                    addonGathering->FireCallbackInt(slot.Index);
+                                else
+                                    addonGathering->FireCallbackInt(-1);
                             }
                         }
                     }
@@ -148,8 +165,12 @@ internal static class DoGather
         }
 
         [SuppressMessage("ReSharper", "UnusedParameter.Local")]
-        private Queue<EAction> GetNextActions(NodeCondition nodeCondition, List<SlotInfo> slots)
+        private Queue<EAction>? GetNextActions(NodeCondition nodeCondition, List<SlotInfo> slots)
         {
+            // it's possible the item has disappeared
+            if (_slotToGather != null && slots.All(x => x.Index != _slotToGather.Index))
+                _slotToGather = null;
+
             //uint gp = clientState.LocalPlayer!.CurrentGp;
             Queue<EAction> actions = new();
 
@@ -194,8 +215,31 @@ internal static class DoGather
                     }
                 }
 
-                var slot = slots.Single(x => x.ItemId == Task.Request.ItemId);
-                if (slot.GatheringChance > 0 && slot.GatheringChance < 100)
+                SlotInfo? slot = slots.SingleOrDefault(x => x.ItemId == Task.Request.ItemId);
+                if (slot == null)
+                {
+                    if (!_usedLuck &&
+                        nodeCondition.CurrentIntegrity == nodeCondition.MaxIntegrity &&
+                        CanUseAction(EAction.LuckOfTheMountaineer, EAction.LuckOfThePioneer))
+                    {
+                        _usedLuck = true;
+                        actions.Enqueue(PickAction(EAction.LuckOfTheMountaineer, EAction.LuckOfThePioneer));
+                        return actions;
+                    }
+                    else if (_usedLuck)
+                    {
+                        // we still can't find the item, if this node has been hit at least once we just close it
+                        if (nodeCondition.CurrentIntegrity != nodeCondition.MaxIntegrity)
+                            return null;
+
+                        // otherwise, there most likely is -any- other item available, probably a shard/crystal
+                        _slotToGather = slots.MinBy(x => x.ItemId);
+                        return actions;
+                    }
+                }
+
+                slot = slots.SingleOrDefault(x => x.ItemId == Task.Request.ItemId);
+                if (slot is { GatheringChance: > 0 and < 100 })
                 {
                     if (slot.GatheringChance >= 95 &&
                         CanUseAction(EAction.SharpVision1, EAction.FieldMastery1))
index 18f236d77a4a9783a52581b4c4649ceba0a683f4..770e5c8bc8c2bf8b20f11b599b14695e1ce945d6 100644 (file)
@@ -1,5 +1,9 @@
 using System.Collections.Generic;
 using System.Linq;
+using System.Threading.Tasks;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Component.GUI;
+using LLib.GameUI;
 using Microsoft.Extensions.Logging;
 using Questionable.Controller.Steps.Shared;
 using Questionable.Data;
@@ -62,4 +66,45 @@ internal static class QuestCleanUp
             return null;
         }
     }
+
+
+    internal sealed class CloseGatheringAddonFactory(IGameGui gameGui) : ITaskFactory
+    {
+        public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
+        {
+            if (IsAddonOpen("GatheringMasterpiece"))
+                yield return new CloseGatheringAddonTask("GatheringMasterpiece");
+
+            if (IsAddonOpen("Gathering"))
+                yield return new CloseGatheringAddonTask("Gathering");
+        }
+
+        private unsafe bool IsAddonOpen(string name)
+        {
+            return gameGui.TryGetAddonByName(name, out AtkUnitBase* addon) && addon->IsVisible;
+        }
+    }
+
+    internal sealed record CloseGatheringAddonTask(string AddonName) : ITask
+    {
+        public override string ToString() => $"CloseAddon({AddonName})";
+    }
+
+    internal sealed class DoCloseAddon(IGameGui gameGui) : TaskExecutor<CloseGatheringAddonTask>
+    {
+        protected override unsafe bool Start()
+        {
+            if (gameGui.TryGetAddonByName(Task.AddonName, out AtkUnitBase* addon))
+            {
+                addon->FireCallbackInt(-1);
+                return true;
+            }
+
+            return false;
+        }
+
+        public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+        public override bool ShouldInterruptOnDamage() => false;
+    }
 }
index 949e0b7d95d12825b82e129806fdff8db53f9171..81102938be2380fbc05d8c6712bd71915dd768b1 100644 (file)
@@ -141,6 +141,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
     {
         // individual tasks
         serviceCollection.AddTaskFactory<QuestCleanUp.CheckAlliedSocietyMount>();
+        serviceCollection.AddTaskFactoryAndExecutor<QuestCleanUp.CloseGatheringAddonTask, QuestCleanUp.CloseGatheringAddonFactory, QuestCleanUp.DoCloseAddon>();
         serviceCollection
             .AddTaskExecutor<MoveToLandingLocation.Task, MoveToLandingLocation.MoveToLandingLocationExecutor>();
         serviceCollection