forked from liza/Questionable
master #3
@ -114,8 +114,6 @@ internal sealed class CombatController : IDisposable
|
||||
else
|
||||
{
|
||||
var nextTarget = FindNextTarget();
|
||||
_logger.LogInformation("NT → {NT}", nextTarget);
|
||||
|
||||
if (nextTarget is { IsDead: false })
|
||||
SetTarget(nextTarget);
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IGameGui _gameGui;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly ILogger<GatheringController> _logger;
|
||||
private readonly Regex _revisitRegex;
|
||||
|
||||
private CurrentRequest? _currentRequest;
|
||||
@ -51,6 +52,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
|
||||
MovementController movementController,
|
||||
MoveTo.Factory moveFactory,
|
||||
Mount.Factory mountFactory,
|
||||
Combat.Factory combatFactory,
|
||||
Interact.Factory interactFactory,
|
||||
GatheringPointRegistry gatheringPointRegistry,
|
||||
GameFunctions gameFunctions,
|
||||
@ -64,7 +66,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
|
||||
IGameGui gameGui,
|
||||
IClientState clientState,
|
||||
IPluginLog pluginLog)
|
||||
: base(chatGui, logger)
|
||||
: base(chatGui, mountFactory, combatFactory, condition, logger)
|
||||
{
|
||||
_movementController = movementController;
|
||||
_moveFactory = moveFactory;
|
||||
@ -78,6 +80,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
|
||||
_loggerFactory = loggerFactory;
|
||||
_gameGui = gameGui;
|
||||
_clientState = clientState;
|
||||
_logger = logger;
|
||||
|
||||
_revisitRegex = dataManager.GetRegex<LogMessage>(5574, x => x.Text, pluginLog)
|
||||
?? throw new InvalidDataException("No regex found for revisit message");
|
||||
|
@ -1,23 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller.Steps;
|
||||
using Questionable.Controller.Steps.Common;
|
||||
using Questionable.Controller.Steps.Interactions;
|
||||
using Questionable.Controller.Steps.Shared;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller;
|
||||
|
||||
internal abstract class MiniTaskController<T>
|
||||
{
|
||||
protected readonly IChatGui _chatGui;
|
||||
protected readonly ILogger<T> _logger;
|
||||
protected readonly TaskQueue _taskQueue = new();
|
||||
|
||||
protected MiniTaskController(IChatGui chatGui, ILogger<T> logger)
|
||||
private readonly IChatGui _chatGui;
|
||||
private readonly Mount.Factory _mountFactory;
|
||||
private readonly Combat.Factory _combatFactory;
|
||||
private readonly ICondition _condition;
|
||||
private readonly ILogger<T> _logger;
|
||||
|
||||
protected MiniTaskController(IChatGui chatGui, Mount.Factory mountFactory, Combat.Factory combatFactory,
|
||||
ICondition condition, ILogger<T> logger)
|
||||
{
|
||||
_chatGui = chatGui;
|
||||
_logger = logger;
|
||||
_mountFactory = mountFactory;
|
||||
_combatFactory = combatFactory;
|
||||
_condition = condition;
|
||||
}
|
||||
|
||||
protected virtual void UpdateCurrentTask()
|
||||
@ -56,6 +68,12 @@ internal abstract class MiniTaskController<T>
|
||||
ETaskResult result;
|
||||
try
|
||||
{
|
||||
if (_taskQueue.CurrentTask.WasInterrupted())
|
||||
{
|
||||
InterruptQueueWithCombat();
|
||||
return;
|
||||
}
|
||||
|
||||
result = _taskQueue.CurrentTask.Update();
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -122,11 +140,27 @@ internal abstract class MiniTaskController<T>
|
||||
|
||||
protected virtual void OnNextStep(ILastTask task)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public abstract void Stop(string label);
|
||||
|
||||
public virtual IList<string> GetRemainingTaskNames() =>
|
||||
_taskQueue.RemainingTasks.Select(x => x.ToString() ?? "?").ToList();
|
||||
|
||||
public void InterruptQueueWithCombat()
|
||||
{
|
||||
_logger.LogWarning("Interrupted, attempting to resolve (if in combat)");
|
||||
if (_condition[ConditionFlag.InCombat])
|
||||
{
|
||||
List<ITask> tasks = [];
|
||||
if (_condition[ConditionFlag.Mounted])
|
||||
tasks.Add(_mountFactory.Unmount());
|
||||
|
||||
tasks.Add(_combatFactory.CreateTask(null, false, EEnemySpawnType.QuestInterruption, [], [], []));
|
||||
tasks.Add(new WaitAtEnd.WaitDelay());
|
||||
_taskQueue.InterruptWith(tasks);
|
||||
}
|
||||
else
|
||||
_taskQueue.InterruptWith([new WaitAtEnd.WaitDelay()]);
|
||||
}
|
||||
}
|
||||
|
@ -35,13 +35,13 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
|
||||
private readonly GatheringController _gatheringController;
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
private readonly IKeyState _keyState;
|
||||
private readonly IChatGui _chatGui;
|
||||
private readonly ICondition _condition;
|
||||
private readonly IToastGui _toastGui;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly YesAlreadyIpc _yesAlreadyIpc;
|
||||
private readonly TaskCreator _taskCreator;
|
||||
private readonly Mount.Factory _mountFactory;
|
||||
private readonly Combat.Factory _combatFactory;
|
||||
private readonly ILogger<QuestController> _logger;
|
||||
|
||||
private readonly string _actionCanceledText;
|
||||
|
||||
@ -85,7 +85,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
|
||||
Mount.Factory mountFactory,
|
||||
Combat.Factory combatFactory,
|
||||
IDataManager dataManager)
|
||||
: base(chatGui, logger)
|
||||
: base(chatGui, mountFactory, combatFactory, condition, logger)
|
||||
{
|
||||
_clientState = clientState;
|
||||
_gameFunctions = gameFunctions;
|
||||
@ -95,13 +95,13 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
|
||||
_gatheringController = gatheringController;
|
||||
_questRegistry = questRegistry;
|
||||
_keyState = keyState;
|
||||
_chatGui = chatGui;
|
||||
_condition = condition;
|
||||
_toastGui = toastGui;
|
||||
_configuration = configuration;
|
||||
_yesAlreadyIpc = yesAlreadyIpc;
|
||||
_taskCreator = taskCreator;
|
||||
_mountFactory = mountFactory;
|
||||
_combatFactory = combatFactory;
|
||||
_logger = logger;
|
||||
|
||||
_condition.ConditionChange += OnConditionChange;
|
||||
_toastGui.Toast += OnNormalToast;
|
||||
@ -659,6 +659,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
|
||||
}
|
||||
|
||||
public bool IsRunning => !_taskQueue.AllTasksComplete;
|
||||
public TaskQueue TaskQueue => _taskQueue;
|
||||
|
||||
public sealed class QuestProgress
|
||||
{
|
||||
@ -813,18 +814,6 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
|
||||
}
|
||||
}
|
||||
|
||||
public void InterruptQueueWithCombat()
|
||||
{
|
||||
_logger.LogWarning("Interrupted with action canceled message, attempting to resolve");
|
||||
List<ITask> tasks = [];
|
||||
if (_condition[ConditionFlag.Mounted])
|
||||
tasks.Add(_mountFactory.Unmount());
|
||||
|
||||
tasks.Add(_combatFactory.CreateTask(null, false, EEnemySpawnType.QuestInterruption, [], [], []));
|
||||
tasks.Add(new WaitAtEnd.WaitDelay());
|
||||
_taskQueue.InterruptWith(tasks);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_toastGui.ErrorToast -= OnErrorToast;
|
||||
|
@ -18,6 +18,8 @@ internal abstract class AbstractDelayedTask : ITask
|
||||
{
|
||||
}
|
||||
|
||||
public virtual InteractionProgressContext? ProgressContext() => null;
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
_continueAt = DateTime.Now.Add(Delay);
|
||||
|
@ -44,8 +44,11 @@ internal static class Mount
|
||||
ILogger<MountTask> logger) : ITask
|
||||
{
|
||||
private bool _mountTriggered;
|
||||
private InteractionProgressContext? _progressContext;
|
||||
private DateTime _retryAt = DateTime.MinValue;
|
||||
|
||||
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||
|
||||
public bool ShouldRedoOnInterrupt() => true;
|
||||
|
||||
public bool Start()
|
||||
@ -108,7 +111,8 @@ internal static class Mount
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
||||
_mountTriggered = gameFunctions.Mount();
|
||||
_progressContext = InteractionProgressContext.FromActionUse(() => _mountTriggered = gameFunctions.Mount());
|
||||
|
||||
_retryAt = DateTime.Now.AddSeconds(5);
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
|
@ -5,6 +5,20 @@ namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface ITask
|
||||
{
|
||||
InteractionProgressContext? ProgressContext() => null;
|
||||
|
||||
bool WasInterrupted()
|
||||
{
|
||||
var progressContext = ProgressContext();
|
||||
if (progressContext != null)
|
||||
{
|
||||
progressContext.Update();
|
||||
return progressContext.WasInterrupted();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShouldRedoOnInterrupt() => false;
|
||||
|
||||
bool Start();
|
||||
|
97
Questionable/Controller/Steps/InteractionProgressContext.cs
Normal file
97
Questionable/Controller/Steps/InteractionProgressContext.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
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 static unsafe InteractionProgressContext Create(bool checkSequence)
|
||||
{
|
||||
if (!checkSequence)
|
||||
{
|
||||
// this is a silly hack; we assume that the previous cast was successful
|
||||
// if not for this, we'd instantly be seen as interrupted
|
||||
ActionManager.Instance()->CastTimeElapsed = ActionManager.Instance()->CastTimeTotal;
|
||||
}
|
||||
|
||||
return new InteractionProgressContext(checkSequence, ActionManager.Instance()->LastUsedActionSequence);
|
||||
}
|
||||
|
||||
private static unsafe (bool, InteractionProgressContext?) FromActionUseInternal(Func<bool> func)
|
||||
{
|
||||
int oldSequence = ActionManager.Instance()->LastUsedActionSequence;
|
||||
if (!func())
|
||||
return (false, null);
|
||||
int newSequence = ActionManager.Instance()->LastUsedActionSequence;
|
||||
if (oldSequence == newSequence)
|
||||
return (true, null);
|
||||
return (true, Create(true));
|
||||
}
|
||||
|
||||
public static InteractionProgressContext? FromActionUse(Func<bool> func)
|
||||
{
|
||||
return FromActionUseInternal(func).Item2;
|
||||
}
|
||||
|
||||
public static InteractionProgressContext? FromActionUseOrDefault(Func<bool> func)
|
||||
{
|
||||
var result = FromActionUseInternal(func);
|
||||
if (!result.Item1)
|
||||
return null;
|
||||
return result.Item2 ?? Create(false);
|
||||
}
|
||||
|
||||
public unsafe void Update()
|
||||
{
|
||||
if (!_firstUpdateDone)
|
||||
{
|
||||
int lastSequence = ActionManager.Instance()->LastUsedActionSequence;
|
||||
if (!CheckSequence && lastSequence > CurrentSequence)
|
||||
{
|
||||
CheckSequence = true;
|
||||
CurrentSequence = lastSequence;
|
||||
}
|
||||
|
||||
_firstUpdateDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe bool WasSuccessful()
|
||||
{
|
||||
if (CheckSequence)
|
||||
{
|
||||
if (CurrentSequence != ActionManager.Instance()->LastUsedActionSequence ||
|
||||
CurrentSequence != ActionManager.Instance()->LastHandledActionSequence)
|
||||
return false;
|
||||
}
|
||||
|
||||
return ActionManager.Instance()->CastTimeElapsed > 0 &&
|
||||
Math.Abs(ActionManager.Instance()->CastTimeElapsed - ActionManager.Instance()->CastTimeTotal) < 0.001f;
|
||||
}
|
||||
|
||||
public unsafe bool WasInterrupted()
|
||||
{
|
||||
if (CheckSequence)
|
||||
{
|
||||
if (CurrentSequence == ActionManager.Instance()->LastHandledActionSequence &&
|
||||
CurrentSequence == ActionManager.Instance()->LastUsedActionSequence)
|
||||
return false;
|
||||
}
|
||||
|
||||
return ActionManager.Instance()->CastTimeElapsed == 0 &&
|
||||
ActionManager.Instance()->CastTimeTotal > 0;
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
$"IPCtx({(CheckSequence ? CurrentSequence : "-")} - {WasSuccessful()}, {WasInterrupted()})";
|
||||
}
|
@ -32,19 +32,29 @@ internal static class AetherCurrent
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DoAttune(step.DataId.Value, step.AetherCurrentId.Value, gameFunctions, loggerFactory.CreateLogger<DoAttune>());
|
||||
return new DoAttune(step.DataId.Value, step.AetherCurrentId.Value, gameFunctions,
|
||||
loggerFactory.CreateLogger<DoAttune>());
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DoAttune(uint dataId, uint aetherCurrentId, GameFunctions gameFunctions, ILogger<DoAttune> logger) : ITask
|
||||
private sealed class DoAttune(
|
||||
uint dataId,
|
||||
uint aetherCurrentId,
|
||||
GameFunctions gameFunctions,
|
||||
ILogger<DoAttune> logger) : ITask
|
||||
{
|
||||
private InteractionProgressContext? _progressContext;
|
||||
|
||||
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
if (!gameFunctions.IsAetherCurrentUnlocked(aetherCurrentId))
|
||||
{
|
||||
logger.LogInformation("Attuning to aether current {AetherCurrentId} / {DataId}", aetherCurrentId,
|
||||
dataId);
|
||||
gameFunctions.InteractWith(dataId);
|
||||
_progressContext =
|
||||
InteractionProgressContext.FromActionUseOrDefault(() => gameFunctions.InteractWith(dataId));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -34,12 +34,17 @@ internal static class AethernetShard
|
||||
GameFunctions gameFunctions,
|
||||
ILogger<DoAttune> logger) : ITask
|
||||
{
|
||||
private InteractionProgressContext? _progressContext;
|
||||
|
||||
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
if (!aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
|
||||
{
|
||||
logger.LogInformation("Attuning to aethernet shard {AethernetShard}", aetheryteLocation);
|
||||
gameFunctions.InteractWith((uint)aetheryteLocation, ObjectKind.Aetheryte);
|
||||
_progressContext = InteractionProgressContext.FromActionUseOrDefault(() =>
|
||||
gameFunctions.InteractWith((uint)aetheryteLocation, ObjectKind.Aetheryte));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -33,12 +33,18 @@ internal static class Aetheryte
|
||||
GameFunctions gameFunctions,
|
||||
ILogger<DoAttune> logger) : ITask
|
||||
{
|
||||
private InteractionProgressContext? _progressContext;
|
||||
|
||||
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
if (!aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
|
||||
{
|
||||
logger.LogInformation("Attuning to aetheryte {Aetheryte}", aetheryteLocation);
|
||||
gameFunctions.InteractWith((uint)aetheryteLocation);
|
||||
_progressContext =
|
||||
InteractionProgressContext.FromActionUseOrDefault(() =>
|
||||
gameFunctions.InteractWith((uint)aetheryteLocation));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -78,10 +78,10 @@ internal static class Interact
|
||||
GameFunctions gameFunctions,
|
||||
ICondition condition,
|
||||
ILogger<DoInteract> logger)
|
||||
: ITask, IConditionChangeAware
|
||||
: ITask
|
||||
{
|
||||
private bool _needsUnmount;
|
||||
private EInteractionState _interactionState = EInteractionState.None;
|
||||
private InteractionProgressContext? _progressContext;
|
||||
private DateTime _continueAt = DateTime.MinValue;
|
||||
|
||||
public Quest? Quest => quest;
|
||||
@ -92,6 +92,8 @@ internal static class Interact
|
||||
set => interactionType = value;
|
||||
}
|
||||
|
||||
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
IGameObject? gameObject = gameFunctions.FindObjectByDataId(dataId);
|
||||
@ -121,9 +123,8 @@ internal static class Interact
|
||||
|
||||
if (gameObject.IsTargetable && HasAnyMarker(gameObject))
|
||||
{
|
||||
_interactionState = gameFunctions.InteractWith(gameObject)
|
||||
? EInteractionState.InteractionTriggered
|
||||
: EInteractionState.None;
|
||||
_progressContext =
|
||||
InteractionProgressContext.FromActionUseOrDefault(() => gameFunctions.InteractWith(gameObject));
|
||||
_continueAt = DateTime.Now.AddSeconds(0.5);
|
||||
return true;
|
||||
}
|
||||
@ -159,7 +160,7 @@ internal static class Interact
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_interactionState == EInteractionState.InteractionConfirmed)
|
||||
if (_progressContext != null && _progressContext.WasSuccessful())
|
||||
return ETaskResult.TaskComplete;
|
||||
|
||||
if (interactionType == EInteractionType.Gather && condition[ConditionFlag.Gathering])
|
||||
@ -170,9 +171,8 @@ internal static class Interact
|
||||
if (gameObject == null || !gameObject.IsTargetable || !HasAnyMarker(gameObject))
|
||||
return ETaskResult.StillRunning;
|
||||
|
||||
_interactionState = gameFunctions.InteractWith(gameObject)
|
||||
? EInteractionState.InteractionTriggered
|
||||
: EInteractionState.None;
|
||||
_progressContext =
|
||||
InteractionProgressContext.FromActionUseOrDefault(() => gameFunctions.InteractWith(gameObject));
|
||||
_continueAt = DateTime.Now.AddSeconds(0.5);
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
@ -187,33 +187,5 @@ internal static class Interact
|
||||
}
|
||||
|
||||
public override string ToString() => $"Interact({dataId})";
|
||||
|
||||
public void OnConditionChange(ConditionFlag flag, bool value)
|
||||
{
|
||||
logger.LogDebug("Condition change: {Flag} = {Value}", flag, value);
|
||||
if (_interactionState == EInteractionState.InteractionTriggered &&
|
||||
flag is ConditionFlag.OccupiedInQuestEvent or ConditionFlag.OccupiedInEvent &&
|
||||
value)
|
||||
{
|
||||
logger.LogInformation("Interaction was most likely triggered");
|
||||
_interactionState = EInteractionState.InteractionConfirmed;
|
||||
}
|
||||
else if (dataId is >= 1047901 and <= 1047905 &&
|
||||
condition[ConditionFlag.Disguised] &&
|
||||
flag == ConditionFlag
|
||||
.Mounting71 && // why the fuck is this the flag that's used, instead of OccupiedIn[Quest]Event
|
||||
value)
|
||||
{
|
||||
logger.LogInformation("(A Knight of Alexandria) Interaction was most likely triggered");
|
||||
_interactionState = EInteractionState.InteractionConfirmed;
|
||||
}
|
||||
}
|
||||
|
||||
private enum EInteractionState
|
||||
{
|
||||
None,
|
||||
InteractionTriggered,
|
||||
InteractionConfirmed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,6 +160,9 @@ internal static class UseItem
|
||||
private bool _usedItem;
|
||||
private DateTime _continueAt;
|
||||
private int _itemCount;
|
||||
private InteractionProgressContext? _progressContext;
|
||||
|
||||
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||
|
||||
public ElementId? QuestId => questId;
|
||||
public uint ItemId => itemId;
|
||||
@ -178,7 +181,7 @@ internal static class UseItem
|
||||
if (_itemCount == 0)
|
||||
throw new TaskException($"Don't have any {ItemId} in inventory (checks NQ only)");
|
||||
|
||||
_usedItem = UseItem();
|
||||
_progressContext = InteractionProgressContext.FromActionUseOrDefault(() => _usedItem = UseItem());
|
||||
_continueAt = DateTime.Now.Add(GetRetryDelay());
|
||||
return true;
|
||||
}
|
||||
@ -221,7 +224,7 @@ internal static class UseItem
|
||||
|
||||
if (!_usedItem)
|
||||
{
|
||||
_usedItem = UseItem();
|
||||
_progressContext = InteractionProgressContext.FromActionUseOrDefault(() => _usedItem = UseItem());
|
||||
_continueAt = DateTime.Now.Add(GetRetryDelay());
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
|
@ -60,6 +60,9 @@ internal static class AetheryteShortcut
|
||||
{
|
||||
private bool _teleported;
|
||||
private DateTime _continueAt;
|
||||
private InteractionProgressContext? _progressContext;
|
||||
|
||||
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||
|
||||
public bool Start() => !ShouldSkipTeleport();
|
||||
|
||||
@ -200,15 +203,21 @@ internal static class AetheryteShortcut
|
||||
chatGui.PrintError($"[Questionable] Aetheryte {targetAetheryte} is not unlocked.");
|
||||
throw new TaskException("Aetheryte is not unlocked");
|
||||
}
|
||||
else if (aetheryteFunctions.TeleportAetheryte(targetAetheryte))
|
||||
{
|
||||
logger.LogInformation("Travelling via aetheryte...");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
chatGui.Print("[Questionable] Unable to teleport to aetheryte.");
|
||||
throw new TaskException("Unable to teleport to aetheryte");
|
||||
_progressContext =
|
||||
InteractionProgressContext.FromActionUseOrDefault(() => aetheryteFunctions.TeleportAetheryte(targetAetheryte));
|
||||
logger.LogInformation("Ctx = {C}", _progressContext);
|
||||
if (_progressContext != null)
|
||||
{
|
||||
logger.LogInformation("Travelling via aetheryte...");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
chatGui.Print("[Questionable] Unable to teleport to aetheryte.");
|
||||
throw new TaskException("Unable to teleport to aetheryte");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,6 +173,8 @@ internal static class MoveTo
|
||||
_canRestart = moveParams.RestartNavigation;
|
||||
}
|
||||
|
||||
public InteractionProgressContext? ProgressContext() => _mountTask?.ProgressContext();
|
||||
|
||||
public bool ShouldRedoOnInterrupt() => true;
|
||||
|
||||
public bool Start()
|
||||
|
@ -6,12 +6,12 @@ namespace Questionable.Controller.Steps;
|
||||
|
||||
internal sealed class TaskQueue
|
||||
{
|
||||
private readonly List<ITask> _completedTasks = [];
|
||||
private readonly List<ITask> _tasks = [];
|
||||
private int _currentTaskIndex;
|
||||
public ITask? CurrentTask { get; set; }
|
||||
|
||||
public IEnumerable<ITask> RemainingTasks => _tasks.Skip(_currentTaskIndex);
|
||||
public bool AllTasksComplete => CurrentTask == null && _currentTaskIndex >= _tasks.Count;
|
||||
public IEnumerable<ITask> RemainingTasks => _tasks;
|
||||
public bool AllTasksComplete => CurrentTask == null && _tasks.Count == 0;
|
||||
|
||||
public void Enqueue(ITask task)
|
||||
{
|
||||
@ -20,48 +20,40 @@ internal sealed class TaskQueue
|
||||
|
||||
public bool TryDequeue([NotNullWhen(true)] out ITask? task)
|
||||
{
|
||||
if (_currentTaskIndex >= _tasks.Count)
|
||||
{
|
||||
task = null;
|
||||
task = _tasks.FirstOrDefault();
|
||||
if (task == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
task = _tasks[_currentTaskIndex];
|
||||
if (task.ShouldRedoOnInterrupt())
|
||||
_currentTaskIndex++;
|
||||
else
|
||||
_tasks.RemoveAt(0);
|
||||
_completedTasks.Add(task);
|
||||
|
||||
_tasks.RemoveAt(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryPeek([NotNullWhen(true)] out ITask? task)
|
||||
{
|
||||
if (_currentTaskIndex >= _tasks.Count)
|
||||
{
|
||||
task = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
task = _tasks[_currentTaskIndex];
|
||||
return true;
|
||||
task = _tasks.FirstOrDefault();
|
||||
return task != null;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_tasks.Clear();
|
||||
_currentTaskIndex = 0;
|
||||
_completedTasks.Clear();
|
||||
CurrentTask = null;
|
||||
}
|
||||
|
||||
public void InterruptWith(List<ITask> interruptionTasks)
|
||||
{
|
||||
if (CurrentTask != null)
|
||||
{
|
||||
_tasks.Insert(0, CurrentTask);
|
||||
CurrentTask = null;
|
||||
_currentTaskIndex = 0;
|
||||
}
|
||||
|
||||
_tasks.InsertRange(0, interruptionTasks);
|
||||
List<ITask?> newTasks =
|
||||
[
|
||||
..interruptionTasks,
|
||||
.._completedTasks.Where(x => !ReferenceEquals(x, CurrentTask)).ToList(),
|
||||
CurrentTask,
|
||||
.._tasks
|
||||
];
|
||||
Reset();
|
||||
_tasks.AddRange(newTasks.Where(x => x != null).Cast<ITask>());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user