1309 lines
37 KiB
C#
1309 lines
37 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Dalamud.Game.ClientState.Conditions;
|
|
using Dalamud.Game.ClientState.Keys;
|
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
|
using Dalamud.Game.Gui.Toast;
|
|
using Dalamud.Game.Text.SeStringHandling;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Controller.Steps;
|
|
using Questionable.Controller.Steps.Interactions;
|
|
using Questionable.Controller.Steps.Shared;
|
|
using Questionable.Data;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
using Questionable.Windows.ConfigComponents;
|
|
|
|
namespace Questionable.Controller;
|
|
|
|
internal sealed class QuestController : MiniTaskController<QuestController>
|
|
{
|
|
public delegate void AutomationTypeChangedEventHandler(object sender, EAutomationType e);
|
|
|
|
public sealed class QuestProgress
|
|
{
|
|
public Quest Quest { get; }
|
|
|
|
public byte Sequence { get; private set; }
|
|
|
|
public int Step { get; private set; }
|
|
|
|
public StepProgress StepProgress { get; private set; } = new StepProgress(DateTime.Now);
|
|
|
|
public QuestProgress(Quest quest, byte sequence = 0, int step = 0)
|
|
{
|
|
Quest = quest;
|
|
SetSequence(sequence, step);
|
|
}
|
|
|
|
public void SetSequence(byte sequence, int step = 0)
|
|
{
|
|
Sequence = sequence;
|
|
SetStep(step);
|
|
}
|
|
|
|
public void SetStep(int step)
|
|
{
|
|
Step = step;
|
|
StepProgress = new StepProgress(DateTime.Now);
|
|
}
|
|
|
|
public void IncreasePointMenuCounter()
|
|
{
|
|
StepProgress = StepProgress with
|
|
{
|
|
PointMenuCounter = StepProgress.PointMenuCounter + 1
|
|
};
|
|
}
|
|
}
|
|
|
|
public sealed record StepProgress(DateTime StartedAt, int PointMenuCounter = 0);
|
|
|
|
public enum ECurrentQuestType
|
|
{
|
|
Normal,
|
|
Next,
|
|
Gathering,
|
|
Simulated
|
|
}
|
|
|
|
public enum EAutomationType
|
|
{
|
|
Manual,
|
|
Automatic,
|
|
GatheringOnly,
|
|
SingleQuestA,
|
|
SingleQuestB
|
|
}
|
|
|
|
private readonly IClientState _clientState;
|
|
|
|
private readonly GameFunctions _gameFunctions;
|
|
|
|
private readonly QuestFunctions _questFunctions;
|
|
|
|
private readonly MovementController _movementController;
|
|
|
|
private readonly CombatController _combatController;
|
|
|
|
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 TaskCreator _taskCreator;
|
|
|
|
private readonly SinglePlayerDutyConfigComponent _singlePlayerDutyConfigComponent;
|
|
|
|
private readonly ILogger<QuestController> _logger;
|
|
|
|
private readonly object _progressLock = new object();
|
|
|
|
private QuestProgress? _startedQuest;
|
|
|
|
private QuestProgress? _nextQuest;
|
|
|
|
private QuestProgress? _simulatedQuest;
|
|
|
|
private QuestProgress? _gatheringQuest;
|
|
|
|
private QuestProgress? _pendingQuest;
|
|
|
|
private EAutomationType _automationType;
|
|
|
|
private DateTime _safeAnimationEnd = DateTime.MinValue;
|
|
|
|
private DateTime _lastTaskUpdate = DateTime.Now;
|
|
|
|
private Vector3 _lastPlayerPosition = Vector3.Zero;
|
|
|
|
private int _lastQuestStep = -1;
|
|
|
|
private byte _lastQuestSequence = byte.MaxValue;
|
|
|
|
private ElementId? _lastQuestId;
|
|
|
|
private DateTime _lastProgressUpdate = DateTime.Now;
|
|
|
|
private DateTime _lastAutoRefresh = DateTime.MinValue;
|
|
|
|
private const char ClipboardSeparator = ';';
|
|
|
|
public EAutomationType AutomationType
|
|
{
|
|
get
|
|
{
|
|
return _automationType;
|
|
}
|
|
set
|
|
{
|
|
if (value != _automationType)
|
|
{
|
|
_logger.LogInformation("Setting automation type to {NewAutomationType} (previous: {OldAutomationType})", value, _automationType);
|
|
_automationType = value;
|
|
this.AutomationTypeChanged?.Invoke(this, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
public (QuestProgress Progress, ECurrentQuestType Type)? CurrentQuestDetails
|
|
{
|
|
get
|
|
{
|
|
if (_simulatedQuest != null)
|
|
{
|
|
return (_simulatedQuest, ECurrentQuestType.Simulated);
|
|
}
|
|
if (_nextQuest != null && _questFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.Id))
|
|
{
|
|
return (_nextQuest, ECurrentQuestType.Next);
|
|
}
|
|
if (_gatheringQuest != null)
|
|
{
|
|
return (_gatheringQuest, ECurrentQuestType.Gathering);
|
|
}
|
|
if (_startedQuest != null)
|
|
{
|
|
return (_startedQuest, ECurrentQuestType.Normal);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public QuestProgress? CurrentQuest => CurrentQuestDetails?.Progress;
|
|
|
|
public QuestProgress? StartedQuest => _startedQuest;
|
|
|
|
public QuestProgress? SimulatedQuest => _simulatedQuest;
|
|
|
|
public QuestProgress? NextQuest => _nextQuest;
|
|
|
|
public QuestProgress? GatheringQuest => _gatheringQuest;
|
|
|
|
public QuestProgress? PendingQuest => _pendingQuest;
|
|
|
|
public List<Quest> ManualPriorityQuests { get; } = new List<Quest>();
|
|
|
|
public string? DebugState { get; private set; }
|
|
|
|
public bool IsQuestWindowOpen => IsQuestWindowOpenFunction?.Invoke() ?? true;
|
|
|
|
public Func<bool>? IsQuestWindowOpenFunction { private get; set; } = () => true;
|
|
|
|
public bool IsRunning => !_taskQueue.AllTasksComplete;
|
|
|
|
public TaskQueue TaskQueue => _taskQueue;
|
|
|
|
public string? CurrentTaskState
|
|
{
|
|
get
|
|
{
|
|
if (_taskQueue.CurrentTaskExecutor is IDebugStateProvider debugStateProvider)
|
|
{
|
|
return debugStateProvider.GetDebugState();
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public event AutomationTypeChangedEventHandler? AutomationTypeChanged;
|
|
|
|
public QuestController(IClientState clientState, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, ILogger<QuestController> logger, QuestRegistry questRegistry, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent)
|
|
: base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
|
|
{
|
|
_clientState = clientState;
|
|
_gameFunctions = gameFunctions;
|
|
_questFunctions = questFunctions;
|
|
_movementController = movementController;
|
|
_combatController = combatController;
|
|
_gatheringController = gatheringController;
|
|
_questRegistry = questRegistry;
|
|
_keyState = keyState;
|
|
_chatGui = chatGui;
|
|
_condition = condition;
|
|
_toastGui = toastGui;
|
|
_configuration = configuration;
|
|
_taskCreator = taskCreator;
|
|
_singlePlayerDutyConfigComponent = singlePlayerDutyConfigComponent;
|
|
_logger = logger;
|
|
_condition.ConditionChange += OnConditionChange;
|
|
_toastGui.Toast += OnNormalToast;
|
|
_toastGui.ErrorToast += base.OnErrorToast;
|
|
}
|
|
|
|
public void Reload()
|
|
{
|
|
lock (_progressLock)
|
|
{
|
|
_logger.LogInformation("Reload, resetting curent quest progress");
|
|
ResetInternalState();
|
|
ResetAutoRefreshState();
|
|
_questRegistry.Reload();
|
|
_singlePlayerDutyConfigComponent.Reload();
|
|
}
|
|
}
|
|
|
|
private void ResetInternalState()
|
|
{
|
|
_startedQuest = null;
|
|
_nextQuest = null;
|
|
_gatheringQuest = null;
|
|
_pendingQuest = null;
|
|
_simulatedQuest = null;
|
|
_safeAnimationEnd = DateTime.MinValue;
|
|
DebugState = null;
|
|
}
|
|
|
|
private void ResetAutoRefreshState()
|
|
{
|
|
_lastPlayerPosition = Vector3.Zero;
|
|
_lastQuestStep = -1;
|
|
_lastQuestSequence = byte.MaxValue;
|
|
_lastQuestId = null;
|
|
_lastProgressUpdate = DateTime.Now;
|
|
_lastAutoRefresh = DateTime.Now;
|
|
}
|
|
|
|
public unsafe void Update()
|
|
{
|
|
ActionManager* ptr = ActionManager.Instance();
|
|
if (ptr != null)
|
|
{
|
|
float num = Math.Max(ptr->AnimationLock, (ptr->CastTimeElapsed > 0f) ? (ptr->CastTimeTotal - ptr->CastTimeElapsed) : 0f);
|
|
if (num > 0f)
|
|
{
|
|
_safeAnimationEnd = DateTime.Now.AddSeconds(1f + num);
|
|
}
|
|
}
|
|
UpdateCurrentQuest();
|
|
if (AutomationType == EAutomationType.Manual && !IsRunning && !IsQuestWindowOpen)
|
|
{
|
|
return;
|
|
}
|
|
if (!_clientState.IsLoggedIn)
|
|
{
|
|
StopAllDueToConditionFailed("Logged out");
|
|
}
|
|
if (_condition[ConditionFlag.Unconscious])
|
|
{
|
|
if ((!_condition[ConditionFlag.Unconscious] || !_condition[ConditionFlag.SufferingStatusAffliction63] || _clientState.TerritoryType != 1052) && !(_taskQueue.CurrentTaskExecutor is Duty.WaitAutoDutyExecutor) && !_taskQueue.AllTasksComplete)
|
|
{
|
|
StopAllDueToConditionFailed("HP = 0");
|
|
}
|
|
}
|
|
else if (_configuration.General.UseEscToCancelQuesting && _keyState[VirtualKey.ESCAPE] && !_taskQueue.AllTasksComplete)
|
|
{
|
|
StopAllDueToConditionFailed("ESC pressed");
|
|
}
|
|
if (_configuration.Stop.Enabled && _configuration.Stop.LevelToStopAfter && _clientState.LocalPlayer != null)
|
|
{
|
|
int level = _clientState.LocalPlayer.Level;
|
|
if (level >= _configuration.Stop.TargetLevel && IsRunning)
|
|
{
|
|
_logger.LogInformation("Reached level stop condition (level: {CurrentLevel}, target: {TargetLevel})", level, _configuration.Stop.TargetLevel);
|
|
_chatGui.Print($"Reached or exceeded target level {_configuration.Stop.TargetLevel}.", "Questionable", 576);
|
|
Stop($"Level stop condition reached [{level}]");
|
|
return;
|
|
}
|
|
}
|
|
if (_configuration.Stop.Enabled && _configuration.Stop.SequenceToStopAfter && CurrentQuest != null)
|
|
{
|
|
int sequence = CurrentQuest.Sequence;
|
|
if (sequence >= _configuration.Stop.TargetSequence && IsRunning)
|
|
{
|
|
_logger.LogInformation("Reached quest sequence stop condition (sequence: {CurrentSequence}, target: {TargetSequence})", sequence, _configuration.Stop.TargetSequence);
|
|
_chatGui.Print($"Quest sequence {sequence} reached target sequence {_configuration.Stop.TargetSequence}.", "Questionable", 576);
|
|
Stop($"Sequence stop condition reached [{sequence}]");
|
|
return;
|
|
}
|
|
}
|
|
bool flag = AutomationType == EAutomationType.Automatic && (_taskQueue.AllTasksComplete || _taskQueue.CurrentTaskExecutor?.CurrentTask is WaitAtEnd.WaitQuestAccepted);
|
|
bool flag2;
|
|
if (flag)
|
|
{
|
|
QuestProgress currentQuest = CurrentQuest;
|
|
if (currentQuest != null && currentQuest.Sequence == 0)
|
|
{
|
|
int step = currentQuest.Step;
|
|
if (step == 0 || step == 255)
|
|
{
|
|
flag2 = true;
|
|
goto IL_0422;
|
|
}
|
|
}
|
|
flag2 = false;
|
|
goto IL_0422;
|
|
}
|
|
goto IL_0426;
|
|
IL_0426:
|
|
if (flag && DateTime.Now >= CurrentQuest.StepProgress.StartedAt.AddSeconds(15.0))
|
|
{
|
|
lock (_progressLock)
|
|
{
|
|
_logger.LogWarning("Quest accept apparently didn't work out, resetting progress");
|
|
CurrentQuest.SetStep(0);
|
|
}
|
|
ExecuteNextStep();
|
|
}
|
|
else
|
|
{
|
|
CheckAutoRefreshCondition();
|
|
UpdateCurrentTask();
|
|
}
|
|
return;
|
|
IL_0422:
|
|
flag = flag2;
|
|
goto IL_0426;
|
|
}
|
|
|
|
private void CheckAutoRefreshCondition()
|
|
{
|
|
if (!ShouldCheckAutoRefresh() || DateTime.Now < _lastAutoRefresh.AddSeconds(5.0))
|
|
{
|
|
return;
|
|
}
|
|
if (ShouldPreventAutoRefresh())
|
|
{
|
|
_lastProgressUpdate = DateTime.Now;
|
|
return;
|
|
}
|
|
IPlayerCharacter localPlayer = _clientState.LocalPlayer;
|
|
if (localPlayer == null)
|
|
{
|
|
return;
|
|
}
|
|
Vector3 position = localPlayer.Position;
|
|
if (CurrentQuest == null)
|
|
{
|
|
return;
|
|
}
|
|
ElementId id = CurrentQuest.Quest.Id;
|
|
byte sequence = CurrentQuest.Sequence;
|
|
int step = CurrentQuest.Step;
|
|
if (Vector3.Distance(position, _lastPlayerPosition) > 0.5f || !id.Equals(_lastQuestId) || sequence != _lastQuestSequence || step != _lastQuestStep)
|
|
{
|
|
_lastPlayerPosition = position;
|
|
_lastQuestId = id;
|
|
_lastQuestSequence = sequence;
|
|
_lastQuestStep = step;
|
|
_lastProgressUpdate = DateTime.Now;
|
|
return;
|
|
}
|
|
TimeSpan timeSpan = DateTime.Now - _lastProgressUpdate;
|
|
TimeSpan timeSpan2 = TimeSpan.FromSeconds(_configuration.General.AutoStepRefreshDelaySeconds);
|
|
if (timeSpan >= timeSpan2)
|
|
{
|
|
_logger.LogInformation("Automatically refreshing quest step as no progress detected for {TimeSinceProgress:F1} seconds (quest: {QuestId}, sequence: {Sequence}, step: {Step})", timeSpan.TotalSeconds, id, sequence, step);
|
|
_chatGui.Print($"Automatically refreshing quest step as no progress detected for {timeSpan.TotalSeconds:F0} seconds.", "Questionable", 576);
|
|
ClearTasksInternal();
|
|
Reload();
|
|
_lastAutoRefresh = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
private bool ShouldCheckAutoRefresh()
|
|
{
|
|
if (_configuration.General.AutoStepRefreshEnabled && AutomationType == EAutomationType.Automatic && IsRunning && CurrentQuest != null && _clientState.IsLoggedIn)
|
|
{
|
|
return _clientState.LocalPlayer != null;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool ShouldPreventAutoRefresh()
|
|
{
|
|
if (HasWaitingTasks())
|
|
{
|
|
return true;
|
|
}
|
|
if (HasManualInterventionStep())
|
|
{
|
|
return true;
|
|
}
|
|
if (HasSystemConditionsPreventingRefresh())
|
|
{
|
|
return true;
|
|
}
|
|
if (HasConfigurationConditionsPreventingRefresh())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool HasWaitingTasks()
|
|
{
|
|
ITask task = _taskQueue.CurrentTaskExecutor?.CurrentTask;
|
|
if (task is WaitAtEnd.WaitObjectAtPosition || task is WaitAtEnd.WaitForCompletionFlags)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool HasManualInterventionStep()
|
|
{
|
|
switch (GetNextStep().Step?.InteractionType)
|
|
{
|
|
case EInteractionType.WaitForManualProgress:
|
|
case EInteractionType.Duty:
|
|
case EInteractionType.SinglePlayerDuty:
|
|
case EInteractionType.Snipe:
|
|
case EInteractionType.Instruction:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private bool HasSystemConditionsPreventingRefresh()
|
|
{
|
|
if (_movementController.IsNavmeshReady && !_condition[ConditionFlag.InCombat] && !_condition[ConditionFlag.Unconscious] && !_condition[ConditionFlag.BoundByDuty] && !_condition[ConditionFlag.InDeepDungeon] && !_condition[ConditionFlag.WatchingCutscene] && !_condition[ConditionFlag.WatchingCutscene78] && !_condition[ConditionFlag.BetweenAreas] && !_condition[ConditionFlag.BetweenAreas51] && !_gameFunctions.IsOccupied() && !_movementController.IsPathfinding && !_movementController.IsPathRunning)
|
|
{
|
|
return DateTime.Now < _safeAnimationEnd;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool HasConfigurationConditionsPreventingRefresh()
|
|
{
|
|
if (_configuration.Advanced.PreventQuestCompletion)
|
|
{
|
|
return CurrentQuest?.Sequence == byte.MaxValue;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void UpdateCurrentQuest()
|
|
{
|
|
lock (_progressLock)
|
|
{
|
|
DebugState = null;
|
|
if (!_clientState.IsLoggedIn)
|
|
{
|
|
ResetInternalState();
|
|
DebugState = "Not logged in";
|
|
return;
|
|
}
|
|
if (_pendingQuest != null)
|
|
{
|
|
if (!_questFunctions.IsQuestAccepted(_pendingQuest.Quest.Id))
|
|
{
|
|
DebugState = $"Waiting for Leve {_pendingQuest.Quest.Id}";
|
|
return;
|
|
}
|
|
_startedQuest = _pendingQuest;
|
|
_pendingQuest = null;
|
|
CheckNextTasks("Pending quest accepted");
|
|
}
|
|
if (_startedQuest != null && !_questFunctions.IsQuestAccepted(_startedQuest.Quest.Id))
|
|
{
|
|
if (_startedQuest.Quest.Info.IsRepeatable)
|
|
{
|
|
_logger.LogInformation("Repeatable quest {QuestId} is no longer accepted, clearing started quest", _startedQuest.Quest.Id);
|
|
}
|
|
else if (!_questFunctions.IsQuestComplete(_startedQuest.Quest.Id))
|
|
{
|
|
_logger.LogInformation("Quest {QuestId} was abandoned, clearing started quest", _startedQuest.Quest.Id);
|
|
(ElementId, byte)? tuple = (from x in ManualPriorityQuests
|
|
where _questFunctions.IsReadyToAcceptQuest(x.Id)
|
|
select ((ElementId Id, byte))(Id: x.Id, 0)).FirstOrDefault();
|
|
if (tuple.HasValue)
|
|
{
|
|
(ElementId, byte) valueOrDefault = tuple.GetValueOrDefault();
|
|
if ((object)valueOrDefault.Item1 != null && _questRegistry.TryGetQuest(valueOrDefault.Item1, out Quest quest))
|
|
{
|
|
_logger.LogInformation("Setting priority quest {QuestId} as next quest", valueOrDefault.Item1);
|
|
SetNextQuest(quest);
|
|
}
|
|
}
|
|
_startedQuest = null;
|
|
Stop("Quest abandoned");
|
|
return;
|
|
}
|
|
}
|
|
if (_simulatedQuest == null && _nextQuest != null && !((!_nextQuest.Quest.Info.IsRepeatable) ? (!_questFunctions.IsQuestAcceptedOrComplete(_nextQuest.Quest.Id)) : (!_questFunctions.IsQuestAccepted(_nextQuest.Quest.Id))))
|
|
{
|
|
_logger.LogInformation("Next quest {QuestId} accepted or completed", _nextQuest.Quest.Id);
|
|
if (AutomationType == EAutomationType.SingleQuestA)
|
|
{
|
|
_startedQuest = _nextQuest;
|
|
AutomationType = EAutomationType.SingleQuestB;
|
|
}
|
|
else if (_questFunctions.IsQuestAccepted(_nextQuest.Quest.Id))
|
|
{
|
|
QuestProgressInfo questProgressInfo = _questFunctions.GetQuestProgressInfo(_nextQuest.Quest.Id);
|
|
if (questProgressInfo != null)
|
|
{
|
|
_startedQuest = new QuestProgress(_nextQuest.Quest, questProgressInfo.Sequence);
|
|
_logger.LogInformation("Moving accepted next quest to started quest (sequence: {Sequence})", questProgressInfo.Sequence);
|
|
_nextQuest = null;
|
|
CheckNextTasks("Next quest already accepted");
|
|
return;
|
|
}
|
|
_logger.LogWarning("Could not get quest progress info for accepted quest {QuestId}", _nextQuest.Quest.Id);
|
|
}
|
|
_logger.LogDebug("Started: {StartedQuest}", _startedQuest?.Quest.Id);
|
|
_nextQuest = null;
|
|
}
|
|
byte b;
|
|
QuestProgress questProgress;
|
|
ElementId elementId;
|
|
MainScenarioQuestState mainScenarioQuestState;
|
|
if (_simulatedQuest != null)
|
|
{
|
|
b = _simulatedQuest.Sequence;
|
|
questProgress = _simulatedQuest;
|
|
}
|
|
else if (_nextQuest != null)
|
|
{
|
|
questProgress = _nextQuest;
|
|
b = _nextQuest.Sequence;
|
|
if (_questFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.Id) && _nextQuest.Step == 0 && _taskQueue.AllTasksComplete && AutomationType == EAutomationType.Automatic)
|
|
{
|
|
ExecuteNextStep();
|
|
}
|
|
}
|
|
else if (_gatheringQuest != null)
|
|
{
|
|
questProgress = _gatheringQuest;
|
|
b = _gatheringQuest.Sequence;
|
|
if (_gatheringQuest.Step == 0 && _taskQueue.AllTasksComplete && AutomationType == EAutomationType.Automatic)
|
|
{
|
|
ExecuteNextStep();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ElementId CurrentQuest;
|
|
byte Sequence;
|
|
MainScenarioQuestState State;
|
|
if (_startedQuest == null)
|
|
{
|
|
_questFunctions.GetCurrentQuest(AutomationType != EAutomationType.SingleQuestB).Deconstruct(out CurrentQuest, out Sequence, out State);
|
|
elementId = CurrentQuest;
|
|
b = Sequence;
|
|
mainScenarioQuestState = State;
|
|
(ElementId, byte)? tuple2 = (from x in ManualPriorityQuests
|
|
where _questFunctions.IsQuestAccepted(x.Id)
|
|
select (Id: x.Id, _questFunctions.GetQuestProgressInfo(x.Id)?.Sequence ?? 0)).FirstOrDefault();
|
|
if (tuple2.HasValue)
|
|
{
|
|
(ElementId, byte) valueOrDefault2 = tuple2.GetValueOrDefault();
|
|
if ((object)valueOrDefault2.Item1 != null)
|
|
{
|
|
(elementId, b) = valueOrDefault2;
|
|
goto IL_0813;
|
|
}
|
|
}
|
|
Quest quest2 = ManualPriorityQuests.FirstOrDefault((Quest x) => _questFunctions.IsReadyToAcceptQuest(x.Id));
|
|
if (quest2 != null)
|
|
{
|
|
_logger.LogInformation("Setting ready priority quest {QuestId} as next quest", quest2.Id);
|
|
SetNextQuest(quest2);
|
|
return;
|
|
}
|
|
goto IL_0813;
|
|
}
|
|
questProgress = _startedQuest;
|
|
b = _startedQuest.Sequence;
|
|
QuestProgressInfo questProgressInfo2 = _questFunctions.GetQuestProgressInfo(_startedQuest.Quest.Id);
|
|
if (questProgressInfo2 != null && questProgressInfo2.Sequence != b)
|
|
{
|
|
_logger.LogInformation("Updating started quest sequence from {OldSequence} to {NewSequence}", b, questProgressInfo2.Sequence);
|
|
b = questProgressInfo2.Sequence;
|
|
}
|
|
if (AutomationType == EAutomationType.Manual || !IsRunning)
|
|
{
|
|
_questFunctions.GetCurrentQuest(AutomationType != EAutomationType.SingleQuestB).Deconstruct(out CurrentQuest, out Sequence, out State);
|
|
ElementId elementId2 = CurrentQuest;
|
|
byte sequence = Sequence;
|
|
(ElementId, byte)? tuple4 = (from x in ManualPriorityQuests
|
|
where _questFunctions.IsQuestAccepted(x.Id)
|
|
select (Id: x.Id, _questFunctions.GetQuestProgressInfo(x.Id)?.Sequence ?? 0)).FirstOrDefault();
|
|
if (tuple4.HasValue)
|
|
{
|
|
(ElementId, byte) valueOrDefault3 = tuple4.GetValueOrDefault();
|
|
if ((object)valueOrDefault3.Item1 != null)
|
|
{
|
|
(elementId2, sequence) = valueOrDefault3;
|
|
}
|
|
}
|
|
if (elementId2 != null && elementId2.Value != 0 && _startedQuest.Quest.Id != elementId2)
|
|
{
|
|
_logger.LogInformation("Game current quest changed from {OldQuest} to {NewQuest}, updating started quest", _startedQuest.Quest.Id, elementId2);
|
|
if (_questRegistry.TryGetQuest(elementId2, out Quest quest3))
|
|
{
|
|
_logger.LogInformation("Switching to new quest: {QuestName}", quest3.Info.Name);
|
|
_startedQuest = new QuestProgress(quest3, sequence);
|
|
if (_clientState.LocalPlayer != null && _clientState.LocalPlayer.Level < quest3.Info.Level)
|
|
{
|
|
_logger.LogInformation("Stopping automation, player level ({PlayerLevel}) < quest level ({QuestLevel}", _clientState.LocalPlayer.Level, quest3.Info.Level);
|
|
Stop("Quest level too high");
|
|
}
|
|
questProgress = _startedQuest;
|
|
}
|
|
else
|
|
{
|
|
_logger.LogInformation("New quest {QuestId} not found in registry", elementId2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
goto IL_0b43;
|
|
IL_0b43:
|
|
if (questProgress == null)
|
|
{
|
|
DebugState = "No quest active";
|
|
if (!IsRunning)
|
|
{
|
|
Stop("No quest active");
|
|
}
|
|
return;
|
|
}
|
|
if (_gameFunctions.IsOccupied() && !_gameFunctions.IsOccupiedWithCustomDeliveryNpc(questProgress.Quest))
|
|
{
|
|
DebugState = "Occupied";
|
|
return;
|
|
}
|
|
if (_movementController.IsPathfinding)
|
|
{
|
|
DebugState = "Pathfinding is running";
|
|
return;
|
|
}
|
|
if (_movementController.IsPathRunning)
|
|
{
|
|
DebugState = "Path is running";
|
|
return;
|
|
}
|
|
if (DateTime.Now < _safeAnimationEnd)
|
|
{
|
|
DebugState = "Waiting for Animation";
|
|
return;
|
|
}
|
|
if (questProgress.Sequence != b)
|
|
{
|
|
questProgress.SetSequence(b);
|
|
CheckNextTasks($"New sequence {questProgress == _startedQuest}/{_questFunctions.GetCurrentQuestInternal(allowNewMsq: true)}");
|
|
}
|
|
QuestSequence questSequence = questProgress.Quest.FindSequence(questProgress.Sequence);
|
|
if (questSequence == null)
|
|
{
|
|
DebugState = $"Sequence {questProgress.Sequence} not found";
|
|
Stop("Unknown sequence");
|
|
}
|
|
else if (questProgress.Step == 255)
|
|
{
|
|
DebugState = "Step completed";
|
|
if (!_taskQueue.AllTasksComplete)
|
|
{
|
|
CheckNextTasks("Step complete");
|
|
}
|
|
}
|
|
else if (questSequence.Steps.Count > 0 && questProgress.Step >= questSequence.Steps.Count)
|
|
{
|
|
DebugState = "Step not found";
|
|
Stop("Unknown step");
|
|
}
|
|
else
|
|
{
|
|
DebugState = null;
|
|
}
|
|
return;
|
|
IL_0813:
|
|
if (elementId == null || elementId.Value == 0)
|
|
{
|
|
if (_startedQuest != null)
|
|
{
|
|
switch (mainScenarioQuestState)
|
|
{
|
|
case MainScenarioQuestState.Unavailable:
|
|
_logger.LogWarning("MSQ information not available, doing nothing");
|
|
return;
|
|
case MainScenarioQuestState.LoadingScreen:
|
|
_logger.LogWarning("On loading screen, no MSQ - doing nothing");
|
|
return;
|
|
}
|
|
_logger.LogInformation("No current quest, resetting data [CQI: {CurrrentQuestData}], [CQ: {QuestData}], [MSQ: {MsqData}]", _questFunctions.GetCurrentQuestInternal(allowNewMsq: true), _questFunctions.GetCurrentQuest(), _questFunctions.GetMainScenarioQuest());
|
|
_startedQuest = null;
|
|
Stop("Resetting current quest");
|
|
}
|
|
questProgress = null;
|
|
}
|
|
else
|
|
{
|
|
if (_startedQuest == null || _startedQuest.Quest.Id != elementId)
|
|
{
|
|
Quest quest4;
|
|
if (_configuration.Stop.Enabled && _startedQuest != null && _configuration.Stop.QuestsToStopAfter.Contains(_startedQuest.Quest.Id) && _questFunctions.IsQuestComplete(_startedQuest.Quest.Id))
|
|
{
|
|
ElementId id = _startedQuest.Quest.Id;
|
|
_logger.LogInformation("Reached stopping point (quest: {QuestId})", id);
|
|
_chatGui.Print("Completed quest '" + _startedQuest.Quest.Info.Name + "', which is configured as a stopping point.", "Questionable", 576);
|
|
_startedQuest = null;
|
|
Stop($"Stopping point [{id}] reached");
|
|
}
|
|
else if (_questRegistry.TryGetQuest(elementId, out quest4))
|
|
{
|
|
_logger.LogInformation("New quest: {QuestName}", quest4.Info.Name);
|
|
_startedQuest = new QuestProgress(quest4, b);
|
|
if (_clientState.LocalPlayer != null && _clientState.LocalPlayer.Level < quest4.Info.Level)
|
|
{
|
|
_logger.LogInformation("Stopping automation, player level ({PlayerLevel}) < quest level ({QuestLevel}", _clientState.LocalPlayer.Level, quest4.Info.Level);
|
|
Stop("Quest level too high");
|
|
return;
|
|
}
|
|
if (AutomationType == EAutomationType.SingleQuestB)
|
|
{
|
|
_logger.LogInformation("Single quest is finished");
|
|
AutomationType = EAutomationType.Manual;
|
|
}
|
|
CheckNextTasks("Different Quest");
|
|
}
|
|
else if (_startedQuest != null)
|
|
{
|
|
_logger.LogInformation("No active quest anymore? Not sure what happened...");
|
|
_startedQuest = null;
|
|
Stop("No active Quest");
|
|
}
|
|
return;
|
|
}
|
|
questProgress = _startedQuest;
|
|
}
|
|
goto IL_0b43;
|
|
}
|
|
}
|
|
|
|
public (QuestSequence? Sequence, QuestStep? Step, bool createTasks) GetNextStep()
|
|
{
|
|
if (CurrentQuest == null)
|
|
{
|
|
return (Sequence: null, Step: null, createTasks: false);
|
|
}
|
|
QuestSequence questSequence = CurrentQuest.Quest.FindSequence(CurrentQuest.Sequence);
|
|
if (questSequence == null)
|
|
{
|
|
return (Sequence: null, Step: null, createTasks: true);
|
|
}
|
|
if (questSequence.Steps.Count == 0)
|
|
{
|
|
return (Sequence: questSequence, Step: null, createTasks: true);
|
|
}
|
|
if (CurrentQuest.Step >= questSequence.Steps.Count)
|
|
{
|
|
return (Sequence: null, Step: null, createTasks: false);
|
|
}
|
|
return (Sequence: questSequence, Step: questSequence.Steps[CurrentQuest.Step], createTasks: true);
|
|
}
|
|
|
|
public void IncreaseStepCount(ElementId? questId, int? sequence, bool shouldContinue = false)
|
|
{
|
|
lock (_progressLock)
|
|
{
|
|
var (questSequence, questStep, _) = GetNextStep();
|
|
if (CurrentQuest == null || questSequence == null || questStep == null)
|
|
{
|
|
_logger.LogWarning("Unable to retrieve next quest step, not increasing step count");
|
|
return;
|
|
}
|
|
if (questId != null && CurrentQuest.Quest.Id != questId)
|
|
{
|
|
_logger.LogWarning("Ignoring 'increase step count' for different quest (expected {ExpectedQuestId}, but we are at {CurrentQuestId}", questId, CurrentQuest.Quest.Id);
|
|
return;
|
|
}
|
|
if (sequence.HasValue && questSequence.Sequence != sequence.Value)
|
|
{
|
|
_logger.LogWarning("Ignoring 'increase step count' for different sequence (expected {ExpectedSequence}, but we are at {CurrentSequence}", sequence, questSequence.Sequence);
|
|
}
|
|
_logger.LogInformation("Increasing step count from {CurrentValue}", CurrentQuest.Step);
|
|
if (CurrentQuest.Step + 1 < questSequence.Steps.Count)
|
|
{
|
|
CurrentQuest.SetStep(CurrentQuest.Step + 1);
|
|
}
|
|
else
|
|
{
|
|
CurrentQuest.SetStep(255);
|
|
}
|
|
ResetAutoRefreshState();
|
|
}
|
|
using (_logger.BeginScope("IncStepCt"))
|
|
{
|
|
if (shouldContinue && AutomationType != EAutomationType.Manual)
|
|
{
|
|
ExecuteNextStep();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ClearTasksInternal()
|
|
{
|
|
if (_taskQueue.CurrentTaskExecutor is IStoppableTaskExecutor stoppableTaskExecutor)
|
|
{
|
|
stoppableTaskExecutor.StopNow();
|
|
}
|
|
_taskQueue.Reset();
|
|
_combatController.Stop("ClearTasksInternal");
|
|
_gatheringController.Stop("ClearTasksInternal");
|
|
}
|
|
|
|
public override void Stop(string label)
|
|
{
|
|
using (_logger.BeginScope("Stop/" + label))
|
|
{
|
|
if (IsRunning || AutomationType != EAutomationType.Manual)
|
|
{
|
|
ClearTasksInternal();
|
|
_logger.LogInformation("Stopping automatic questing");
|
|
AutomationType = EAutomationType.Manual;
|
|
_nextQuest = null;
|
|
_gatheringQuest = null;
|
|
_lastTaskUpdate = DateTime.Now;
|
|
ResetAutoRefreshState();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void StopAllDueToConditionFailed(string label)
|
|
{
|
|
Stop(label);
|
|
_movementController.Stop();
|
|
_combatController.Stop(label);
|
|
_gatheringController.Stop(label);
|
|
}
|
|
|
|
private void CheckNextTasks(string label)
|
|
{
|
|
EAutomationType automationType = AutomationType;
|
|
if ((automationType == EAutomationType.Automatic || (uint)(automationType - 3) <= 1u) ? true : false)
|
|
{
|
|
using (_logger.BeginScope(label))
|
|
{
|
|
ClearTasksInternal();
|
|
int? num = CurrentQuest?.Step;
|
|
if (num.HasValue)
|
|
{
|
|
int valueOrDefault = num.GetValueOrDefault();
|
|
if (valueOrDefault >= 0 && valueOrDefault < 255)
|
|
{
|
|
ExecuteNextStep();
|
|
goto IL_008e;
|
|
}
|
|
}
|
|
_logger.LogInformation("Couldn't execute next step during Stop() call");
|
|
goto IL_008e;
|
|
IL_008e:
|
|
_lastTaskUpdate = DateTime.Now;
|
|
ResetAutoRefreshState();
|
|
return;
|
|
}
|
|
}
|
|
Stop(label);
|
|
}
|
|
|
|
public void SimulateQuest(Quest? quest, byte sequence, int step)
|
|
{
|
|
_logger.LogInformation("SimulateQuest: {QuestId}", quest?.Id);
|
|
if (quest != null)
|
|
{
|
|
_simulatedQuest = new QuestProgress(quest, sequence, step);
|
|
}
|
|
else
|
|
{
|
|
_simulatedQuest = null;
|
|
}
|
|
}
|
|
|
|
public void SetNextQuest(Quest? quest)
|
|
{
|
|
_logger.LogInformation("NextQuest: {QuestId}", quest?.Id);
|
|
if (quest != null)
|
|
{
|
|
_nextQuest = new QuestProgress(quest, 0);
|
|
}
|
|
else
|
|
{
|
|
_nextQuest = null;
|
|
}
|
|
}
|
|
|
|
public void SetStartedQuest(Quest quest, byte sequence = 0)
|
|
{
|
|
_logger.LogInformation("Setting started quest: {QuestId}", quest.Id);
|
|
_startedQuest = new QuestProgress(quest, sequence);
|
|
_nextQuest = null;
|
|
}
|
|
|
|
public void SetGatheringQuest(Quest? quest)
|
|
{
|
|
_logger.LogInformation("GatheringQuest: {QuestId}", quest?.Id);
|
|
if (quest != null)
|
|
{
|
|
_gatheringQuest = new QuestProgress(quest, 0);
|
|
}
|
|
else
|
|
{
|
|
_gatheringQuest = null;
|
|
}
|
|
}
|
|
|
|
public void SetPendingQuest(QuestProgress? quest)
|
|
{
|
|
_logger.LogInformation("PendingQuest: {QuestId}", quest?.Quest.Id);
|
|
_pendingQuest = quest;
|
|
}
|
|
|
|
protected override void UpdateCurrentTask()
|
|
{
|
|
if (!_gameFunctions.IsOccupied() || _gameFunctions.IsOccupiedWithCustomDeliveryNpc(CurrentQuest?.Quest))
|
|
{
|
|
base.UpdateCurrentTask();
|
|
}
|
|
}
|
|
|
|
protected override void OnTaskComplete(ITask task)
|
|
{
|
|
if (task is WaitAtEnd.WaitQuestCompleted)
|
|
{
|
|
_simulatedQuest = null;
|
|
}
|
|
}
|
|
|
|
protected override void OnNextStep(ILastTask task)
|
|
{
|
|
IncreaseStepCount(task.ElementId, task.Sequence, shouldContinue: true);
|
|
}
|
|
|
|
public void Start(string label)
|
|
{
|
|
using (_logger.BeginScope("Q/" + label))
|
|
{
|
|
AutomationType = EAutomationType.Automatic;
|
|
ExecuteNextStep();
|
|
}
|
|
}
|
|
|
|
public void StartGatheringQuest(string label)
|
|
{
|
|
using (_logger.BeginScope("GQ/" + label))
|
|
{
|
|
AutomationType = EAutomationType.GatheringOnly;
|
|
ExecuteNextStep();
|
|
}
|
|
}
|
|
|
|
public void StartSingleQuest(string label)
|
|
{
|
|
using (_logger.BeginScope("SQ/" + label))
|
|
{
|
|
AutomationType = EAutomationType.SingleQuestA;
|
|
ExecuteNextStep();
|
|
}
|
|
}
|
|
|
|
public void StartSingleStep(string label)
|
|
{
|
|
using (_logger.BeginScope("SS/" + label))
|
|
{
|
|
AutomationType = EAutomationType.Manual;
|
|
ExecuteNextStep();
|
|
}
|
|
}
|
|
|
|
private void ExecuteNextStep()
|
|
{
|
|
ClearTasksInternal();
|
|
if (TryPickPriorityQuest())
|
|
{
|
|
_logger.LogInformation("Using priority quest over current quest");
|
|
}
|
|
var (questSequence, step, flag) = GetNextStep();
|
|
if (CurrentQuest == null || questSequence == null)
|
|
{
|
|
if (CurrentQuestDetails?.Progress.Quest.Id is SatisfactionSupplyNpcId && CurrentQuestDetails?.Progress.Sequence == 1)
|
|
{
|
|
(QuestProgress, ECurrentQuestType)? currentQuestDetails = CurrentQuestDetails;
|
|
if (currentQuestDetails.HasValue && currentQuestDetails.GetValueOrDefault().Item1.Step == 255)
|
|
{
|
|
currentQuestDetails = CurrentQuestDetails;
|
|
if (currentQuestDetails.HasValue && currentQuestDetails.GetValueOrDefault().Item2 == ECurrentQuestType.Gathering)
|
|
{
|
|
_logger.LogInformation("Completed delivery quest");
|
|
SetGatheringQuest(null);
|
|
Stop("Gathering quest complete");
|
|
goto IL_01dc;
|
|
}
|
|
}
|
|
}
|
|
_logger.LogWarning("Could not retrieve next quest step, not doing anything [{QuestId}, {Sequence}, {Step}]", CurrentQuest?.Quest.Id, CurrentQuest?.Sequence, CurrentQuest?.Step);
|
|
goto IL_01dc;
|
|
}
|
|
goto IL_01e8;
|
|
IL_01e8:
|
|
_movementController.Stop();
|
|
_combatController.Stop("Execute next step");
|
|
_gatheringController.Stop("Execute next step");
|
|
try
|
|
{
|
|
foreach (ITask item in _taskCreator.CreateTasks(CurrentQuest.Quest, CurrentQuest.Sequence, questSequence, step))
|
|
{
|
|
_taskQueue.Enqueue(item);
|
|
}
|
|
ResetAutoRefreshState();
|
|
return;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogError(exception, "Failed to create tasks");
|
|
_chatGui.PrintError("Failed to start next task sequence, please check /xllog for details.", "Questionable", 576);
|
|
Stop("Tasks failed to create");
|
|
return;
|
|
}
|
|
IL_01dc:
|
|
if (CurrentQuest == null || !flag)
|
|
{
|
|
return;
|
|
}
|
|
goto IL_01e8;
|
|
}
|
|
|
|
public string ToStatString()
|
|
{
|
|
ITask task = _taskQueue.CurrentTaskExecutor?.CurrentTask;
|
|
if (task != null)
|
|
{
|
|
return $"{task} (+{_taskQueue.RemainingTasks.Count()})";
|
|
}
|
|
return $"- (+{_taskQueue.RemainingTasks.Count()})";
|
|
}
|
|
|
|
public bool HasCurrentTaskExecutorMatching<T>([NotNullWhen(true)] out T? task) where T : class, ITaskExecutor
|
|
{
|
|
if (_taskQueue.CurrentTaskExecutor is T val)
|
|
{
|
|
task = val;
|
|
return true;
|
|
}
|
|
task = null;
|
|
return false;
|
|
}
|
|
|
|
public bool HasCurrentTaskMatching<T>([NotNullWhen(true)] out T? task) where T : class, ITask
|
|
{
|
|
if (_taskQueue.CurrentTaskExecutor?.CurrentTask is T val)
|
|
{
|
|
task = val;
|
|
return true;
|
|
}
|
|
task = null;
|
|
return false;
|
|
}
|
|
|
|
public void Skip(ElementId elementId, byte currentQuestSequence)
|
|
{
|
|
lock (_progressLock)
|
|
{
|
|
if (_taskQueue.CurrentTaskExecutor?.CurrentTask is ISkippableTask)
|
|
{
|
|
_taskQueue.CurrentTaskExecutor = null;
|
|
}
|
|
else if (_taskQueue.CurrentTaskExecutor != null)
|
|
{
|
|
_taskQueue.CurrentTaskExecutor = null;
|
|
ITask task;
|
|
while (_taskQueue.TryPeek(out task))
|
|
{
|
|
_taskQueue.TryDequeue(out ITask _);
|
|
if (task is ISkippableTask)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (_taskQueue.AllTasksComplete)
|
|
{
|
|
Stop("Skip");
|
|
IncreaseStepCount(elementId, currentQuestSequence);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Stop("SkipNx");
|
|
IncreaseStepCount(elementId, currentQuestSequence);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SkipSimulatedTask()
|
|
{
|
|
_taskQueue.CurrentTaskExecutor = null;
|
|
}
|
|
|
|
public bool IsInterruptible()
|
|
{
|
|
EAutomationType automationType = AutomationType;
|
|
if ((uint)(automationType - 3) <= 1u)
|
|
{
|
|
return false;
|
|
}
|
|
(QuestProgress, ECurrentQuestType)? currentQuestDetails = CurrentQuestDetails;
|
|
if (!currentQuestDetails.HasValue)
|
|
{
|
|
return false;
|
|
}
|
|
(QuestProgress, ECurrentQuestType) value = currentQuestDetails.Value;
|
|
var (questProgress, _) = value;
|
|
if (value.Item2 != ECurrentQuestType.Normal || !questProgress.Quest.Root.Interruptible || questProgress.Sequence == 0)
|
|
{
|
|
return false;
|
|
}
|
|
if (ManualPriorityQuests.Contains(questProgress.Quest))
|
|
{
|
|
return false;
|
|
}
|
|
if (QuestData.HardModePrimals.Contains(questProgress.Quest.Id))
|
|
{
|
|
return false;
|
|
}
|
|
if (questProgress.Quest.Info.AlliedSociety != EAlliedSociety.None)
|
|
{
|
|
return false;
|
|
}
|
|
QuestSequence questSequence = questProgress.Quest.FindSequence(questProgress.Sequence);
|
|
if (questProgress.Step > 0)
|
|
{
|
|
return false;
|
|
}
|
|
QuestStep questStep = questSequence?.FindStep(questProgress.Step);
|
|
if (questStep != null && questStep.AetheryteShortcut.HasValue && (questStep.SkipConditions?.AetheryteShortcutIf?.QuestsCompleted.Count).GetValueOrDefault() == 0)
|
|
{
|
|
return (questStep.SkipConditions?.AetheryteShortcutIf?.QuestsAccepted.Count).GetValueOrDefault() == 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool TryPickPriorityQuest()
|
|
{
|
|
if (!IsInterruptible() || _nextQuest != null || _gatheringQuest != null || _simulatedQuest != null)
|
|
{
|
|
return false;
|
|
}
|
|
ElementId elementId = (from x in _questFunctions.GetNextPriorityQuestsThatCanBeAccepted()
|
|
where x.IsAvailable
|
|
select x.QuestId).FirstOrDefault();
|
|
if (elementId == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (_startedQuest != null && elementId == _startedQuest.Quest.Id)
|
|
{
|
|
return false;
|
|
}
|
|
if (_questRegistry.TryGetQuest(elementId, out Quest quest))
|
|
{
|
|
SetNextQuest(quest);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void ImportQuestPriority(List<ElementId> questElements)
|
|
{
|
|
foreach (ElementId questElement in questElements)
|
|
{
|
|
if (_questRegistry.TryGetQuest(questElement, out Quest quest) && !ManualPriorityQuests.Contains(quest))
|
|
{
|
|
ManualPriorityQuests.Add(quest);
|
|
}
|
|
}
|
|
}
|
|
|
|
public string ExportQuestPriority()
|
|
{
|
|
return string.Join(';', ManualPriorityQuests.Select((Quest x) => x.Id.ToString()));
|
|
}
|
|
|
|
public void ClearQuestPriority()
|
|
{
|
|
ManualPriorityQuests.Clear();
|
|
}
|
|
|
|
public bool AddQuestPriority(ElementId elementId)
|
|
{
|
|
if (_questRegistry.TryGetQuest(elementId, out Quest quest) && !ManualPriorityQuests.Contains(quest))
|
|
{
|
|
ManualPriorityQuests.Add(quest);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool InsertQuestPriority(int index, ElementId elementId)
|
|
{
|
|
try
|
|
{
|
|
if (_questRegistry.TryGetQuest(elementId, out Quest quest) && !ManualPriorityQuests.Contains(quest))
|
|
{
|
|
ManualPriorityQuests.Insert(index, quest);
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogError(exception, "Failed to insert quest in priority list");
|
|
_chatGui.PrintError("Failed to insert quest in priority list, please check /xllog for details.", "Questionable", 576);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool WasLastTaskUpdateWithin(TimeSpan timeSpan)
|
|
{
|
|
_logger.LogInformation("Last update: {Update}", _lastTaskUpdate);
|
|
if (!IsRunning)
|
|
{
|
|
return DateTime.Now <= _lastTaskUpdate.Add(timeSpan);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void OnConditionChange(ConditionFlag flag, bool value)
|
|
{
|
|
if (_taskQueue.CurrentTaskExecutor is IConditionChangeAware conditionChangeAware)
|
|
{
|
|
conditionChangeAware.OnConditionChange(flag, value);
|
|
}
|
|
}
|
|
|
|
private void OnNormalToast(ref SeString message, ref ToastOptions options, ref bool isHandled)
|
|
{
|
|
_gatheringController.OnNormalToast(message);
|
|
}
|
|
|
|
protected override void HandleInterruption(object? sender, EventArgs e)
|
|
{
|
|
if (IsRunning && AutomationType != EAutomationType.Manual)
|
|
{
|
|
base.HandleInterruption(sender, e);
|
|
}
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
_toastGui.ErrorToast -= base.OnErrorToast;
|
|
_toastGui.Toast -= OnNormalToast;
|
|
_condition.ConditionChange -= OnConditionChange;
|
|
base.Dispose();
|
|
}
|
|
}
|