qstbak/Questionable/Questionable.Controller.Steps.Gathering/DoGatherCollectable.cs
2025-10-09 07:47:19 +10:00

199 lines
6.5 KiB
C#

using System.Collections.Generic;
using Dalamud.Game.Text;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI;
using Microsoft.Extensions.Logging;
using Questionable.Functions;
using Questionable.Model.Gathering;
using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Gathering;
internal static class DoGatherCollectable
{
internal sealed record Task(GatheringController.GatheringRequest Request, GatheringNode Node, bool RevisitRequired) : ITask, IRevisitAware
{
public bool RevisitTriggered { get; private set; }
public void OnRevisit()
{
RevisitTriggered = true;
}
public override string ToString()
{
return $"DoGatherCollectable({SeIconChar.Collectible.ToIconString()}/{Request.Collectability}){(RevisitRequired ? " if revist" : "")}";
}
}
internal sealed class GatherCollectableExecutor(GatheringController gatheringController, GameFunctions gameFunctions, IClientState clientState, IGameGui gameGui, ILogger<GatherCollectableExecutor> logger) : TaskExecutor<Task>()
{
private Queue<EAction>? _actionQueue;
private bool? _expectedScrutiny;
protected override bool Start()
{
return true;
}
public unsafe override ETaskResult Update()
{
if (base.Task.RevisitRequired && !base.Task.RevisitTriggered)
{
logger.LogInformation("No revisit");
return ETaskResult.TaskComplete;
}
if (gatheringController.HasNodeDisappeared(base.Task.Node))
{
logger.LogInformation("Node disappeared");
return ETaskResult.TaskComplete;
}
if (gatheringController.HasRequestedItems())
{
if (gameGui.TryGetAddonByName<AtkUnitBase>("GatheringMasterpiece", out var addonPtr))
{
addonPtr->FireCallbackInt(1);
return ETaskResult.StillRunning;
}
if (gameGui.TryGetAddonByName<AtkUnitBase>("Gathering", out addonPtr))
{
addonPtr->FireCallbackInt(-1);
return ETaskResult.TaskComplete;
}
}
if (gameFunctions.GetFreeInventorySlots() == 0)
{
throw new TaskException("Inventory full");
}
NodeCondition nodeCondition = GetNodeCondition();
if (nodeCondition == null)
{
return ETaskResult.TaskComplete;
}
if (_expectedScrutiny.HasValue)
{
if (nodeCondition.ScrutinyActive != _expectedScrutiny)
{
return ETaskResult.StillRunning;
}
_expectedScrutiny = null;
return ETaskResult.StillRunning;
}
if (_actionQueue != null && _actionQueue.TryPeek(out var result))
{
if (gameFunctions.UseAction(result))
{
bool? expectedScrutiny;
switch (result)
{
case EAction.ScrutinyMiner:
case EAction.ScrutinyBotanist:
expectedScrutiny = true;
break;
case EAction.ScourMiner:
case EAction.MeticulousMiner:
case EAction.ScourBotanist:
case EAction.MeticulousBotanist:
expectedScrutiny = false;
break;
default:
expectedScrutiny = null;
break;
}
_expectedScrutiny = expectedScrutiny;
logger.LogInformation("Used action {Action} on node", result);
_actionQueue.Dequeue();
}
return ETaskResult.StillRunning;
}
if (nodeCondition.CollectabilityToGoal(base.Task.Request.Collectability) != 0)
{
_actionQueue = GetNextActions(nodeCondition);
if (_actionQueue != null)
{
foreach (EAction item in _actionQueue)
{
logger.LogInformation("Next Actions {Action}", item);
}
return ETaskResult.StillRunning;
}
}
_actionQueue = new Queue<EAction>();
_actionQueue.Enqueue(PickAction(EAction.CollectMiner, EAction.CollectBotanist));
return ETaskResult.StillRunning;
}
private unsafe NodeCondition? GetNodeCondition()
{
if (gameGui.TryGetAddonByName<AtkUnitBase>("GatheringMasterpiece", out var addonPtr))
{
AtkValue* atkValues = addonPtr->AtkValues;
return new NodeCondition(atkValues[13].UInt, atkValues[14].UInt, atkValues[62].UInt, atkValues[63].UInt, atkValues[54].Bool, atkValues[48].UInt, atkValues[51].UInt);
}
return null;
}
private Queue<EAction> GetNextActions(NodeCondition nodeCondition)
{
uint currentGp = clientState.LocalPlayer.CurrentGp;
logger.LogTrace("Getting next actions (with {GP} GP, {MeticulousCollectability}~ meticulous, {ScourCollectability}~ scour)", currentGp, nodeCondition.CollectabilityFromMeticulous, nodeCondition.CollectabilityFromScour);
Queue<EAction> queue = new Queue<EAction>();
uint num = nodeCondition.CollectabilityToGoal(base.Task.Request.Collectability);
if (num <= nodeCondition.CollectabilityFromMeticulous)
{
logger.LogTrace("Can get all needed {NeededCollectability} from {Collectability}~ meticulous", num, nodeCondition.CollectabilityFromMeticulous);
queue.Enqueue(PickAction(EAction.MeticulousMiner, EAction.MeticulousBotanist));
return queue;
}
if (num <= nodeCondition.CollectabilityFromScour)
{
logger.LogTrace("Can get all needed {NeededCollectability} from {Collectability}~ scour", num, nodeCondition.CollectabilityFromScour);
queue.Enqueue(PickAction(EAction.ScourMiner, EAction.ScourBotanist));
return queue;
}
if (!nodeCondition.ScrutinyActive && currentGp >= 200)
{
logger.LogTrace("Still missing {NeededCollectability} collectability, scrutiny inactive", num);
queue.Enqueue(PickAction(EAction.ScrutinyMiner, EAction.ScrutinyBotanist));
return queue;
}
if (nodeCondition.ScrutinyActive)
{
logger.LogTrace("Scrutiny active, need {NeededCollectability} and we expect {Collectability}~ meticulous", num, nodeCondition.CollectabilityFromMeticulous);
queue.Enqueue(PickAction(EAction.MeticulousMiner, EAction.MeticulousBotanist));
return queue;
}
logger.LogTrace("Scrutiny active, need {NeededCollectability} and we expect {Collectability}~ scour", num, nodeCondition.CollectabilityFromScour);
queue.Enqueue(PickAction(EAction.ScourMiner, EAction.ScourBotanist));
return queue;
}
private EAction PickAction(EAction minerAction, EAction botanistAction)
{
if (clientState.LocalPlayer?.ClassJob.RowId == 16)
{
return minerAction;
}
return botanistAction;
}
public override bool ShouldInterruptOnDamage()
{
return false;
}
}
private sealed record NodeCondition(uint CurrentCollectability, uint MaxCollectability, uint CurrentIntegrity, uint MaxIntegrity, bool ScrutinyActive, uint CollectabilityFromScour, uint CollectabilityFromMeticulous)
{
public uint CollectabilityToGoal(uint goal)
{
if (goal >= CurrentCollectability)
{
return goal - CurrentCollectability;
}
return (CurrentCollectability == 0) ? 1u : 0u;
}
}
}