punish v6.8.18.0
This commit is contained in:
commit
cfb4dea47e
316 changed files with 554088 additions and 0 deletions
11
Questionable/Questionable.Controller.Steps/ETaskResult.cs
Normal file
11
Questionable/Questionable.Controller.Steps/ETaskResult.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal enum ETaskResult
|
||||
{
|
||||
StillRunning,
|
||||
TaskComplete,
|
||||
SkipRemainingTasksForStep,
|
||||
CreateNewTasks,
|
||||
NextStep,
|
||||
End
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using Dalamud.Game.ClientState.Conditions;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface IConditionChangeAware : ITaskExecutor
|
||||
{
|
||||
void OnConditionChange(ConditionFlag flag, bool value);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface IDebugStateProvider : ITaskExecutor
|
||||
{
|
||||
string? GetDebugState();
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface IExtraTaskCreator : ITaskExecutor
|
||||
{
|
||||
IEnumerable<ITask> CreateExtraTasks();
|
||||
}
|
10
Questionable/Questionable.Controller.Steps/ILastTask.cs
Normal file
10
Questionable/Questionable.Controller.Steps/ILastTask.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface ILastTask : ITask
|
||||
{
|
||||
ElementId ElementId { get; }
|
||||
|
||||
int Sequence { get; }
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface IRevisitAware
|
||||
{
|
||||
void OnRevisit();
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface ISkippableTask : ITask
|
||||
{
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface IStoppableTaskExecutor : ITaskExecutor
|
||||
{
|
||||
void StopNow();
|
||||
}
|
9
Questionable/Questionable.Controller.Steps/ITask.cs
Normal file
9
Questionable/Questionable.Controller.Steps/ITask.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface ITask
|
||||
{
|
||||
bool ShouldRedoOnInterrupt()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
20
Questionable/Questionable.Controller.Steps/ITaskExecutor.cs
Normal file
20
Questionable/Questionable.Controller.Steps/ITaskExecutor.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface ITaskExecutor
|
||||
{
|
||||
ITask CurrentTask { get; }
|
||||
|
||||
InteractionProgressContext? ProgressContext { get; }
|
||||
|
||||
Type GetTaskType();
|
||||
|
||||
bool Start(ITask task);
|
||||
|
||||
bool ShouldInterruptOnDamage();
|
||||
|
||||
bool WasInterrupted();
|
||||
|
||||
ETaskResult Update();
|
||||
}
|
10
Questionable/Questionable.Controller.Steps/ITaskFactory.cs
Normal file
10
Questionable/Questionable.Controller.Steps/ITaskFactory.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface ITaskFactory
|
||||
{
|
||||
IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface IToastAware : ITaskExecutor
|
||||
{
|
||||
bool OnErrorToast(SeString message);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal sealed class InteractionProgressContext
|
||||
{
|
||||
private bool _firstUpdateDone;
|
||||
|
||||
public bool CheckSequence { get; private set; }
|
||||
|
||||
public int CurrentSequence { get; private set; }
|
||||
|
||||
private InteractionProgressContext(bool checkSequence, int currentSequence)
|
||||
{
|
||||
CheckSequence = checkSequence;
|
||||
CurrentSequence = currentSequence;
|
||||
}
|
||||
|
||||
public unsafe static InteractionProgressContext Create(bool checkSequence)
|
||||
{
|
||||
if (!checkSequence)
|
||||
{
|
||||
ActionManager.Instance()->CastTimeElapsed = ActionManager.Instance()->CastTimeTotal;
|
||||
}
|
||||
return new InteractionProgressContext(checkSequence, ActionManager.Instance()->LastUsedActionSequence);
|
||||
}
|
||||
|
||||
private unsafe static (bool, InteractionProgressContext?) FromActionUseInternal(Func<bool> func)
|
||||
{
|
||||
int lastUsedActionSequence = ActionManager.Instance()->LastUsedActionSequence;
|
||||
if (!func())
|
||||
{
|
||||
return (false, null);
|
||||
}
|
||||
int lastUsedActionSequence2 = ActionManager.Instance()->LastUsedActionSequence;
|
||||
if (lastUsedActionSequence == lastUsedActionSequence2)
|
||||
{
|
||||
return (true, null);
|
||||
}
|
||||
return (true, Create(checkSequence: true));
|
||||
}
|
||||
|
||||
public static InteractionProgressContext? FromActionUse(Func<bool> func)
|
||||
{
|
||||
return FromActionUseInternal(func).Item2;
|
||||
}
|
||||
|
||||
public static InteractionProgressContext? FromActionUseOrDefault(Func<bool> func)
|
||||
{
|
||||
(bool, InteractionProgressContext) tuple = FromActionUseInternal(func);
|
||||
if (!tuple.Item1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return tuple.Item2 ?? Create(checkSequence: false);
|
||||
}
|
||||
|
||||
public unsafe void Update()
|
||||
{
|
||||
if (!_firstUpdateDone)
|
||||
{
|
||||
int lastUsedActionSequence = ActionManager.Instance()->LastUsedActionSequence;
|
||||
if (!CheckSequence && lastUsedActionSequence > CurrentSequence)
|
||||
{
|
||||
CheckSequence = true;
|
||||
CurrentSequence = lastUsedActionSequence;
|
||||
}
|
||||
_firstUpdateDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe bool WasSuccessful()
|
||||
{
|
||||
if (CheckSequence && (CurrentSequence != ActionManager.Instance()->LastUsedActionSequence || CurrentSequence != ActionManager.Instance()->LastHandledActionSequence))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ActionManager.Instance()->CastTimeElapsed > 0f)
|
||||
{
|
||||
return Math.Abs(ActionManager.Instance()->CastTimeElapsed - ActionManager.Instance()->CastTimeTotal) < 0.001f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public unsafe bool WasInterrupted()
|
||||
{
|
||||
if (CheckSequence && CurrentSequence == ActionManager.Instance()->LastHandledActionSequence && CurrentSequence == ActionManager.Instance()->LastUsedActionSequence)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ActionManager.Instance()->CastTimeElapsed == 0f)
|
||||
{
|
||||
return ActionManager.Instance()->CastTimeTotal > 0f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(12, 3);
|
||||
defaultInterpolatedStringHandler.AppendLiteral("IPCtx(");
|
||||
defaultInterpolatedStringHandler.AppendFormatted(CheckSequence ? ((object)CurrentSequence) : "-");
|
||||
defaultInterpolatedStringHandler.AppendLiteral(" - ");
|
||||
defaultInterpolatedStringHandler.AppendFormatted(WasSuccessful());
|
||||
defaultInterpolatedStringHandler.AppendLiteral(", ");
|
||||
defaultInterpolatedStringHandler.AppendFormatted(WasInterrupted());
|
||||
defaultInterpolatedStringHandler.AppendLiteral(")");
|
||||
return defaultInterpolatedStringHandler.ToStringAndClear();
|
||||
}
|
||||
}
|
124
Questionable/Questionable.Controller.Steps/QuestCleanUp.cs
Normal file
124
Questionable/Questionable.Controller.Steps/QuestCleanUp.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using LLib.GameUI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller.Steps.Shared;
|
||||
using Questionable.Data;
|
||||
using Questionable.Functions;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Common;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal static class QuestCleanUp
|
||||
{
|
||||
internal sealed class CheckAlliedSocietyMount(GameFunctions gameFunctions, AetheryteData aetheryteData, AlliedSocietyData alliedSocietyData, ILogger<CheckAlliedSocietyMount> logger) : SimpleTaskFactory()
|
||||
{
|
||||
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
{
|
||||
if (sequence.Sequence == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ushort? mountId = gameFunctions.GetMountId();
|
||||
if (mountId.HasValue)
|
||||
{
|
||||
ushort valueOrDefault = mountId.GetValueOrDefault();
|
||||
if (alliedSocietyData.Mounts.TryGetValue(valueOrDefault, out AlliedSocietyMountConfiguration mountConfiguration))
|
||||
{
|
||||
logger.LogInformation("We are on a known allied society mount with id = {MountId}", valueOrDefault);
|
||||
EAetheryteLocation eAetheryteLocation = step.AetheryteShortcut ?? mountConfiguration.ClosestAetheryte;
|
||||
AetheryteShortcut.Task result = new AetheryteShortcut.Task(null, quest.Id, eAetheryteLocation, aetheryteData.TerritoryIds[eAetheryteLocation]);
|
||||
if (sequence.Sequence == byte.MaxValue)
|
||||
{
|
||||
logger.LogInformation("Mount can't be used to finish quest, teleporting to {Aetheryte}", mountConfiguration.ClosestAetheryte);
|
||||
return result;
|
||||
}
|
||||
if (!quest.AllSteps().Any<(QuestSequence, int, QuestStep)>(delegate((QuestSequence Sequence, int StepId, QuestStep Step) x)
|
||||
{
|
||||
EAction? action = x.Step.Action;
|
||||
if (action.HasValue)
|
||||
{
|
||||
EAction valueOrDefault2 = action.GetValueOrDefault();
|
||||
if (valueOrDefault2.RequiresMount())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return x.Step.InteractionType == EInteractionType.Combat && x.Step.KillEnemyDataIds.Contains(8593u);
|
||||
}))
|
||||
{
|
||||
logger.LogInformation("Quest doesn't use any mount actions, teleporting to {Aetheryte}", mountConfiguration.ClosestAetheryte);
|
||||
return result;
|
||||
}
|
||||
if (!(from x in quest.AllSequences()
|
||||
where x.Sequence > 0 && x.Sequence < sequence.Sequence
|
||||
select x).SelectMany((QuestSequence x) => x.Steps).ToList().Any((QuestStep x) => x.DataId.HasValue && mountConfiguration.IssuerDataIds.Contains(x.DataId.Value)))
|
||||
{
|
||||
logger.LogInformation("Haven't talked to mount NPC for this allied society quest; {Aetheryte}", mountConfiguration.ClosestAetheryte);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CloseGatheringAddonFactory(IGameGui gameGui) : ITaskFactory
|
||||
{
|
||||
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
{
|
||||
if (IsAddonOpen("GatheringMasterpiece"))
|
||||
{
|
||||
yield return new CloseGatheringAddonTask("GatheringMasterpiece");
|
||||
}
|
||||
if (IsAddonOpen("Gathering"))
|
||||
{
|
||||
yield return new CloseGatheringAddonTask("Gathering");
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe bool IsAddonOpen(string name)
|
||||
{
|
||||
if (gameGui.TryGetAddonByName<AtkUnitBase>(name, out var addonPtr))
|
||||
{
|
||||
return addonPtr->IsVisible;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record CloseGatheringAddonTask(string AddonName) : ITask
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return "CloseAddon(" + AddonName + ")";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DoCloseAddon(IGameGui gameGui) : TaskExecutor<CloseGatheringAddonTask>()
|
||||
{
|
||||
protected unsafe override bool Start()
|
||||
{
|
||||
if (gameGui.TryGetAddonByName<AtkUnitBase>(base.Task.AddonName, out var addonPtr))
|
||||
{
|
||||
addonPtr->FireCallbackInt(-1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override ETaskResult Update()
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
||||
public override bool ShouldInterruptOnDamage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal abstract class SimpleTaskFactory : ITaskFactory
|
||||
{
|
||||
public abstract ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step);
|
||||
|
||||
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
{
|
||||
ITask task = CreateTask(quest, sequence, step);
|
||||
if (task != null)
|
||||
{
|
||||
yield return task;
|
||||
}
|
||||
}
|
||||
}
|
99
Questionable/Questionable.Controller.Steps/TaskCreator.cs
Normal file
99
Questionable/Questionable.Controller.Steps/TaskCreator.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller.Steps.Interactions;
|
||||
using Questionable.Controller.Steps.Shared;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal sealed class TaskCreator
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
private readonly TerritoryData _territoryData;
|
||||
|
||||
private readonly IClientState _clientState;
|
||||
|
||||
private readonly IChatGui _chatGui;
|
||||
|
||||
private readonly ILogger<TaskCreator> _logger;
|
||||
|
||||
public TaskCreator(IServiceProvider serviceProvider, TerritoryData territoryData, IClientState clientState, IChatGui chatGui, ILogger<TaskCreator> logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_territoryData = territoryData;
|
||||
_clientState = clientState;
|
||||
_chatGui = chatGui;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IReadOnlyList<ITask> CreateTasks(Quest quest, byte sequenceNumber, QuestSequence? sequence, QuestStep? step)
|
||||
{
|
||||
List<ITask> list2;
|
||||
if (sequence == null)
|
||||
{
|
||||
_chatGui.PrintError($"Path for quest '{quest.Info.Name}' ({quest.Id}) does not contain sequence {sequenceNumber}, please report this: https://github.com/PunishXIV/Questionable/discussions/20", "Questionable", 576);
|
||||
int num = 1;
|
||||
List<ITask> list = new List<ITask>(num);
|
||||
CollectionsMarshal.SetCount(list, num);
|
||||
Span<ITask> span = CollectionsMarshal.AsSpan(list);
|
||||
int index = 0;
|
||||
span[index] = new WaitAtEnd.WaitNextStepOrSequence();
|
||||
list2 = list;
|
||||
}
|
||||
else if (step == null)
|
||||
{
|
||||
int index = 1;
|
||||
List<ITask> list3 = new List<ITask>(index);
|
||||
CollectionsMarshal.SetCount(list3, index);
|
||||
Span<ITask> span = CollectionsMarshal.AsSpan(list3);
|
||||
int num = 0;
|
||||
span[num] = new WaitAtEnd.WaitNextStepOrSequence();
|
||||
list2 = list3;
|
||||
}
|
||||
else
|
||||
{
|
||||
using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
||||
list2 = serviceScope.ServiceProvider.GetRequiredService<IEnumerable<ITaskFactory>>().SelectMany(delegate(ITaskFactory x)
|
||||
{
|
||||
List<ITask> list4 = x.CreateAllTasks(quest, sequence, step).ToList();
|
||||
if (list4.Count > 0 && _logger.IsEnabled(LogLevel.Trace))
|
||||
{
|
||||
string text = x.GetType().FullName ?? x.GetType().Name;
|
||||
if (text.Contains('.', StringComparison.Ordinal))
|
||||
{
|
||||
string text2 = text;
|
||||
int num3 = text.LastIndexOf('.') + 1;
|
||||
text = text2.Substring(num3, text2.Length - num3);
|
||||
}
|
||||
_logger.LogTrace("Factory {FactoryName} created Task {TaskNames}", text, string.Join(", ", list4.Select((ITask y) => y.ToString())));
|
||||
}
|
||||
return list4;
|
||||
}).ToList();
|
||||
SinglePlayerDuty.StartSinglePlayerDuty startSinglePlayerDuty = list2.Where((ITask y) => y is SinglePlayerDuty.StartSinglePlayerDuty).Cast<SinglePlayerDuty.StartSinglePlayerDuty>().FirstOrDefault();
|
||||
if (startSinglePlayerDuty != null && _territoryData.TryGetContentFinderCondition(startSinglePlayerDuty.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData) && _clientState.TerritoryType == contentFinderConditionData.TerritoryId)
|
||||
{
|
||||
int num2 = list2.IndexOf(startSinglePlayerDuty);
|
||||
_logger.LogWarning("Skipping {SkippedTaskCount} out of {TotalCount} tasks, questionable was started while in single player duty", num2 + 1, list2.Count);
|
||||
list2.RemoveRange(0, num2 + 1);
|
||||
_logger.LogInformation("Next actual task: {NextTask}, total tasks left: {RemainingTaskCount}", list2.FirstOrDefault(), list2.Count);
|
||||
}
|
||||
}
|
||||
if (list2.Count == 0)
|
||||
{
|
||||
_logger.LogInformation("Nothing to execute for step?");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Tasks for {QuestId}, {Sequence}, {Step}: {Tasks}", quest.Id, sequenceNumber, (step == null) ? ((int?)null) : sequence?.Steps.IndexOf(step), string.Join(", ", list2.Select((ITask x) => x.ToString())));
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
}
|
20
Questionable/Questionable.Controller.Steps/TaskException.cs
Normal file
20
Questionable/Questionable.Controller.Steps/TaskException.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
public class TaskException : Exception
|
||||
{
|
||||
public TaskException()
|
||||
{
|
||||
}
|
||||
|
||||
public TaskException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public TaskException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
44
Questionable/Questionable.Controller.Steps/TaskExecutor.cs
Normal file
44
Questionable/Questionable.Controller.Steps/TaskExecutor.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal abstract class TaskExecutor<T> : ITaskExecutor where T : class, ITask
|
||||
{
|
||||
protected T Task { get; set; }
|
||||
|
||||
public InteractionProgressContext? ProgressContext { get; set; }
|
||||
|
||||
ITask ITaskExecutor.CurrentTask => Task;
|
||||
|
||||
public virtual bool WasInterrupted()
|
||||
{
|
||||
InteractionProgressContext progressContext = ProgressContext;
|
||||
if (progressContext != null)
|
||||
{
|
||||
progressContext.Update();
|
||||
return progressContext.WasInterrupted();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Type GetTaskType()
|
||||
{
|
||||
return typeof(T);
|
||||
}
|
||||
|
||||
protected abstract bool Start();
|
||||
|
||||
public bool Start(ITask task)
|
||||
{
|
||||
if (task is T task2)
|
||||
{
|
||||
Task = task2;
|
||||
return Start();
|
||||
}
|
||||
throw new TaskException($"Unable to cast {task.GetType()} to {typeof(T)}");
|
||||
}
|
||||
|
||||
public abstract ETaskResult Update();
|
||||
|
||||
public abstract bool ShouldInterruptOnDamage();
|
||||
}
|
78
Questionable/Questionable.Controller.Steps/TaskQueue.cs
Normal file
78
Questionable/Questionable.Controller.Steps/TaskQueue.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal sealed class TaskQueue
|
||||
{
|
||||
private readonly List<ITask> _completedTasks = new List<ITask>();
|
||||
|
||||
private readonly List<ITask> _tasks = new List<ITask>();
|
||||
|
||||
public ITaskExecutor? CurrentTaskExecutor { get; set; }
|
||||
|
||||
public IEnumerable<ITask> RemainingTasks => _tasks;
|
||||
|
||||
public bool AllTasksComplete
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CurrentTaskExecutor == null)
|
||||
{
|
||||
return _tasks.Count == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(ITask task)
|
||||
{
|
||||
_tasks.Add(task);
|
||||
}
|
||||
|
||||
public void EnqueueAll(IEnumerable<ITask> tasks)
|
||||
{
|
||||
_tasks.InsertRange(0, tasks);
|
||||
}
|
||||
|
||||
public bool TryDequeue([NotNullWhen(true)] out ITask? task)
|
||||
{
|
||||
task = _tasks.FirstOrDefault();
|
||||
if (task == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (task.ShouldRedoOnInterrupt())
|
||||
{
|
||||
_completedTasks.Add(task);
|
||||
}
|
||||
_tasks.RemoveAt(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryPeek([NotNullWhen(true)] out ITask? task)
|
||||
{
|
||||
task = _tasks.FirstOrDefault();
|
||||
return task != null;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_tasks.Clear();
|
||||
_completedTasks.Clear();
|
||||
CurrentTaskExecutor = null;
|
||||
}
|
||||
|
||||
public void InterruptWith(List<ITask> interruptionTasks)
|
||||
{
|
||||
List<ITask> list = new List<ITask>();
|
||||
list.AddRange(interruptionTasks);
|
||||
list.AddRange(_completedTasks.Where((ITask x) => x != CurrentTaskExecutor?.CurrentTask).ToList());
|
||||
list.Add(CurrentTaskExecutor?.CurrentTask);
|
||||
list.AddRange(_tasks);
|
||||
List<ITask> source = list;
|
||||
Reset();
|
||||
_tasks.AddRange(source.Where((ITask x) => x != null).Cast<ITask>());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue