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

293 lines
9 KiB
C#

using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
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 DoGather
{
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 "DoGather" + (RevisitRequired ? " if revist" : "");
}
}
internal sealed class GatherExecutor(GatheringController gatheringController, GameFunctions gameFunctions, IGameGui gameGui, IClientState clientState, ICondition condition, ILogger<GatherExecutor> logger) : TaskExecutor<Task>()
{
private bool _wasGathering;
private bool _usedLuck;
private SlotInfo? _slotToGather;
private Queue<EAction>? _actionQueue;
protected override bool Start()
{
return true;
}
public unsafe override ETaskResult Update()
{
Task task = base.Task;
if ((object)task != null && task.RevisitRequired && !task.RevisitTriggered)
{
logger.LogInformation("No revisit");
return ETaskResult.TaskComplete;
}
if (gatheringController.HasNodeDisappeared(base.Task.Node))
{
logger.LogInformation("Node disappeared");
return ETaskResult.TaskComplete;
}
if (gameFunctions.GetFreeInventorySlots() == 0)
{
throw new TaskException("Inventory full");
}
if (condition[ConditionFlag.Gathering])
{
if (gameGui.TryGetAddonByName<AtkUnitBase>("GatheringMasterpiece", out var _))
{
return ETaskResult.TaskComplete;
}
_wasGathering = true;
if (gameGui.TryGetAddonByName<AddonGathering>("Gathering", out var addonPtr2))
{
if (gatheringController.HasRequestedItems())
{
addonPtr2->FireCallbackInt(-1);
}
else
{
List<SlotInfo> list = ReadSlots(addonPtr2);
if (base.Task.Request.Collectability > 0)
{
SlotInfo slotInfo = list.Single((SlotInfo x) => x.ItemId == base.Task.Request.ItemId);
addonPtr2->FireCallbackInt(slotInfo.Index);
}
else
{
NodeCondition nodeCondition = new NodeCondition(addonPtr2->AtkValues[110].UInt, addonPtr2->AtkValues[111].UInt);
if (_actionQueue != null && _actionQueue.TryPeek(out var result))
{
if (gameFunctions.UseAction(result))
{
logger.LogInformation("Used action {Action} on node", result);
_actionQueue.Dequeue();
}
return ETaskResult.StillRunning;
}
_actionQueue = GetNextActions(nodeCondition, list);
if (_actionQueue == null)
{
logger.LogInformation("Skipping the rest of gathering...");
addonPtr2->FireCallbackInt(-1);
return ETaskResult.TaskComplete;
}
if (_actionQueue.Count == 0)
{
SlotInfo slotInfo2 = _slotToGather ?? list.SingleOrDefault((SlotInfo x) => x.ItemId == base.Task.Request.ItemId) ?? list.MinBy((SlotInfo x) => x.ItemId);
switch (slotInfo2?.ItemId)
{
case 2u:
case 3u:
case 4u:
case 5u:
case 6u:
case 7u:
case 8u:
case 9u:
case 10u:
case 11u:
case 12u:
case 13u:
case 14u:
case 15u:
case 16u:
case 17u:
case 18u:
case 19u:
if (InventoryManager.Instance()->GetInventoryItemCount(slotInfo2.ItemId, isHq: false, checkEquipped: true, checkArmory: true, 0) == 9999)
{
slotInfo2 = null;
}
break;
}
if (slotInfo2 != null)
{
addonPtr2->FireCallbackInt(slotInfo2.Index);
}
else
{
addonPtr2->FireCallbackInt(-1);
}
}
}
}
}
}
if (!_wasGathering || condition[ConditionFlag.Gathering])
{
return ETaskResult.StillRunning;
}
return ETaskResult.TaskComplete;
}
private unsafe List<SlotInfo> ReadSlots(AddonGathering* addonGathering)
{
List<SlotInfo> list = new List<SlotInfo>();
for (int i = 0; i < 8; i++)
{
uint num = addonGathering->ItemIds[i];
if (num != 0)
{
AtkComponentCheckBox* value = addonGathering->GatheredItemComponentCheckbox[i].Value;
if (!int.TryParse(value->UldManager.SearchNodeById(10u)->GetAsAtkTextNode()->NodeText.ToString(), out var result))
{
result = 0;
}
if (!int.TryParse(value->UldManager.SearchNodeById(16u)->GetAsAtkTextNode()->NodeText.ToString(), out var result2))
{
result2 = 0;
}
AtkTextNode* asAtkTextNode = value->UldManager.SearchNodeById(31u)->GetAsAtkComponentNode()->Component->UldManager.SearchNodeById(7u)->GetAsAtkTextNode();
if (!asAtkTextNode->IsVisible() || !int.TryParse(asAtkTextNode->NodeText.ToString(), out var result3))
{
result3 = 1;
}
SlotInfo item = new SlotInfo(i, num, result, result2, result3);
list.Add(item);
}
}
logger.LogTrace("Slots: {Slots}", string.Join(", ", list));
return list;
}
private Queue<EAction>? GetNextActions(NodeCondition nodeCondition, List<SlotInfo> slots)
{
if (_slotToGather != null && slots.All((SlotInfo x) => x.Index != _slotToGather.Index))
{
_slotToGather = null;
}
Queue<EAction> queue = new Queue<EAction>();
if (!gameFunctions.HasStatus(EStatus.GatheringRateUp))
{
if (base.Task.Request.AlternativeItemId != 0)
{
SlotInfo slotInfo = slots.Single((SlotInfo x) => x.ItemId == base.Task.Request.AlternativeItemId);
if (slotInfo.GatheringChance == 100)
{
_slotToGather = slotInfo;
return queue;
}
if (slotInfo.GatheringChance > 0)
{
if (slotInfo.GatheringChance >= 95 && CanUseAction(EAction.SharpVision1, EAction.FieldMastery1))
{
_slotToGather = slotInfo;
queue.Enqueue(PickAction(EAction.SharpVision1, EAction.FieldMastery1));
return queue;
}
if (slotInfo.GatheringChance >= 85 && CanUseAction(EAction.SharpVision2, EAction.FieldMastery2))
{
_slotToGather = slotInfo;
queue.Enqueue(PickAction(EAction.SharpVision2, EAction.FieldMastery2));
return queue;
}
if (slotInfo.GatheringChance >= 50 && CanUseAction(EAction.SharpVision3, EAction.FieldMastery3))
{
_slotToGather = slotInfo;
queue.Enqueue(PickAction(EAction.SharpVision3, EAction.FieldMastery3));
return queue;
}
}
}
SlotInfo slotInfo2 = slots.SingleOrDefault((SlotInfo x) => x.ItemId == base.Task.Request.ItemId);
if (slotInfo2 == null)
{
if (!_usedLuck && nodeCondition.CurrentIntegrity == nodeCondition.MaxIntegrity && CanUseAction(EAction.LuckOfTheMountaineer, EAction.LuckOfThePioneer))
{
_usedLuck = true;
queue.Enqueue(PickAction(EAction.LuckOfTheMountaineer, EAction.LuckOfThePioneer));
return queue;
}
if (_usedLuck)
{
if (nodeCondition.CurrentIntegrity != nodeCondition.MaxIntegrity)
{
return null;
}
_slotToGather = slots.MinBy((SlotInfo x) => x.ItemId);
return queue;
}
}
slotInfo2 = slots.SingleOrDefault((SlotInfo x) => x.ItemId == base.Task.Request.ItemId);
if ((object)slotInfo2 != null)
{
int gatheringChance = slotInfo2.GatheringChance;
if (gatheringChance > 0 && gatheringChance < 100)
{
if (slotInfo2.GatheringChance >= 95 && CanUseAction(EAction.SharpVision1, EAction.FieldMastery1))
{
queue.Enqueue(PickAction(EAction.SharpVision1, EAction.FieldMastery1));
return queue;
}
if (slotInfo2.GatheringChance >= 85 && CanUseAction(EAction.SharpVision2, EAction.FieldMastery2))
{
queue.Enqueue(PickAction(EAction.SharpVision2, EAction.FieldMastery2));
return queue;
}
if (slotInfo2.GatheringChance >= 50 && CanUseAction(EAction.SharpVision3, EAction.FieldMastery3))
{
queue.Enqueue(PickAction(EAction.SharpVision3, EAction.FieldMastery3));
return queue;
}
}
}
}
return queue;
}
private EAction PickAction(EAction minerAction, EAction botanistAction)
{
if (clientState.LocalPlayer?.ClassJob.RowId == 16)
{
return minerAction;
}
return botanistAction;
}
private unsafe bool CanUseAction(EAction minerAction, EAction botanistAction)
{
EAction actionId = PickAction(minerAction, botanistAction);
return ActionManager.Instance()->GetActionStatus(ActionType.Action, (uint)actionId, 3758096384uL, checkRecastActive: true, checkCastingActive: true, null) == 0;
}
public override bool ShouldInterruptOnDamage()
{
return false;
}
}
private sealed record SlotInfo(int Index, uint ItemId, int GatheringChance, int BoonChance, int Quantity);
private sealed record NodeCondition(uint CurrentIntegrity, uint MaxIntegrity);
}