"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": {
                 "Z": 421.5481
               },
               "MinimumAngle": 0,
-              "MaximumAngle": 145
+              "MaximumAngle": 145,
+              "MinimumDistance": 1.5,
+              "MaximumDistance": 3
             },
             {
               "Position": {
                 "Z": 408.2164
               },
               "MinimumAngle": 155,
-              "MaximumAngle": 225
+              "MaximumAngle": 225,
+              "MinimumDistance": 1.5,
+              "MaximumDistance": 3
             }
           ]
         }
       ]
     }
   ]
-}
+}
\ No newline at end of file
 
 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; } = [];
 }
 
     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; } = [];
 }
 
 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;
     private readonly IObjectTable _objectTable;
     private readonly IServiceProvider _serviceProvider;
     private readonly ICondition _condition;
+    private readonly Regex _revisitRegex;
 
     private CurrentRequest? _currentRequest;
 
         IChatGui chatGui,
         ILogger<GatheringController> logger,
         IServiceProvider serviceProvider,
-        ICondition condition)
+        ICondition condition,
+        IDataManager dataManager,
+        IPluginLog pluginLog)
         : base(chatGui, logger)
     {
         _movementController = movementController;
         _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)
     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();
 
         _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
                 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) };
         }
 
         _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));
+            }
         }
     }
 
             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; }
 
 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;
         _taskFactories = taskFactories.ToList().AsReadOnly();
 
         _condition.ConditionChange += OnConditionChange;
+        _toastGui.Toast += OnNormalToast;
         _toastGui.ErrorToast += OnErrorToast;
     }
 
             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)
     public void Dispose()
     {
         _toastGui.ErrorToast -= OnErrorToast;
+        _toastGui.Toast -= OnNormalToast;
         _condition.ConditionChange -= OnConditionChange;
     }
 
 
     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;
     }
 
 
     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");
         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);
 
 
     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;
     }
 
 
     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())
         {
             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(
 
--- /dev/null
+namespace Questionable.Controller.Steps;
+
+public interface IRevisitAware
+{
+    void OnRevisit();
+}