192 lines
5.6 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|