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

192 lines
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using LLib.GameData;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.Controller.Steps.Interactions;
using Questionable.Data;
using Questionable.Model;
using Questionable.Model.Gathering;
using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
internal static class Gather
{
internal sealed class Factory : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
if (step.InteractionType != EInteractionType.Gather)
{
yield break;
}
foreach (GatheredItem item in step.ItemsToGather)
{
yield return new DelayedGatheringTask(item, quest, sequence.Sequence);
}
}
}
internal sealed record DelayedGatheringTask(GatheredItem GatheredItem, Quest Quest, byte Sequence) : ITask
{
public override string ToString()
{
return $"Gathering(pending for {GatheredItem.ItemId})";
}
}
internal sealed class DelayedGatheringExecutor(GatheringPointRegistry gatheringPointRegistry, TerritoryData territoryData, IClientState clientState, IServiceProvider serviceProvider, ILogger<DelayedGatheringExecutor> logger) : TaskExecutor<DelayedGatheringTask>(), IExtraTaskCreator, ITaskExecutor
{
protected override bool Start()
{
return true;
}
public override ETaskResult Update()
{
return ETaskResult.CreateNewTasks;
}
public IEnumerable<ITask> CreateExtraTasks()
{
EClassJob rowId = (EClassJob)clientState.LocalPlayer.ClassJob.RowId;
if (!gatheringPointRegistry.TryGetGatheringPointId(base.Task.GatheredItem.ItemId, rowId, out GatheringPointId gatheringPointId))
{
throw new TaskException($"No gathering point found for item {base.Task.GatheredItem.ItemId}");
}
if (!gatheringPointRegistry.TryGetGatheringPoint(gatheringPointId, out GatheringRoot gatheringRoot))
{
throw new TaskException($"No path found for gathering point {gatheringPointId}");
}
if (HasRequiredItems(base.Task.GatheredItem))
{
yield break;
}
switch (rowId)
{
case EClassJob.Miner:
yield return new Questionable.Controller.Steps.Interactions.Action.TriggerStatusIfMissing(EStatus.Prospect, EAction.Prospect);
break;
case EClassJob.Botanist:
yield return new Questionable.Controller.Steps.Interactions.Action.TriggerStatusIfMissing(EStatus.Triangulate, EAction.Triangulate);
break;
}
using (logger.BeginScope("Gathering(inner)"))
{
QuestSequence gatheringSequence = new QuestSequence
{
Sequence = 0,
Steps = gatheringRoot.Steps
};
foreach (QuestStep step in gatheringSequence.Steps)
{
foreach (ITask item in serviceProvider.GetRequiredService<TaskCreator>().CreateTasks(base.Task.Quest, base.Task.Sequence, gatheringSequence, step))
{
if (item is WaitAtEnd.NextStep)
{
yield return new SkipMarker();
}
else
{
yield return item;
}
}
}
}
ushort territoryId = gatheringRoot.Steps.Last().TerritoryId;
yield return new WaitCondition.Task(() => clientState.TerritoryType == territoryId, "Wait(territory: " + territoryData.GetNameAndId(territoryId) + ")");
yield return new WaitNavmesh.Task();
yield return new GatheringTask(gatheringPointId, base.Task.GatheredItem);
yield return new WaitAtEnd.WaitDelay();
}
private unsafe bool HasRequiredItems(GatheredItem itemToGather)
{
InventoryManager* ptr = InventoryManager.Instance();
if (ptr != null)
{
return ptr->GetInventoryItemCount(itemToGather.ItemId, isHq: false, checkEquipped: true, checkArmory: true, (short)itemToGather.Collectability) >= itemToGather.ItemCount;
}
return false;
}
public override bool ShouldInterruptOnDamage()
{
return false;
}
}
internal sealed record GatheringTask(GatheringPointId gatheringPointId, GatheredItem gatheredItem) : ITask
{
public override string ToString()
{
if (gatheredItem.Collectability != 0)
{
return $"Gather({gatheredItem.ItemCount}x {gatheredItem.ItemId} {SeIconChar.Collectible.ToIconString()} {gatheredItem.Collectability})";
}
return $"Gather({gatheredItem.ItemCount}x {gatheredItem.ItemId})";
}
}
internal sealed class StartGathering(GatheringController gatheringController) : TaskExecutor<GatheringTask>(), IToastAware, ITaskExecutor
{
protected override bool Start()
{
return gatheringController.Start(new GatheringController.GatheringRequest(base.Task.gatheringPointId, base.Task.gatheredItem.ItemId, base.Task.gatheredItem.AlternativeItemId, base.Task.gatheredItem.ItemCount, base.Task.gatheredItem.Collectability));
}
public override ETaskResult Update()
{
if (gatheringController.Update() == GatheringController.EStatus.Complete)
{
return ETaskResult.TaskComplete;
}
return ETaskResult.StillRunning;
}
public bool OnErrorToast(SeString message)
{
bool isHandled = false;
gatheringController.OnErrorToast(ref message, ref isHandled);
return isHandled;
}
public override bool ShouldInterruptOnDamage()
{
return false;
}
}
internal sealed class SkipMarker : ITask
{
public override string ToString()
{
return "Gather/SkipMarker";
}
}
internal sealed class DoSkip : TaskExecutor<SkipMarker>
{
protected override bool Start()
{
return true;
}
public override ETaskResult Update()
{
return ETaskResult.TaskComplete;
}
public override bool ShouldInterruptOnDamage()
{
return false;
}
}
}