From 63f975ff4fb36070a3316c47a6fc90379c4b24fb Mon Sep 17 00:00:00 2001 From: alydev Date: Sun, 28 Dec 2025 12:35:39 +1000 Subject: [PATCH] muffin v7.4.7 --- .../Duty.cs | 136 ++ .../MiniTaskController.cs | 4 + .../QuestController.cs | 190 +- .../Questionable.Data/ChangelogData.cs | 1562 +++++++++-------- .../Questionable.External/AutoDutyIpc.cs | 52 + .../Questionable.External/QuestionableIpc.cs | 195 +- .../Questionable.Functions/QuestFunctions.cs | 138 +- .../Questionable.Validation/QuestValidator.cs | 2 +- .../DutyConfigComponent.cs | 81 +- .../StopConditionComponent.cs | 88 +- .../QuestJournalUtils.cs | 2 +- .../ActiveQuestComponent.cs | 111 +- .../EventInfoComponent.cs | 14 +- .../PresetBuilderComponent.cs | 6 +- Questionable/Questionable/Configuration.cs | 15 +- .../Questionable/QuestionablePlugin.cs | 2 + 16 files changed, 1659 insertions(+), 939 deletions(-) diff --git a/Questionable/Questionable.Controller.Steps.Interactions/Duty.cs b/Questionable/Questionable.Controller.Steps.Interactions/Duty.cs index 64dee02..0549ab9 100644 --- a/Questionable/Questionable.Controller.Steps.Interactions/Duty.cs +++ b/Questionable/Questionable.Controller.Steps.Interactions/Duty.cs @@ -252,4 +252,140 @@ internal static class Duty return false; } } + + internal sealed record StartLevelingModeTask(int RequiredLevel, string? QuestName) : ITask + { + public override string ToString() + { + return $"StartLevelingMode(target: Lv{RequiredLevel} for '{QuestName}')"; + } + } + + internal sealed class StartLevelingModeExecutor(AutoDutyIpc autoDutyIpc, ICondition condition, ILogger logger) : TaskExecutor(), IStoppableTaskExecutor, ITaskExecutor + { + private bool _started; + + private DateTime _startTime; + + private DateTime _lastRetryTime = DateTime.MinValue; + + protected override bool Start() + { + logger.LogInformation("Starting AutoDuty Leveling mode to reach level {RequiredLevel} for quest '{QuestName}'", base.Task.RequiredLevel, base.Task.QuestName); + _started = autoDutyIpc.StartLevelingMode(); + _startTime = DateTime.Now; + return _started; + } + + public override ETaskResult Update() + { + bool flag = condition[ConditionFlag.BoundByDuty]; + bool flag2 = condition[ConditionFlag.InDutyQueue]; + if (flag || flag2) + { + logger.LogInformation("AutoDuty started successfully (inDuty={InDuty}, inQueue={InQueue})", flag, flag2); + return ETaskResult.TaskComplete; + } + if (!autoDutyIpc.IsStopped()) + { + return ETaskResult.StillRunning; + } + if (DateTime.Now - _lastRetryTime > TimeSpan.FromSeconds(5L)) + { + logger.LogWarning("AutoDuty stopped before entering duty, retrying..."); + _started = autoDutyIpc.StartLevelingMode(); + _lastRetryTime = DateTime.Now; + } + if (DateTime.Now - _startTime > TimeSpan.FromSeconds(60L)) + { + logger.LogError("AutoDuty failed to start after 60 seconds"); + return ETaskResult.TaskComplete; + } + return ETaskResult.StillRunning; + } + + public void StopNow() + { + autoDutyIpc.Stop(); + } + + public override bool ShouldInterruptOnDamage() + { + return false; + } + } + + internal sealed record WaitLevelingModeTask(int RequiredLevel) : ITask + { + public override string ToString() + { + return $"WaitLevelingMode(until Lv{RequiredLevel})"; + } + } + + internal sealed class WaitLevelingModeExecutor(AutoDutyIpc autoDutyIpc, IObjectTable objectTable, ICondition condition, IChatGui chatGui, ILogger logger) : TaskExecutor(), IStoppableTaskExecutor, ITaskExecutor + { + private bool _wasInDuty; + + private DateTime _lastStatusMessage = DateTime.MinValue; + + private DateTime _idleStartTime = DateTime.MinValue; + + protected override bool Start() + { + _wasInDuty = false; + _idleStartTime = DateTime.MinValue; + return true; + } + + public override ETaskResult Update() + { + bool flag = condition[ConditionFlag.BoundByDuty]; + bool flag2 = condition[ConditionFlag.InDutyQueue]; + if (flag && !_wasInDuty) + { + logger.LogInformation("Entered duty for leveling"); + _wasInDuty = true; + _idleStartTime = DateTime.MinValue; + } + if (flag || flag2) + { + _idleStartTime = DateTime.MinValue; + } + byte b = objectTable.LocalPlayer?.Level ?? 0; + if (b >= base.Task.RequiredLevel) + { + logger.LogInformation("Reached required level {RequiredLevel} (current: {CurrentLevel})", base.Task.RequiredLevel, b); + chatGui.Print($"Reached level {b}, can now continue MSQ.", "Questionable", 576); + return ETaskResult.TaskComplete; + } + if (autoDutyIpc.IsStopped() && !flag && !flag2) + { + if (_idleStartTime == DateTime.MinValue) + { + _idleStartTime = DateTime.Now; + } + if (_wasInDuty && DateTime.Now - _idleStartTime > TimeSpan.FromSeconds(3L)) + { + _ = base.Task.RequiredLevel; + _lastStatusMessage = DateTime.Now; + logger.LogInformation("Starting another leveling run (current: {CurrentLevel}, need: {RequiredLevel})", b, base.Task.RequiredLevel); + autoDutyIpc.StartLevelingMode(); + _wasInDuty = false; + _idleStartTime = DateTime.MinValue; + } + } + return ETaskResult.StillRunning; + } + + public void StopNow() + { + autoDutyIpc.Stop(); + } + + public override bool ShouldInterruptOnDamage() + { + return false; + } + } } diff --git a/Questionable/Questionable.Controller/MiniTaskController.cs b/Questionable/Questionable.Controller/MiniTaskController.cs index 7c428a4..b69281a 100644 --- a/Questionable/Questionable.Controller/MiniTaskController.cs +++ b/Questionable/Questionable.Controller/MiniTaskController.cs @@ -215,6 +215,10 @@ internal abstract class MiniTaskController : IDisposable public void OnErrorToast(ref SeString message, ref bool isHandled) { + if (_taskQueue.AllTasksComplete) + { + return; + } if (_taskQueue.CurrentTaskExecutor is IToastAware toastAware && toastAware.OnErrorToast(message)) { isHandled = true; diff --git a/Questionable/Questionable.Controller/QuestController.cs b/Questionable/Questionable.Controller/QuestController.cs index 8f29938..dfeb74e 100644 --- a/Questionable/Questionable.Controller/QuestController.cs +++ b/Questionable/Questionable.Controller/QuestController.cs @@ -16,6 +16,7 @@ using Questionable.Controller.Steps; using Questionable.Controller.Steps.Interactions; using Questionable.Controller.Steps.Shared; using Questionable.Data; +using Questionable.External; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; @@ -115,6 +116,8 @@ internal sealed class QuestController : MiniTaskController private readonly SinglePlayerDutyConfigComponent _singlePlayerDutyConfigComponent; + private readonly AutoDutyIpc _autoDutyIpc; + private readonly ILogger _logger; private readonly object _progressLock = new object(); @@ -155,6 +158,8 @@ internal sealed class QuestController : MiniTaskController private static readonly TimeSpan EscDoublePressWindow = TimeSpan.FromSeconds(1L); + private HashSet _stopConditionsMetAtStart = new HashSet(); + private const char ClipboardSeparator = ';'; public EAutomationType AutomationType @@ -236,7 +241,7 @@ internal sealed class QuestController : MiniTaskController public event AutomationTypeChangedEventHandler? AutomationTypeChanged; - public QuestController(IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, ILogger logger, QuestRegistry questRegistry, JournalData journalData, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent) + public QuestController(IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, ILogger logger, QuestRegistry questRegistry, JournalData journalData, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent, AutoDutyIpc autoDutyIpc) : base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger) { _clientState = clientState; @@ -255,6 +260,7 @@ internal sealed class QuestController : MiniTaskController _configuration = configuration; _taskCreator = taskCreator; _singlePlayerDutyConfigComponent = singlePlayerDutyConfigComponent; + _autoDutyIpc = autoDutyIpc; _logger = logger; _toastGui.ErrorToast += base.OnErrorToast; _toastGui.Toast += OnNormalToast; @@ -361,33 +367,46 @@ internal sealed class QuestController : MiniTaskController if (_configuration.Stop.Enabled && _startedQuest != null) { string text = _startedQuest.Quest.Id.ToString(); - if (_configuration.Stop.LevelToStopAfter && IsRunning && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel) + if (_configuration.Stop.LevelStopMode != Configuration.EStopConditionMode.Off && IsRunning && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel) { - _logger.LogInformation("Reached level stop condition (current: {CurrentLevel}, target: {TargetLevel})", playerCharacter.Level, _configuration.Stop.TargetLevel); - _chatGui.Print($"Character level {playerCharacter.Level} reached target level {_configuration.Stop.TargetLevel}.", "Questionable", 576); - Stop($"Level stop condition reached [{playerCharacter.Level}]"); - return; + string item = $"level:{_configuration.Stop.TargetLevel}"; + if (_configuration.Stop.LevelStopMode != Configuration.EStopConditionMode.Pause || !_stopConditionsMetAtStart.Contains(item)) + { + _logger.LogInformation("Reached level stop condition (current: {CurrentLevel}, target: {TargetLevel}, mode: {Mode})", playerCharacter.Level, _configuration.Stop.TargetLevel, _configuration.Stop.LevelStopMode); + _chatGui.Print($"Character level {playerCharacter.Level} reached target level {_configuration.Stop.TargetLevel}.", "Questionable", 576); + Stop($"Level stop condition reached [{playerCharacter.Level}]"); + return; + } } if (_configuration.Stop.QuestSequences.TryGetValue(text, out var value) && value.HasValue) { int sequence = _startedQuest.Sequence; if (sequence >= value.Value && IsRunning) { - _logger.LogInformation("Reached quest-specific sequence stop condition (quest: {QuestId}, sequence: {CurrentSequence}, target: {TargetSequence})", _startedQuest.Quest.Id, sequence, value.Value); - _chatGui.Print($"Quest '{_startedQuest.Quest.Info.Name}' reached sequence {sequence}, configured stop sequence is {value.Value}.", "Questionable", 576); - Stop($"Quest-specific sequence stop condition reached [{text}@{sequence}]"); - return; + Configuration.EStopConditionMode valueOrDefault = _configuration.Stop.QuestStopModes.GetValueOrDefault(text, Configuration.EStopConditionMode.Pause); + string item2 = $"questseq:{text}:{sequence}"; + if (valueOrDefault != Configuration.EStopConditionMode.Pause || !_stopConditionsMetAtStart.Contains(item2)) + { + _logger.LogInformation("Reached quest-specific sequence stop condition (quest: {QuestId}, sequence: {CurrentSequence}, target: {TargetSequence})", _startedQuest.Quest.Id, sequence, value.Value); + _chatGui.Print($"Quest '{_startedQuest.Quest.Info.Name}' reached sequence {sequence}, configured stop sequence is {value.Value}.", "Questionable", 576); + Stop($"Quest-specific sequence stop condition reached [{text}@{sequence}]"); + return; + } } } - else if (_configuration.Stop.SequenceToStopAfter && CurrentQuest != null) + else if (_configuration.Stop.SequenceStopMode != Configuration.EStopConditionMode.Off && CurrentQuest != null) { int sequence2 = CurrentQuest.Sequence; if (sequence2 >= _configuration.Stop.TargetSequence && IsRunning) { - _logger.LogInformation("Reached global quest sequence stop condition (sequence: {CurrentSequence}, target: {TargetSequence})", sequence2, _configuration.Stop.TargetSequence); - _chatGui.Print($"Quest sequence {sequence2} reached target sequence {_configuration.Stop.TargetSequence}.", "Questionable", 576); - Stop($"Sequence stop condition reached [{sequence2}]"); - return; + string item3 = $"sequence:{text}:{sequence2}"; + if (_configuration.Stop.SequenceStopMode != Configuration.EStopConditionMode.Pause || !_stopConditionsMetAtStart.Contains(item3)) + { + _logger.LogInformation("Reached global quest sequence stop condition (sequence: {CurrentSequence}, target: {TargetSequence}, mode: {Mode})", sequence2, _configuration.Stop.TargetSequence, _configuration.Stop.SequenceStopMode); + _chatGui.Print($"Quest sequence {sequence2} reached target sequence {_configuration.Stop.TargetSequence}.", "Questionable", 576); + Stop($"Sequence stop condition reached [{sequence2}]"); + return; + } } } } @@ -402,17 +421,14 @@ internal sealed class QuestController : MiniTaskController if (step == 0 || step == 255) { flag2 = true; - goto IL_0691; + goto IL_07e7; } } flag2 = false; - goto IL_0691; + goto IL_07e7; } - goto IL_0695; - IL_0691: - flag = flag2; - goto IL_0695; - IL_0695: + goto IL_07eb; + IL_07eb: if (flag && DateTime.Now >= CurrentQuest.StepProgress.StartedAt.AddSeconds(15.0)) { lock (_progressLock) @@ -427,6 +443,10 @@ internal sealed class QuestController : MiniTaskController CheckAutoRefreshCondition(); UpdateCurrentTask(); } + return; + IL_07e7: + flag = flag2; + goto IL_07eb; } private void CheckAutoRefreshCondition() @@ -563,7 +583,7 @@ internal sealed class QuestController : MiniTaskController int num = ManualPriorityQuests.RemoveAll((Quest q) => _questFunctions.IsQuestComplete(q.Id)); if (num > 0) { - _logger.LogInformation("Removed {Count} completed priority quest(s)", num); + _logger.LogInformation("Removed {Count} completed priority {QuestWord}", num, (num == 1) ? "quest" : "quests"); } } if (_pendingQuest != null) @@ -632,6 +652,19 @@ internal sealed class QuestController : MiniTaskController } if (elementId == null || elementId.Value == 0) { + (bool isLevelLocked, int levelsNeeded, int requiredLevel, string? questName) msqLevelLockInfo = _questFunctions.GetMsqLevelLockInfo(); + bool item = msqLevelLockInfo.isLevelLocked; + int item2 = msqLevelLockInfo.levelsNeeded; + int item3 = msqLevelLockInfo.requiredLevel; + string item4 = msqLevelLockInfo.questName; + int currentPlayerLevel = (_objectTable[0] as IPlayerCharacter)?.Level ?? 0; + if (item && _autoDutyIpc.IsConfiguredToRunLevelingMode(currentPlayerLevel) && AutomationType == EAutomationType.Automatic && !_condition[ConditionFlag.BoundByDuty] && _taskQueue.AllTasksComplete) + { + _logger.LogInformation("MSQ '{QuestName}' requires level {RequiredLevel}, current level is {CurrentLevel} ({LevelsNeeded} levels needed). Starting AutoDuty Leveling mode.", item4, item3, item3 - item2, item2); + _taskQueue.Enqueue(new Duty.StartLevelingModeTask(item3, item4)); + _taskQueue.Enqueue(new Duty.WaitLevelingModeTask(item3)); + return; + } if (_startedQuest != null) { switch (mainScenarioQuestState) @@ -668,16 +701,28 @@ internal sealed class QuestController : MiniTaskController _startedQuest = new QuestProgress(quest, b); if (_objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level < quest.Info.Level) { - _logger.LogInformation("Stopping automation, player level ({PlayerLevel}) < quest level ({QuestLevel}", playerCharacter.Level, quest.Info.Level); - Stop("Quest level too high"); - return; + if (_autoDutyIpc.IsConfiguredToRunLevelingMode(playerCharacter.Level) && AutomationType == EAutomationType.Automatic && !_condition[ConditionFlag.BoundByDuty]) + { + _logger.LogInformation("Player level ({PlayerLevel}) < quest level ({QuestLevel}), starting AutoDuty Leveling mode", playerCharacter.Level, quest.Info.Level); + ClearTasksInternal(); + _taskQueue.Enqueue(new Duty.StartLevelingModeTask(quest.Info.Level, quest.Info.Name)); + _taskQueue.Enqueue(new Duty.WaitLevelingModeTask(quest.Info.Level)); + } + else + { + _logger.LogInformation("Stopping automation, player level ({PlayerLevel}) < quest level ({QuestLevel})", playerCharacter.Level, quest.Info.Level); + Stop("Quest level too high"); + } } - if (AutomationType == EAutomationType.SingleQuestB) + else { - _logger.LogInformation("Single quest is finished"); - AutomationType = EAutomationType.Manual; + if (AutomationType == EAutomationType.SingleQuestB) + { + _logger.LogInformation("Single quest is finished"); + AutomationType = EAutomationType.Manual; + } + CheckNextTasks("Different Quest"); } - CheckNextTasks("Different Quest"); } else if (_startedQuest != null) { @@ -853,6 +898,7 @@ internal sealed class QuestController : MiniTaskController _nextQuest = null; _gatheringQuest = null; _lastTaskUpdate = DateTime.Now; + _stopConditionsMetAtStart.Clear(); ResetAutoRefreshState(); } } @@ -967,6 +1013,7 @@ internal sealed class QuestController : MiniTaskController { if (!CheckAndBlockForStopConditions()) { + RecordStopConditionsMetAtStart(); AutomationType = EAutomationType.Automatic; ExecuteNextStep(); } @@ -979,6 +1026,7 @@ internal sealed class QuestController : MiniTaskController { if (!CheckAndBlockForStopConditions()) { + RecordStopConditionsMetAtStart(); AutomationType = EAutomationType.GatheringOnly; ExecuteNextStep(); } @@ -991,6 +1039,7 @@ internal sealed class QuestController : MiniTaskController { if (!CheckAndBlockForStopConditions()) { + RecordStopConditionsMetAtStart(); AutomationType = EAutomationType.SingleQuestA; ExecuteNextStep(); } @@ -1006,13 +1055,53 @@ internal sealed class QuestController : MiniTaskController } } + private void RecordStopConditionsMetAtStart() + { + _stopConditionsMetAtStart.Clear(); + if (!_configuration.Stop.Enabled) + { + return; + } + if (_configuration.Stop.LevelStopMode == Configuration.EStopConditionMode.Pause && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel) + { + _stopConditionsMetAtStart.Add($"level:{_configuration.Stop.TargetLevel}"); + _logger.LogDebug("Recording level stop condition as already met at start: {Level}", _configuration.Stop.TargetLevel); + } + if (_configuration.Stop.SequenceStopMode == Configuration.EStopConditionMode.Pause && _startedQuest != null) + { + string text = _startedQuest.Quest.Id.ToString(); + if (!_configuration.Stop.QuestSequences.ContainsKey(text)) + { + int sequence = _startedQuest.Sequence; + if (sequence >= _configuration.Stop.TargetSequence) + { + _stopConditionsMetAtStart.Add($"sequence:{text}:{sequence}"); + _logger.LogDebug("Recording global sequence stop condition as already met at start: quest {QuestId}, sequence {Sequence}", text, sequence); + } + } + } + foreach (ElementId item in _configuration.Stop.QuestsToStopAfter) + { + string text2 = item.ToString(); + if (_configuration.Stop.QuestStopModes.GetValueOrDefault(text2, Configuration.EStopConditionMode.Pause) == Configuration.EStopConditionMode.Pause && _configuration.Stop.QuestSequences.TryGetValue(text2, out var value) && value.HasValue && _questFunctions.IsQuestAccepted(item)) + { + QuestProgressInfo questProgressInfo = _questFunctions.GetQuestProgressInfo(item); + if (questProgressInfo != null && questProgressInfo.Sequence >= value.Value) + { + _stopConditionsMetAtStart.Add($"questseq:{text2}:{questProgressInfo.Sequence}"); + _logger.LogDebug("Recording quest sequence stop condition as already met at start: quest {QuestId}, sequence {Sequence}", text2, questProgressInfo.Sequence); + } + } + } + } + private bool CheckAndBlockForStopConditions() { if (!_configuration.Stop.Enabled) { return false; } - if (_configuration.Stop.LevelToStopAfter && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel) + if (_configuration.Stop.LevelStopMode == Configuration.EStopConditionMode.Stop && _objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level >= _configuration.Stop.TargetLevel) { _logger.LogInformation("Blocking start: Level stop condition already met (current: {CurrentLevel}, target: {TargetLevel})", playerCharacter.Level, _configuration.Stop.TargetLevel); _chatGui.Print($"Cannot start: Character level {playerCharacter.Level} has reached target level {_configuration.Stop.TargetLevel}.", "Questionable", 576); @@ -1021,6 +1110,10 @@ internal sealed class QuestController : MiniTaskController foreach (ElementId item in _configuration.Stop.QuestsToStopAfter) { string key = item.ToString(); + if (_configuration.Stop.QuestStopModes.GetValueOrDefault(key, Configuration.EStopConditionMode.Pause) != Configuration.EStopConditionMode.Stop) + { + continue; + } if (_configuration.Stop.QuestSequences.TryGetValue(key, out var value) && value.HasValue) { if (!_questFunctions.IsQuestAccepted(item)) @@ -1048,7 +1141,7 @@ internal sealed class QuestController : MiniTaskController return true; } } - if (_configuration.Stop.SequenceToStopAfter && _startedQuest != null) + if (_configuration.Stop.SequenceStopMode == Configuration.EStopConditionMode.Stop && _startedQuest != null) { string key2 = _startedQuest.Quest.Id.ToString(); if (!_configuration.Stop.QuestSequences.ContainsKey(key2)) @@ -1075,6 +1168,23 @@ internal sealed class QuestController : MiniTaskController var (questSequence, step, flag) = GetNextStep(); if (CurrentQuest == null || questSequence == null) { + _logger.LogDebug("ExecuteNextStep: No current quest or sequence. Checking leveling mode conditions."); + if (AutomationType == EAutomationType.Automatic && !_condition[ConditionFlag.BoundByDuty]) + { + (bool isLevelLocked, int levelsNeeded, int requiredLevel, string? questName) msqLevelLockInfo = _questFunctions.GetMsqLevelLockInfo(); + bool item = msqLevelLockInfo.isLevelLocked; + int item2 = msqLevelLockInfo.levelsNeeded; + int item3 = msqLevelLockInfo.requiredLevel; + string item4 = msqLevelLockInfo.questName; + int currentPlayerLevel = (_objectTable[0] as IPlayerCharacter)?.Level ?? 0; + if (item && _autoDutyIpc.IsConfiguredToRunLevelingMode(currentPlayerLevel)) + { + _logger.LogInformation("MSQ '{QuestName}' requires level {RequiredLevel}, current level is {CurrentLevel} ({LevelsNeeded} levels needed). Starting AutoDuty Leveling mode.", item4, item3, item3 - item2, item2); + _taskQueue.Enqueue(new Duty.StartLevelingModeTask(item3, item4)); + _taskQueue.Enqueue(new Duty.WaitLevelingModeTask(item3)); + return; + } + } if (CurrentQuestDetails?.Progress.Quest.Id is SatisfactionSupplyNpcId && CurrentQuestDetails?.Progress.Sequence == 1) { (QuestProgress, ECurrentQuestType)? currentQuestDetails = CurrentQuestDetails; @@ -1086,23 +1196,23 @@ internal sealed class QuestController : MiniTaskController _logger.LogInformation("Completed delivery quest"); SetGatheringQuest(null); Stop("Gathering quest complete"); - goto IL_01dc; + goto IL_02d0; } } } _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_02d0; } - goto IL_01e8; - IL_01e8: + goto IL_02dc; + IL_02dc: _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)) + foreach (ITask item5 in _taskCreator.CreateTasks(CurrentQuest.Quest, CurrentQuest.Sequence, questSequence, step)) { - _taskQueue.Enqueue(item); + _taskQueue.Enqueue(item5); } ResetAutoRefreshState(); return; @@ -1114,12 +1224,12 @@ internal sealed class QuestController : MiniTaskController Stop("Tasks failed to create"); return; } - IL_01dc: + IL_02d0: if (CurrentQuest == null || !flag) { return; } - goto IL_01e8; + goto IL_02dc; } public string ToStatString() diff --git a/Questionable/Questionable.Data/ChangelogData.cs b/Questionable/Questionable.Data/ChangelogData.cs index b11ca41..aedcd1a 100644 --- a/Questionable/Questionable.Data/ChangelogData.cs +++ b/Questionable/Questionable.Data/ChangelogData.cs @@ -11,14 +11,14 @@ internal static class ChangelogData static ChangelogData() { - int num = 51; + int num = 52; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); int num2 = 0; ref ChangelogEntry reference = ref span[num2]; - DateOnly releaseDate = new DateOnly(2025, 12, 23); - int num3 = 2; + DateOnly releaseDate = new DateOnly(2025, 12, 28); + int num3 = 4; List list2 = new List(num3); CollectionsMarshal.SetCount(list2, num3); Span span2 = CollectionsMarshal.AsSpan(list2); @@ -29,1020 +29,1068 @@ internal static class ChangelogData CollectionsMarshal.SetCount(list3, num5); Span span3 = CollectionsMarshal.AsSpan(list3); int index = 0; - span3[index] = "Added Cinema Mode to not skip cutscenes"; + span3[index] = "Added leveling mode when underleveled for MSQ"; reference2 = new ChangeEntry(EChangeCategory.Added, "Major Features", list3); num4++; ref ChangeEntry reference3 = ref span2[num4]; - index = 2; + index = 3; List list4 = new List(index); CollectionsMarshal.SetCount(list4, index); Span span4 = CollectionsMarshal.AsSpan(list4); num5 = 0; - span4[num5] = "Added handling for Unsync (Party) to Party Watchdog and configuration to disable if in party"; + span4[num5] = "Added missing dungeons to Duties"; num5++; - span4[num5] = "Stop conditions now act as a true stop"; + span4[num5] = "Added Normal Raids and Alliance Raids to duties tab"; + num5++; + span4[num5] = "Added Pause/Stop modes to stop conditions"; reference3 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list4); - reference = new ChangelogEntry("7.4.6", releaseDate, list2); - num2++; - ref ChangelogEntry reference4 = ref span[num2]; - DateOnly releaseDate2 = new DateOnly(2025, 12, 22); - num4 = 1; - List list5 = new List(num4); - CollectionsMarshal.SetCount(list5, num4); - Span span5 = CollectionsMarshal.AsSpan(list5); - num3 = 0; - ref ChangeEntry reference5 = ref span5[num3]; - num5 = 2; - List list6 = new List(num5); - CollectionsMarshal.SetCount(list6, num5); - Span span6 = CollectionsMarshal.AsSpan(list6); + num4++; + ref ChangeEntry reference4 = ref span2[num4]; + num5 = 1; + List list5 = new List(num5); + CollectionsMarshal.SetCount(list5, num5); + Span span5 = CollectionsMarshal.AsSpan(list5); index = 0; - span6[index] = "Dive adjustments"; - index++; - span6[index] = "Logging message adjustments"; - reference5 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list6); - reference4 = new ChangelogEntry("7.4.5", releaseDate2, list5); + span5[index] = "Added leveling mode IPC: IsLevelingModeEnabled, SetLevelingModeEnabled, GetMsqLevelLockInfo, StartLevelingMode, StopLevelingMode"; + reference4 = new ChangeEntry(EChangeCategory.Added, "IPC Changes", list5); + num4++; + ref ChangeEntry reference5 = ref span2[num4]; + index = 1; + List list6 = new List(index); + CollectionsMarshal.SetCount(list6, index); + Span span6 = CollectionsMarshal.AsSpan(list6); + num5 = 0; + span6[num5] = "Fixed UI appearing in duties when debuff or interrupts occur"; + reference5 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list6); + reference = new ChangelogEntry("7.4.7", releaseDate, list2); num2++; ref ChangelogEntry reference6 = ref span[num2]; - DateOnly releaseDate3 = new DateOnly(2025, 12, 21); - num3 = 2; - List list7 = new List(num3); - CollectionsMarshal.SetCount(list7, num3); + DateOnly releaseDate2 = new DateOnly(2025, 12, 23); + num4 = 2; + List list7 = new List(num4); + CollectionsMarshal.SetCount(list7, num4); Span span7 = CollectionsMarshal.AsSpan(list7); - num4 = 0; - ref ChangeEntry reference7 = ref span7[num4]; - index = 1; - List list8 = new List(index); - CollectionsMarshal.SetCount(list8, index); - Span span8 = CollectionsMarshal.AsSpan(list8); - num5 = 0; - span8[num5] = "Changelog only shows once per update now"; - reference7 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list8); - num4++; - ref ChangeEntry reference8 = ref span7[num4]; + num3 = 0; + ref ChangeEntry reference7 = ref span7[num3]; num5 = 1; - List list9 = new List(num5); - CollectionsMarshal.SetCount(list9, num5); - Span span9 = CollectionsMarshal.AsSpan(list9); + List list8 = new List(num5); + CollectionsMarshal.SetCount(list8, num5); + Span span8 = CollectionsMarshal.AsSpan(list8); index = 0; - span9[index] = "Fixed gathering paths loading"; - reference8 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list9); - reference6 = new ChangelogEntry("7.4.4", releaseDate3, list7); + span8[index] = "Added Cinema Mode to not skip cutscenes"; + reference7 = new ChangeEntry(EChangeCategory.Added, "Major Features", list8); + num3++; + ref ChangeEntry reference8 = ref span7[num3]; + index = 2; + List list9 = new List(index); + CollectionsMarshal.SetCount(list9, index); + Span span9 = CollectionsMarshal.AsSpan(list9); + num5 = 0; + span9[num5] = "Added handling for Unsync (Party) to Party Watchdog and configuration to disable if in party"; + num5++; + span9[num5] = "Stop conditions now act as a true stop"; + reference8 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list9); + reference6 = new ChangelogEntry("7.4.6", releaseDate2, list7); num2++; ref ChangelogEntry reference9 = ref span[num2]; - DateOnly releaseDate4 = new DateOnly(2025, 12, 21); - num4 = 1; - List list10 = new List(num4); - CollectionsMarshal.SetCount(list10, num4); + DateOnly releaseDate3 = new DateOnly(2025, 12, 22); + num3 = 1; + List list10 = new List(num3); + CollectionsMarshal.SetCount(list10, num3); Span span10 = CollectionsMarshal.AsSpan(list10); - num3 = 0; - ref ChangeEntry reference10 = ref span10[num3]; - index = 1; - List list11 = new List(index); - CollectionsMarshal.SetCount(list11, index); + num4 = 0; + ref ChangeEntry reference10 = ref span10[num4]; + num5 = 2; + List list11 = new List(num5); + CollectionsMarshal.SetCount(list11, num5); Span span11 = CollectionsMarshal.AsSpan(list11); - num5 = 0; - span11[num5] = "Fixed changelog version checks"; - reference10 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list11); - reference9 = new ChangelogEntry("7.4.3", releaseDate4, list10); + index = 0; + span11[index] = "Dive adjustments"; + index++; + span11[index] = "Logging message adjustments"; + reference10 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list11); + reference9 = new ChangelogEntry("7.4.5", releaseDate3, list10); num2++; ref ChangelogEntry reference11 = ref span[num2]; - DateOnly releaseDate5 = new DateOnly(2025, 12, 20); - num3 = 2; - List list12 = new List(num3); - CollectionsMarshal.SetCount(list12, num3); + DateOnly releaseDate4 = new DateOnly(2025, 12, 21); + num4 = 2; + List list12 = new List(num4); + CollectionsMarshal.SetCount(list12, num4); Span span12 = CollectionsMarshal.AsSpan(list12); - num4 = 0; - ref ChangeEntry reference12 = ref span12[num4]; - num5 = 1; - List list13 = new List(num5); - CollectionsMarshal.SetCount(list13, num5); - Span span13 = CollectionsMarshal.AsSpan(list13); - index = 0; - span13[index] = "Add 7.4 Starlight Celebration (2025) quest"; - reference12 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list13); - num4++; - ref ChangeEntry reference13 = ref span12[num4]; + num3 = 0; + ref ChangeEntry reference12 = ref span12[num3]; index = 1; - List list14 = new List(index); - CollectionsMarshal.SetCount(list14, index); - Span span14 = CollectionsMarshal.AsSpan(list14); + List list13 = new List(index); + CollectionsMarshal.SetCount(list13, index); + Span span13 = CollectionsMarshal.AsSpan(list13); num5 = 0; - span14[num5] = "Fixed 7.4 MSQ"; + span13[num5] = "Changelog only shows once per update now"; + reference12 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list13); + num3++; + ref ChangeEntry reference13 = ref span12[num3]; + num5 = 1; + List list14 = new List(num5); + CollectionsMarshal.SetCount(list14, num5); + Span span14 = CollectionsMarshal.AsSpan(list14); + index = 0; + span14[index] = "Fixed gathering paths loading"; reference13 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list14); - reference11 = new ChangelogEntry("7.4.2", releaseDate5, list12); + reference11 = new ChangelogEntry("7.4.4", releaseDate4, list12); num2++; ref ChangelogEntry reference14 = ref span[num2]; - DateOnly releaseDate6 = new DateOnly(2025, 12, 19); - num4 = 1; - List list15 = new List(num4); - CollectionsMarshal.SetCount(list15, num4); + DateOnly releaseDate5 = new DateOnly(2025, 12, 21); + num3 = 1; + List list15 = new List(num3); + CollectionsMarshal.SetCount(list15, num3); Span span15 = CollectionsMarshal.AsSpan(list15); - num3 = 0; - ref ChangeEntry reference15 = ref span15[num3]; - num5 = 2; - List list16 = new List(num5); - CollectionsMarshal.SetCount(list16, num5); + num4 = 0; + ref ChangeEntry reference15 = ref span15[num4]; + index = 1; + List list16 = new List(index); + CollectionsMarshal.SetCount(list16, index); Span span16 = CollectionsMarshal.AsSpan(list16); - index = 0; - span16[index] = "Add 7.4 MSQ"; - index++; - span16[index] = "Add 7.4 Arcadion Raid quests"; - reference15 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list16); - reference14 = new ChangelogEntry("7.4.1", releaseDate6, list15); + num5 = 0; + span16[num5] = "Fixed changelog version checks"; + reference15 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list16); + reference14 = new ChangelogEntry("7.4.3", releaseDate5, list15); num2++; ref ChangelogEntry reference16 = ref span[num2]; - DateOnly releaseDate7 = new DateOnly(2025, 12, 17); - num3 = 1; - List list17 = new List(num3); - CollectionsMarshal.SetCount(list17, num3); - Span span17 = CollectionsMarshal.AsSpan(list17); - num4 = 0; - span17[num4] = new ChangeEntry(EChangeCategory.Changed, "Api 14 update"); - reference16 = new ChangelogEntry("7.4.0", releaseDate7, list17); - num2++; - ref ChangelogEntry reference17 = ref span[num2]; - DateOnly releaseDate8 = new DateOnly(2025, 12, 6); + DateOnly releaseDate6 = new DateOnly(2025, 12, 20); num4 = 2; - List list18 = new List(num4); - CollectionsMarshal.SetCount(list18, num4); - Span span18 = CollectionsMarshal.AsSpan(list18); + List list17 = new List(num4); + CollectionsMarshal.SetCount(list17, num4); + Span span17 = CollectionsMarshal.AsSpan(list17); num3 = 0; - ref ChangeEntry reference18 = ref span18[num3]; - index = 4; + ref ChangeEntry reference17 = ref span17[num3]; + num5 = 1; + List list18 = new List(num5); + CollectionsMarshal.SetCount(list18, num5); + Span span18 = CollectionsMarshal.AsSpan(list18); + index = 0; + span18[index] = "Add 7.4 Starlight Celebration (2025) quest"; + reference17 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list18); + num3++; + ref ChangeEntry reference18 = ref span17[num3]; + index = 1; List list19 = new List(index); CollectionsMarshal.SetCount(list19, index); Span span19 = CollectionsMarshal.AsSpan(list19); num5 = 0; - span19[num5] = "Added reloading and rebuilding to movement system"; - num5++; - span19[num5] = "Improved interrupts and refresh states to allow continuation of questing"; - num5++; - span19[num5] = "Added player input detection to stop automation when manually moving character"; - num5++; - span19[num5] = "Added various missing quest sequences"; - reference18 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list19); - num3++; - ref ChangeEntry reference19 = ref span18[num3]; - num5 = 1; - List list20 = new List(num5); - CollectionsMarshal.SetCount(list20, num5); - Span span20 = CollectionsMarshal.AsSpan(list20); - index = 0; - span20[index] = "Fixed reset task state to prevent stuck interactions after interruption"; - reference19 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list20); - reference17 = new ChangelogEntry("7.38.9", releaseDate8, list18); + span19[num5] = "Fixed 7.4 MSQ"; + reference18 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list19); + reference16 = new ChangelogEntry("7.4.2", releaseDate6, list17); num2++; - ref ChangelogEntry reference20 = ref span[num2]; - DateOnly releaseDate9 = new DateOnly(2025, 11, 29); - num3 = 2; - List list21 = new List(num3); - CollectionsMarshal.SetCount(list21, num3); - Span span21 = CollectionsMarshal.AsSpan(list21); + ref ChangelogEntry reference19 = ref span[num2]; + DateOnly releaseDate7 = new DateOnly(2025, 12, 19); + num3 = 1; + List list20 = new List(num3); + CollectionsMarshal.SetCount(list20, num3); + Span span20 = CollectionsMarshal.AsSpan(list20); num4 = 0; - ref ChangeEntry reference21 = ref span21[num4]; - index = 3; - List list22 = new List(index); - CollectionsMarshal.SetCount(list22, index); - Span span22 = CollectionsMarshal.AsSpan(list22); - num5 = 0; - span22[num5] = "Movement update with automatic retrying if character can't reach target position"; - num5++; - span22[num5] = "Added Hunt mob data"; - num5++; - span22[num5] = "Refactored commands"; - reference21 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list22); - num4++; - ref ChangeEntry reference22 = ref span21[num4]; - num5 = 3; - List list23 = new List(num5); - CollectionsMarshal.SetCount(list23, num5); - Span span23 = CollectionsMarshal.AsSpan(list23); + ref ChangeEntry reference20 = ref span20[num4]; + num5 = 2; + List list21 = new List(num5); + CollectionsMarshal.SetCount(list21, num5); + Span span21 = CollectionsMarshal.AsSpan(list21); index = 0; - span23[index] = "Fixed quest (Way of the Archer)"; + span21[index] = "Add 7.4 MSQ"; index++; - span23[index] = "Fixed quest (Spirithold Broken)"; - index++; - span23[index] = "Fixed quest (It's Probably Not Pirates)"; - reference22 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list23); - reference20 = new ChangelogEntry("7.38.8", releaseDate9, list21); + span21[index] = "Add 7.4 Arcadion Raid quests"; + reference20 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list21); + reference19 = new ChangelogEntry("7.4.1", releaseDate7, list20); num2++; - ref ChangelogEntry reference23 = ref span[num2]; - DateOnly releaseDate10 = new DateOnly(2025, 11, 25); - num4 = 2; - List list24 = new List(num4); - CollectionsMarshal.SetCount(list24, num4); - Span span24 = CollectionsMarshal.AsSpan(list24); + ref ChangelogEntry reference21 = ref span[num2]; + DateOnly releaseDate8 = new DateOnly(2025, 12, 17); + num4 = 1; + List list22 = new List(num4); + CollectionsMarshal.SetCount(list22, num4); + Span span22 = CollectionsMarshal.AsSpan(list22); num3 = 0; - ref ChangeEntry reference24 = ref span24[num3]; - index = 2; - List list25 = new List(index); - CollectionsMarshal.SetCount(list25, index); - Span span25 = CollectionsMarshal.AsSpan(list25); - num5 = 0; - span25[num5] = "Added individual sequence stop condition for each quest"; - num5++; - span25[num5] = "Added Trials to Duties tab in config"; - reference24 = new ChangeEntry(EChangeCategory.Added, "Major features", list25); - num3++; - ref ChangeEntry reference25 = ref span24[num3]; - num5 = 1; - List list26 = new List(num5); - CollectionsMarshal.SetCount(list26, num5); - Span span26 = CollectionsMarshal.AsSpan(list26); - index = 0; - span26[index] = "Added IPC for stop conditions: GetQuestSequenceStopCondition, SetQuestSequenceStopCondition, RemoveQuestSequenceStopCondition, GetAllQuestSequenceStopConditions"; - reference25 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list26); - reference23 = new ChangelogEntry("7.38.7", releaseDate10, list24); + span22[num3] = new ChangeEntry(EChangeCategory.Changed, "Api 14 update"); + reference21 = new ChangelogEntry("7.4.0", releaseDate8, list22); num2++; - ref ChangelogEntry reference26 = ref span[num2]; - DateOnly releaseDate11 = new DateOnly(2025, 11, 25); - num3 = 3; - List list27 = new List(num3); - CollectionsMarshal.SetCount(list27, num3); - Span span27 = CollectionsMarshal.AsSpan(list27); + ref ChangelogEntry reference22 = ref span[num2]; + DateOnly releaseDate9 = new DateOnly(2025, 12, 6); + num3 = 2; + List list23 = new List(num3); + CollectionsMarshal.SetCount(list23, num3); + Span span23 = CollectionsMarshal.AsSpan(list23); num4 = 0; - ref ChangeEntry reference27 = ref span27[num4]; - index = 2; - List list28 = new List(index); - CollectionsMarshal.SetCount(list28, index); - Span span28 = CollectionsMarshal.AsSpan(list28); + ref ChangeEntry reference23 = ref span23[num4]; + index = 4; + List list24 = new List(index); + CollectionsMarshal.SetCount(list24, index); + Span span24 = CollectionsMarshal.AsSpan(list24); num5 = 0; - span28[num5] = "Updated Allied Society journal text"; + span24[num5] = "Added reloading and rebuilding to movement system"; num5++; - span28[num5] = "Improved Allied Society rank handling"; - reference27 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list28); + span24[num5] = "Improved interrupts and refresh states to allow continuation of questing"; + num5++; + span24[num5] = "Added player input detection to stop automation when manually moving character"; + num5++; + span24[num5] = "Added various missing quest sequences"; + reference23 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list24); num4++; - ref ChangeEntry reference28 = ref span27[num4]; + ref ChangeEntry reference24 = ref span23[num4]; num5 = 1; - List list29 = new List(num5); - CollectionsMarshal.SetCount(list29, num5); - Span span29 = CollectionsMarshal.AsSpan(list29); + List list25 = new List(num5); + CollectionsMarshal.SetCount(list25, num5); + Span span25 = CollectionsMarshal.AsSpan(list25); index = 0; - span29[index] = "Added IPC for Allied Society: AddAlliedSocietyOptimalQuests, GetAlliedSocietyOptimalQuests"; - reference28 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list29); - num4++; - ref ChangeEntry reference29 = ref span27[num4]; - index = 1; + span25[index] = "Fixed reset task state to prevent stuck interactions after interruption"; + reference24 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list25); + reference22 = new ChangelogEntry("7.38.9", releaseDate9, list23); + num2++; + ref ChangelogEntry reference25 = ref span[num2]; + DateOnly releaseDate10 = new DateOnly(2025, 11, 29); + num4 = 2; + List list26 = new List(num4); + CollectionsMarshal.SetCount(list26, num4); + Span span26 = CollectionsMarshal.AsSpan(list26); + num3 = 0; + ref ChangeEntry reference26 = ref span26[num3]; + index = 3; + List list27 = new List(index); + CollectionsMarshal.SetCount(list27, index); + Span span27 = CollectionsMarshal.AsSpan(list27); + num5 = 0; + span27[num5] = "Movement update with automatic retrying if character can't reach target position"; + num5++; + span27[num5] = "Added Hunt mob data"; + num5++; + span27[num5] = "Refactored commands"; + reference26 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list27); + num3++; + ref ChangeEntry reference27 = ref span26[num3]; + num5 = 3; + List list28 = new List(num5); + CollectionsMarshal.SetCount(list28, num5); + Span span28 = CollectionsMarshal.AsSpan(list28); + index = 0; + span28[index] = "Fixed quest (Way of the Archer)"; + index++; + span28[index] = "Fixed quest (Spirithold Broken)"; + index++; + span28[index] = "Fixed quest (It's Probably Not Pirates)"; + reference27 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list28); + reference25 = new ChangelogEntry("7.38.8", releaseDate10, list26); + num2++; + ref ChangelogEntry reference28 = ref span[num2]; + DateOnly releaseDate11 = new DateOnly(2025, 11, 25); + num3 = 2; + List list29 = new List(num3); + CollectionsMarshal.SetCount(list29, num3); + Span span29 = CollectionsMarshal.AsSpan(list29); + num4 = 0; + ref ChangeEntry reference29 = ref span29[num4]; + index = 2; List list30 = new List(index); CollectionsMarshal.SetCount(list30, index); Span span30 = CollectionsMarshal.AsSpan(list30); num5 = 0; - span30[num5] = "Fixed quest (We Come in Peace)"; - reference29 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list30); - reference26 = new ChangelogEntry("7.38.6", releaseDate11, list27); - num2++; - ref ChangelogEntry reference30 = ref span[num2]; - DateOnly releaseDate12 = new DateOnly(2025, 11, 24); - num4 = 2; - List list31 = new List(num4); - CollectionsMarshal.SetCount(list31, num4); - Span span31 = CollectionsMarshal.AsSpan(list31); - num3 = 0; - ref ChangeEntry reference31 = ref span31[num3]; + span30[num5] = "Added individual sequence stop condition for each quest"; + num5++; + span30[num5] = "Added Trials to Duties tab in config"; + reference29 = new ChangeEntry(EChangeCategory.Added, "Major features", list30); + num4++; + ref ChangeEntry reference30 = ref span29[num4]; num5 = 1; - List list32 = new List(num5); - CollectionsMarshal.SetCount(list32, num5); - Span span32 = CollectionsMarshal.AsSpan(list32); + List list31 = new List(num5); + CollectionsMarshal.SetCount(list31, num5); + Span span31 = CollectionsMarshal.AsSpan(list31); index = 0; - span32[index] = "Added Allied Society daily allowance tracker with bulk quest adding buttons"; - reference31 = new ChangeEntry(EChangeCategory.Added, "Major features", list32); - num3++; - ref ChangeEntry reference32 = ref span31[num3]; - index = 1; + span31[index] = "Added IPC for stop conditions: GetQuestSequenceStopCondition, SetQuestSequenceStopCondition, RemoveQuestSequenceStopCondition, GetAllQuestSequenceStopConditions"; + reference30 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list31); + reference28 = new ChangelogEntry("7.38.7", releaseDate11, list29); + num2++; + ref ChangelogEntry reference31 = ref span[num2]; + DateOnly releaseDate12 = new DateOnly(2025, 11, 25); + num4 = 3; + List list32 = new List(num4); + CollectionsMarshal.SetCount(list32, num4); + Span span32 = CollectionsMarshal.AsSpan(list32); + num3 = 0; + ref ChangeEntry reference32 = ref span32[num3]; + index = 2; List list33 = new List(index); CollectionsMarshal.SetCount(list33, index); Span span33 = CollectionsMarshal.AsSpan(list33); num5 = 0; - span33[num5] = "Added IPC for Allied Society: GetRemainingAllowances, GetTimeUntilReset, GetAvailableQuestIds, GetAllAvailableQuestCounts, IsMaxRank, GetCurrentRank, GetSocietiesWithAvailableQuests"; - reference32 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list33); - reference30 = new ChangelogEntry("7.38.5", releaseDate12, list31); - num2++; - ref ChangelogEntry reference33 = ref span[num2]; - DateOnly releaseDate13 = new DateOnly(2025, 11, 23); - num3 = 2; - List list34 = new List(num3); - CollectionsMarshal.SetCount(list34, num3); - Span span34 = CollectionsMarshal.AsSpan(list34); - num4 = 0; - ref ChangeEntry reference34 = ref span34[num4]; + span33[num5] = "Updated Allied Society journal text"; + num5++; + span33[num5] = "Improved Allied Society rank handling"; + reference32 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list33); + num3++; + ref ChangeEntry reference33 = ref span32[num3]; num5 = 1; - List list35 = new List(num5); - CollectionsMarshal.SetCount(list35, num5); + List list34 = new List(num5); + CollectionsMarshal.SetCount(list34, num5); + Span span34 = CollectionsMarshal.AsSpan(list34); + index = 0; + span34[index] = "Added IPC for Allied Society: AddAlliedSocietyOptimalQuests, GetAlliedSocietyOptimalQuests"; + reference33 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list34); + num3++; + ref ChangeEntry reference34 = ref span32[num3]; + index = 1; + List list35 = new List(index); + CollectionsMarshal.SetCount(list35, index); Span span35 = CollectionsMarshal.AsSpan(list35); - index = 0; - span35[index] = "Explicitly declare support for BMR singleplayer duty (The Rematch)"; - reference34 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list35); - num4++; - ref ChangeEntry reference35 = ref span34[num4]; - index = 8; - List list36 = new List(index); - CollectionsMarshal.SetCount(list36, index); - Span span36 = CollectionsMarshal.AsSpan(list36); num5 = 0; - span36[num5] = "Fixed quest (Microbrewing) to not get stuck near ramp"; - num5++; - span36[num5] = "Fixed quest (The Illuminated Land) where pathing would kill the player due to fall damage"; - num5++; - span36[num5] = "Fixed quest (It's Probably Not Pirates) improper pathing and removed unneeded step"; - num5++; - span36[num5] = "Fixed quest (The Black Wolf's Ultimatum) not exiting landing area"; - num5++; - span36[num5] = "Fixed quest (Magiteknical Failure) from not interacting with NPC due to being mounted"; - num5++; - span36[num5] = "Fixed quest (We Come in Peace) shortcut navigation"; - num5++; - span36[num5] = "Fixed quest (Poisoned Hearts) where incorrect pathing caused the player to die"; - num5++; - span36[num5] = "Fixed quests (Savage Snares) and (An Apple a Day) not detecting kills"; - reference35 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list36); - reference33 = new ChangelogEntry("7.38.4", releaseDate13, list34); + span35[num5] = "Fixed quest (We Come in Peace)"; + reference34 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list35); + reference31 = new ChangelogEntry("7.38.6", releaseDate12, list32); num2++; - ref ChangelogEntry reference36 = ref span[num2]; - DateOnly releaseDate14 = new DateOnly(2025, 11, 23); - num4 = 3; - List list37 = new List(num4); - CollectionsMarshal.SetCount(list37, num4); - Span span37 = CollectionsMarshal.AsSpan(list37); - num3 = 0; - ref ChangeEntry reference37 = ref span37[num3]; - num5 = 2; - List list38 = new List(num5); - CollectionsMarshal.SetCount(list38, num5); - Span span38 = CollectionsMarshal.AsSpan(list38); + ref ChangelogEntry reference35 = ref span[num2]; + DateOnly releaseDate13 = new DateOnly(2025, 11, 24); + num3 = 2; + List list36 = new List(num3); + CollectionsMarshal.SetCount(list36, num3); + Span span36 = CollectionsMarshal.AsSpan(list36); + num4 = 0; + ref ChangeEntry reference36 = ref span36[num4]; + num5 = 1; + List list37 = new List(num5); + CollectionsMarshal.SetCount(list37, num5); + Span span37 = CollectionsMarshal.AsSpan(list37); index = 0; - span38[index] = "Added RequireHq to crafting InteractionType"; - index++; - span38[index] = "Mark GC quests as Locked if rank not achieved"; - reference37 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list38); - num3++; - ref ChangeEntry reference38 = ref span37[num3]; - index = 2; - List list39 = new List(index); - CollectionsMarshal.SetCount(list39, index); - Span span39 = CollectionsMarshal.AsSpan(list39); + span37[index] = "Added Allied Society daily allowance tracker with bulk quest adding buttons"; + reference36 = new ChangeEntry(EChangeCategory.Added, "Major features", list37); + num4++; + ref ChangeEntry reference37 = ref span36[num4]; + index = 1; + List list38 = new List(index); + CollectionsMarshal.SetCount(list38, index); + Span span38 = CollectionsMarshal.AsSpan(list38); num5 = 0; - span39[num5] = "Added IPC for stop conditions: GetStopConditionsEnabled, SetStopConditionsEnabled, GetStopQuestList, AddStopQuest, RemoveStopQuest, ClearStopQuests, GetLevelStopCondition, SetLevelStopCondition, GetSequenceStopCondition, SetSequenceStopCondition"; - num5++; - span39[num5] = "Added IPC for priority quests: GetPriorityQuests, RemovePriorityQuest, ReorderPriorityQuest, GetAvailablePresets, GetPresetQuests, AddPresetToPriority, IsPresetAvailable, IsQuestInPriority, GetQuestPriorityIndex, HasAvailablePriorityQuests"; - reference38 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list39); - num3++; - ref ChangeEntry reference39 = ref span37[num3]; - num5 = 3; + span38[num5] = "Added IPC for Allied Society: GetRemainingAllowances, GetTimeUntilReset, GetAvailableQuestIds, GetAllAvailableQuestCounts, IsMaxRank, GetCurrentRank, GetSocietiesWithAvailableQuests"; + reference37 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list38); + reference35 = new ChangelogEntry("7.38.5", releaseDate13, list36); + num2++; + ref ChangelogEntry reference38 = ref span[num2]; + DateOnly releaseDate14 = new DateOnly(2025, 11, 23); + num4 = 2; + List list39 = new List(num4); + CollectionsMarshal.SetCount(list39, num4); + Span span39 = CollectionsMarshal.AsSpan(list39); + num3 = 0; + ref ChangeEntry reference39 = ref span39[num3]; + num5 = 1; List list40 = new List(num5); CollectionsMarshal.SetCount(list40, num5); Span span40 = CollectionsMarshal.AsSpan(list40); index = 0; - span40[index] = "Fixed line breaks not working in dialog strings"; - index++; - span40[index] = "Fixed quest (Labor of Love)"; - index++; - span40[index] = "Fixed quest (Sea of Sorrow)"; - reference39 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list40); - reference36 = new ChangelogEntry("7.38.3", releaseDate14, list37); - num2++; - ref ChangelogEntry reference40 = ref span[num2]; - DateOnly releaseDate15 = new DateOnly(2025, 11, 18); - num3 = 3; - List list41 = new List(num3); - CollectionsMarshal.SetCount(list41, num3); - Span span41 = CollectionsMarshal.AsSpan(list41); - num4 = 0; - ref ChangeEntry reference41 = ref span41[num4]; - index = 2; - List list42 = new List(index); - CollectionsMarshal.SetCount(list42, index); - Span span42 = CollectionsMarshal.AsSpan(list42); + span40[index] = "Explicitly declare support for BMR singleplayer duty (The Rematch)"; + reference39 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list40); + num3++; + ref ChangeEntry reference40 = ref span39[num3]; + index = 8; + List list41 = new List(index); + CollectionsMarshal.SetCount(list41, index); + Span span41 = CollectionsMarshal.AsSpan(list41); num5 = 0; - span42[num5] = "Auto Duty unsync options for each duty (Duty Support, Unsync Solo, Unsync Party)"; + span41[num5] = "Fixed quest (Microbrewing) to not get stuck near ramp"; num5++; - span42[num5] = "Added Auto Duty unsync options to quest schema and updated quests using old unsync method"; - reference41 = new ChangeEntry(EChangeCategory.Added, "Major features", list42); - num4++; - ref ChangeEntry reference42 = ref span41[num4]; - num5 = 3; + span41[num5] = "Fixed quest (The Illuminated Land) where pathing would kill the player due to fall damage"; + num5++; + span41[num5] = "Fixed quest (It's Probably Not Pirates) improper pathing and removed unneeded step"; + num5++; + span41[num5] = "Fixed quest (The Black Wolf's Ultimatum) not exiting landing area"; + num5++; + span41[num5] = "Fixed quest (Magiteknical Failure) from not interacting with NPC due to being mounted"; + num5++; + span41[num5] = "Fixed quest (We Come in Peace) shortcut navigation"; + num5++; + span41[num5] = "Fixed quest (Poisoned Hearts) where incorrect pathing caused the player to die"; + num5++; + span41[num5] = "Fixed quests (Savage Snares) and (An Apple a Day) not detecting kills"; + reference40 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list41); + reference38 = new ChangelogEntry("7.38.4", releaseDate14, list39); + num2++; + ref ChangelogEntry reference41 = ref span[num2]; + DateOnly releaseDate15 = new DateOnly(2025, 11, 23); + num3 = 3; + List list42 = new List(num3); + CollectionsMarshal.SetCount(list42, num3); + Span span42 = CollectionsMarshal.AsSpan(list42); + num4 = 0; + ref ChangeEntry reference42 = ref span42[num4]; + num5 = 2; List list43 = new List(num5); CollectionsMarshal.SetCount(list43, num5); Span span43 = CollectionsMarshal.AsSpan(list43); index = 0; - span43[index] = "Added IPC for duty sync handling: GetDefaultDutyMode, SetDefaultDutyMode"; + span43[index] = "Added RequireHq to crafting InteractionType"; index++; - span43[index] = "Added IPC for duty mode overrides: GetDutyModeOverride, SetDutyModeOverride"; - index++; - span43[index] = "Added IPC for clearing overrides: ClearDutyModeOverride, ClearAllDutyModeOverrides"; - reference42 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list43); + span43[index] = "Mark GC quests as Locked if rank not achieved"; + reference42 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list43); num4++; - span41[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest (Constant Cravings)"); - reference40 = new ChangelogEntry("7.38.2", releaseDate15, list41); + ref ChangeEntry reference43 = ref span42[num4]; + index = 2; + List list44 = new List(index); + CollectionsMarshal.SetCount(list44, index); + Span span44 = CollectionsMarshal.AsSpan(list44); + num5 = 0; + span44[num5] = "Added IPC for stop conditions: GetStopConditionsEnabled, SetStopConditionsEnabled, GetStopQuestList, AddStopQuest, RemoveStopQuest, ClearStopQuests, GetLevelStopCondition, SetLevelStopCondition, GetSequenceStopCondition, SetSequenceStopCondition"; + num5++; + span44[num5] = "Added IPC for priority quests: GetPriorityQuests, RemovePriorityQuest, ReorderPriorityQuest, GetAvailablePresets, GetPresetQuests, AddPresetToPriority, IsPresetAvailable, IsQuestInPriority, GetQuestPriorityIndex, HasAvailablePriorityQuests"; + reference43 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list44); + num4++; + ref ChangeEntry reference44 = ref span42[num4]; + num5 = 3; + List list45 = new List(num5); + CollectionsMarshal.SetCount(list45, num5); + Span span45 = CollectionsMarshal.AsSpan(list45); + index = 0; + span45[index] = "Fixed line breaks not working in dialog strings"; + index++; + span45[index] = "Fixed quest (Labor of Love)"; + index++; + span45[index] = "Fixed quest (Sea of Sorrow)"; + reference44 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list45); + reference41 = new ChangelogEntry("7.38.3", releaseDate15, list42); num2++; - ref ChangelogEntry reference43 = ref span[num2]; + ref ChangelogEntry reference45 = ref span[num2]; DateOnly releaseDate16 = new DateOnly(2025, 11, 18); num4 = 3; - List list44 = new List(num4); - CollectionsMarshal.SetCount(list44, num4); - Span span44 = CollectionsMarshal.AsSpan(list44); + List list46 = new List(num4); + CollectionsMarshal.SetCount(list46, num4); + Span span46 = CollectionsMarshal.AsSpan(list46); num3 = 0; - ref ChangeEntry reference44 = ref span44[num3]; - index = 1; - List list45 = new List(index); - CollectionsMarshal.SetCount(list45, index); - Span span45 = CollectionsMarshal.AsSpan(list45); - num5 = 0; - span45[num5] = "Added new fields to quest schema"; - reference44 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list45); - num3++; - ref ChangeEntry reference45 = ref span44[num3]; - num5 = 3; - List list46 = new List(num5); - CollectionsMarshal.SetCount(list46, num5); - Span span46 = CollectionsMarshal.AsSpan(list46); - index = 0; - span46[index] = "A Faerie Tale Come True"; - index++; - span46[index] = "Constant Cravings"; - index++; - span46[index] = "A Bridge Too Full"; - reference45 = new ChangeEntry(EChangeCategory.QuestUpdates, "Added new quest paths", list46); - num3++; - ref ChangeEntry reference46 = ref span44[num3]; - index = 3; + ref ChangeEntry reference46 = ref span46[num3]; + index = 2; List list47 = new List(index); CollectionsMarshal.SetCount(list47, index); Span span47 = CollectionsMarshal.AsSpan(list47); num5 = 0; - span47[num5] = "Fixed various quest schemas"; + span47[num5] = "Auto Duty unsync options for each duty (Duty Support, Unsync Solo, Unsync Party)"; num5++; - span47[num5] = "Fixed changelog bullet point encoding"; - num5++; - span47[num5] = "Fixed item use to wait until item is used before next action"; - reference46 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list47); - reference43 = new ChangelogEntry("7.38.1", releaseDate16, list44); - num2++; - ref ChangelogEntry reference47 = ref span[num2]; - DateOnly releaseDate17 = new DateOnly(2025, 11, 17); - num3 = 5; - List list48 = new List(num3); - CollectionsMarshal.SetCount(list48, num3); - Span span48 = CollectionsMarshal.AsSpan(list48); - num4 = 0; - ref ChangeEntry reference48 = ref span48[num4]; - num5 = 2; - List list49 = new List(num5); - CollectionsMarshal.SetCount(list49, num5); - Span span49 = CollectionsMarshal.AsSpan(list49); + span47[num5] = "Added Auto Duty unsync options to quest schema and updated quests using old unsync method"; + reference46 = new ChangeEntry(EChangeCategory.Added, "Major features", list47); + num3++; + ref ChangeEntry reference47 = ref span46[num3]; + num5 = 3; + List list48 = new List(num5); + CollectionsMarshal.SetCount(list48, num5); + Span span48 = CollectionsMarshal.AsSpan(list48); index = 0; - span49[index] = "Quest sequence window to show expected sequences in each quest (with quest searching)"; + span48[index] = "Added IPC for duty sync handling: GetDefaultDutyMode, SetDefaultDutyMode"; index++; - span49[index] = "Changelog"; - reference48 = new ChangeEntry(EChangeCategory.Added, "Major features", list49); - num4++; - ref ChangeEntry reference49 = ref span48[num4]; - index = 2; + span48[index] = "Added IPC for duty mode overrides: GetDutyModeOverride, SetDutyModeOverride"; + index++; + span48[index] = "Added IPC for clearing overrides: ClearDutyModeOverride, ClearAllDutyModeOverrides"; + reference47 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list48); + num3++; + span46[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest (Constant Cravings)"); + reference45 = new ChangelogEntry("7.38.2", releaseDate16, list46); + num2++; + ref ChangelogEntry reference48 = ref span[num2]; + DateOnly releaseDate17 = new DateOnly(2025, 11, 18); + num3 = 3; + List list49 = new List(num3); + CollectionsMarshal.SetCount(list49, num3); + Span span49 = CollectionsMarshal.AsSpan(list49); + num4 = 0; + ref ChangeEntry reference49 = ref span49[num4]; + index = 1; List list50 = new List(index); CollectionsMarshal.SetCount(list50, index); Span span50 = CollectionsMarshal.AsSpan(list50); num5 = 0; - span50[num5] = "Updated quest schemas"; - num5++; - span50[num5] = "Added search bar to preferred mounts and capitalization to mirror game mount names"; + span50[num5] = "Added new fields to quest schema"; reference49 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list50); num4++; - ref ChangeEntry reference50 = ref span48[num4]; + ref ChangeEntry reference50 = ref span49[num4]; num5 = 3; List list51 = new List(num5); CollectionsMarshal.SetCount(list51, num5); Span span51 = CollectionsMarshal.AsSpan(list51); index = 0; - span51[index] = "Renamed IsQuestCompleted → IsQuestComplete"; + span51[index] = "A Faerie Tale Come True"; index++; - span51[index] = "Renamed IsQuestAvailable → IsReadyToAcceptQuest"; + span51[index] = "Constant Cravings"; index++; - span51[index] = "Added GetCurrentTask IPC"; - reference50 = new ChangeEntry(EChangeCategory.Changed, "IPC changes", list51); + span51[index] = "A Bridge Too Full"; + reference50 = new ChangeEntry(EChangeCategory.QuestUpdates, "Added new quest paths", list51); num4++; - span48[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added all Hildibrand quests"); - num4++; - span48[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed credits/cutscenes playback"); - reference47 = new ChangelogEntry("7.38.0", releaseDate17, list48); - num2++; - ref ChangelogEntry reference51 = ref span[num2]; - DateOnly releaseDate18 = new DateOnly(2025, 11, 8); - num4 = 1; - List list52 = new List(num4); - CollectionsMarshal.SetCount(list52, num4); - Span span52 = CollectionsMarshal.AsSpan(list52); - num3 = 0; - span52[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Fall Guys quest (Just Crowning Around)"); - reference51 = new ChangelogEntry("6.38", releaseDate18, list52); + ref ChangeEntry reference51 = ref span49[num4]; + index = 3; + List list52 = new List(index); + CollectionsMarshal.SetCount(list52, index); + Span span52 = CollectionsMarshal.AsSpan(list52); + num5 = 0; + span52[num5] = "Fixed various quest schemas"; + num5++; + span52[num5] = "Fixed changelog bullet point encoding"; + num5++; + span52[num5] = "Fixed item use to wait until item is used before next action"; + reference51 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list52); + reference48 = new ChangelogEntry("7.38.1", releaseDate17, list49); num2++; ref ChangelogEntry reference52 = ref span[num2]; - DateOnly releaseDate19 = new DateOnly(2025, 11, 8); - num3 = 1; - List list53 = new List(num3); - CollectionsMarshal.SetCount(list53, num3); + DateOnly releaseDate18 = new DateOnly(2025, 11, 17); + num4 = 5; + List list53 = new List(num4); + CollectionsMarshal.SetCount(list53, num4); Span span53 = CollectionsMarshal.AsSpan(list53); - num4 = 0; - span53[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Cosmic Exploration and various unlock quests"); - reference52 = new ChangelogEntry("6.37", releaseDate19, list53); - num2++; - ref ChangelogEntry reference53 = ref span[num2]; - DateOnly releaseDate20 = new DateOnly(2025, 11, 2); - num4 = 1; - List list54 = new List(num4); - CollectionsMarshal.SetCount(list54, num4); - Span span54 = CollectionsMarshal.AsSpan(list54); num3 = 0; - span54[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy Rank 6 quest (With High Spirits)"); - reference53 = new ChangelogEntry("6.36", releaseDate20, list54); - num2++; - ref ChangelogEntry reference54 = ref span[num2]; - DateOnly releaseDate21 = new DateOnly(2025, 10, 28); - num3 = 1; - List list55 = new List(num3); - CollectionsMarshal.SetCount(list55, num3); - Span span55 = CollectionsMarshal.AsSpan(list55); - num4 = 0; - span55[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed level 3 MSQ handling if character started on non-XP buff world"); - reference54 = new ChangelogEntry("6.35", releaseDate21, list55); - num2++; - ref ChangelogEntry reference55 = ref span[num2]; - DateOnly releaseDate22 = new DateOnly(2025, 10, 23); - num4 = 2; - List list56 = new List(num4); - CollectionsMarshal.SetCount(list56, num4); - Span span56 = CollectionsMarshal.AsSpan(list56); - num3 = 0; - span56[num3] = new ChangeEntry(EChangeCategory.Added, "Added clear priority quests on logout and on completion config settings"); + ref ChangeEntry reference53 = ref span53[num3]; + num5 = 2; + List list54 = new List(num5); + CollectionsMarshal.SetCount(list54, num5); + Span span54 = CollectionsMarshal.AsSpan(list54); + index = 0; + span54[index] = "Quest sequence window to show expected sequences in each quest (with quest searching)"; + index++; + span54[index] = "Changelog"; + reference53 = new ChangeEntry(EChangeCategory.Added, "Major features", list54); num3++; - span56[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed priority quest importing to respect import order"); - reference55 = new ChangelogEntry("6.34", releaseDate22, list56); + ref ChangeEntry reference54 = ref span53[num3]; + index = 2; + List list55 = new List(index); + CollectionsMarshal.SetCount(list55, index); + Span span55 = CollectionsMarshal.AsSpan(list55); + num5 = 0; + span55[num5] = "Updated quest schemas"; + num5++; + span55[num5] = "Added search bar to preferred mounts and capitalization to mirror game mount names"; + reference54 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list55); + num3++; + ref ChangeEntry reference55 = ref span53[num3]; + num5 = 3; + List list56 = new List(num5); + CollectionsMarshal.SetCount(list56, num5); + Span span56 = CollectionsMarshal.AsSpan(list56); + index = 0; + span56[index] = "Renamed IsQuestCompleted → IsQuestComplete"; + index++; + span56[index] = "Renamed IsQuestAvailable → IsReadyToAcceptQuest"; + index++; + span56[index] = "Added GetCurrentTask IPC"; + reference55 = new ChangeEntry(EChangeCategory.Changed, "IPC changes", list56); + num3++; + span53[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added all Hildibrand quests"); + num3++; + span53[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed credits/cutscenes playback"); + reference52 = new ChangelogEntry("7.38.0", releaseDate18, list53); num2++; ref ChangelogEntry reference56 = ref span[num2]; - DateOnly releaseDate23 = new DateOnly(2025, 10, 23); + DateOnly releaseDate19 = new DateOnly(2025, 11, 8); num3 = 1; List list57 = new List(num3); CollectionsMarshal.SetCount(list57, num3); Span span57 = CollectionsMarshal.AsSpan(list57); num4 = 0; - span57[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed RSR combat module"); - reference56 = new ChangelogEntry("6.33", releaseDate23, list57); + span57[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Fall Guys quest (Just Crowning Around)"); + reference56 = new ChangelogEntry("6.38", releaseDate19, list57); num2++; ref ChangelogEntry reference57 = ref span[num2]; - DateOnly releaseDate24 = new DateOnly(2025, 10, 23); + DateOnly releaseDate20 = new DateOnly(2025, 11, 8); num4 = 1; List list58 = new List(num4); CollectionsMarshal.SetCount(list58, num4); Span span58 = CollectionsMarshal.AsSpan(list58); num3 = 0; - span58[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy Rank 5 quest (Forged in Corn)"); - reference57 = new ChangelogEntry("6.32", releaseDate24, list58); + span58[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Cosmic Exploration and various unlock quests"); + reference57 = new ChangelogEntry("6.37", releaseDate20, list58); num2++; ref ChangelogEntry reference58 = ref span[num2]; - DateOnly releaseDate25 = new DateOnly(2025, 10, 21); + DateOnly releaseDate21 = new DateOnly(2025, 11, 2); num3 = 1; List list59 = new List(num3); CollectionsMarshal.SetCount(list59, num3); Span span59 = CollectionsMarshal.AsSpan(list59); num4 = 0; - span59[num4] = new ChangeEntry(EChangeCategory.Changed, "Added checks for moogle and allied society quests when using add all available quests"); - reference58 = new ChangelogEntry("6.31", releaseDate25, list59); + span59[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy Rank 6 quest (With High Spirits)"); + reference58 = new ChangelogEntry("6.36", releaseDate21, list59); num2++; ref ChangelogEntry reference59 = ref span[num2]; - DateOnly releaseDate26 = new DateOnly(2025, 10, 21); + DateOnly releaseDate22 = new DateOnly(2025, 10, 28); num4 = 1; List list60 = new List(num4); CollectionsMarshal.SetCount(list60, num4); Span span60 = CollectionsMarshal.AsSpan(list60); num3 = 0; - span60[num3] = new ChangeEntry(EChangeCategory.Added, "Added button to journal that allows adding all available quests to priority"); - reference59 = new ChangelogEntry("6.30", releaseDate26, list60); + span60[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed level 3 MSQ handling if character started on non-XP buff world"); + reference59 = new ChangelogEntry("6.35", releaseDate22, list60); num2++; ref ChangelogEntry reference60 = ref span[num2]; - DateOnly releaseDate27 = new DateOnly(2025, 10, 20); + DateOnly releaseDate23 = new DateOnly(2025, 10, 23); num3 = 2; List list61 = new List(num3); CollectionsMarshal.SetCount(list61, num3); Span span61 = CollectionsMarshal.AsSpan(list61); num4 = 0; - ref ChangeEntry reference61 = ref span61[num4]; - index = 2; - List list62 = new List(index); - CollectionsMarshal.SetCount(list62, index); - Span span62 = CollectionsMarshal.AsSpan(list62); - num5 = 0; - span62[num5] = "Added item count to combat handling rework"; - num5++; - span62[num5] = "Updated Pandora conflicting features"; - reference61 = new ChangeEntry(EChangeCategory.Changed, "Combat handling improvements", list62); + span61[num4] = new ChangeEntry(EChangeCategory.Added, "Added clear priority quests on logout and on completion config settings"); num4++; - span61[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest to purchase Gysahl Greens if not in inventory"); - reference60 = new ChangelogEntry("6.29", releaseDate27, list61); + span61[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed priority quest importing to respect import order"); + reference60 = new ChangelogEntry("6.34", releaseDate23, list61); + num2++; + ref ChangelogEntry reference61 = ref span[num2]; + DateOnly releaseDate24 = new DateOnly(2025, 10, 23); + num4 = 1; + List list62 = new List(num4); + CollectionsMarshal.SetCount(list62, num4); + Span span62 = CollectionsMarshal.AsSpan(list62); + num3 = 0; + span62[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed RSR combat module"); + reference61 = new ChangelogEntry("6.33", releaseDate24, list62); num2++; ref ChangelogEntry reference62 = ref span[num2]; - DateOnly releaseDate28 = new DateOnly(2025, 10, 19); - num4 = 1; - List list63 = new List(num4); - CollectionsMarshal.SetCount(list63, num4); + DateOnly releaseDate25 = new DateOnly(2025, 10, 23); + num3 = 1; + List list63 = new List(num3); + CollectionsMarshal.SetCount(list63, num3); Span span63 = CollectionsMarshal.AsSpan(list63); - num3 = 0; - span63[num3] = new ChangeEntry(EChangeCategory.Changed, "Reworked kill count combat handling - combat and enemy kills are now processed instantly"); - reference62 = new ChangelogEntry("6.28", releaseDate28, list63); + num4 = 0; + span63[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy Rank 5 quest (Forged in Corn)"); + reference62 = new ChangelogEntry("6.32", releaseDate25, list63); num2++; ref ChangelogEntry reference63 = ref span[num2]; - DateOnly releaseDate29 = new DateOnly(2025, 10, 18); - num3 = 2; - List list64 = new List(num3); - CollectionsMarshal.SetCount(list64, num3); + DateOnly releaseDate26 = new DateOnly(2025, 10, 21); + num4 = 1; + List list64 = new List(num4); + CollectionsMarshal.SetCount(list64, num4); Span span64 = CollectionsMarshal.AsSpan(list64); - num4 = 0; - span64[num4] = new ChangeEntry(EChangeCategory.Changed, "Improved Aether Current checking logic"); - num4++; - span64[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Chocobo Taxi Stand CheckSkip error and Patch 7.3 Fantasia unlock quest date/time"); - reference63 = new ChangelogEntry("6.27", releaseDate29, list64); + num3 = 0; + span64[num3] = new ChangeEntry(EChangeCategory.Changed, "Added checks for moogle and allied society quests when using add all available quests"); + reference63 = new ChangelogEntry("6.31", releaseDate26, list64); num2++; ref ChangelogEntry reference64 = ref span[num2]; - DateOnly releaseDate30 = new DateOnly(2025, 10, 18); - num4 = 1; - List list65 = new List(num4); - CollectionsMarshal.SetCount(list65, num4); + DateOnly releaseDate27 = new DateOnly(2025, 10, 21); + num3 = 1; + List list65 = new List(num3); + CollectionsMarshal.SetCount(list65, num3); Span span65 = CollectionsMarshal.AsSpan(list65); - num3 = 0; - span65[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 4 quests"); - reference64 = new ChangelogEntry("6.26", releaseDate30, list65); + num4 = 0; + span65[num4] = new ChangeEntry(EChangeCategory.Added, "Added button to journal that allows adding all available quests to priority"); + reference64 = new ChangelogEntry("6.30", releaseDate27, list65); num2++; ref ChangelogEntry reference65 = ref span[num2]; - DateOnly releaseDate31 = new DateOnly(2025, 10, 17); - num3 = 1; - List list66 = new List(num3); - CollectionsMarshal.SetCount(list66, num3); + DateOnly releaseDate28 = new DateOnly(2025, 10, 20); + num4 = 2; + List list66 = new List(num4); + CollectionsMarshal.SetCount(list66, num4); Span span66 = CollectionsMarshal.AsSpan(list66); - num4 = 0; - span66[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added All Saints' Wake 2025 quests and 7.35 Yok Huy rank 4 quests"); - reference65 = new ChangelogEntry("6.25", releaseDate31, list66); - num2++; - ref ChangelogEntry reference66 = ref span[num2]; - DateOnly releaseDate32 = new DateOnly(2025, 10, 16); - num4 = 1; - List list67 = new List(num4); - CollectionsMarshal.SetCount(list67, num4); - Span span67 = CollectionsMarshal.AsSpan(list67); num3 = 0; - span67[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 4 quests and Deep Dungeon quest"); - reference66 = new ChangelogEntry("6.24", releaseDate32, list67); + ref ChangeEntry reference66 = ref span66[num3]; + index = 2; + List list67 = new List(index); + CollectionsMarshal.SetCount(list67, index); + Span span67 = CollectionsMarshal.AsSpan(list67); + num5 = 0; + span67[num5] = "Added item count to combat handling rework"; + num5++; + span67[num5] = "Updated Pandora conflicting features"; + reference66 = new ChangeEntry(EChangeCategory.Changed, "Combat handling improvements", list67); + num3++; + span66[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest to purchase Gysahl Greens if not in inventory"); + reference65 = new ChangelogEntry("6.29", releaseDate28, list66); num2++; ref ChangelogEntry reference67 = ref span[num2]; - DateOnly releaseDate33 = new DateOnly(2025, 10, 13); + DateOnly releaseDate29 = new DateOnly(2025, 10, 19); num3 = 1; List list68 = new List(num3); CollectionsMarshal.SetCount(list68, num3); Span span68 = CollectionsMarshal.AsSpan(list68); num4 = 0; - span68[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quest (Larder Logistics)"); - reference67 = new ChangelogEntry("6.23", releaseDate33, list68); + span68[num4] = new ChangeEntry(EChangeCategory.Changed, "Reworked kill count combat handling - combat and enemy kills are now processed instantly"); + reference67 = new ChangelogEntry("6.28", releaseDate29, list68); num2++; ref ChangelogEntry reference68 = ref span[num2]; - DateOnly releaseDate34 = new DateOnly(2025, 10, 12); - num4 = 3; + DateOnly releaseDate30 = new DateOnly(2025, 10, 18); + num4 = 2; List list69 = new List(num4); CollectionsMarshal.SetCount(list69, num4); Span span69 = CollectionsMarshal.AsSpan(list69); num3 = 0; - span69[num3] = new ChangeEntry(EChangeCategory.Changed, "Prevent disabled or Locked quests from being started as 'Start as next quest'"); + span69[num3] = new ChangeEntry(EChangeCategory.Changed, "Improved Aether Current checking logic"); num3++; - span69[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quests"); - num3++; - span69[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Yok Huy quest and journal quest chain priority issues"); - reference68 = new ChangelogEntry("6.22", releaseDate34, list69); + span69[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Chocobo Taxi Stand CheckSkip error and Patch 7.3 Fantasia unlock quest date/time"); + reference68 = new ChangelogEntry("6.27", releaseDate30, list69); num2++; ref ChangelogEntry reference69 = ref span[num2]; - DateOnly releaseDate35 = new DateOnly(2025, 10, 12); - num3 = 2; + DateOnly releaseDate31 = new DateOnly(2025, 10, 18); + num3 = 1; List list70 = new List(num3); CollectionsMarshal.SetCount(list70, num3); Span span70 = CollectionsMarshal.AsSpan(list70); num4 = 0; - span70[num4] = new ChangeEntry(EChangeCategory.Added, "Added expansion abbreviation to journal window"); - num4++; - span70[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quests"); - reference69 = new ChangelogEntry("6.21", releaseDate35, list70); + span70[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 4 quests"); + reference69 = new ChangelogEntry("6.26", releaseDate31, list70); num2++; ref ChangelogEntry reference70 = ref span[num2]; - DateOnly releaseDate36 = new DateOnly(2025, 10, 10); - num4 = 2; + DateOnly releaseDate32 = new DateOnly(2025, 10, 17); + num4 = 1; List list71 = new List(num4); CollectionsMarshal.SetCount(list71, num4); Span span71 = CollectionsMarshal.AsSpan(list71); num3 = 0; - span71[num3] = new ChangeEntry(EChangeCategory.Changed, "Allow completed repeatable quests to be used with 'Add quest and requirements to priority' feature"); - num3++; - span71[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 1 quest (A Work of Cart)"); - reference70 = new ChangelogEntry("6.20", releaseDate36, list71); + span71[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added All Saints' Wake 2025 quests and 7.35 Yok Huy rank 4 quests"); + reference70 = new ChangelogEntry("6.25", releaseDate32, list71); num2++; ref ChangelogEntry reference71 = ref span[num2]; - DateOnly releaseDate37 = new DateOnly(2025, 10, 9); - num3 = 3; + DateOnly releaseDate33 = new DateOnly(2025, 10, 16); + num3 = 1; List list72 = new List(num3); CollectionsMarshal.SetCount(list72, num3); Span span72 = CollectionsMarshal.AsSpan(list72); num4 = 0; - span72[num4] = new ChangeEntry(EChangeCategory.Added, "Added config to batch Allied Society quest turn-ins"); - num4++; - span72[num4] = new ChangeEntry(EChangeCategory.Changed, "Repeatable quests now show correct availability state in journal"); - num4++; - span72[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 2 quests"); - reference71 = new ChangelogEntry("6.19", releaseDate37, list72); + span72[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 4 quests and Deep Dungeon quest"); + reference71 = new ChangelogEntry("6.24", releaseDate33, list72); num2++; ref ChangelogEntry reference72 = ref span[num2]; - DateOnly releaseDate38 = new DateOnly(2025, 10, 9); - num4 = 2; + DateOnly releaseDate34 = new DateOnly(2025, 10, 13); + num4 = 1; List list73 = new List(num4); CollectionsMarshal.SetCount(list73, num4); Span span73 = CollectionsMarshal.AsSpan(list73); num3 = 0; - span73[num3] = new ChangeEntry(EChangeCategory.Changed, "Show once completed quests with improved state display"); - num3++; - span73[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy daily quest and improvements to various Yok Huy quests"); - reference72 = new ChangelogEntry("6.18", releaseDate38, list73); + span73[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quest (Larder Logistics)"); + reference72 = new ChangelogEntry("6.23", releaseDate34, list73); num2++; ref ChangelogEntry reference73 = ref span[num2]; - DateOnly releaseDate39 = new DateOnly(2025, 10, 8); - num3 = 1; + DateOnly releaseDate35 = new DateOnly(2025, 10, 12); + num3 = 3; List list74 = new List(num3); CollectionsMarshal.SetCount(list74, num3); Span span74 = CollectionsMarshal.AsSpan(list74); num4 = 0; - span74[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 1 and rank 2 quests"); - reference73 = new ChangelogEntry("6.17", releaseDate39, list74); + span74[num4] = new ChangeEntry(EChangeCategory.Changed, "Prevent disabled or Locked quests from being started as 'Start as next quest'"); + num4++; + span74[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quests"); + num4++; + span74[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Yok Huy quest and journal quest chain priority issues"); + reference73 = new ChangelogEntry("6.22", releaseDate35, list74); num2++; ref ChangelogEntry reference74 = ref span[num2]; - DateOnly releaseDate40 = new DateOnly(2025, 10, 8); - num4 = 1; + DateOnly releaseDate36 = new DateOnly(2025, 10, 12); + num4 = 2; List list75 = new List(num4); CollectionsMarshal.SetCount(list75, num4); Span span75 = CollectionsMarshal.AsSpan(list75); num3 = 0; - span75[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Deep Dungeon quest (Faerie Tale)"); - reference74 = new ChangelogEntry("6.16", releaseDate40, list75); + span75[num3] = new ChangeEntry(EChangeCategory.Added, "Added expansion abbreviation to journal window"); + num3++; + span75[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quests"); + reference74 = new ChangelogEntry("6.21", releaseDate36, list75); num2++; ref ChangelogEntry reference75 = ref span[num2]; - DateOnly releaseDate41 = new DateOnly(2025, 10, 8); + DateOnly releaseDate37 = new DateOnly(2025, 10, 10); num3 = 2; List list76 = new List(num3); CollectionsMarshal.SetCount(list76, num3); Span span76 = CollectionsMarshal.AsSpan(list76); num4 = 0; - span76[num4] = new ChangeEntry(EChangeCategory.Changed, "Dalamud cleanup"); + span76[num4] = new ChangeEntry(EChangeCategory.Changed, "Allow completed repeatable quests to be used with 'Add quest and requirements to priority' feature"); num4++; - span76[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest level requirement check log spam"); - reference75 = new ChangelogEntry("6.15", releaseDate41, list76); + span76[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 1 quest (A Work of Cart)"); + reference75 = new ChangelogEntry("6.20", releaseDate37, list76); num2++; ref ChangelogEntry reference76 = ref span[num2]; - DateOnly releaseDate42 = new DateOnly(2025, 10, 8); - num4 = 1; + DateOnly releaseDate38 = new DateOnly(2025, 10, 9); + num4 = 3; List list77 = new List(num4); CollectionsMarshal.SetCount(list77, num4); Span span77 = CollectionsMarshal.AsSpan(list77); num3 = 0; - span77[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed abandoned quest check logic if quest were MSQ"); - reference76 = new ChangelogEntry("6.14", releaseDate42, list77); + span77[num3] = new ChangeEntry(EChangeCategory.Added, "Added config to batch Allied Society quest turn-ins"); + num3++; + span77[num3] = new ChangeEntry(EChangeCategory.Changed, "Repeatable quests now show correct availability state in journal"); + num3++; + span77[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 2 quests"); + reference76 = new ChangelogEntry("6.19", releaseDate38, list77); num2++; ref ChangelogEntry reference77 = ref span[num2]; - DateOnly releaseDate43 = new DateOnly(2025, 10, 8); + DateOnly releaseDate39 = new DateOnly(2025, 10, 9); num3 = 2; List list78 = new List(num3); CollectionsMarshal.SetCount(list78, num3); Span span78 = CollectionsMarshal.AsSpan(list78); num4 = 0; - ref ChangeEntry reference78 = ref span78[num4]; - num5 = 3; - List list79 = new List(num5); - CollectionsMarshal.SetCount(list79, num5); - Span span79 = CollectionsMarshal.AsSpan(list79); - index = 0; - span79[index] = "Context menu option to add required quests and their chain to priority list"; - index++; - span79[index] = "AetheryteShortcut to multiple quests"; - index++; - span79[index] = "Artisan as a recommended plugin/dependency"; - reference78 = new ChangeEntry(EChangeCategory.Added, "Quest improvements", list79); + span78[num4] = new ChangeEntry(EChangeCategory.Changed, "Show once completed quests with improved state display"); num4++; - span78[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed abandoned quest check and priority list issues"); - reference77 = new ChangelogEntry("6.13", releaseDate43, list78); + span78[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy daily quest and improvements to various Yok Huy quests"); + reference77 = new ChangelogEntry("6.18", releaseDate39, list78); + num2++; + ref ChangelogEntry reference78 = ref span[num2]; + DateOnly releaseDate40 = new DateOnly(2025, 10, 8); + num4 = 1; + List list79 = new List(num4); + CollectionsMarshal.SetCount(list79, num4); + Span span79 = CollectionsMarshal.AsSpan(list79); + num3 = 0; + span79[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 1 and rank 2 quests"); + reference78 = new ChangelogEntry("6.17", releaseDate40, list79); num2++; ref ChangelogEntry reference79 = ref span[num2]; - DateOnly releaseDate44 = new DateOnly(2025, 10, 7); - num4 = 4; - List list80 = new List(num4); - CollectionsMarshal.SetCount(list80, num4); + DateOnly releaseDate41 = new DateOnly(2025, 10, 8); + num3 = 1; + List list80 = new List(num3); + CollectionsMarshal.SetCount(list80, num3); Span span80 = CollectionsMarshal.AsSpan(list80); + num4 = 0; + span80[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Deep Dungeon quest (Faerie Tale)"); + reference79 = new ChangelogEntry("6.16", releaseDate41, list80); + num2++; + ref ChangelogEntry reference80 = ref span[num2]; + DateOnly releaseDate42 = new DateOnly(2025, 10, 8); + num4 = 2; + List list81 = new List(num4); + CollectionsMarshal.SetCount(list81, num4); + Span span81 = CollectionsMarshal.AsSpan(list81); num3 = 0; - ref ChangeEntry reference80 = ref span80[num3]; - index = 4; - List list81 = new List(index); - CollectionsMarshal.SetCount(list81, index); - Span span81 = CollectionsMarshal.AsSpan(list81); - num5 = 0; - span81[num5] = "FATE combat handling with auto level syncing"; - num5++; - span81[num5] = "Start accepted quests from journal with 'Start as next quest'"; - num5++; - span81[num5] = "Update quest tracking when quests are hidden or prioritised in game"; - num5++; - span81[num5] = "QuestMap as a recommended plugin/dependency"; - reference80 = new ChangeEntry(EChangeCategory.Added, "FATE and quest tracking", list81); + span81[num3] = new ChangeEntry(EChangeCategory.Changed, "Dalamud cleanup"); num3++; - ref ChangeEntry reference81 = ref span80[num3]; - num5 = 3; - List list82 = new List(num5); - CollectionsMarshal.SetCount(list82, num5); - Span span82 = CollectionsMarshal.AsSpan(list82); - index = 0; - span82[index] = "Always prioritise next quest during teleportation/zone transitions"; - index++; - span82[index] = "Improved accepted quest logic with abandoned quest detection"; - index++; - span82[index] = "Show quests without quest paths as Locked"; - reference81 = new ChangeEntry(EChangeCategory.Changed, "Quest prioritisation improvements", list82); - num3++; - span80[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Deep Dungeon, Hildibrand, Yok Huy, Monster Hunter Wilds Collab, and Doman Enclave quests"); - num3++; - span80[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed accepted/active quest display and Hildibrand quest issues"); - reference79 = new ChangelogEntry("6.12", releaseDate44, list80); + span81[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest level requirement check log spam"); + reference80 = new ChangelogEntry("6.15", releaseDate42, list81); + num2++; + ref ChangelogEntry reference81 = ref span[num2]; + DateOnly releaseDate43 = new DateOnly(2025, 10, 8); + num3 = 1; + List list82 = new List(num3); + CollectionsMarshal.SetCount(list82, num3); + Span span82 = CollectionsMarshal.AsSpan(list82); + num4 = 0; + span82[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed abandoned quest check logic if quest were MSQ"); + reference81 = new ChangelogEntry("6.14", releaseDate43, list82); num2++; ref ChangelogEntry reference82 = ref span[num2]; - DateOnly releaseDate45 = new DateOnly(2025, 10, 3); - num3 = 1; - List list83 = new List(num3); - CollectionsMarshal.SetCount(list83, num3); + DateOnly releaseDate44 = new DateOnly(2025, 10, 8); + num4 = 2; + List list83 = new List(num4); + CollectionsMarshal.SetCount(list83, num4); Span span83 = CollectionsMarshal.AsSpan(list83); - num4 = 0; - span83[num4] = new ChangeEntry(EChangeCategory.Changed, "Added remaining checks for quest priority to prevent infinite teleport looping"); - reference82 = new ChangelogEntry("6.11", releaseDate45, list83); - num2++; - ref ChangelogEntry reference83 = ref span[num2]; - DateOnly releaseDate46 = new DateOnly(2025, 10, 2); - num4 = 1; - List list84 = new List(num4); - CollectionsMarshal.SetCount(list84, num4); - Span span84 = CollectionsMarshal.AsSpan(list84); num3 = 0; - ref ChangeEntry reference84 = ref span84[num3]; - index = 2; - List list85 = new List(index); - CollectionsMarshal.SetCount(list85, index); - Span span85 = CollectionsMarshal.AsSpan(list85); - num5 = 0; - span85[num5] = "Don't show quests as available if player doesn't meet level requirements"; - num5++; - span85[num5] = "Updated 'required for MSQ' text in Crystal Tower quest preset window"; - reference84 = new ChangeEntry(EChangeCategory.Changed, "Quest window improvements", list85); - reference83 = new ChangelogEntry("6.10", releaseDate46, list84); + ref ChangeEntry reference83 = ref span83[num3]; + num5 = 3; + List list84 = new List(num5); + CollectionsMarshal.SetCount(list84, num5); + Span span84 = CollectionsMarshal.AsSpan(list84); + index = 0; + span84[index] = "Context menu option to add required quests and their chain to priority list"; + index++; + span84[index] = "AetheryteShortcut to multiple quests"; + index++; + span84[index] = "Artisan as a recommended plugin/dependency"; + reference83 = new ChangeEntry(EChangeCategory.Added, "Quest improvements", list84); + num3++; + span83[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed abandoned quest check and priority list issues"); + reference82 = new ChangelogEntry("6.13", releaseDate44, list83); num2++; - ref ChangelogEntry reference85 = ref span[num2]; - DateOnly releaseDate47 = new DateOnly(2025, 9, 21); - num3 = 5; - List list86 = new List(num3); - CollectionsMarshal.SetCount(list86, num3); - Span span86 = CollectionsMarshal.AsSpan(list86); + ref ChangelogEntry reference84 = ref span[num2]; + DateOnly releaseDate45 = new DateOnly(2025, 10, 7); + num3 = 4; + List list85 = new List(num3); + CollectionsMarshal.SetCount(list85, num3); + Span span85 = CollectionsMarshal.AsSpan(list85); num4 = 0; - ref ChangeEntry reference86 = ref span86[num4]; - num5 = 4; + ref ChangeEntry reference85 = ref span85[num4]; + index = 4; + List list86 = new List(index); + CollectionsMarshal.SetCount(list86, index); + Span span86 = CollectionsMarshal.AsSpan(list86); + num5 = 0; + span86[num5] = "FATE combat handling with auto level syncing"; + num5++; + span86[num5] = "Start accepted quests from journal with 'Start as next quest'"; + num5++; + span86[num5] = "Update quest tracking when quests are hidden or prioritised in game"; + num5++; + span86[num5] = "QuestMap as a recommended plugin/dependency"; + reference85 = new ChangeEntry(EChangeCategory.Added, "FATE and quest tracking", list86); + num4++; + ref ChangeEntry reference86 = ref span85[num4]; + num5 = 3; List list87 = new List(num5); CollectionsMarshal.SetCount(list87, num5); Span span87 = CollectionsMarshal.AsSpan(list87); index = 0; - span87[index] = "Reworked event quest handling - automatically displays when events are active"; + span87[index] = "Always prioritise next quest during teleportation/zone transitions"; index++; - span87[index] = "Reworked journal system with improved filtering and display"; + span87[index] = "Improved accepted quest logic with abandoned quest detection"; index++; - span87[index] = "Reworked Priority Quests tab (Manual Priority and Quest Presets)"; - index++; - span87[index] = "Quest path viewer site (https://wigglymuffin.github.io/FFXIV-Tools/)"; - reference86 = new ChangeEntry(EChangeCategory.Added, "Major system reworks", list87); + span87[index] = "Show quests without quest paths as Locked"; + reference86 = new ChangeEntry(EChangeCategory.Changed, "Quest prioritisation improvements", list87); num4++; - ref ChangeEntry reference87 = ref span86[num4]; - index = 4; - List list88 = new List(index); - CollectionsMarshal.SetCount(list88, index); - Span span88 = CollectionsMarshal.AsSpan(list88); - num5 = 0; - span88[num5] = "Questionable.IsQuestCompleted"; - num5++; - span88[num5] = "Questionable.IsQuestAvailable"; - num5++; - span88[num5] = "Questionable.IsQuestAccepted"; - num5++; - span88[num5] = "Questionable.IsQuestUnobtainable"; - reference87 = new ChangeEntry(EChangeCategory.Added, "New IPC commands", list88); + span85[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Deep Dungeon, Hildibrand, Yok Huy, Monster Hunter Wilds Collab, and Doman Enclave quests"); num4++; - ref ChangeEntry reference88 = ref span86[num4]; - num5 = 5; - List list89 = new List(num5); - CollectionsMarshal.SetCount(list89, num5); - Span span89 = CollectionsMarshal.AsSpan(list89); - index = 0; - span89[index] = "Improved JSON quest validation with specific error reasons"; - index++; - span89[index] = "Added stop at sequence stop condition"; - index++; - span89[index] = "Improved Pandora plugin conflict detection"; - index++; - span89[index] = "Improved DialogueChoices regex matching"; - index++; - span89[index] = "Improved refresh checker for all quest states"; - reference88 = new ChangeEntry(EChangeCategory.Changed, "Various improvements", list89); - num4++; - span86[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.31 Occult Crescent quests"); - num4++; - span86[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed cutscene crashes, Single Player Duty triggers, and various quest issues"); - reference85 = new ChangelogEntry("6.9", releaseDate47, list86); + span85[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed accepted/active quest display and Hildibrand quest issues"); + reference84 = new ChangelogEntry("6.12", releaseDate45, list85); num2++; - ref ChangelogEntry reference89 = ref span[num2]; - DateOnly releaseDate48 = new DateOnly(2025, 9, 2); - num4 = 4; - List list90 = new List(num4); - CollectionsMarshal.SetCount(list90, num4); - Span span90 = CollectionsMarshal.AsSpan(list90); + ref ChangelogEntry reference87 = ref span[num2]; + DateOnly releaseDate46 = new DateOnly(2025, 10, 3); + num4 = 1; + List list88 = new List(num4); + CollectionsMarshal.SetCount(list88, num4); + Span span88 = CollectionsMarshal.AsSpan(list88); num3 = 0; - ref ChangeEntry reference90 = ref span90[num3]; - index = 4; - List list91 = new List(index); - CollectionsMarshal.SetCount(list91, index); - Span span91 = CollectionsMarshal.AsSpan(list91); - num5 = 0; - span91[num5] = "Help commands and priority quest command"; - num5++; - span91[num5] = "Prevent 'CompleteQuest' step setting"; - num5++; - span91[num5] = "Duty counts and controls in 'Quest Battles' tab"; - num5++; - span91[num5] = "'Refresh quest timer' setting (WIP)"; - reference90 = new ChangeEntry(EChangeCategory.Added, "Command and UI improvements", list91); - num3++; - span90[num3] = new ChangeEntry(EChangeCategory.Changed, "Improved 'Clear All' buttons to require CTRL being held"); - num3++; - span90[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Zodiac quests and 7.31 Cosmic/Occult Crescent quests"); - num3++; - span90[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Fishing for Friendship and Cosmic Exploration quests"); - reference89 = new ChangelogEntry("6.8", releaseDate48, list90); + span88[num3] = new ChangeEntry(EChangeCategory.Changed, "Added remaining checks for quest priority to prevent infinite teleport looping"); + reference87 = new ChangelogEntry("6.11", releaseDate46, list88); num2++; - ref ChangelogEntry reference91 = ref span[num2]; - DateOnly releaseDate49 = new DateOnly(2025, 8, 27); - num3 = 4; - List list92 = new List(num3); - CollectionsMarshal.SetCount(list92, num3); - Span span92 = CollectionsMarshal.AsSpan(list92); + ref ChangelogEntry reference88 = ref span[num2]; + DateOnly releaseDate47 = new DateOnly(2025, 10, 2); + num3 = 1; + List list89 = new List(num3); + CollectionsMarshal.SetCount(list89, num3); + Span span89 = CollectionsMarshal.AsSpan(list89); num4 = 0; - ref ChangeEntry reference92 = ref span92[num4]; - num5 = 2; - List list93 = new List(num5); - CollectionsMarshal.SetCount(list93, num5); - Span span93 = CollectionsMarshal.AsSpan(list93); - index = 0; - span93[index] = "Icon to 'Clear All' button in stop conditions"; - index++; - span93[index] = "Duty counts and 'Enable All' button in 'Duties' tab"; - reference92 = new ChangeEntry(EChangeCategory.Added, "UI improvements", list93); - num4++; - span92[num4] = new ChangeEntry(EChangeCategory.Changed, "Renamed 'Clear' button to 'Clear All' in priority window"); - num4++; - span92[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Rising 2025 Event Quests"); - num4++; - span92[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed clipboard assigning blacklist to whitelist in 'Duties' tab"); - reference91 = new ChangelogEntry("6.7", releaseDate49, list92); - num2++; - ref ChangelogEntry reference93 = ref span[num2]; - DateOnly releaseDate50 = new DateOnly(2025, 8, 25); - num4 = 2; - List list94 = new List(num4); - CollectionsMarshal.SetCount(list94, num4); - Span span94 = CollectionsMarshal.AsSpan(list94); - num3 = 0; - ref ChangeEntry reference94 = ref span94[num3]; + ref ChangeEntry reference89 = ref span89[num4]; index = 2; - List list95 = new List(index); - CollectionsMarshal.SetCount(list95, index); - Span span95 = CollectionsMarshal.AsSpan(list95); + List list90 = new List(index); + CollectionsMarshal.SetCount(list90, index); + Span span90 = CollectionsMarshal.AsSpan(list90); num5 = 0; - span95[num5] = "Missing emotes to schema and emote handler"; + span90[num5] = "Don't show quests as available if player doesn't meet level requirements"; num5++; - span95[num5] = "Improved stop conditions with 'Clear All' button"; - reference94 = new ChangeEntry(EChangeCategory.Added, "Emote support and stop conditions", list95); - num3++; - span94[num3] = new ChangeEntry(EChangeCategory.Changed, "Stop at level functionality"); - reference93 = new ChangelogEntry("6.6", releaseDate50, list94); + span90[num5] = "Updated 'required for MSQ' text in Crystal Tower quest preset window"; + reference89 = new ChangeEntry(EChangeCategory.Changed, "Quest window improvements", list90); + reference88 = new ChangelogEntry("6.10", releaseDate47, list89); num2++; - ref ChangelogEntry reference95 = ref span[num2]; + ref ChangelogEntry reference90 = ref span[num2]; + DateOnly releaseDate48 = new DateOnly(2025, 9, 21); + num4 = 5; + List list91 = new List(num4); + CollectionsMarshal.SetCount(list91, num4); + Span span91 = CollectionsMarshal.AsSpan(list91); + num3 = 0; + ref ChangeEntry reference91 = ref span91[num3]; + num5 = 4; + List list92 = new List(num5); + CollectionsMarshal.SetCount(list92, num5); + Span span92 = CollectionsMarshal.AsSpan(list92); + index = 0; + span92[index] = "Reworked event quest handling - automatically displays when events are active"; + index++; + span92[index] = "Reworked journal system with improved filtering and display"; + index++; + span92[index] = "Reworked Priority Quests tab (Manual Priority and Quest Presets)"; + index++; + span92[index] = "Quest path viewer site (https://wigglymuffin.github.io/FFXIV-Tools/)"; + reference91 = new ChangeEntry(EChangeCategory.Added, "Major system reworks", list92); + num3++; + ref ChangeEntry reference92 = ref span91[num3]; + index = 4; + List list93 = new List(index); + CollectionsMarshal.SetCount(list93, index); + Span span93 = CollectionsMarshal.AsSpan(list93); + num5 = 0; + span93[num5] = "Questionable.IsQuestCompleted"; + num5++; + span93[num5] = "Questionable.IsQuestAvailable"; + num5++; + span93[num5] = "Questionable.IsQuestAccepted"; + num5++; + span93[num5] = "Questionable.IsQuestUnobtainable"; + reference92 = new ChangeEntry(EChangeCategory.Added, "New IPC commands", list93); + num3++; + ref ChangeEntry reference93 = ref span91[num3]; + num5 = 5; + List list94 = new List(num5); + CollectionsMarshal.SetCount(list94, num5); + Span span94 = CollectionsMarshal.AsSpan(list94); + index = 0; + span94[index] = "Improved JSON quest validation with specific error reasons"; + index++; + span94[index] = "Added stop at sequence stop condition"; + index++; + span94[index] = "Improved Pandora plugin conflict detection"; + index++; + span94[index] = "Improved DialogueChoices regex matching"; + index++; + span94[index] = "Improved refresh checker for all quest states"; + reference93 = new ChangeEntry(EChangeCategory.Changed, "Various improvements", list94); + num3++; + span91[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.31 Occult Crescent quests"); + num3++; + span91[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed cutscene crashes, Single Player Duty triggers, and various quest issues"); + reference90 = new ChangelogEntry("6.9", releaseDate48, list91); + num2++; + ref ChangelogEntry reference94 = ref span[num2]; + DateOnly releaseDate49 = new DateOnly(2025, 9, 2); + num3 = 4; + List list95 = new List(num3); + CollectionsMarshal.SetCount(list95, num3); + Span span95 = CollectionsMarshal.AsSpan(list95); + num4 = 0; + ref ChangeEntry reference95 = ref span95[num4]; + index = 4; + List list96 = new List(index); + CollectionsMarshal.SetCount(list96, index); + Span span96 = CollectionsMarshal.AsSpan(list96); + num5 = 0; + span96[num5] = "Help commands and priority quest command"; + num5++; + span96[num5] = "Prevent 'CompleteQuest' step setting"; + num5++; + span96[num5] = "Duty counts and controls in 'Quest Battles' tab"; + num5++; + span96[num5] = "'Refresh quest timer' setting (WIP)"; + reference95 = new ChangeEntry(EChangeCategory.Added, "Command and UI improvements", list96); + num4++; + span95[num4] = new ChangeEntry(EChangeCategory.Changed, "Improved 'Clear All' buttons to require CTRL being held"); + num4++; + span95[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Zodiac quests and 7.31 Cosmic/Occult Crescent quests"); + num4++; + span95[num4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Fishing for Friendship and Cosmic Exploration quests"); + reference94 = new ChangelogEntry("6.8", releaseDate49, list95); + num2++; + ref ChangelogEntry reference96 = ref span[num2]; + DateOnly releaseDate50 = new DateOnly(2025, 8, 27); + num4 = 4; + List list97 = new List(num4); + CollectionsMarshal.SetCount(list97, num4); + Span span97 = CollectionsMarshal.AsSpan(list97); + num3 = 0; + ref ChangeEntry reference97 = ref span97[num3]; + num5 = 2; + List list98 = new List(num5); + CollectionsMarshal.SetCount(list98, num5); + Span span98 = CollectionsMarshal.AsSpan(list98); + index = 0; + span98[index] = "Icon to 'Clear All' button in stop conditions"; + index++; + span98[index] = "Duty counts and 'Enable All' button in 'Duties' tab"; + reference97 = new ChangeEntry(EChangeCategory.Added, "UI improvements", list98); + num3++; + span97[num3] = new ChangeEntry(EChangeCategory.Changed, "Renamed 'Clear' button to 'Clear All' in priority window"); + num3++; + span97[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Rising 2025 Event Quests"); + num3++; + span97[num3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed clipboard assigning blacklist to whitelist in 'Duties' tab"); + reference96 = new ChangelogEntry("6.7", releaseDate50, list97); + num2++; + ref ChangelogEntry reference98 = ref span[num2]; DateOnly releaseDate51 = new DateOnly(2025, 8, 25); num3 = 2; - List list96 = new List(num3); - CollectionsMarshal.SetCount(list96, num3); - Span span96 = CollectionsMarshal.AsSpan(list96); + List list99 = new List(num3); + CollectionsMarshal.SetCount(list99, num3); + Span span99 = CollectionsMarshal.AsSpan(list99); num4 = 0; - span96[num4] = new ChangeEntry(EChangeCategory.Fixed, "Potential fix to single/solo duties softlocking"); + ref ChangeEntry reference99 = ref span99[num4]; + index = 2; + List list100 = new List(index); + CollectionsMarshal.SetCount(list100, index); + Span span100 = CollectionsMarshal.AsSpan(list100); + num5 = 0; + span100[num5] = "Missing emotes to schema and emote handler"; + num5++; + span100[num5] = "Improved stop conditions with 'Clear All' button"; + reference99 = new ChangeEntry(EChangeCategory.Added, "Emote support and stop conditions", list100); num4++; - span96[num4] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added San d'Oria: The Second Walk and various side quests"); - reference95 = new ChangelogEntry("6.5", releaseDate51, list96); + span99[num4] = new ChangeEntry(EChangeCategory.Changed, "Stop at level functionality"); + reference98 = new ChangelogEntry("6.6", releaseDate51, list99); + num2++; + ref ChangelogEntry reference100 = ref span[num2]; + DateOnly releaseDate52 = new DateOnly(2025, 8, 25); + num4 = 2; + List list101 = new List(num4); + CollectionsMarshal.SetCount(list101, num4); + Span span101 = CollectionsMarshal.AsSpan(list101); + num3 = 0; + span101[num3] = new ChangeEntry(EChangeCategory.Fixed, "Potential fix to single/solo duties softlocking"); + num3++; + span101[num3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added San d'Oria: The Second Walk and various side quests"); + reference100 = new ChangelogEntry("6.5", releaseDate52, list101); Changelogs = list; } } diff --git a/Questionable/Questionable.External/AutoDutyIpc.cs b/Questionable/Questionable.External/AutoDutyIpc.cs index ad81204..12c6a83 100644 --- a/Questionable/Questionable.External/AutoDutyIpc.cs +++ b/Questionable/Questionable.External/AutoDutyIpc.cs @@ -35,6 +35,10 @@ internal sealed class AutoDutyIpc private bool _loggedContentHasPathQueryWarning; + private bool _loggedLevelingModeWarning; + + public const int MinimumLevelForLevelingMode = 15; + public AutoDutyIpc(IDalamudPluginInterface pluginInterface, Configuration configuration, TerritoryData territoryData, ILogger logger) { _configuration = configuration; @@ -46,6 +50,7 @@ internal sealed class AutoDutyIpc _isStopped = pluginInterface.GetIpcSubscriber("AutoDuty.IsStopped"); _stop = pluginInterface.GetIpcSubscriber("AutoDuty.Stop"); _loggedContentHasPathQueryWarning = false; + _loggedLevelingModeWarning = false; } public bool IsConfiguredToRunContent(DutyOptions? dutyOptions) @@ -73,6 +78,24 @@ internal sealed class AutoDutyIpc return false; } + public bool IsConfiguredToRunLevelingMode() + { + return _configuration.Duties.RunLevelingModeWhenUnderleveled; + } + + public bool IsConfiguredToRunLevelingMode(int currentPlayerLevel) + { + if (!_configuration.Duties.RunLevelingModeWhenUnderleveled) + { + return false; + } + if (currentPlayerLevel < 15) + { + return false; + } + return true; + } + public bool HasPath(uint cfcId) { if (!_territoryData.TryGetContentFinderCondition(cfcId, out TerritoryData.ContentFinderConditionData contentFinderConditionData)) @@ -118,6 +141,35 @@ internal sealed class AutoDutyIpc } } + public bool StartLevelingMode() + { + try + { + _logger.LogInformation("Starting AutoDuty Leveling mode (Support) - AutoDuty will select the best dungeon"); + _setConfig.InvokeAction("leveling", "Support"); + _run.InvokeAction(0u, 1, !_configuration.Advanced.DisableAutoDutyBareMode); + return true; + } + catch (IpcError ipcError) + { + if (!_loggedLevelingModeWarning) + { + _logger.LogWarning("Unable to start AutoDuty Leveling mode: {Message}", ipcError.Message); + _loggedLevelingModeWarning = true; + } + return false; + } + catch (Exception exception) + { + if (!_loggedLevelingModeWarning) + { + _logger.LogWarning(exception, "Unable to start AutoDuty Leveling mode"); + _loggedLevelingModeWarning = true; + } + return false; + } + } + public bool IsStopped() { try diff --git a/Questionable/Questionable.External/QuestionableIpc.cs b/Questionable/Questionable.External/QuestionableIpc.cs index fa5c1c4..605dc92 100644 --- a/Questionable/Questionable.External/QuestionableIpc.cs +++ b/Questionable/Questionable.External/QuestionableIpc.cs @@ -51,6 +51,17 @@ internal sealed class QuestionableIpc : IDisposable public required int TargetValue { get; init; } } + public sealed class MsqLevelLockData + { + public required bool IsLevelLocked { get; init; } + + public required int LevelsNeeded { get; init; } + + public required int RequiredLevel { get; init; } + + public required string? QuestName { get; init; } + } + private const string IpcIsRunning = "Questionable.IsRunning"; private const string IpcGetCurrentQuestId = "Questionable.GetCurrentQuestId"; @@ -145,6 +156,18 @@ internal sealed class QuestionableIpc : IDisposable private const string IpcGetAllQuestSequenceStopConditions = "Questionable.GetAllQuestSequenceStopConditions"; + private const string IpcGetLevelStopMode = "Questionable.GetLevelStopMode"; + + private const string IpcSetLevelStopMode = "Questionable.SetLevelStopMode"; + + private const string IpcGetSequenceStopMode = "Questionable.GetSequenceStopMode"; + + private const string IpcSetSequenceStopMode = "Questionable.SetSequenceStopMode"; + + private const string IpcGetQuestStopMode = "Questionable.GetQuestStopMode"; + + private const string IpcSetQuestStopMode = "Questionable.SetQuestStopMode"; + private const string IpcGetAlliedSocietyRemainingAllowances = "Questionable.AlliedSociety.GetRemainingAllowances"; private const string IpcGetAlliedSocietyTimeUntilReset = "Questionable.AlliedSociety.GetTimeUntilReset"; @@ -163,6 +186,16 @@ internal sealed class QuestionableIpc : IDisposable private const string IpcGetAlliedSocietyOptimalQuests = "Questionable.AlliedSociety.GetOptimalQuests"; + private const string IpcIsLevelingModeEnabled = "Questionable.IsLevelingModeEnabled"; + + private const string IpcSetLevelingModeEnabled = "Questionable.SetLevelingModeEnabled"; + + private const string IpcGetMsqLevelLockInfo = "Questionable.GetMsqLevelLockInfo"; + + private const string IpcStartLevelingMode = "Questionable.StartLevelingMode"; + + private const string IpcStopLevelingMode = "Questionable.StopLevelingMode"; + private readonly QuestController _questController; private readonly QuestRegistry _questRegistry; @@ -181,6 +214,8 @@ internal sealed class QuestionableIpc : IDisposable private readonly IServiceProvider _serviceProvider; + private readonly AutoDutyIpc _autoDutyIpc; + private readonly ICallGateProvider _isRunning; private readonly ICallGateProvider _getCurrentQuestId; @@ -275,6 +310,18 @@ internal sealed class QuestionableIpc : IDisposable private readonly ICallGateProvider> _getAllQuestSequenceStopConditions; + private readonly ICallGateProvider _getLevelStopMode; + + private readonly ICallGateProvider _setLevelStopMode; + + private readonly ICallGateProvider _getSequenceStopMode; + + private readonly ICallGateProvider _setSequenceStopMode; + + private readonly ICallGateProvider _getQuestStopMode; + + private readonly ICallGateProvider _setQuestStopMode; + private readonly ICallGateProvider _getAlliedSocietyRemainingAllowances; private readonly ICallGateProvider _getAlliedSocietyTimeUntilReset; @@ -293,7 +340,17 @@ internal sealed class QuestionableIpc : IDisposable private readonly ICallGateProvider> _getAlliedSocietyOptimalQuests; - public QuestionableIpc(QuestController questController, EventInfoComponent eventInfoComponent, QuestRegistry questRegistry, QuestFunctions questFunctions, QuestData questData, ManualPriorityComponent manualPriorityComponent, PresetBuilderComponent presetBuilderComponent, Configuration configuration, IDalamudPluginInterface pluginInterface, IServiceProvider serviceProvider) + private readonly ICallGateProvider _isLevelingModeEnabled; + + private readonly ICallGateProvider _setLevelingModeEnabled; + + private readonly ICallGateProvider _getMsqLevelLockInfo; + + private readonly ICallGateProvider _startLevelingMode; + + private readonly ICallGateProvider _stopLevelingMode; + + public QuestionableIpc(QuestController questController, EventInfoComponent eventInfoComponent, QuestRegistry questRegistry, QuestFunctions questFunctions, QuestData questData, ManualPriorityComponent manualPriorityComponent, PresetBuilderComponent presetBuilderComponent, Configuration configuration, IDalamudPluginInterface pluginInterface, IServiceProvider serviceProvider, AutoDutyIpc autoDutyIpc) { QuestionableIpc questionableIpc = this; _questController = questController; @@ -305,6 +362,7 @@ internal sealed class QuestionableIpc : IDisposable _configuration = configuration; _pluginInterface = pluginInterface; _serviceProvider = serviceProvider; + _autoDutyIpc = autoDutyIpc; _isRunning = pluginInterface.GetIpcProvider("Questionable.IsRunning"); _isRunning.RegisterFunc(() => questController.AutomationType != QuestController.EAutomationType.Manual || questController.IsRunning); _getCurrentQuestId = pluginInterface.GetIpcProvider("Questionable.GetCurrentQuestId"); @@ -400,6 +458,62 @@ internal sealed class QuestionableIpc : IDisposable _removeQuestSequenceStopCondition.RegisterFunc(RemoveQuestSequenceStopCondition); _getAllQuestSequenceStopConditions = pluginInterface.GetIpcProvider>("Questionable.GetAllQuestSequenceStopConditions"); _getAllQuestSequenceStopConditions.RegisterFunc(GetAllQuestSequenceStopConditions); + _getLevelStopMode = pluginInterface.GetIpcProvider("Questionable.GetLevelStopMode"); + _getLevelStopMode.RegisterFunc(() => (int)questionableIpc._configuration.Stop.LevelStopMode); + _setLevelStopMode = pluginInterface.GetIpcProvider("Questionable.SetLevelStopMode"); + _setLevelStopMode.RegisterFunc(delegate(int mode) + { + if (!Enum.IsDefined(typeof(Configuration.EStopConditionMode), mode)) + { + return false; + } + questionableIpc._configuration.Stop.LevelStopMode = (Configuration.EStopConditionMode)mode; + questionableIpc._pluginInterface.SavePluginConfig(questionableIpc._configuration); + return true; + }); + _getSequenceStopMode = pluginInterface.GetIpcProvider("Questionable.GetSequenceStopMode"); + _getSequenceStopMode.RegisterFunc(() => (int)questionableIpc._configuration.Stop.SequenceStopMode); + _setSequenceStopMode = pluginInterface.GetIpcProvider("Questionable.SetSequenceStopMode"); + _setSequenceStopMode.RegisterFunc(delegate(int mode) + { + if (!Enum.IsDefined(typeof(Configuration.EStopConditionMode), mode)) + { + return false; + } + questionableIpc._configuration.Stop.SequenceStopMode = (Configuration.EStopConditionMode)mode; + questionableIpc._pluginInterface.SavePluginConfig(questionableIpc._configuration); + return true; + }); + _getQuestStopMode = pluginInterface.GetIpcProvider("Questionable.GetQuestStopMode"); + _getQuestStopMode.RegisterFunc((string questId) => (int)(questionableIpc._configuration.Stop.QuestStopModes.TryGetValue(questId, out var value) ? value : ((Configuration.EStopConditionMode)(-1)))); + _setQuestStopMode = pluginInterface.GetIpcProvider("Questionable.SetQuestStopMode"); + _setQuestStopMode.RegisterFunc(delegate(string questId, int mode) + { + if (!Enum.IsDefined(typeof(Configuration.EStopConditionMode), mode)) + { + return false; + } + if (ElementId.TryFromString(questId, out ElementId elementId) && elementId != null && questionableIpc._questRegistry.IsKnownQuest(elementId)) + { + if (mode == 0) + { + questionableIpc._configuration.Stop.QuestsToStopAfter.Remove(elementId); + questionableIpc._configuration.Stop.QuestStopModes.Remove(questId); + questionableIpc._configuration.Stop.QuestSequences.Remove(questId); + } + else + { + if (!questionableIpc._configuration.Stop.QuestsToStopAfter.Contains(elementId)) + { + questionableIpc._configuration.Stop.QuestsToStopAfter.Add(elementId); + } + questionableIpc._configuration.Stop.QuestStopModes[questId] = (Configuration.EStopConditionMode)mode; + } + questionableIpc._pluginInterface.SavePluginConfig(questionableIpc._configuration); + return true; + } + return false; + }); _getAlliedSocietyRemainingAllowances = pluginInterface.GetIpcProvider("Questionable.AlliedSociety.GetRemainingAllowances"); _getAlliedSocietyRemainingAllowances.RegisterFunc(GetAlliedSocietyRemainingAllowances); _getAlliedSocietyTimeUntilReset = pluginInterface.GetIpcProvider("Questionable.AlliedSociety.GetTimeUntilReset"); @@ -418,6 +532,16 @@ internal sealed class QuestionableIpc : IDisposable _addAlliedSocietyOptimalQuests.RegisterFunc(AddAlliedSocietyOptimalQuests); _getAlliedSocietyOptimalQuests = pluginInterface.GetIpcProvider>("Questionable.AlliedSociety.GetOptimalQuests"); _getAlliedSocietyOptimalQuests.RegisterFunc(GetAlliedSocietyOptimalQuests); + _isLevelingModeEnabled = pluginInterface.GetIpcProvider("Questionable.IsLevelingModeEnabled"); + _isLevelingModeEnabled.RegisterFunc(IsLevelingModeEnabled); + _setLevelingModeEnabled = pluginInterface.GetIpcProvider("Questionable.SetLevelingModeEnabled"); + _setLevelingModeEnabled.RegisterFunc(SetLevelingModeEnabled); + _getMsqLevelLockInfo = pluginInterface.GetIpcProvider("Questionable.GetMsqLevelLockInfo"); + _getMsqLevelLockInfo.RegisterFunc(GetMsqLevelLockInfo); + _startLevelingMode = pluginInterface.GetIpcProvider("Questionable.StartLevelingMode"); + _startLevelingMode.RegisterFunc(StartLevelingMode); + _stopLevelingMode = pluginInterface.GetIpcProvider("Questionable.StopLevelingMode"); + _stopLevelingMode.RegisterFunc(StopLevelingMode); } private bool StartQuest(string questId, bool single) @@ -969,7 +1093,7 @@ internal sealed class QuestionableIpc : IDisposable { return new StopConditionData { - Enabled = _configuration.Stop.LevelToStopAfter, + Enabled = (_configuration.Stop.LevelStopMode != Configuration.EStopConditionMode.Off), TargetValue = _configuration.Stop.TargetLevel }; } @@ -980,7 +1104,7 @@ internal sealed class QuestionableIpc : IDisposable { return false; } - _configuration.Stop.LevelToStopAfter = enabled; + _configuration.Stop.LevelStopMode = (enabled ? Configuration.EStopConditionMode.Pause : Configuration.EStopConditionMode.Off); _configuration.Stop.TargetLevel = targetLevel; _pluginInterface.SavePluginConfig(_configuration); return true; @@ -990,7 +1114,7 @@ internal sealed class QuestionableIpc : IDisposable { return new StopConditionData { - Enabled = _configuration.Stop.SequenceToStopAfter, + Enabled = (_configuration.Stop.SequenceStopMode != Configuration.EStopConditionMode.Off), TargetValue = _configuration.Stop.TargetSequence }; } @@ -1001,7 +1125,7 @@ internal sealed class QuestionableIpc : IDisposable { return false; } - _configuration.Stop.SequenceToStopAfter = enabled; + _configuration.Stop.SequenceStopMode = (enabled ? Configuration.EStopConditionMode.Pause : Configuration.EStopConditionMode.Off); _configuration.Stop.TargetSequence = targetSequence; _pluginInterface.SavePluginConfig(_configuration); return true; @@ -1213,6 +1337,56 @@ internal sealed class QuestionableIpc : IDisposable select q.QuestId.ToString()).ToList(); } + private bool IsLevelingModeEnabled() + { + return _configuration.Duties.RunLevelingModeWhenUnderleveled; + } + + private bool SetLevelingModeEnabled(bool enabled) + { + _configuration.Duties.RunLevelingModeWhenUnderleveled = enabled; + _pluginInterface.SavePluginConfig(_configuration); + return true; + } + + private MsqLevelLockData? GetMsqLevelLockInfo() + { + var (flag, levelsNeeded, requiredLevel, questName) = _questFunctions.GetMsqLevelLockInfo(); + if (!flag) + { + return null; + } + return new MsqLevelLockData + { + IsLevelLocked = flag, + LevelsNeeded = levelsNeeded, + RequiredLevel = requiredLevel, + QuestName = questName + }; + } + + private bool StartLevelingMode() + { + if (!_autoDutyIpc.IsConfiguredToRunLevelingMode()) + { + return false; + } + return _autoDutyIpc.StartLevelingMode(); + } + + private bool StopLevelingMode() + { + try + { + _autoDutyIpc.Stop(); + return true; + } + catch + { + return false; + } + } + public void Dispose() { _exportQuestPriority.UnregisterFunc(); @@ -1262,6 +1436,12 @@ internal sealed class QuestionableIpc : IDisposable _getStopQuestList.UnregisterFunc(); _setStopConditionsEnabled.UnregisterFunc(); _getStopConditionsEnabled.UnregisterFunc(); + _setQuestStopMode.UnregisterFunc(); + _getQuestStopMode.UnregisterFunc(); + _setSequenceStopMode.UnregisterFunc(); + _getSequenceStopMode.UnregisterFunc(); + _setLevelStopMode.UnregisterFunc(); + _getLevelStopMode.UnregisterFunc(); _getAlliedSocietiesWithAvailableQuests.UnregisterFunc(); _getAlliedSocietyCurrentRank.UnregisterFunc(); _getAlliedSocietyIsMaxRank.UnregisterFunc(); @@ -1271,5 +1451,10 @@ internal sealed class QuestionableIpc : IDisposable _getAlliedSocietyRemainingAllowances.UnregisterFunc(); _addAlliedSocietyOptimalQuests.UnregisterFunc(); _getAlliedSocietyOptimalQuests.UnregisterFunc(); + _stopLevelingMode.UnregisterFunc(); + _startLevelingMode.UnregisterFunc(); + _getMsqLevelLockInfo.UnregisterFunc(); + _setLevelingModeEnabled.UnregisterFunc(); + _isLevelingModeEnabled.UnregisterFunc(); } } diff --git a/Questionable/Questionable.Functions/QuestFunctions.cs b/Questionable/Questionable.Functions/QuestFunctions.cs index df0b450..5057ca8 100644 --- a/Questionable/Questionable.Functions/QuestFunctions.cs +++ b/Questionable/Questionable.Functions/QuestFunctions.cs @@ -72,6 +72,10 @@ internal sealed class QuestFunctions private ElementId? _lastLoggedAcceptedHiddenMsq; + private bool _loggedNoClassQuestsAvailable; + + private bool _loggedAdventurerClass; + public QuestFunctions(QuestRegistry questRegistry, QuestData questData, JournalData journalData, AetheryteFunctions aetheryteFunctions, AlliedSocietyQuestFunctions alliedSocietyQuestFunctions, AlliedSocietyData alliedSocietyData, AetheryteData aetheryteData, Configuration configuration, IDataManager dataManager, IObjectTable objectTable, IClientState clientState, IGameGui gameGui, IAetheryteList aetheryteList, ILogger logger) { _questRegistry = questRegistry; @@ -195,6 +199,8 @@ internal sealed class QuestFunctions { _logger.LogInformation("MSQ {MsqId} requires level {RequiredLevel}, current level {CurrentLevel}. Checking for early class quests to level up.", questReference.CurrentQuest, quest.Info.Level, currentLevel); _lastLoggedLevelLockedMsq = questReference.CurrentQuest; + _loggedNoClassQuestsAvailable = false; + _loggedAdventurerClass = false; } if (valueOrDefault != EClassJob.Adventurer) { @@ -222,10 +228,10 @@ internal sealed class QuestFunctions return new QuestReference(questId2, QuestManager.GetQuestSequence(questId2.Value), questReference.State); } } - else if (_lastLoggedLevelLockedMsq == questReference.CurrentQuest && _lastLoggedForcedClassQuest == null) + else if (!_loggedNoClassQuestsAvailable) { _logger.LogWarning("No class quests passed the filter for {ClassJob} at level {CurrentLevel}", valueOrDefault, currentLevel); - _lastLoggedLevelLockedMsq = null; + _loggedNoClassQuestsAvailable = true; } List list2 = (from x in _questRegistry.GetKnownClassJobQuests(valueOrDefault, includeRoleQuests: false) where x.Level <= currentLevel && x.Level <= 5 && !IsQuestAcceptedOrComplete(x.QuestId) && IsReadyToAcceptQuest(x.QuestId) @@ -237,11 +243,16 @@ internal sealed class QuestFunctions _logger.LogInformation("MSQ level locked. Prioritizing class quest {ClassQuestId} for {ClassJob} (level {QuestLevel}) from registry", questId3, valueOrDefault, list2.First().Level); return new QuestReference(questId3, QuestManager.GetQuestSequence(questId3.Value), questReference.State); } - _logger.LogWarning("MSQ level locked but no available early class quests found for {ClassJob} at level {CurrentLevel}", valueOrDefault, currentLevel); + if (!_loggedNoClassQuestsAvailable) + { + _logger.LogWarning("MSQ level locked but no available early class quests found for {ClassJob} at level {CurrentLevel}", valueOrDefault, currentLevel); + _loggedNoClassQuestsAvailable = true; + } } - else + else if (!_loggedAdventurerClass) { _logger.LogWarning("Current class is Adventurer, cannot find class quests"); + _loggedAdventurerClass = true; } Questionable.Model.Quest quest2; ElementId elementId = (from x in GetNextPriorityQuestsThatCanBeAccepted() @@ -253,11 +264,17 @@ internal sealed class QuestFunctions _logger.LogInformation("MSQ {MsqId} requires level {RequiredLevel}, current level {CurrentLevel}. Prioritizing early class quest {ClassQuestId} (from priority list)", questReference.CurrentQuest, quest.Info.Level, currentLevel, elementId); return new QuestReference(elementId, QuestManager.GetQuestSequence(elementId.Value), questReference.State); } - _logger.LogWarning("MSQ {MsqId} is level locked (requires {RequiredLevel}, current {CurrentLevel}) and no early class quests available. Cannot proceed.", questReference.CurrentQuest, quest.Info.Level, currentLevel); + if (!_loggedNoClassQuestsAvailable) + { + _logger.LogWarning("MSQ {MsqId} is level locked (requires {RequiredLevel}, current {CurrentLevel}) and no early class quests available. Cannot proceed.", questReference.CurrentQuest, quest.Info.Level, currentLevel); + _loggedNoClassQuestsAvailable = true; + } return QuestReference.NoQuest(MainScenarioQuestState.Unavailable); } _lastLoggedLevelLockedMsq = null; _lastLoggedForcedClassQuest = null; + _loggedNoClassQuestsAvailable = false; + _loggedAdventurerClass = false; } if (questReference.CurrentQuest != null && !IsQuestAccepted(questReference.CurrentQuest)) { @@ -931,7 +948,7 @@ internal sealed class QuestFunctions private static bool IsQuestLocked(UnlockLinkId unlockLinkId) { - return IsQuestUnobtainable(unlockLinkId); + return IsQuestUnobtainableStatic(unlockLinkId); } private bool IsQuestLocked(AethernetId aethernetId) @@ -995,11 +1012,24 @@ internal sealed class QuestFunctions } if (elementId is UnlockLinkId unlockLinkId) { - return IsQuestUnobtainable(unlockLinkId); + return IsQuestUnobtainableStatic(unlockLinkId); } return false; } + private static bool IsQuestUnobtainableStatic(UnlockLinkId unlockLinkId) + { + if (unlockLinkId.Value == 506) + { + return !IsFestivalActive(160, (ushort)2); + } + if (unlockLinkId.Value == 568) + { + return !IsFestivalActive(160, (ushort)3); + } + return true; + } + public unsafe bool IsQuestUnobtainable(QuestId questId, ElementId? extraCompletedQuest = null) { IQuestInfo questInfo = _questData.GetQuestInfo(questId); @@ -1024,10 +1054,6 @@ internal sealed class QuestFunctions } if (DateTime.UtcNow > dateTime) { - if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value)) - { - _logger.LogDebug("UnlockLink quest {QuestId} unobtainable: expiry {ExpiryUtc} (UTC) is before now {NowUtc}", questId, dateTime.ToString("o"), DateTime.UtcNow.ToString("o")); - } return true; } } @@ -1043,7 +1069,7 @@ internal sealed class QuestFunctions List list = quest?.Root?.QuestSequence; if (list != null && list.Count > 0) { - goto IL_027a; + goto IL_0228; } } if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value)) @@ -1053,8 +1079,8 @@ internal sealed class QuestFunctions } return true; } - goto IL_027a; - IL_027a: + goto IL_0228; + IL_0228: if (questInfo2.QuestLocks.Count > 0) { int num = questInfo2.QuestLocks.Count((QuestId x) => IsQuestComplete(x) || x.Equals(extraCompletedQuest)); @@ -1073,41 +1099,15 @@ internal sealed class QuestFunctions DateTime valueOrDefault = seasonalQuestExpiry.GetValueOrDefault(); TimeSpan timeOfDay2 = valueOrDefault.TimeOfDay; TimeSpan timeSpan2 = new TimeSpan(23, 59, 59); - bool flag2 = false; - DateTime dateTime2; - if (timeOfDay2 == TimeSpan.Zero || timeOfDay2 == timeSpan2) - { - dateTime2 = EventInfoComponent.AtDailyReset(DateOnly.FromDateTime(valueOrDefault)); - flag2 = true; - } - else - { - dateTime2 = ((valueOrDefault.Kind == DateTimeKind.Utc) ? valueOrDefault : valueOrDefault.ToUniversalTime()); - } - if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value)) - { - _logger.LogDebug("Quest {QuestId} seasonal expiry raw={ExpiryRaw} Kind={Kind} TimeOfDay={TimeOfDay} treatedAsDailyReset={TreatedAsDailyReset}", questId, valueOrDefault.ToString("o"), valueOrDefault.Kind, valueOrDefault.TimeOfDay, flag2); - _logger.LogDebug("Quest {QuestId} expiry check: nowUtc={Now:o}, expiryUtc={Expiry:o}, expired={Expired}", questId, DateTime.UtcNow, dateTime2, DateTime.UtcNow > dateTime2); - } + DateTime dateTime2 = ((!(timeOfDay2 == TimeSpan.Zero) && !(timeOfDay2 == timeSpan2)) ? ((valueOrDefault.Kind == DateTimeKind.Utc) ? valueOrDefault : valueOrDefault.ToUniversalTime()) : EventInfoComponent.AtDailyReset(DateOnly.FromDateTime(valueOrDefault))); if (DateTime.UtcNow > dateTime2) { - if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value)) - { - _logger.LogDebug("Quest {QuestId} unobtainable: seasonal expiry {ExpiryUtc} (UTC) is before now {NowUtc}", questId, dateTime2.ToString("o"), DateTime.UtcNow.ToString("o")); - } return true; } } - if ((questInfo2.IsSeasonalEvent || questInfo2.IsSeasonalQuest) && !(questInfo2.SeasonalQuestExpiry is DateTime)) + if ((questInfo2.IsSeasonalEvent || questInfo2.IsSeasonalQuest) && !questInfo2.SeasonalQuestExpiry.HasValue && !_configuration.General.ShowIncompleteSeasonalEvents) { - if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value)) - { - _logger.LogDebug("Quest {QuestId} is seasonal/event with no expiry; ShowIncompleteSeasonalEvents={ShowIncomplete}", questId, _configuration.General.ShowIncompleteSeasonalEvents); - } - if (!_configuration.General.ShowIncompleteSeasonalEvents) - { - return true; - } + return true; } if (_questData.GetLockedClassQuests().Contains(questId)) { @@ -1172,19 +1172,6 @@ internal sealed class QuestFunctions return false; } - private static bool IsQuestUnobtainable(UnlockLinkId unlockLinkId) - { - if (unlockLinkId.Value == 506) - { - return !IsFestivalActive(160, (ushort)2); - } - if (unlockLinkId.Value == 568) - { - return !IsFestivalActive(160, (ushort)3); - } - return true; - } - private unsafe static bool IsFestivalActive(ushort id, ushort? phase = null) { for (int i = 0; i < GameMain.Instance()->ActiveFestivals.Length; i++) @@ -1310,4 +1297,43 @@ internal sealed class QuestFunctions { return IsQuestComplete(_questData.LastMainScenarioQuestId); } + + public (bool isLevelLocked, int levelsNeeded, int requiredLevel, string? questName) GetMsqLevelLockInfo() + { + byte b = _objectTable.LocalPlayer?.Level ?? 0; + if (b == 0) + { + return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: 0, questName: null); + } + QuestReference item = GetMainScenarioQuest().Item1; + if (item.CurrentQuest == null) + { + return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: 0, questName: null); + } + if (IsQuestAccepted(item.CurrentQuest)) + { + return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: 0, questName: null); + } + if (_questRegistry.TryGetQuest(item.CurrentQuest, out Questionable.Model.Quest quest)) + { + int level = quest.Info.Level; + if (level <= b) + { + return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: level, questName: null); + } + int item2 = level - b; + return (isLevelLocked: true, levelsNeeded: item2, requiredLevel: level, questName: quest.Info.Name); + } + if (item.CurrentQuest is QuestId elementId && _questData.TryGetQuestInfo(elementId, out IQuestInfo questInfo) && questInfo is QuestInfo questInfo2) + { + int level2 = questInfo2.Level; + if (level2 <= b) + { + return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: level2, questName: null); + } + int item3 = level2 - b; + return (isLevelLocked: true, levelsNeeded: item3, requiredLevel: level2, questName: questInfo2.Name); + } + return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: 0, questName: null); + } } diff --git a/Questionable/Questionable.Validation/QuestValidator.cs b/Questionable/Questionable.Validation/QuestValidator.cs index a35e9fa..053f4b8 100644 --- a/Questionable/Questionable.Validation/QuestValidator.cs +++ b/Questionable/Questionable.Validation/QuestValidator.cs @@ -107,7 +107,7 @@ internal sealed class QuestValidator AlliedSociety = x.Key, Type = EIssueType.QuestDisabled, Severity = EIssueSeverity.None, - Description = $"{x.Value.Count} disabled quest(s): {value}" + Description = $"{x.Value.Count} disabled {((x.Value.Count == 1) ? "quest" : "quests")}: {value}" }; }); } diff --git a/Questionable/Questionable.Windows.ConfigComponents/DutyConfigComponent.cs b/Questionable/Questionable.Windows.ConfigComponents/DutyConfigComponent.cs index c4ba81b..426c4c3 100644 --- a/Questionable/Questionable.Windows.ConfigComponents/DutyConfigComponent.cs +++ b/Questionable/Questionable.Windows.ConfigComponents/DutyConfigComponent.cs @@ -39,29 +39,27 @@ internal sealed class DutyConfigComponent : ConfigComponent private readonly Dictionary> _allTrialNames; + private readonly Dictionary> _allNormalRaidNames; + + private readonly Dictionary> _allAllianceRaidNames; + public DutyConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, IDataManager dataManager, QuestRegistry questRegistry, AutoDutyIpc autoDutyIpc, TerritoryData territoryData) : base(pluginInterface, configuration) { _questRegistry = questRegistry; _autoDutyIpc = autoDutyIpc; - var source = (from x in dataManager.GetExcelSheet() - where x.RowId != 0 && !x.Unknown16 - orderby x.Unknown15 - select x.Content.ValueNullable into x - where x.HasValue - select x.Value into x + _contentFinderConditionNames = (from x in dataManager.GetExcelSheet() + where x.RowId != 0 && x.Content.RowId != 0 && x.ContentType.RowId == 2 select new { Expansion = (EExpansionVersion)x.TerritoryType.Value.ExVersion.RowId, CfcId = x.RowId, - Name = (territoryData.GetContentFinderCondition(x.RowId)?.Name ?? "?"), + Name = (territoryData.GetContentFinderCondition(x.RowId)?.Name ?? x.Name.ToDalamudString().ToString()), TerritoryId = x.TerritoryType.RowId, - ContentType = x.ContentType.RowId, Level = x.ClassJobLevelRequired, SortKey = x.SortKey - }).ToList(); - _contentFinderConditionNames = (from x in source - where x.ContentType == 2 + } into x + orderby x.SortKey group x by x.Expansion).ToDictionary(x => x.Key, x => x.Select(y => new DutyInfo(y.CfcId, y.TerritoryId, ConfigComponent.FormatLevel(y.Level) + " " + y.Name)).ToList()); _allTrialNames = (from x in dataManager.GetExcelSheet() where x.RowId != 0 && x.Content.RowId != 0 && x.ContentType.RowId == 4 @@ -76,6 +74,34 @@ internal sealed class DutyConfigComponent : ConfigComponent } into x orderby x.SortKey group x by x.Expansion).ToDictionary(x => x.Key, x => x.Select(y => new DutyInfo(y.CfcId, y.TerritoryId, ConfigComponent.FormatLevel(y.Level) + " " + y.Name)).ToList()); + _allNormalRaidNames = (from x in dataManager.GetExcelSheet() + where x.RowId != 0 && x.Content.RowId != 0 && x.ContentType.RowId == 5 + where x.ContentMemberType.RowId == 3 + select new + { + Expansion = (EExpansionVersion)x.TerritoryType.Value.ExVersion.RowId, + CfcId = x.RowId, + Name = (territoryData.GetContentFinderCondition(x.RowId)?.Name ?? x.Name.ToDalamudString().ToString()), + TerritoryId = x.TerritoryType.RowId, + Level = x.ClassJobLevelRequired, + SortKey = x.SortKey + } into x + orderby x.SortKey + group x by x.Expansion).ToDictionary(x => x.Key, x => x.Select(y => new DutyInfo(y.CfcId, y.TerritoryId, ConfigComponent.FormatLevel(y.Level) + " " + y.Name)).ToList()); + _allAllianceRaidNames = (from x in dataManager.GetExcelSheet() + where x.RowId != 0 && x.Content.RowId != 0 && x.ContentType.RowId == 5 + where x.ContentMemberType.RowId == 4 + select new + { + Expansion = (EExpansionVersion)x.TerritoryType.Value.ExVersion.RowId, + CfcId = x.RowId, + Name = (territoryData.GetContentFinderCondition(x.RowId)?.Name ?? x.Name.ToDalamudString().ToString()), + TerritoryId = x.TerritoryType.RowId, + Level = x.ClassJobLevelRequired, + SortKey = x.SortKey + } into x + orderby x.SortKey + group x by x.Expansion).ToDictionary(x => x.Key, x => x.Select(y => new DutyInfo(y.CfcId, y.TerritoryId, ConfigComponent.FormatLevel(y.Level) + " " + y.Name)).ToList()); } public override void DrawTab() @@ -106,6 +132,15 @@ internal sealed class DutyConfigComponent : ConfigComponent } } ImGui.Spacing(); + bool v2 = base.Configuration.Duties.RunLevelingModeWhenUnderleveled; + if (ImGui.Checkbox("Run AutoDuty Leveling mode when underleveled for MSQ", ref v2)) + { + base.Configuration.Duties.RunLevelingModeWhenUnderleveled = v2; + Save(); + } + ImGui.SameLine(); + ImGuiComponents.HelpMarker("When enabled, Questionable will automatically run AutoDuty's Leveling mode when your character is underleveled for the next Main Scenario Quest.\n\nLeveling mode runs the highest available dungeon for your level to gain XP.\n\nThis is useful for characters without the Road to 90 XP buff who may not have enough XP to continue the MSQ."); + ImGui.Spacing(); ImGui.Text("Default duty mode:"); ImGui.SameLine(); int currentItem = (int)base.Configuration.Duties.DefaultDutyMode; @@ -128,7 +163,7 @@ internal sealed class DutyConfigComponent : ConfigComponent Util.OpenLink("https://docs.google.com/spreadsheets/d/151RlpqRcCpiD_VbQn6Duf-u-S71EP7d0mx3j1PDNoNA/edit?pli=1#gid=0"); } ImGui.Separator(); - ImGui.Text("You can override the settings for each individual dungeon/trial:"); + ImGui.Text("You can override the settings for each individual duty:"); using ImRaii.IEndObject endObject2 = ImRaii.TabBar("DutyTypeTabs"); if (endObject2) { @@ -139,10 +174,24 @@ internal sealed class DutyConfigComponent : ConfigComponent DrawConfigTable(v, _contentFinderConditionNames); } } - using ImRaii.IEndObject endObject4 = ImRaii.TabItem("Trials"); - if (endObject4) + using (ImRaii.IEndObject endObject4 = ImRaii.TabItem("Trials")) { - DrawConfigTable(v, _allTrialNames); + if (endObject4) + { + DrawConfigTable(v, _allTrialNames); + } + } + using (ImRaii.IEndObject endObject5 = ImRaii.TabItem("Normal Raids")) + { + if (endObject5) + { + DrawConfigTable(v, _allNormalRaidNames); + } + } + using ImRaii.IEndObject endObject6 = ImRaii.TabItem("Alliance Raids"); + if (endObject6) + { + DrawConfigTable(v, _allAllianceRaidNames); } } DrawEnableAllButton(); @@ -375,7 +424,7 @@ internal sealed class DutyConfigComponent : ConfigComponent { base.Configuration.Duties.BlacklistedDutyCfcIds.Clear(); base.Configuration.Duties.WhitelistedDutyCfcIds.Clear(); - foreach (List item2 in _contentFinderConditionNames.Values.Concat>(_allTrialNames.Values)) + foreach (List item2 in _contentFinderConditionNames.Values.Concat>(_allTrialNames.Values).Concat>(_allNormalRaidNames.Values).Concat>(_allAllianceRaidNames.Values)) { foreach (var (item, _, _) in item2) { diff --git a/Questionable/Questionable.Windows.ConfigComponents/StopConditionComponent.cs b/Questionable/Questionable.Windows.ConfigComponents/StopConditionComponent.cs index cad44de..a37963d 100644 --- a/Questionable/Questionable.Windows.ConfigComponents/StopConditionComponent.cs +++ b/Questionable/Questionable.Windows.ConfigComponents/StopConditionComponent.cs @@ -19,6 +19,8 @@ namespace Questionable.Windows.ConfigComponents; internal sealed class StopConditionComponent : ConfigComponent { + private static readonly string[] StopModeNames = new string[3] { "Off", "Pause", "Stop" }; + private readonly IDalamudPluginInterface _pluginInterface; private readonly QuestSelector _questSelector; @@ -55,6 +57,7 @@ internal sealed class StopConditionComponent : ConfigComponent _questSelector.QuestSelected = delegate(Quest quest) { configuration.Stop.QuestsToStopAfter.Add(quest.Id); + configuration.Stop.QuestStopModes[quest.Id.ToString()] = Questionable.Configuration.EStopConditionMode.Stop; stopConditionComponent.Save(); }; } @@ -67,26 +70,30 @@ internal sealed class StopConditionComponent : ConfigComponent return; } bool v = base.Configuration.Stop.Enabled; - if (ImGui.Checkbox("Stop Questionable when any of the conditions below are met", ref v)) + if (ImGui.Checkbox("Enable stop conditions", ref v)) { base.Configuration.Stop.Enabled = v; Save(); } + ImGui.SameLine(); + ImGuiComponents.HelpMarker("Pause: Stops automation when condition is met, but allows resuming past it.\nStop: True stop, blocks automation from starting/resuming if condition is already met."); ImGui.Separator(); using (ImRaii.Disabled(!v)) { ImGui.Text("Stop when character level reaches:"); - bool v2 = base.Configuration.Stop.LevelToStopAfter; - if (ImGui.Checkbox("Enable level stop condition", ref v2)) + int currentItem = (int)base.Configuration.Stop.LevelStopMode; + ImGui.SetNextItemWidth(100f); + if (ImGui.Combo((ImU8String)"##LevelMode", ref currentItem, (ReadOnlySpan)StopModeNames, StopModeNames.Length)) { - base.Configuration.Stop.LevelToStopAfter = v2; + base.Configuration.Stop.LevelStopMode = (Configuration.EStopConditionMode)currentItem; Save(); } - using (ImRaii.Disabled(!v2)) + ImGui.SameLine(); + using (ImRaii.Disabled(base.Configuration.Stop.LevelStopMode == Questionable.Configuration.EStopConditionMode.Off)) { int data = base.Configuration.Stop.TargetLevel; ImGui.SetNextItemWidth(100f); - if (ImGui.InputInt("Stop at level", ref data, 1, 5)) + if (ImGui.InputInt("Target level", ref data, 1, 5)) { base.Configuration.Stop.TargetLevel = Math.Max(1, Math.Min(100, data)); Save(); @@ -103,18 +110,20 @@ internal sealed class StopConditionComponent : ConfigComponent } } ImGui.Separator(); - ImGui.Text("Stop on quest sequence:"); - bool v3 = base.Configuration.Stop.SequenceToStopAfter; - if (ImGui.Checkbox("Enable global quest sequence stop condition", ref v3)) + ImGui.Text("Stop on quest sequence (global):"); + int currentItem2 = (int)base.Configuration.Stop.SequenceStopMode; + ImGui.SetNextItemWidth(100f); + if (ImGui.Combo((ImU8String)"##SequenceMode", ref currentItem2, (ReadOnlySpan)StopModeNames, StopModeNames.Length)) { - base.Configuration.Stop.SequenceToStopAfter = v3; + base.Configuration.Stop.SequenceStopMode = (Configuration.EStopConditionMode)currentItem2; Save(); } - using (ImRaii.Disabled(!v3)) + ImGui.SameLine(); + using (ImRaii.Disabled(base.Configuration.Stop.SequenceStopMode == Questionable.Configuration.EStopConditionMode.Off)) { int data2 = base.Configuration.Stop.TargetSequence; ImGui.SetNextItemWidth(100f); - if (ImGui.InputInt("Stop at sequence", ref data2, 1, 1)) + if (ImGui.InputInt("Target sequence", ref data2, 1, 1)) { base.Configuration.Stop.TargetSequence = Math.Max(0, Math.Min(255, data2)); Save(); @@ -130,8 +139,8 @@ internal sealed class StopConditionComponent : ConfigComponent text2.AppendLiteral(")"); ImGui.TextDisabled(text2); } - ImGui.TextWrapped("Note: Individual quest sequences below override this global setting."); } + ImGui.TextWrapped("Note: Individual quest sequences below override this global setting."); ImGui.Separator(); ImGui.Text("Stop when completing quests (or reaching specific sequences):"); DrawCurrentlyAcceptedQuests(); @@ -143,11 +152,13 @@ internal sealed class StopConditionComponent : ConfigComponent { if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Trash, "Clear All")) { - base.Configuration.Stop.QuestsToStopAfter.Clear(); foreach (ElementId item in questsToStopAfter) { - base.Configuration.Stop.QuestSequences.Remove(item.ToString()); + string key = item.ToString(); + base.Configuration.Stop.QuestSequences.Remove(key); + base.Configuration.Stop.QuestStopModes.Remove(key); } + base.Configuration.Stop.QuestsToStopAfter.Clear(); Save(); } } @@ -186,16 +197,34 @@ internal sealed class StopConditionComponent : ConfigComponent { _questTooltipComponent.Draw(quest2.Info); } - ImGui.SameLine(); string text3 = elementId.ToString(); - base.Configuration.Stop.QuestSequences.TryGetValue(text3, out var value); - bool v4 = value.HasValue; - ImU8String label = new ImU8String(8, 1); - label.AppendLiteral("##UseSeq"); + int currentItem3 = (int)base.Configuration.Stop.QuestStopModes.GetValueOrDefault(text3, Questionable.Configuration.EStopConditionMode.Stop); + ImGui.SameLine(); + ImGui.SetNextItemWidth(70f); + ImU8String label = new ImU8String(6, 1); + label.AppendLiteral("##Mode"); label.AppendFormatted(text3); - if (ImGui.Checkbox(label, ref v4)) + if (ImGui.Combo(label, ref currentItem3, (ReadOnlySpan)StopModeNames, StopModeNames.Length)) { - if (v4) + if (currentItem3 == 0) + { + quest = quest2; + } + else + { + base.Configuration.Stop.QuestStopModes[text3] = (Configuration.EStopConditionMode)currentItem3; + Save(); + } + } + ImGui.SameLine(); + base.Configuration.Stop.QuestSequences.TryGetValue(text3, out var value); + bool v2 = value.HasValue; + ImU8String label2 = new ImU8String(8, 1); + label2.AppendLiteral("##UseSeq"); + label2.AppendFormatted(text3); + if (ImGui.Checkbox(label2, ref v2)) + { + if (v2) { base.Configuration.Stop.QuestSequences[text3] = 1; } @@ -209,17 +238,17 @@ internal sealed class StopConditionComponent : ConfigComponent { ImGui.SetTooltip("Stop at specific sequence (unchecked = stop on quest completion)"); } - using (ImRaii.Disabled(!v4)) + using (ImRaii.Disabled(!v2)) { ImGui.SameLine(); ImGui.Text("Seq:"); ImGui.SameLine(); ImGui.SetNextItemWidth(85f); int data3 = value ?? 1; - ImU8String label2 = new ImU8String(10, 1); - label2.AppendLiteral("##SeqValue"); - label2.AppendFormatted(text3); - if (ImGui.InputInt(label2, ref data3, 1, 1) && v4) + ImU8String label3 = new ImU8String(10, 1); + label3.AppendLiteral("##SeqValue"); + label3.AppendFormatted(text3); + if (ImGui.InputInt(label3, ref data3, 1, 1) && v2) { base.Configuration.Stop.QuestSequences[text3] = Math.Max(0, Math.Min(255, data3)); Save(); @@ -237,8 +266,10 @@ internal sealed class StopConditionComponent : ConfigComponent } if (quest != null) { + string key2 = quest.Id.ToString(); base.Configuration.Stop.QuestsToStopAfter.Remove(quest.Id); - base.Configuration.Stop.QuestSequences.Remove(quest.Id.ToString()); + base.Configuration.Stop.QuestSequences.Remove(key2); + base.Configuration.Stop.QuestStopModes.Remove(key2); Save(); } } @@ -286,6 +317,7 @@ internal sealed class StopConditionComponent : ConfigComponent if (ImGuiComponents.IconButton($"##Add{item.Id}", FontAwesomeIcon.Plus)) { base.Configuration.Stop.QuestsToStopAfter.Add(item.Id); + base.Configuration.Stop.QuestStopModes[item.Id.ToString()] = Questionable.Configuration.EStopConditionMode.Stop; Save(); } } diff --git a/Questionable/Questionable.Windows.JournalComponents/QuestJournalUtils.cs b/Questionable/Questionable.Windows.JournalComponents/QuestJournalUtils.cs index c8e53d7..6db7508 100644 --- a/Questionable/Questionable.Windows.JournalComponents/QuestJournalUtils.cs +++ b/Questionable/Questionable.Windows.JournalComponents/QuestJournalUtils.cs @@ -103,7 +103,7 @@ internal sealed class QuestJournalUtils } else { - ImGui.SetTooltip((incompletePrerequisiteQuests.Count == 1) ? "Add this quest to the priority list" : $"Add this quest and {incompletePrerequisiteQuests.Count - 1} required quest(s) to the priority list in completion order"); + ImGui.SetTooltip((incompletePrerequisiteQuests.Count == 1) ? "Add this quest to the priority list" : $"Add this quest and {incompletePrerequisiteQuests.Count - 1} required {((incompletePrerequisiteQuests.Count - 1 == 1) ? "quest" : "quests")} to the priority list in completion order"); } } bool flag = _commandManager.Commands.ContainsKey("/questinfo"); diff --git a/Questionable/Questionable.Windows.QuestComponents/ActiveQuestComponent.cs b/Questionable/Questionable.Windows.QuestComponents/ActiveQuestComponent.cs index d8b45a3..d42a064 100644 --- a/Questionable/Questionable.Windows.QuestComponents/ActiveQuestComponent.cs +++ b/Questionable/Questionable.Windows.QuestComponents/ActiveQuestComponent.cs @@ -15,6 +15,7 @@ using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Questionable.Controller; using Questionable.Controller.Steps.Shared; +using Questionable.External; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; @@ -49,6 +50,8 @@ internal sealed class ActiveQuestComponent private readonly IChatGui _chatGui; + private readonly AutoDutyIpc _autoDutyIpc; + private readonly ILogger _logger; public event EventHandler? Reload; @@ -60,7 +63,7 @@ internal sealed class ActiveQuestComponent return _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__MultipleWhitespaceRegex_0.Instance; } - public ActiveQuestComponent(QuestController questController, MovementController movementController, CombatController combatController, GatheringController gatheringController, QuestFunctions questFunctions, ICommandManager commandManager, Configuration configuration, QuestRegistry questRegistry, PriorityWindow priorityWindow, UiUtils uiUtils, IObjectTable objectTable, IClientState clientState, IChatGui chatGui, ILogger logger) + public ActiveQuestComponent(QuestController questController, MovementController movementController, CombatController combatController, GatheringController gatheringController, QuestFunctions questFunctions, ICommandManager commandManager, Configuration configuration, QuestRegistry questRegistry, PriorityWindow priorityWindow, UiUtils uiUtils, IObjectTable objectTable, IClientState clientState, IChatGui chatGui, AutoDutyIpc autoDutyIpc, ILogger logger) { _questController = questController; _movementController = movementController; @@ -75,6 +78,7 @@ internal sealed class ActiveQuestComponent _objectTable = objectTable; _clientState = clientState; _chatGui = chatGui; + _autoDutyIpc = autoDutyIpc; _logger = logger; } @@ -146,30 +150,97 @@ internal sealed class ActiveQuestComponent _logger.LogError(ex, "Could not handle active quest buttons"); } DrawSimulationControls(); + return; + } + (bool isLevelLocked, int levelsNeeded, int requiredLevel, string? questName) msqLevelLockInfo = _questFunctions.GetMsqLevelLockInfo(); + bool item = msqLevelLockInfo.isLevelLocked; + int item2 = msqLevelLockInfo.levelsNeeded; + int item3 = msqLevelLockInfo.requiredLevel; + string item4 = msqLevelLockInfo.questName; + int currentPlayerLevel = _objectTable.LocalPlayer?.Level ?? 0; + if (item && _autoDutyIpc.IsConfiguredToRunLevelingMode(currentPlayerLevel) && item4 != null) + { + Vector4 col = ImGuiColors.DalamudYellow; + ImU8String text = new ImU8String(22, 2); + text.AppendLiteral("MSQ '"); + text.AppendFormatted(item4); + text.AppendLiteral("' requires level "); + text.AppendFormatted(item3); + ImGui.TextColored(in col, text); + col = ImGuiColors.DalamudGrey; + ImU8String text2 = new ImU8String(61, 2); + text2.AppendLiteral("You need "); + text2.AppendFormatted(item2); + text2.AppendLiteral(" more level"); + text2.AppendFormatted((item2 == 1) ? string.Empty : "s"); + text2.AppendLiteral(" - Leveling mode will start automatically"); + ImGui.TextColored(in col, text2); + using (ImRaii.Disabled(_questController.IsRunning || !_autoDutyIpc.IsStopped())) + { + if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) + { + _logger.LogInformation("Start button clicked with MSQ level-locked. Starting AutoDuty leveling mode."); + if (_autoDutyIpc.StartLevelingMode()) + { + _chatGui.Print($"Starting AutoDuty Leveling mode to reach level {item3} for MSQ '{item4}'.", "Questionable", 576); + } + else + { + _chatGui.PrintError("Failed to start AutoDuty Leveling mode. Please check that AutoDuty is installed and configured.", "Questionable", 576); + } + } + } + if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) + { + if (!_autoDutyIpc.IsStopped()) + { + ImGui.SetTooltip("AutoDuty is currently running."); + } + else + { + ImU8String tooltip = new ImU8String(53, 1); + tooltip.AppendLiteral("Start AutoDuty Leveling mode to reach level "); + tooltip.AppendFormatted(item3); + tooltip.AppendLiteral(" for MSQ."); + ImGui.SetTooltip(tooltip); + } + } } else { ImGui.Text("No active quest"); - if (!isMinimized) + } + if (!isMinimized) + { + Vector4 col = ImGuiColors.DalamudGrey; + ImU8String text3 = new ImU8String(14, 1); + text3.AppendFormatted(_questRegistry.Count); + text3.AppendLiteral(" quests loaded"); + ImGui.TextColored(in col, text3); + } + ImGui.SameLine(); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop)) + { + _movementController.Stop(); + _questController.Stop("Manual (no active quest)"); + _gatheringController.Stop("Manual (no active quest)"); + if (!_autoDutyIpc.IsStopped()) { - Vector4 col = ImGuiColors.DalamudGrey; - ImU8String text = new ImU8String(14, 1); - text.AppendFormatted(_questRegistry.Count); - text.AppendLiteral(" quests loaded"); - ImGui.TextColored(in col, text); - } - if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop)) - { - _movementController.Stop(); - _questController.Stop("Manual (no active quest)"); - _gatheringController.Stop("Manual (no active quest)"); - } - ImGui.SameLine(); - if (ImGuiComponents.IconButton(FontAwesomeIcon.SortAmountDown)) - { - _priorityWindow.ToggleOrUncollapse(); + try + { + _autoDutyIpc.Stop(); + } + catch (Exception exception) + { + _logger.LogWarning(exception, "Failed to stop AutoDuty"); + } } } + ImGui.SameLine(); + if (ImGuiComponents.IconButton(FontAwesomeIcon.SortAmountDown)) + { + _priorityWindow.ToggleOrUncollapse(); + } } private void DrawQuestNames(QuestController.QuestProgress currentQuest, QuestController.ECurrentQuestType? currentQuestType) @@ -237,9 +308,9 @@ internal sealed class ActiveQuestComponent ImGui.SameLine(); ImGui.TextColored(ImGuiColors.DalamudRed, "Disabled"); } - bool flag = _configuration.Stop.Enabled && _configuration.Stop.LevelToStopAfter; + bool flag = _configuration.Stop.Enabled && _configuration.Stop.LevelStopMode != Configuration.EStopConditionMode.Off; bool flag2 = _configuration.Stop.Enabled && _configuration.Stop.QuestsToStopAfter.Any((ElementId x) => !_questFunctions.IsQuestComplete(x) && !_questFunctions.IsQuestUnobtainable(x)); - bool flag3 = _configuration.Stop.Enabled && _configuration.Stop.SequenceToStopAfter; + bool flag3 = _configuration.Stop.Enabled && _configuration.Stop.SequenceStopMode != Configuration.EStopConditionMode.Off; if (flag || flag2 || flag3) { ImGui.SameLine(); diff --git a/Questionable/Questionable.Windows.QuestComponents/EventInfoComponent.cs b/Questionable/Questionable.Windows.QuestComponents/EventInfoComponent.cs index 7b966a9..bf96f41 100644 --- a/Questionable/Questionable.Windows.QuestComponents/EventInfoComponent.cs +++ b/Questionable/Questionable.Windows.QuestComponents/EventInfoComponent.cs @@ -253,12 +253,9 @@ internal sealed class EventInfoComponent } if (_questFunctions.IsQuestUnobtainable(questInfo.QuestId)) { - if (_alreadyLoggedActiveSeasonalSkip.Add(questInfo.QuestId.Value)) - { - _logger.LogDebug("Skipping quest {QuestId} '{Name}': marked unobtainable", questInfo.QuestId, questInfo.Name); - } + continue; } - else if (questInfo is UnlockLinkQuestInfo { QuestExpiry: var questExpiry }) + if (questInfo is UnlockLinkQuestInfo { QuestExpiry: var questExpiry }) { if (questExpiry.HasValue) { @@ -287,15 +284,10 @@ internal sealed class EventInfoComponent if (seasonalQuestExpiry.HasValue) { DateTime valueOrDefault2 = seasonalQuestExpiry.GetValueOrDefault(); - DateTime dateTime2 = NormalizeExpiry(valueOrDefault2); - if (dateTime2 > DateTime.UtcNow) + if (NormalizeExpiry(valueOrDefault2) > DateTime.UtcNow) { yield return questInfo; } - else if (_alreadyLoggedActiveSeasonalSkip.Add(questInfo.QuestId.Value)) - { - _logger.LogDebug("Skipping quest {QuestId} '{Name}': seasonal expiry {Expiry:o} UTC is not in the future", questInfo.QuestId, questInfo.Name, dateTime2); - } } else if (questInfo2.IsSeasonalQuest && !questInfo2.SeasonalQuestExpiry.HasValue) { diff --git a/Questionable/Questionable.Windows.QuestComponents/PresetBuilderComponent.cs b/Questionable/Questionable.Windows.QuestComponents/PresetBuilderComponent.cs index a29d39f..90ec00b 100644 --- a/Questionable/Questionable.Windows.QuestComponents/PresetBuilderComponent.cs +++ b/Questionable/Questionable.Windows.QuestComponents/PresetBuilderComponent.cs @@ -103,10 +103,12 @@ internal sealed class PresetBuilderComponent ImGui.SetTooltip("No preset quests are currently in the priority list."); return; } - ImU8String tooltip = new ImU8String(59, 1); + ImU8String tooltip = new ImU8String(51, 2); tooltip.AppendLiteral("Remove all "); tooltip.AppendFormatted(list.Count); - tooltip.AppendLiteral(" preset-related quest(s) from the priority list."); + tooltip.AppendLiteral(" preset-related "); + tooltip.AppendFormatted((list.Count == 1) ? "quest" : "quests"); + tooltip.AppendLiteral(" from the priority list."); ImGui.SetTooltip(tooltip); } } diff --git a/Questionable/Questionable/Configuration.cs b/Questionable/Questionable/Configuration.cs index e2dc621..14390e8 100644 --- a/Questionable/Questionable/Configuration.cs +++ b/Questionable/Questionable/Configuration.cs @@ -58,11 +58,13 @@ internal sealed class Configuration : IPluginConfiguration public Dictionary QuestSequences { get; set; } = new Dictionary(); - public bool LevelToStopAfter { get; set; } + public Dictionary QuestStopModes { get; set; } = new Dictionary(); + + public EStopConditionMode LevelStopMode { get; set; } public int TargetLevel { get; set; } = 50; - public bool SequenceToStopAfter { get; set; } + public EStopConditionMode SequenceStopMode { get; set; } public int TargetSequence { get; set; } = 1; } @@ -80,6 +82,8 @@ internal sealed class Configuration : IPluginConfiguration public Dictionary DutyModeOverrides { get; set; } = new Dictionary(); public Dictionary ExpansionHeaderStates { get; set; } = new Dictionary(); + + public bool RunLevelingModeWhenUnderleveled { get; set; } } internal sealed class SinglePlayerDutyConfiguration @@ -135,6 +139,13 @@ internal sealed class Configuration : IPluginConfiguration RotationSolverReborn } + internal enum EStopConditionMode + { + Off, + Pause, + Stop + } + public sealed class ElementIdNConverter : JsonConverter { public override void WriteJson(JsonWriter writer, ElementId? value, JsonSerializer serializer) diff --git a/Questionable/Questionable/QuestionablePlugin.cs b/Questionable/Questionable/QuestionablePlugin.cs index 840e9e2..0cae44d 100644 --- a/Questionable/Questionable/QuestionablePlugin.cs +++ b/Questionable/Questionable/QuestionablePlugin.cs @@ -157,6 +157,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin, IDisposable serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor(); + serviceCollection.AddTaskExecutor(); + serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskFactory(); serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor();