1
0
forked from liza/Questionable

Fix gathering for tribal quests

This commit is contained in:
Liza 2024-11-18 19:05:22 +01:00
parent d4c1b4b3ac
commit ed4d279432
Signed by: liza
GPG Key ID: 7199F8D727D55F67
6 changed files with 86 additions and 48 deletions

View File

@ -120,11 +120,15 @@ internal abstract class MiniTaskController<T>
return; return;
case ETaskResult.TaskComplete: case ETaskResult.TaskComplete:
case ETaskResult.CreateNewTasks:
_logger.LogInformation("{Task} → {Result}, remaining tasks: {RemainingTaskCount}", _logger.LogInformation("{Task} → {Result}, remaining tasks: {RemainingTaskCount}",
_taskQueue.CurrentTaskExecutor.CurrentTask, result, _taskQueue.RemainingTasks.Count()); _taskQueue.CurrentTaskExecutor.CurrentTask, result, _taskQueue.RemainingTasks.Count());
OnTaskComplete(_taskQueue.CurrentTaskExecutor.CurrentTask); OnTaskComplete(_taskQueue.CurrentTaskExecutor.CurrentTask);
if (result == ETaskResult.CreateNewTasks && _taskQueue.CurrentTaskExecutor is IExtraTaskCreator extraTaskCreator)
_taskQueue.EnqueueAll(extraTaskCreator.CreateExtraTasks());
_taskQueue.CurrentTaskExecutor = null; _taskQueue.CurrentTaskExecutor = null;
// handled in next update // handled in next update

View File

@ -11,6 +11,11 @@ internal enum ETaskResult
/// </summary> /// </summary>
SkipRemainingTasksForStep, SkipRemainingTasksForStep,
/// <summary>
/// Assumes the task executor implements <see cref="IExtraTaskCreator"/>.
/// </summary>
CreateNewTasks,
NextStep, NextStep,
End, End,
} }

View File

@ -4,7 +4,6 @@ using System.Linq;
using Dalamud.Game.Text; using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using LLib.GameData; using LLib.GameData;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -19,14 +18,7 @@ namespace Questionable.Controller.Steps.Shared;
internal static class Gather internal static class Gather
{ {
internal sealed class Factory( internal sealed class Factory : ITaskFactory
IServiceProvider serviceProvider,
MovementController movementController,
GatheringPointRegistry gatheringPointRegistry,
IClientState clientState,
GatheringData gatheringData,
TerritoryData territoryData,
ILogger<Factory> logger) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -35,46 +27,70 @@ internal static class Gather
foreach (var itemToGather in step.ItemsToGather) foreach (var itemToGather in step.ItemsToGather)
{ {
EClassJob currentClassJob = (EClassJob)clientState.LocalPlayer!.ClassJob.RowId; yield return new DelayedGatheringTask(itemToGather, quest);
if (!gatheringData.TryGetGatheringPointId(itemToGather.ItemId, currentClassJob,
out GatheringPointId? gatheringPointId))
throw new TaskException($"No gathering point found for item {itemToGather.ItemId}");
if (!gatheringPointRegistry.TryGetGatheringPoint(gatheringPointId, out GatheringRoot? gatheringRoot))
throw new TaskException($"No path found for gathering point {gatheringPointId}");
if (HasRequiredItems(itemToGather))
continue;
using (var _ = logger.BeginScope("Gathering(inner)"))
{
QuestSequence gatheringSequence = new QuestSequence
{
Sequence = 0,
Steps = gatheringRoot.Steps
};
foreach (var gatheringStep in gatheringSequence.Steps)
{
foreach (var task in serviceProvider.GetRequiredService<TaskCreator>()
.CreateTasks(quest, gatheringSequence, gatheringStep))
if (task is WaitAtEnd.NextStep)
yield return new SkipMarker();
else
yield return task;
}
}
ushort territoryId = gatheringRoot.Steps.Last().TerritoryId;
yield return new WaitCondition.Task(() => clientState.TerritoryType == territoryId,
$"Wait(territory: {territoryData.GetNameAndId(territoryId)})");
yield return new WaitCondition.Task(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)");
yield return new GatheringTask(gatheringPointId, itemToGather);
yield return new WaitAtEnd.WaitDelay();
} }
} }
}
internal sealed record DelayedGatheringTask(GatheredItem GatheredItem, Quest Quest) : ITask
{
public override string ToString() => $"Gathering(pending for {GatheredItem.ItemId})";
}
internal sealed class DelayedGatheringExecutor(
MovementController movementController,
GatheringData gatheringData,
GatheringPointRegistry gatheringPointRegistry,
TerritoryData territoryData,
IClientState clientState,
IServiceProvider serviceProvider,
ILogger<DelayedGatheringExecutor> logger) : TaskExecutor<DelayedGatheringTask>, IExtraTaskCreator
{
protected override bool Start() => true;
public override ETaskResult Update() => ETaskResult.CreateNewTasks;
public IEnumerable<ITask> CreateExtraTasks()
{
EClassJob currentClassJob = (EClassJob)clientState.LocalPlayer!.ClassJob.RowId;
if (!gatheringData.TryGetGatheringPointId(Task.GatheredItem.ItemId, currentClassJob,
out GatheringPointId? gatheringPointId))
throw new TaskException($"No gathering point found for item {Task.GatheredItem.ItemId}");
if (!gatheringPointRegistry.TryGetGatheringPoint(gatheringPointId, out GatheringRoot? gatheringRoot))
throw new TaskException($"No path found for gathering point {gatheringPointId}");
if (HasRequiredItems(Task.GatheredItem))
yield break;
using (var _ = logger.BeginScope("Gathering(inner)"))
{
QuestSequence gatheringSequence = new QuestSequence
{
Sequence = 0,
Steps = gatheringRoot.Steps
};
foreach (var gatheringStep in gatheringSequence.Steps)
{
foreach (var task in serviceProvider.GetRequiredService<TaskCreator>()
.CreateTasks(Task.Quest, gatheringSequence, gatheringStep))
if (task is WaitAtEnd.NextStep)
yield return new SkipMarker();
else
yield return task;
}
}
ushort territoryId = gatheringRoot.Steps.Last().TerritoryId;
yield return new WaitCondition.Task(() => clientState.TerritoryType == territoryId,
$"Wait(territory: {territoryData.GetNameAndId(territoryId)})");
yield return new WaitCondition.Task(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)");
yield return new GatheringTask(gatheringPointId, Task.GatheredItem);
yield return new WaitAtEnd.WaitDelay();
}
private unsafe bool HasRequiredItems(GatheredItem itemToGather) private unsafe bool HasRequiredItems(GatheredItem itemToGather)
{ {

View File

@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using Questionable.Model;
namespace Questionable.Controller.Steps; namespace Questionable.Controller.Steps;
@ -16,6 +18,11 @@ internal interface ITaskExecutor
ETaskResult Update(); ETaskResult Update();
} }
internal interface IExtraTaskCreator : ITaskExecutor
{
IEnumerable<ITask> CreateExtraTasks();
}
internal abstract class TaskExecutor<T> : ITaskExecutor internal abstract class TaskExecutor<T> : ITaskExecutor
where T : class, ITask where T : class, ITask
{ {

View File

@ -18,6 +18,11 @@ internal sealed class TaskQueue
_tasks.Add(task); _tasks.Add(task);
} }
public void EnqueueAll(IEnumerable<ITask> tasks)
{
_tasks.InsertRange(0, tasks);
}
public bool TryDequeue([NotNullWhen(true)] out ITask? task) public bool TryDequeue([NotNullWhen(true)] out ITask? task)
{ {
task = _tasks.FirstOrDefault(); task = _tasks.FirstOrDefault();

View File

@ -147,7 +147,6 @@ public sealed class QuestionablePlugin : IDalamudPlugin
.AddTaskFactoryAndExecutor<StepDisabled.SkipRemainingTasks, StepDisabled.Factory, .AddTaskFactoryAndExecutor<StepDisabled.SkipRemainingTasks, StepDisabled.Factory,
StepDisabled.SkipDisabledStepsExecutor>(); StepDisabled.SkipDisabledStepsExecutor>();
serviceCollection.AddTaskFactory<EquipRecommended.BeforeDutyOrInstance>(); serviceCollection.AddTaskFactory<EquipRecommended.BeforeDutyOrInstance>();
serviceCollection.AddTaskFactoryAndExecutor<Gather.GatheringTask, Gather.Factory, Gather.StartGathering>();
serviceCollection.AddTaskExecutor<Gather.SkipMarker, Gather.DoSkip>(); serviceCollection.AddTaskExecutor<Gather.SkipMarker, Gather.DoSkip>();
serviceCollection serviceCollection
.AddTaskFactoryAndExecutor<AetheryteShortcut.Task, AetheryteShortcut.Factory, .AddTaskFactoryAndExecutor<AetheryteShortcut.Task, AetheryteShortcut.Factory,
@ -156,6 +155,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin
.AddTaskExecutor<AetheryteShortcut.MoveAwayFromAetheryte, AetheryteShortcut.MoveAwayFromAetheryteExecutor>(); .AddTaskExecutor<AetheryteShortcut.MoveAwayFromAetheryte, AetheryteShortcut.MoveAwayFromAetheryteExecutor>();
serviceCollection serviceCollection
.AddTaskFactoryAndExecutor<SkipCondition.SkipTask, SkipCondition.Factory, SkipCondition.CheckSkip>(); .AddTaskFactoryAndExecutor<SkipCondition.SkipTask, SkipCondition.Factory, SkipCondition.CheckSkip>();
serviceCollection.AddTaskFactoryAndExecutor<Gather.GatheringTask, Gather.Factory, Gather.StartGathering>();
serviceCollection.AddTaskExecutor<Gather.DelayedGatheringTask, Gather.DelayedGatheringExecutor>();
serviceCollection serviceCollection
.AddTaskFactoryAndExecutor<AethernetShortcut.Task, AethernetShortcut.Factory, .AddTaskFactoryAndExecutor<AethernetShortcut.Task, AethernetShortcut.Factory,
AethernetShortcut.UseAethernetShortcut>(); AethernetShortcut.UseAethernetShortcut>();