From cbd4d1bba96ed5fc3971014ec5651efa72a2f23f Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Sun, 23 Mar 2025 16:07:33 +0100 Subject: [PATCH] Ixal gathering fixes --- .../208_Whitebrim_MIN.json | 43 ++++++++------ .../Mor Dhona/206_The Tangle_MIN.json | 55 ++++++++++-------- .../Mor Dhona/244_The Tangle_BTN.json | 43 +++++++------- Questionable.Model/Questing/EAction.cs | 2 + .../Controller/Steps/Gathering/DoGather.cs | 56 +++++++++++++++++-- Questionable/Controller/Steps/QuestCleanUp.cs | 45 +++++++++++++++ Questionable/QuestionablePlugin.cs | 1 + 7 files changed, 182 insertions(+), 63 deletions(-) diff --git a/GatheringPaths/2.x - A Realm Reborn/Coerthas Central Highlands/208_Whitebrim_MIN.json b/GatheringPaths/2.x - A Realm Reborn/Coerthas Central Highlands/208_Whitebrim_MIN.json index 667379c5..30bc81e4 100644 --- a/GatheringPaths/2.x - A Realm Reborn/Coerthas Central Highlands/208_Whitebrim_MIN.json +++ b/GatheringPaths/2.x - A Realm Reborn/Coerthas Central Highlands/208_Whitebrim_MIN.json @@ -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": [ @@ -31,8 +36,10 @@ "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 } ] } @@ -102,18 +109,22 @@ "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 +} diff --git a/GatheringPaths/2.x - A Realm Reborn/Mor Dhona/206_The Tangle_MIN.json b/GatheringPaths/2.x - A Realm Reborn/Mor Dhona/206_The Tangle_MIN.json index 36d3abeb..90e8e720 100644 --- a/GatheringPaths/2.x - A Realm Reborn/Mor Dhona/206_The Tangle_MIN.json +++ b/GatheringPaths/2.x - A Realm Reborn/Mor Dhona/206_The Tangle_MIN.json @@ -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 } ] } @@ -51,8 +56,10 @@ "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": { @@ -87,8 +94,10 @@ "Y": -6.345774, "Z": -325.4775 }, - "MinimumAngle": 95, - "MaximumAngle": 290 + "MinimumAngle": 135, + "MaximumAngle": 250, + "MinimumDistance": 1.4, + "MaximumDistance": 3 } ] } @@ -105,8 +114,8 @@ "Y": -5.395673, "Z": -317.087 }, - "MinimumAngle": 145, - "MaximumAngle": 305 + "MinimumAngle": 180, + "MaximumAngle": 275 }, { "Position": { @@ -114,12 +123,14 @@ "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 +} diff --git a/GatheringPaths/2.x - A Realm Reborn/Mor Dhona/244_The Tangle_BTN.json b/GatheringPaths/2.x - A Realm Reborn/Mor Dhona/244_The Tangle_BTN.json index 4267cb0c..501297ec 100644 --- a/GatheringPaths/2.x - A Realm Reborn/Mor Dhona/244_The Tangle_BTN.json +++ b/GatheringPaths/2.x - A Realm Reborn/Mor Dhona/244_The Tangle_BTN.json @@ -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": { @@ -101,12 +106,12 @@ "Y": -16.14678, "Z": -363.7992 }, - "MinimumAngle": -50, - "MaximumAngle": 175 + "MinimumAngle": -20, + "MaximumAngle": 145 } ] } ] } ] -} \ No newline at end of file +} diff --git a/Questionable.Model/Questing/EAction.cs b/Questionable.Model/Questing/EAction.cs index 7ee4787c..c80da8bb 100644 --- a/Questionable.Model/Questing/EAction.cs +++ b/Questionable.Model/Questing/EAction.cs @@ -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, diff --git a/Questionable/Controller/Steps/Gathering/DoGather.cs b/Questionable/Controller/Steps/Gathering/DoGather.cs index bf4ab4aa..e9abd7d0 100644 --- a/Questionable/Controller/Steps/Gathering/DoGather.cs +++ b/Questionable/Controller/Steps/Gathering/DoGather.cs @@ -38,6 +38,7 @@ internal static class DoGather ILogger logger) : TaskExecutor { private bool _wasGathering; + private bool _usedLuck; private SlotInfo? _slotToGather; private Queue? _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 GetNextActions(NodeCondition nodeCondition, List slots) + private Queue? GetNextActions(NodeCondition nodeCondition, List 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 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)) diff --git a/Questionable/Controller/Steps/QuestCleanUp.cs b/Questionable/Controller/Steps/QuestCleanUp.cs index 18f236d7..770e5c8b 100644 --- a/Questionable/Controller/Steps/QuestCleanUp.cs +++ b/Questionable/Controller/Steps/QuestCleanUp.cs @@ -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 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 + { + 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; + } } diff --git a/Questionable/QuestionablePlugin.cs b/Questionable/QuestionablePlugin.cs index 949e0b7d..81102938 100644 --- a/Questionable/QuestionablePlugin.cs +++ b/Questionable/QuestionablePlugin.cs @@ -141,6 +141,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin { // individual tasks serviceCollection.AddTaskFactory(); + serviceCollection.AddTaskFactoryAndExecutor(); serviceCollection .AddTaskExecutor(); serviceCollection -- 2.30.2