Attempt to handle Revisit triggering

This commit is contained in:
Liza 2024-08-11 21:48:01 +02:00
parent 606b9d52ba
commit 9d8c67155f
Signed by: liza
GPG Key ID: 7199F8D727D55F67
8 changed files with 121 additions and 27 deletions

View File

@ -16,12 +16,15 @@
"Z": 405.1829 "Z": 405.1829
}, },
"MinimumAngle": 100, "MinimumAngle": 100,
"MaximumAngle": 250 "MaximumAngle": 250,
"MinimumDistance": 1.5,
"MaximumDistance": 3
} }
] ]
}, },
{ {
"DataId": 31345, "DataId": 31345,
"Fly": false,
"Locations": [ "Locations": [
{ {
"Position": { "Position": {
@ -29,8 +32,10 @@
"Y": 216.5585, "Y": 216.5585,
"Z": 412.4353 "Z": 412.4353
}, },
"MinimumAngle": 50, "MinimumAngle": 75,
"MaximumAngle": 165 "MaximumAngle": 145,
"MinimumDistance": 1.5,
"MaximumDistance": 3
}, },
{ {
"Position": { "Position": {
@ -39,7 +44,9 @@
"Z": 421.5481 "Z": 421.5481
}, },
"MinimumAngle": 0, "MinimumAngle": 0,
"MaximumAngle": 145 "MaximumAngle": 145,
"MinimumDistance": 1.5,
"MaximumDistance": 3
}, },
{ {
"Position": { "Position": {
@ -48,7 +55,9 @@
"Z": 408.2164 "Z": 408.2164
}, },
"MinimumAngle": 155, "MinimumAngle": 155,
"MaximumAngle": 225 "MaximumAngle": 225,
"MinimumDistance": 1.5,
"MaximumDistance": 3
} }
] ]
} }

View File

@ -5,7 +5,7 @@ namespace Questionable.Model.Gathering;
public sealed class GatheringNode public sealed class GatheringNode
{ {
public uint DataId { get; set; } public uint DataId { get; set; }
public bool Fly { get; set; } public bool? Fly { get; set; }
public List<GatheringLocation> Locations { get; set; } = []; public List<GatheringLocation> Locations { get; set; } = [];
} }

View File

@ -16,6 +16,6 @@ public sealed class GatheringRoot
public EAetheryteLocation? AetheryteShortcut { get; set; } public EAetheryteLocation? AetheryteShortcut { get; set; }
public AethernetShortcut? AethernetShortcut { get; set; } public AethernetShortcut? AethernetShortcut { get; set; }
public bool FlyBetweenNodes { get; set; } = true; public bool? FlyBetweenNodes { get; set; }
public List<GatheringNodeGroup> Groups { get; set; } = []; public List<GatheringNodeGroup> Groups { get; set; } = [];
} }

View File

@ -1,13 +1,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Text.RegularExpressions;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Event; using FFXIVClientStructs.FFXIV.Client.Game.Event;
using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.Game.UI;
using LLib;
using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps; using Questionable.Controller.Steps;
@ -32,6 +37,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
private readonly IObjectTable _objectTable; private readonly IObjectTable _objectTable;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly ICondition _condition; private readonly ICondition _condition;
private readonly Regex _revisitRegex;
private CurrentRequest? _currentRequest; private CurrentRequest? _currentRequest;
@ -44,7 +50,9 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
IChatGui chatGui, IChatGui chatGui,
ILogger<GatheringController> logger, ILogger<GatheringController> logger,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
ICondition condition) ICondition condition,
IDataManager dataManager,
IPluginLog pluginLog)
: base(chatGui, logger) : base(chatGui, logger)
{ {
_movementController = movementController; _movementController = movementController;
@ -54,6 +62,9 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
_objectTable = objectTable; _objectTable = objectTable;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_condition = condition; _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 bool Start(GatheringRequest gatheringRequest)
@ -88,13 +99,19 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
public EStatus Update() public EStatus Update()
{ {
if (_currentRequest == null) if (_currentRequest == null)
{
Stop("No request");
return EStatus.Complete; return EStatus.Complete;
}
if (_movementController.IsPathfinding || _movementController.IsPathfinding) if (_movementController.IsPathfinding || _movementController.IsPathfinding)
return EStatus.Moving; return EStatus.Moving;
if (HasRequestedItems() && !_condition[ConditionFlag.Gathering]) if (HasRequestedItems() && !_condition[ConditionFlag.Gathering])
{
Stop("Has all items");
return EStatus.Complete; return EStatus.Complete;
}
if (_currentTask == null && _taskQueue.Count == 0) if (_currentTask == null && _taskQueue.Count == 0)
GoToNextNode(); GoToNextNode();
@ -136,6 +153,9 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<MountTask>() _taskQueue.Enqueue(_serviceProvider.GetRequiredService<MountTask>()
.With(_currentRequest.Root.TerritoryId, MountTask.EMountIf.Always)); .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) if (currentNode.Locations.Count > 1)
{ {
Vector3 averagePosition = new Vector3 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, Y = currentNode.Locations.Select(x => x.Position.Y).Max() + 5f,
Z = currentNode.Locations.Sum(x => x.Position.Z) / currentNode.Locations.Count, 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); Vector3? pointOnFloor = _navmeshIpc.GetPointOnFloor(averagePosition, true);
if (pointOnFloor != null) if (pointOnFloor != null)
pointOnFloor = pointOnFloor.Value with { Y = pointOnFloor.Value.Y + (fly ? 3f : 0f) }; 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>() _taskQueue.Enqueue(_serviceProvider.GetRequiredService<MoveToLandingLocation>()
.With(_currentRequest.Root.TerritoryId, .With(_currentRequest.Root.TerritoryId, fly, currentNode));
currentNode.Fly || _currentRequest.Root.FlyBetweenNodes,
currentNode));
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<Interact.DoInteract>() _taskQueue.Enqueue(_serviceProvider.GetRequiredService<Interact.DoInteract>()
.With(currentNode.DataId, null, EInteractionType.InternalGather, true)); .With(currentNode.DataId, null, EInteractionType.InternalGather, true));
foreach (bool revisitRequired in new[] { false, true })
{
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGather>() _taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGather>()
.With(_currentRequest.Data, currentNode)); .With(_currentRequest.Data, currentNode, revisitRequired));
if (_currentRequest.Data.Collectability > 0) if (_currentRequest.Data.Collectability > 0)
{ {
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGatherCollectable>() _taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGatherCollectable>()
.With(_currentRequest.Data, currentNode)); .With(_currentRequest.Data, currentNode, revisitRequired));
}
} }
} }
@ -232,6 +253,21 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
return base.GetRemainingTaskNames(); 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 private sealed class CurrentRequest
{ {
public required GatheringRequest Data { get; init; } public required GatheringRequest Data { get; init; }

View File

@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Keys; using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.Gui.Toast;
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
@ -90,6 +91,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
_taskFactories = taskFactories.ToList().AsReadOnly(); _taskFactories = taskFactories.ToList().AsReadOnly();
_condition.ConditionChange += OnConditionChange; _condition.ConditionChange += OnConditionChange;
_toastGui.Toast += OnNormalToast;
_toastGui.ErrorToast += OnErrorToast; _toastGui.ErrorToast += OnErrorToast;
} }
@ -786,6 +788,11 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
conditionChangeAware.OnConditionChange(flag, value); 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) private void OnErrorToast(ref SeString message, ref bool ishandled)
{ {
if (_currentTask is IToastAware toastAware) if (_currentTask is IToastAware toastAware)
@ -795,6 +802,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
public void Dispose() public void Dispose()
{ {
_toastGui.ErrorToast -= OnErrorToast; _toastGui.ErrorToast -= OnErrorToast;
_toastGui.Toast -= OnNormalToast;
_condition.ConditionChange -= OnConditionChange; _condition.ConditionChange -= OnConditionChange;
} }

View File

@ -22,20 +22,24 @@ internal sealed class DoGather(
IGameGui gameGui, IGameGui gameGui,
IClientState clientState, IClientState clientState,
ICondition condition, ICondition condition,
ILogger<DoGather> logger) : ITask ILogger<DoGather> logger) : ITask, IRevisitAware
{ {
private const uint StatusGatheringRateUp = 218; private const uint StatusGatheringRateUp = 218;
private GatheringController.GatheringRequest _currentRequest = null!; private GatheringController.GatheringRequest _currentRequest = null!;
private GatheringNode _currentNode = null!; private GatheringNode _currentNode = null!;
private bool _revisitRequired;
private bool _revisitTriggered;
private bool _wasGathering; private bool _wasGathering;
private SlotInfo? _slotToGather; private SlotInfo? _slotToGather;
private Queue<EAction>? _actionQueue; private Queue<EAction>? _actionQueue;
public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode) public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode,
bool revisitRequired)
{ {
_currentRequest = currentRequest; _currentRequest = currentRequest;
_currentNode = currentNode; _currentNode = currentNode;
_revisitRequired = revisitRequired;
return this; return this;
} }
@ -43,8 +47,17 @@ internal sealed class DoGather(
public unsafe ETaskResult Update() public unsafe ETaskResult Update()
{ {
if (gatheringController.HasNodeDisappeared(_currentNode)) if (_revisitRequired && !_revisitTriggered)
{
logger.LogInformation("No revisit");
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
}
if (gatheringController.HasNodeDisappeared(_currentNode))
{
logger.LogInformation("Node disappeared");
return ETaskResult.TaskComplete;
}
if (gameFunctions.GetFreeInventorySlots() == 0) if (gameFunctions.GetFreeInventorySlots() == 0)
throw new TaskException("Inventory full"); throw new TaskException("Inventory full");
@ -225,7 +238,12 @@ internal sealed class DoGather(
return ActionManager.Instance()->GetActionStatus(ActionType.Action, (uint)action) == 0; 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); private sealed record SlotInfo(int Index, uint ItemId, int GatheringChance, int BoonChance, int Quantity);

View File

@ -17,16 +17,19 @@ internal sealed class DoGatherCollectable(
GameFunctions gameFunctions, GameFunctions gameFunctions,
IClientState clientState, IClientState clientState,
IGameGui gameGui, IGameGui gameGui,
ILogger<DoGatherCollectable> logger) : ITask ILogger<DoGatherCollectable> logger) : ITask, IRevisitAware
{ {
private GatheringController.GatheringRequest _currentRequest = null!; private GatheringController.GatheringRequest _currentRequest = null!;
private GatheringNode _currentNode = null!; private GatheringNode _currentNode = null!;
private bool _revisitRequired;
private bool _revisitTriggered;
private Queue<EAction>? _actionQueue; private Queue<EAction>? _actionQueue;
public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode) public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode, bool revisitRequired)
{ {
_currentRequest = currentRequest; _currentRequest = currentRequest;
_currentNode = currentNode; _currentNode = currentNode;
_revisitRequired = revisitRequired;
return this; return this;
} }
@ -34,8 +37,17 @@ internal sealed class DoGatherCollectable(
public unsafe ETaskResult Update() public unsafe ETaskResult Update()
{ {
if (gatheringController.HasNodeDisappeared(_currentNode)) if (_revisitRequired && !_revisitTriggered)
{
logger.LogInformation("No revisit");
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
}
if (gatheringController.HasNodeDisappeared(_currentNode))
{
logger.LogInformation("Node disappeared");
return ETaskResult.TaskComplete;
}
if (gatheringController.HasRequestedItems()) if (gatheringController.HasRequestedItems())
{ {
@ -150,8 +162,13 @@ internal sealed class DoGatherCollectable(
return botanistAction; return botanistAction;
} }
public void OnRevisit()
{
_revisitTriggered = true;
}
public override string ToString() => public override string ToString() =>
$"DoGatherCollectable({SeIconChar.Collectible.ToIconString()} {_currentRequest.Collectability})"; $"DoGatherCollectable({SeIconChar.Collectible.ToIconString()} {_currentRequest.Collectability}){(_revisitRequired ? " if revist" : "")}";
[SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")] [SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
private sealed record NodeCondition( private sealed record NodeCondition(

View File

@ -0,0 +1,6 @@
namespace Questionable.Controller.Steps;
public interface IRevisitAware
{
void OnRevisit();
}