punish v6.8.18.0
This commit is contained in:
commit
cfb4dea47e
316 changed files with 554088 additions and 0 deletions
293
Questionable/Questionable.Controller.Steps.Gathering/DoGather.cs
Normal file
293
Questionable/Questionable.Controller.Steps.Gathering/DoGather.cs
Normal file
|
@ -0,0 +1,293 @@
|
|||
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);
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller.Steps.Movement;
|
||||
using Questionable.Functions;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Gathering;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps.Gathering;
|
||||
|
||||
internal static class MoveToLandingLocation
|
||||
{
|
||||
internal sealed record Task(ushort TerritoryId, bool FlyBetweenNodes, GatheringNode GatheringNode) : ITask
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Land/{FlyBetweenNodes}";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class MoveToLandingLocationExecutor(MoveExecutor moveExecutor, GameFunctions gameFunctions, IObjectTable objectTable, ILogger<MoveToLandingLocationExecutor> logger) : TaskExecutor<Task>(), IToastAware, ITaskExecutor
|
||||
{
|
||||
private ITask _moveTask;
|
||||
|
||||
protected override bool Start()
|
||||
{
|
||||
GatheringLocation location = base.Task.GatheringNode.Locations.First();
|
||||
if (base.Task.GatheringNode.Locations.Count > 1)
|
||||
{
|
||||
IGameObject gameObject = objectTable.SingleOrDefault((IGameObject x) => x.ObjectKind == ObjectKind.GatheringPoint && x.DataId == base.Task.GatheringNode.DataId && x.IsTargetable);
|
||||
if (gameObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
location = base.Task.GatheringNode.Locations.Single((GatheringLocation x) => Vector3.Distance(x.Position, gameObject.Position) < 0.1f);
|
||||
}
|
||||
var (vector, num, num2) = GatheringMath.CalculateLandingLocation(location);
|
||||
logger.LogInformation("Preliminary landing location: {Location}, with degrees = {Degrees}, range = {Range}", vector.ToString("G", CultureInfo.InvariantCulture), num, num2);
|
||||
bool flag = base.Task.FlyBetweenNodes && gameFunctions.IsFlyingUnlocked(base.Task.TerritoryId);
|
||||
ushort territoryId = base.Task.TerritoryId;
|
||||
Vector3 destination = vector;
|
||||
float? stopDistance = 0.25f;
|
||||
uint? dataId = base.Task.GatheringNode.DataId;
|
||||
bool fly = flag;
|
||||
_moveTask = new MoveTask(territoryId, destination, null, stopDistance, dataId, DisableNavmesh: false, null, fly, Land: false, IgnoreDistanceToObject: true, RestartNavigation: true, EInteractionType.Gather);
|
||||
return moveExecutor.Start(_moveTask);
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
return moveExecutor.Update();
|
||||
}
|
||||
|
||||
public bool OnErrorToast(SeString message)
|
||||
{
|
||||
return moveExecutor.OnErrorToast(message);
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return moveExecutor.ShouldInterruptOnDamage();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using LLib.GameUI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps.Gathering;
|
||||
|
||||
internal static class TurnInDelivery
|
||||
{
|
||||
internal sealed class Factory : SimpleTaskFactory
|
||||
{
|
||||
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
{
|
||||
if (!(quest.Id is SatisfactionSupplyNpcId) || sequence.Sequence != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new Task();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record Task : ITask
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return "WeeklyDeliveryTurnIn";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SatisfactionSupplyTurnIn(ILogger<SatisfactionSupplyTurnIn> logger) : TaskExecutor<Task>()
|
||||
{
|
||||
private ushort? _remainingAllowances;
|
||||
|
||||
protected override bool Start()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public unsafe override ETaskResult Update()
|
||||
{
|
||||
AgentSatisfactionSupply* ptr = AgentSatisfactionSupply.Instance();
|
||||
if (ptr == null || !ptr->IsAgentActive())
|
||||
{
|
||||
if (_remainingAllowances.HasValue)
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
uint addonId = ptr->GetAddonId();
|
||||
if (addonId == 0)
|
||||
{
|
||||
if (_remainingAllowances.HasValue)
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
AtkUnitBase* addonById = LAddon.GetAddonById(addonId);
|
||||
if (addonById == null || !LAddon.IsAddonReady(addonById))
|
||||
{
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
ushort remainingAllowances = ptr->NpcData.RemainingAllowances;
|
||||
if (remainingAllowances == 0)
|
||||
{
|
||||
logger.LogInformation("No remaining weekly allowances");
|
||||
addonById->FireCallbackInt(0);
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
if (InventoryManager.Instance()->GetInventoryItemCount(ptr->Items[1].Id, isHq: false, checkEquipped: true, checkArmory: true, (short)ptr->Items[1].Collectability1) == 0)
|
||||
{
|
||||
logger.LogInformation("Inventory has no {ItemId}", ptr->Items[1].Id);
|
||||
addonById->FireCallbackInt(0);
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
if (_remainingAllowances == remainingAllowances)
|
||||
{
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
logger.LogInformation("Attempting turn-in (remaining allowances: {RemainingAllowances})", remainingAllowances);
|
||||
_remainingAllowances = remainingAllowances;
|
||||
AtkValue* values = stackalloc AtkValue[2]
|
||||
{
|
||||
new AtkValue
|
||||
{
|
||||
Type = ValueType.Int,
|
||||
Int = 1
|
||||
},
|
||||
new AtkValue
|
||||
{
|
||||
Type = ValueType.Int,
|
||||
Int = 1
|
||||
}
|
||||
};
|
||||
addonById->FireCallback(2u, values);
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue