Attempt to handle Revisit triggering
authorLiza Carvelli <liza@carvel.li>
Sun, 11 Aug 2024 19:48:01 +0000 (21:48 +0200)
committerLiza Carvelli <liza@carvel.li>
Sun, 11 Aug 2024 19:57:33 +0000 (21:57 +0200)
GatheringPaths/3.x - Heavensward/Coerthas Western Highlands/351_Red Rim_MIN.json
Questionable.Model/Gathering/GatheringNode.cs
Questionable.Model/Gathering/GatheringRoot.cs
Questionable/Controller/GatheringController.cs
Questionable/Controller/QuestController.cs
Questionable/Controller/Steps/Gathering/DoGather.cs
Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs
Questionable/Controller/Steps/IRevisitAware.cs [new file with mode: 0644]

index cb1d96af8a7f5f691668eb270ea0e8e3ae0743fe..e61ee374530f6cbeb6df425d4da7b15cec1264cb 100644 (file)
                 "Z": 405.1829
               },
               "MinimumAngle": 100,
-              "MaximumAngle": 250
+              "MaximumAngle": 250,
+              "MinimumDistance": 1.5,
+              "MaximumDistance": 3
             }
           ]
         },
         {
           "DataId": 31345,
+          "Fly": false,
           "Locations": [
             {
               "Position": {
                 "Y": 216.5585,
                 "Z": 412.4353
               },
-              "MinimumAngle": 50,
-              "MaximumAngle": 165
+              "MinimumAngle": 75,
+              "MaximumAngle": 145,
+              "MinimumDistance": 1.5,
+              "MaximumDistance": 3
             },
             {
               "Position": {
@@ -39,7 +44,9 @@
                 "Z": 421.5481
               },
               "MinimumAngle": 0,
-              "MaximumAngle": 145
+              "MaximumAngle": 145,
+              "MinimumDistance": 1.5,
+              "MaximumDistance": 3
             },
             {
               "Position": {
@@ -48,7 +55,9 @@
                 "Z": 408.2164
               },
               "MinimumAngle": 155,
-              "MaximumAngle": 225
+              "MaximumAngle": 225,
+              "MinimumDistance": 1.5,
+              "MaximumDistance": 3
             }
           ]
         }
       ]
     }
   ]
-}
+}
\ No newline at end of file
index 9a3ed58cb3f8677840199c76e7fc5c1dc4528721..d5bbce44ace1b6c8a37ace269fba4f0ffaf70d14 100644 (file)
@@ -5,7 +5,7 @@ namespace Questionable.Model.Gathering;
 public sealed class GatheringNode
 {
     public uint DataId { get; set; }
-    public bool Fly { get; set; }
+    public bool? Fly { get; set; }
 
     public List<GatheringLocation> Locations { get; set; } = [];
 }
index b1f98ca853b39498c1c9d7e6666354880c1f5c7d..c76f45c77c2784be4f8040d34bcaf4a1d14cc792 100644 (file)
@@ -16,6 +16,6 @@ public sealed class GatheringRoot
     public EAetheryteLocation? AetheryteShortcut { get; set; }
 
     public AethernetShortcut? AethernetShortcut { get; set; }
-    public bool FlyBetweenNodes { get; set; } = true;
+    public bool? FlyBetweenNodes { get; set; }
     public List<GatheringNodeGroup> Groups { get; set; } = [];
 }
index 53878e531f01ccb369c84701db623230122fc9d8..f5a517fa72bdca4868cbbd886d1fc18290ff5863 100644 (file)
@@ -1,13 +1,18 @@
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Numerics;
+using System.Text.RegularExpressions;
 using Dalamud.Game.ClientState.Conditions;
 using Dalamud.Game.ClientState.Objects.Enums;
+using Dalamud.Game.Text.SeStringHandling;
 using Dalamud.Plugin.Services;
 using FFXIVClientStructs.FFXIV.Client.Game;
 using FFXIVClientStructs.FFXIV.Client.Game.Event;
 using FFXIVClientStructs.FFXIV.Client.Game.UI;
+using LLib;
+using Lumina.Excel.GeneratedSheets;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Questionable.Controller.Steps;
@@ -32,6 +37,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
     private readonly IObjectTable _objectTable;
     private readonly IServiceProvider _serviceProvider;
     private readonly ICondition _condition;
+    private readonly Regex _revisitRegex;
 
     private CurrentRequest? _currentRequest;
 
@@ -44,7 +50,9 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
         IChatGui chatGui,
         ILogger<GatheringController> logger,
         IServiceProvider serviceProvider,
-        ICondition condition)
+        ICondition condition,
+        IDataManager dataManager,
+        IPluginLog pluginLog)
         : base(chatGui, logger)
     {
         _movementController = movementController;
@@ -54,6 +62,9 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
         _objectTable = objectTable;
         _serviceProvider = serviceProvider;
         _condition = condition;
+
+        _revisitRegex = dataManager.GetRegex<LogMessage>(5574, x => x.Text, pluginLog)
+                        ?? throw new InvalidDataException("No regex found for revisit message");
     }
 
     public bool Start(GatheringRequest gatheringRequest)
@@ -88,13 +99,19 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
     public EStatus Update()
     {
         if (_currentRequest == null)
+        {
+            Stop("No request");
             return EStatus.Complete;
+        }
 
         if (_movementController.IsPathfinding || _movementController.IsPathfinding)
             return EStatus.Moving;
 
         if (HasRequestedItems() && !_condition[ConditionFlag.Gathering])
+        {
+            Stop("Has all items");
             return EStatus.Complete;
+        }
 
         if (_currentTask == null && _taskQueue.Count == 0)
             GoToNextNode();
@@ -136,6 +153,9 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
 
         _taskQueue.Enqueue(_serviceProvider.GetRequiredService<MountTask>()
             .With(_currentRequest.Root.TerritoryId, MountTask.EMountIf.Always));
+
+        bool fly = currentNode.Fly.GetValueOrDefault(_currentRequest.Root.FlyBetweenNodes.GetValueOrDefault(true)) &&
+                   _gameFunctions.IsFlyingUnlocked(_currentRequest.Root.TerritoryId);
         if (currentNode.Locations.Count > 1)
         {
             Vector3 averagePosition = new Vector3
@@ -144,8 +164,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
                 Y = currentNode.Locations.Select(x => x.Position.Y).Max() + 5f,
                 Z = currentNode.Locations.Sum(x => x.Position.Z) / currentNode.Locations.Count,
             };
-            bool fly = (currentNode.Fly || _currentRequest.Root.FlyBetweenNodes)
-                       && _gameFunctions.IsFlyingUnlocked(_currentRequest.Root.TerritoryId);
+
             Vector3? pointOnFloor = _navmeshIpc.GetPointOnFloor(averagePosition, true);
             if (pointOnFloor != null)
                 pointOnFloor = pointOnFloor.Value with { Y = pointOnFloor.Value.Y + (fly ? 3f : 0f) };
@@ -156,17 +175,19 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
         }
 
         _taskQueue.Enqueue(_serviceProvider.GetRequiredService<MoveToLandingLocation>()
-            .With(_currentRequest.Root.TerritoryId,
-                currentNode.Fly || _currentRequest.Root.FlyBetweenNodes,
-                currentNode));
+            .With(_currentRequest.Root.TerritoryId, fly, currentNode));
         _taskQueue.Enqueue(_serviceProvider.GetRequiredService<Interact.DoInteract>()
             .With(currentNode.DataId, null, EInteractionType.InternalGather, true));
-        _taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGather>()
-            .With(_currentRequest.Data, currentNode));
-        if (_currentRequest.Data.Collectability > 0)
+
+        foreach (bool revisitRequired in new[] { false, true })
         {
-            _taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGatherCollectable>()
-                .With(_currentRequest.Data, currentNode));
+            _taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGather>()
+                .With(_currentRequest.Data, currentNode, revisitRequired));
+            if (_currentRequest.Data.Collectability > 0)
+            {
+                _taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGatherCollectable>()
+                    .With(_currentRequest.Data, currentNode, revisitRequired));
+            }
         }
     }
 
@@ -232,6 +253,21 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
             return base.GetRemainingTaskNames();
     }
 
+    public void OnNormalToast(SeString message)
+    {
+        if (_revisitRegex.IsMatch(message.TextValue))
+        {
+            if (_currentTask is IRevisitAware currentTaskRevisitAware)
+                currentTaskRevisitAware.OnRevisit();
+
+            foreach (ITask task in _taskQueue)
+            {
+                if (task is IRevisitAware taskRevisitAware)
+                    taskRevisitAware.OnRevisit();
+            }
+        }
+    }
+
     private sealed class CurrentRequest
     {
         public required GatheringRequest Data { get; init; }
index 140f3fa78b0d7900093bf91c5c4c7361b86dc3a4..0df885de69e00bfca105d0fe82db797d6c94d9f0 100644 (file)
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using Dalamud.Game.ClientState.Conditions;
 using Dalamud.Game.ClientState.Keys;
+using Dalamud.Game.Gui.Toast;
 using Dalamud.Game.Text.SeStringHandling;
 using Dalamud.Plugin.Services;
 using FFXIVClientStructs.FFXIV.Client.Game;
@@ -90,6 +91,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
         _taskFactories = taskFactories.ToList().AsReadOnly();
 
         _condition.ConditionChange += OnConditionChange;
+        _toastGui.Toast += OnNormalToast;
         _toastGui.ErrorToast += OnErrorToast;
     }
 
@@ -786,6 +788,11 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
             conditionChangeAware.OnConditionChange(flag, value);
     }
 
+    private void OnNormalToast(ref SeString message, ref ToastOptions options, ref bool ishandled)
+    {
+        _gatheringController.OnNormalToast(message);
+    }
+
     private void OnErrorToast(ref SeString message, ref bool ishandled)
     {
         if (_currentTask is IToastAware toastAware)
@@ -795,6 +802,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
     public void Dispose()
     {
         _toastGui.ErrorToast -= OnErrorToast;
+        _toastGui.Toast -= OnNormalToast;
         _condition.ConditionChange -= OnConditionChange;
     }
 
index e947212cf956a2b38f008919f82f6896f0963b4d..6cd4b3e702f386fea4fb0038263adbf1d604aea2 100644 (file)
@@ -22,20 +22,24 @@ internal sealed class DoGather(
     IGameGui gameGui,
     IClientState clientState,
     ICondition condition,
-    ILogger<DoGather> logger) : ITask
+    ILogger<DoGather> logger) : ITask, IRevisitAware
 {
     private const uint StatusGatheringRateUp = 218;
 
     private GatheringController.GatheringRequest _currentRequest = null!;
     private GatheringNode _currentNode = null!;
+    private bool _revisitRequired;
+    private bool _revisitTriggered;
     private bool _wasGathering;
     private SlotInfo? _slotToGather;
     private Queue<EAction>? _actionQueue;
 
-    public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode)
+    public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode,
+        bool revisitRequired)
     {
         _currentRequest = currentRequest;
         _currentNode = currentNode;
+        _revisitRequired = revisitRequired;
         return this;
     }
 
@@ -43,8 +47,17 @@ internal sealed class DoGather(
 
     public unsafe ETaskResult Update()
     {
+        if (_revisitRequired && !_revisitTriggered)
+        {
+            logger.LogInformation("No revisit");
+            return ETaskResult.TaskComplete;
+        }
+
         if (gatheringController.HasNodeDisappeared(_currentNode))
+        {
+            logger.LogInformation("Node disappeared");
             return ETaskResult.TaskComplete;
+        }
 
         if (gameFunctions.GetFreeInventorySlots() == 0)
             throw new TaskException("Inventory full");
@@ -225,7 +238,12 @@ internal sealed class DoGather(
         return ActionManager.Instance()->GetActionStatus(ActionType.Action, (uint)action) == 0;
     }
 
-    public override string ToString() => "DoGather";
+    public void OnRevisit()
+    {
+        _revisitTriggered = true;
+    }
+
+    public override string ToString() => $"DoGather{(_revisitRequired ? " if revist" : "")}";
 
     private sealed record SlotInfo(int Index, uint ItemId, int GatheringChance, int BoonChance, int Quantity);
 
index 63432931556533b92ab7a8be5c1d4e81ce62f58f..10fb9faa2cebcf0ae6950ffc5f0be65ff7e4fc21 100644 (file)
@@ -17,16 +17,19 @@ internal sealed class DoGatherCollectable(
     GameFunctions gameFunctions,
     IClientState clientState,
     IGameGui gameGui,
-    ILogger<DoGatherCollectable> logger) : ITask
+    ILogger<DoGatherCollectable> logger) : ITask, IRevisitAware
 {
     private GatheringController.GatheringRequest _currentRequest = null!;
     private GatheringNode _currentNode = null!;
+    private bool _revisitRequired;
+    private bool _revisitTriggered;
     private Queue<EAction>? _actionQueue;
 
-    public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode)
+    public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode, bool revisitRequired)
     {
         _currentRequest = currentRequest;
         _currentNode = currentNode;
+        _revisitRequired = revisitRequired;
         return this;
     }
 
@@ -34,8 +37,17 @@ internal sealed class DoGatherCollectable(
 
     public unsafe ETaskResult Update()
     {
+        if (_revisitRequired && !_revisitTriggered)
+        {
+            logger.LogInformation("No revisit");
+            return ETaskResult.TaskComplete;
+        }
+
         if (gatheringController.HasNodeDisappeared(_currentNode))
+        {
+            logger.LogInformation("Node disappeared");
             return ETaskResult.TaskComplete;
+        }
 
         if (gatheringController.HasRequestedItems())
         {
@@ -150,8 +162,13 @@ internal sealed class DoGatherCollectable(
             return botanistAction;
     }
 
+    public void OnRevisit()
+    {
+        _revisitTriggered = true;
+    }
+
     public override string ToString() =>
-        $"DoGatherCollectable({SeIconChar.Collectible.ToIconString()} {_currentRequest.Collectability})";
+        $"DoGatherCollectable({SeIconChar.Collectible.ToIconString()} {_currentRequest.Collectability}){(_revisitRequired ? " if revist" : "")}";
 
     [SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
     private sealed record NodeCondition(
diff --git a/Questionable/Controller/Steps/IRevisitAware.cs b/Questionable/Controller/Steps/IRevisitAware.cs
new file mode 100644 (file)
index 0000000..4faf1c0
--- /dev/null
@@ -0,0 +1,6 @@
+namespace Questionable.Controller.Steps;
+
+public interface IRevisitAware
+{
+    void OnRevisit();
+}