199 lines
6.5 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|