forked from liza/Questionable
master #3
@ -114,8 +114,6 @@ internal sealed class CombatController : IDisposable
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var nextTarget = FindNextTarget();
|
var nextTarget = FindNextTarget();
|
||||||
_logger.LogInformation("NT → {NT}", nextTarget);
|
|
||||||
|
|
||||||
if (nextTarget is { IsDead: false })
|
if (nextTarget is { IsDead: false })
|
||||||
SetTarget(nextTarget);
|
SetTarget(nextTarget);
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
|
|||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly IGameGui _gameGui;
|
private readonly IGameGui _gameGui;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
|
private readonly ILogger<GatheringController> _logger;
|
||||||
private readonly Regex _revisitRegex;
|
private readonly Regex _revisitRegex;
|
||||||
|
|
||||||
private CurrentRequest? _currentRequest;
|
private CurrentRequest? _currentRequest;
|
||||||
@ -51,6 +52,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
|
|||||||
MovementController movementController,
|
MovementController movementController,
|
||||||
MoveTo.Factory moveFactory,
|
MoveTo.Factory moveFactory,
|
||||||
Mount.Factory mountFactory,
|
Mount.Factory mountFactory,
|
||||||
|
Combat.Factory combatFactory,
|
||||||
Interact.Factory interactFactory,
|
Interact.Factory interactFactory,
|
||||||
GatheringPointRegistry gatheringPointRegistry,
|
GatheringPointRegistry gatheringPointRegistry,
|
||||||
GameFunctions gameFunctions,
|
GameFunctions gameFunctions,
|
||||||
@ -64,7 +66,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
|
|||||||
IGameGui gameGui,
|
IGameGui gameGui,
|
||||||
IClientState clientState,
|
IClientState clientState,
|
||||||
IPluginLog pluginLog)
|
IPluginLog pluginLog)
|
||||||
: base(chatGui, logger)
|
: base(chatGui, mountFactory, combatFactory, condition, logger)
|
||||||
{
|
{
|
||||||
_movementController = movementController;
|
_movementController = movementController;
|
||||||
_moveFactory = moveFactory;
|
_moveFactory = moveFactory;
|
||||||
@ -78,6 +80,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
|
|||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_gameGui = gameGui;
|
_gameGui = gameGui;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
_revisitRegex = dataManager.GetRegex<LogMessage>(5574, x => x.Text, pluginLog)
|
_revisitRegex = dataManager.GetRegex<LogMessage>(5574, x => x.Text, pluginLog)
|
||||||
?? throw new InvalidDataException("No regex found for revisit message");
|
?? throw new InvalidDataException("No regex found for revisit message");
|
||||||
|
@ -1,23 +1,35 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Questionable.Controller.Steps;
|
using Questionable.Controller.Steps;
|
||||||
|
using Questionable.Controller.Steps.Common;
|
||||||
|
using Questionable.Controller.Steps.Interactions;
|
||||||
using Questionable.Controller.Steps.Shared;
|
using Questionable.Controller.Steps.Shared;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
namespace Questionable.Controller;
|
namespace Questionable.Controller;
|
||||||
|
|
||||||
internal abstract class MiniTaskController<T>
|
internal abstract class MiniTaskController<T>
|
||||||
{
|
{
|
||||||
protected readonly IChatGui _chatGui;
|
|
||||||
protected readonly ILogger<T> _logger;
|
|
||||||
protected readonly TaskQueue _taskQueue = new();
|
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;
|
_chatGui = chatGui;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_mountFactory = mountFactory;
|
||||||
|
_combatFactory = combatFactory;
|
||||||
|
_condition = condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdateCurrentTask()
|
protected virtual void UpdateCurrentTask()
|
||||||
@ -56,6 +68,12 @@ internal abstract class MiniTaskController<T>
|
|||||||
ETaskResult result;
|
ETaskResult result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_taskQueue.CurrentTask.WasInterrupted())
|
||||||
|
{
|
||||||
|
InterruptQueueWithCombat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
result = _taskQueue.CurrentTask.Update();
|
result = _taskQueue.CurrentTask.Update();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -122,11 +140,27 @@ internal abstract class MiniTaskController<T>
|
|||||||
|
|
||||||
protected virtual void OnNextStep(ILastTask task)
|
protected virtual void OnNextStep(ILastTask task)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Stop(string label);
|
public abstract void Stop(string label);
|
||||||
|
|
||||||
public virtual IList<string> GetRemainingTaskNames() =>
|
public virtual IList<string> GetRemainingTaskNames() =>
|
||||||
_taskQueue.RemainingTasks.Select(x => x.ToString() ?? "?").ToList();
|
_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 GatheringController _gatheringController;
|
||||||
private readonly QuestRegistry _questRegistry;
|
private readonly QuestRegistry _questRegistry;
|
||||||
private readonly IKeyState _keyState;
|
private readonly IKeyState _keyState;
|
||||||
|
private readonly IChatGui _chatGui;
|
||||||
private readonly ICondition _condition;
|
private readonly ICondition _condition;
|
||||||
private readonly IToastGui _toastGui;
|
private readonly IToastGui _toastGui;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly YesAlreadyIpc _yesAlreadyIpc;
|
private readonly YesAlreadyIpc _yesAlreadyIpc;
|
||||||
private readonly TaskCreator _taskCreator;
|
private readonly TaskCreator _taskCreator;
|
||||||
private readonly Mount.Factory _mountFactory;
|
private readonly ILogger<QuestController> _logger;
|
||||||
private readonly Combat.Factory _combatFactory;
|
|
||||||
|
|
||||||
private readonly string _actionCanceledText;
|
private readonly string _actionCanceledText;
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
|
|||||||
Mount.Factory mountFactory,
|
Mount.Factory mountFactory,
|
||||||
Combat.Factory combatFactory,
|
Combat.Factory combatFactory,
|
||||||
IDataManager dataManager)
|
IDataManager dataManager)
|
||||||
: base(chatGui, logger)
|
: base(chatGui, mountFactory, combatFactory, condition, logger)
|
||||||
{
|
{
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_gameFunctions = gameFunctions;
|
_gameFunctions = gameFunctions;
|
||||||
@ -95,13 +95,13 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
|
|||||||
_gatheringController = gatheringController;
|
_gatheringController = gatheringController;
|
||||||
_questRegistry = questRegistry;
|
_questRegistry = questRegistry;
|
||||||
_keyState = keyState;
|
_keyState = keyState;
|
||||||
|
_chatGui = chatGui;
|
||||||
_condition = condition;
|
_condition = condition;
|
||||||
_toastGui = toastGui;
|
_toastGui = toastGui;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_yesAlreadyIpc = yesAlreadyIpc;
|
_yesAlreadyIpc = yesAlreadyIpc;
|
||||||
_taskCreator = taskCreator;
|
_taskCreator = taskCreator;
|
||||||
_mountFactory = mountFactory;
|
_logger = logger;
|
||||||
_combatFactory = combatFactory;
|
|
||||||
|
|
||||||
_condition.ConditionChange += OnConditionChange;
|
_condition.ConditionChange += OnConditionChange;
|
||||||
_toastGui.Toast += OnNormalToast;
|
_toastGui.Toast += OnNormalToast;
|
||||||
@ -659,6 +659,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsRunning => !_taskQueue.AllTasksComplete;
|
public bool IsRunning => !_taskQueue.AllTasksComplete;
|
||||||
|
public TaskQueue TaskQueue => _taskQueue;
|
||||||
|
|
||||||
public sealed class QuestProgress
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_toastGui.ErrorToast -= OnErrorToast;
|
_toastGui.ErrorToast -= OnErrorToast;
|
||||||
|
@ -18,6 +18,8 @@ internal abstract class AbstractDelayedTask : ITask
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual InteractionProgressContext? ProgressContext() => null;
|
||||||
|
|
||||||
public bool Start()
|
public bool Start()
|
||||||
{
|
{
|
||||||
_continueAt = DateTime.Now.Add(Delay);
|
_continueAt = DateTime.Now.Add(Delay);
|
||||||
|
@ -44,8 +44,11 @@ internal static class Mount
|
|||||||
ILogger<MountTask> logger) : ITask
|
ILogger<MountTask> logger) : ITask
|
||||||
{
|
{
|
||||||
private bool _mountTriggered;
|
private bool _mountTriggered;
|
||||||
|
private InteractionProgressContext? _progressContext;
|
||||||
private DateTime _retryAt = DateTime.MinValue;
|
private DateTime _retryAt = DateTime.MinValue;
|
||||||
|
|
||||||
|
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||||
|
|
||||||
public bool ShouldRedoOnInterrupt() => true;
|
public bool ShouldRedoOnInterrupt() => true;
|
||||||
|
|
||||||
public bool Start()
|
public bool Start()
|
||||||
@ -108,7 +111,8 @@ internal static class Mount
|
|||||||
return ETaskResult.TaskComplete;
|
return ETaskResult.TaskComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
_mountTriggered = gameFunctions.Mount();
|
_progressContext = InteractionProgressContext.FromActionUse(() => _mountTriggered = gameFunctions.Mount());
|
||||||
|
|
||||||
_retryAt = DateTime.Now.AddSeconds(5);
|
_retryAt = DateTime.Now.AddSeconds(5);
|
||||||
return ETaskResult.StillRunning;
|
return ETaskResult.StillRunning;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,20 @@ namespace Questionable.Controller.Steps;
|
|||||||
|
|
||||||
internal interface ITask
|
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 ShouldRedoOnInterrupt() => false;
|
||||||
|
|
||||||
bool Start();
|
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 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()
|
public bool Start()
|
||||||
{
|
{
|
||||||
if (!gameFunctions.IsAetherCurrentUnlocked(aetherCurrentId))
|
if (!gameFunctions.IsAetherCurrentUnlocked(aetherCurrentId))
|
||||||
{
|
{
|
||||||
logger.LogInformation("Attuning to aether current {AetherCurrentId} / {DataId}", aetherCurrentId,
|
logger.LogInformation("Attuning to aether current {AetherCurrentId} / {DataId}", aetherCurrentId,
|
||||||
dataId);
|
dataId);
|
||||||
gameFunctions.InteractWith(dataId);
|
_progressContext =
|
||||||
|
InteractionProgressContext.FromActionUseOrDefault(() => gameFunctions.InteractWith(dataId));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,12 +34,17 @@ internal static class AethernetShard
|
|||||||
GameFunctions gameFunctions,
|
GameFunctions gameFunctions,
|
||||||
ILogger<DoAttune> logger) : ITask
|
ILogger<DoAttune> logger) : ITask
|
||||||
{
|
{
|
||||||
|
private InteractionProgressContext? _progressContext;
|
||||||
|
|
||||||
|
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||||
|
|
||||||
public bool Start()
|
public bool Start()
|
||||||
{
|
{
|
||||||
if (!aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
|
if (!aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
|
||||||
{
|
{
|
||||||
logger.LogInformation("Attuning to aethernet shard {AethernetShard}", 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,12 +33,18 @@ internal static class Aetheryte
|
|||||||
GameFunctions gameFunctions,
|
GameFunctions gameFunctions,
|
||||||
ILogger<DoAttune> logger) : ITask
|
ILogger<DoAttune> logger) : ITask
|
||||||
{
|
{
|
||||||
|
private InteractionProgressContext? _progressContext;
|
||||||
|
|
||||||
|
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||||
|
|
||||||
public bool Start()
|
public bool Start()
|
||||||
{
|
{
|
||||||
if (!aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
|
if (!aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
|
||||||
{
|
{
|
||||||
logger.LogInformation("Attuning to aetheryte {Aetheryte}", aetheryteLocation);
|
logger.LogInformation("Attuning to aetheryte {Aetheryte}", aetheryteLocation);
|
||||||
gameFunctions.InteractWith((uint)aetheryteLocation);
|
_progressContext =
|
||||||
|
InteractionProgressContext.FromActionUseOrDefault(() =>
|
||||||
|
gameFunctions.InteractWith((uint)aetheryteLocation));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,10 +78,10 @@ internal static class Interact
|
|||||||
GameFunctions gameFunctions,
|
GameFunctions gameFunctions,
|
||||||
ICondition condition,
|
ICondition condition,
|
||||||
ILogger<DoInteract> logger)
|
ILogger<DoInteract> logger)
|
||||||
: ITask, IConditionChangeAware
|
: ITask
|
||||||
{
|
{
|
||||||
private bool _needsUnmount;
|
private bool _needsUnmount;
|
||||||
private EInteractionState _interactionState = EInteractionState.None;
|
private InteractionProgressContext? _progressContext;
|
||||||
private DateTime _continueAt = DateTime.MinValue;
|
private DateTime _continueAt = DateTime.MinValue;
|
||||||
|
|
||||||
public Quest? Quest => quest;
|
public Quest? Quest => quest;
|
||||||
@ -92,6 +92,8 @@ internal static class Interact
|
|||||||
set => interactionType = value;
|
set => interactionType = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||||
|
|
||||||
public bool Start()
|
public bool Start()
|
||||||
{
|
{
|
||||||
IGameObject? gameObject = gameFunctions.FindObjectByDataId(dataId);
|
IGameObject? gameObject = gameFunctions.FindObjectByDataId(dataId);
|
||||||
@ -121,9 +123,8 @@ internal static class Interact
|
|||||||
|
|
||||||
if (gameObject.IsTargetable && HasAnyMarker(gameObject))
|
if (gameObject.IsTargetable && HasAnyMarker(gameObject))
|
||||||
{
|
{
|
||||||
_interactionState = gameFunctions.InteractWith(gameObject)
|
_progressContext =
|
||||||
? EInteractionState.InteractionTriggered
|
InteractionProgressContext.FromActionUseOrDefault(() => gameFunctions.InteractWith(gameObject));
|
||||||
: EInteractionState.None;
|
|
||||||
_continueAt = DateTime.Now.AddSeconds(0.5);
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -159,7 +160,7 @@ internal static class Interact
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_interactionState == EInteractionState.InteractionConfirmed)
|
if (_progressContext != null && _progressContext.WasSuccessful())
|
||||||
return ETaskResult.TaskComplete;
|
return ETaskResult.TaskComplete;
|
||||||
|
|
||||||
if (interactionType == EInteractionType.Gather && condition[ConditionFlag.Gathering])
|
if (interactionType == EInteractionType.Gather && condition[ConditionFlag.Gathering])
|
||||||
@ -170,9 +171,8 @@ internal static class Interact
|
|||||||
if (gameObject == null || !gameObject.IsTargetable || !HasAnyMarker(gameObject))
|
if (gameObject == null || !gameObject.IsTargetable || !HasAnyMarker(gameObject))
|
||||||
return ETaskResult.StillRunning;
|
return ETaskResult.StillRunning;
|
||||||
|
|
||||||
_interactionState = gameFunctions.InteractWith(gameObject)
|
_progressContext =
|
||||||
? EInteractionState.InteractionTriggered
|
InteractionProgressContext.FromActionUseOrDefault(() => gameFunctions.InteractWith(gameObject));
|
||||||
: EInteractionState.None;
|
|
||||||
_continueAt = DateTime.Now.AddSeconds(0.5);
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
||||||
return ETaskResult.StillRunning;
|
return ETaskResult.StillRunning;
|
||||||
}
|
}
|
||||||
@ -187,33 +187,5 @@ internal static class Interact
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"Interact({dataId})";
|
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 bool _usedItem;
|
||||||
private DateTime _continueAt;
|
private DateTime _continueAt;
|
||||||
private int _itemCount;
|
private int _itemCount;
|
||||||
|
private InteractionProgressContext? _progressContext;
|
||||||
|
|
||||||
|
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||||
|
|
||||||
public ElementId? QuestId => questId;
|
public ElementId? QuestId => questId;
|
||||||
public uint ItemId => itemId;
|
public uint ItemId => itemId;
|
||||||
@ -178,7 +181,7 @@ internal static class UseItem
|
|||||||
if (_itemCount == 0)
|
if (_itemCount == 0)
|
||||||
throw new TaskException($"Don't have any {ItemId} in inventory (checks NQ only)");
|
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());
|
_continueAt = DateTime.Now.Add(GetRetryDelay());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -221,7 +224,7 @@ internal static class UseItem
|
|||||||
|
|
||||||
if (!_usedItem)
|
if (!_usedItem)
|
||||||
{
|
{
|
||||||
_usedItem = UseItem();
|
_progressContext = InteractionProgressContext.FromActionUseOrDefault(() => _usedItem = UseItem());
|
||||||
_continueAt = DateTime.Now.Add(GetRetryDelay());
|
_continueAt = DateTime.Now.Add(GetRetryDelay());
|
||||||
return ETaskResult.StillRunning;
|
return ETaskResult.StillRunning;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,9 @@ internal static class AetheryteShortcut
|
|||||||
{
|
{
|
||||||
private bool _teleported;
|
private bool _teleported;
|
||||||
private DateTime _continueAt;
|
private DateTime _continueAt;
|
||||||
|
private InteractionProgressContext? _progressContext;
|
||||||
|
|
||||||
|
public InteractionProgressContext? ProgressContext() => _progressContext;
|
||||||
|
|
||||||
public bool Start() => !ShouldSkipTeleport();
|
public bool Start() => !ShouldSkipTeleport();
|
||||||
|
|
||||||
@ -200,7 +203,12 @@ internal static class AetheryteShortcut
|
|||||||
chatGui.PrintError($"[Questionable] Aetheryte {targetAetheryte} is not unlocked.");
|
chatGui.PrintError($"[Questionable] Aetheryte {targetAetheryte} is not unlocked.");
|
||||||
throw new TaskException("Aetheryte is not unlocked");
|
throw new TaskException("Aetheryte is not unlocked");
|
||||||
}
|
}
|
||||||
else if (aetheryteFunctions.TeleportAetheryte(targetAetheryte))
|
else
|
||||||
|
{
|
||||||
|
_progressContext =
|
||||||
|
InteractionProgressContext.FromActionUseOrDefault(() => aetheryteFunctions.TeleportAetheryte(targetAetheryte));
|
||||||
|
logger.LogInformation("Ctx = {C}", _progressContext);
|
||||||
|
if (_progressContext != null)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Travelling via aetheryte...");
|
logger.LogInformation("Travelling via aetheryte...");
|
||||||
return true;
|
return true;
|
||||||
@ -211,6 +219,7 @@ internal static class AetheryteShortcut
|
|||||||
throw new TaskException("Unable to teleport to aetheryte");
|
throw new TaskException("Unable to teleport to aetheryte");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString() => $"UseAetheryte({targetAetheryte})";
|
public override string ToString() => $"UseAetheryte({targetAetheryte})";
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,8 @@ internal static class MoveTo
|
|||||||
_canRestart = moveParams.RestartNavigation;
|
_canRestart = moveParams.RestartNavigation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InteractionProgressContext? ProgressContext() => _mountTask?.ProgressContext();
|
||||||
|
|
||||||
public bool ShouldRedoOnInterrupt() => true;
|
public bool ShouldRedoOnInterrupt() => true;
|
||||||
|
|
||||||
public bool Start()
|
public bool Start()
|
||||||
|
@ -6,12 +6,12 @@ namespace Questionable.Controller.Steps;
|
|||||||
|
|
||||||
internal sealed class TaskQueue
|
internal sealed class TaskQueue
|
||||||
{
|
{
|
||||||
|
private readonly List<ITask> _completedTasks = [];
|
||||||
private readonly List<ITask> _tasks = [];
|
private readonly List<ITask> _tasks = [];
|
||||||
private int _currentTaskIndex;
|
|
||||||
public ITask? CurrentTask { get; set; }
|
public ITask? CurrentTask { get; set; }
|
||||||
|
|
||||||
public IEnumerable<ITask> RemainingTasks => _tasks.Skip(_currentTaskIndex);
|
public IEnumerable<ITask> RemainingTasks => _tasks;
|
||||||
public bool AllTasksComplete => CurrentTask == null && _currentTaskIndex >= _tasks.Count;
|
public bool AllTasksComplete => CurrentTask == null && _tasks.Count == 0;
|
||||||
|
|
||||||
public void Enqueue(ITask task)
|
public void Enqueue(ITask task)
|
||||||
{
|
{
|
||||||
@ -20,48 +20,40 @@ internal sealed class TaskQueue
|
|||||||
|
|
||||||
public bool TryDequeue([NotNullWhen(true)] out ITask? task)
|
public bool TryDequeue([NotNullWhen(true)] out ITask? task)
|
||||||
{
|
{
|
||||||
if (_currentTaskIndex >= _tasks.Count)
|
task = _tasks.FirstOrDefault();
|
||||||
{
|
if (task == null)
|
||||||
task = null;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
task = _tasks[_currentTaskIndex];
|
|
||||||
if (task.ShouldRedoOnInterrupt())
|
if (task.ShouldRedoOnInterrupt())
|
||||||
_currentTaskIndex++;
|
_completedTasks.Add(task);
|
||||||
else
|
|
||||||
_tasks.RemoveAt(0);
|
_tasks.RemoveAt(0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryPeek([NotNullWhen(true)] out ITask? task)
|
public bool TryPeek([NotNullWhen(true)] out ITask? task)
|
||||||
{
|
{
|
||||||
if (_currentTaskIndex >= _tasks.Count)
|
task = _tasks.FirstOrDefault();
|
||||||
{
|
return task != null;
|
||||||
task = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
task = _tasks[_currentTaskIndex];
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
_tasks.Clear();
|
_tasks.Clear();
|
||||||
_currentTaskIndex = 0;
|
_completedTasks.Clear();
|
||||||
CurrentTask = null;
|
CurrentTask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InterruptWith(List<ITask> interruptionTasks)
|
public void InterruptWith(List<ITask> interruptionTasks)
|
||||||
{
|
{
|
||||||
if (CurrentTask != null)
|
List<ITask?> newTasks =
|
||||||
{
|
[
|
||||||
_tasks.Insert(0, CurrentTask);
|
..interruptionTasks,
|
||||||
CurrentTask = null;
|
.._completedTasks.Where(x => !ReferenceEquals(x, CurrentTask)).ToList(),
|
||||||
_currentTaskIndex = 0;
|
CurrentTask,
|
||||||
}
|
.._tasks
|
||||||
|
];
|
||||||
_tasks.InsertRange(0, interruptionTasks);
|
Reset();
|
||||||
|
_tasks.AddRange(newTasks.Where(x => x != null).Cast<ITask>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user