using Dalamud.Game.ClientState.Objects;
 using Dalamud.Plugin.Services;
 using FFXIVClientStructs.FFXIV.Client.Game.Event;
-using FFXIVClientStructs.FFXIV.Client.Game.Object;
 using FFXIVClientStructs.FFXIV.Client.Game.UI;
 using FFXIVClientStructs.FFXIV.Client.UI;
 using FFXIVClientStructs.FFXIV.Client.UI.Agent;
 {
     private readonly IAddonLifecycle _addonLifecycle;
     private readonly IDataManager _dataManager;
-    private readonly GameFunctions _gameFunctions;
     private readonly QuestFunctions _questFunctions;
+    private readonly AetheryteFunctions _aetheryteFunctions;
     private readonly ExcelFunctions _excelFunctions;
     private readonly QuestController _questController;
     private readonly QuestRegistry _questRegistry;
     public GameUiController(
         IAddonLifecycle addonLifecycle,
         IDataManager dataManager,
-        GameFunctions gameFunctions,
         QuestFunctions questFunctions,
+        AetheryteFunctions aetheryteFunctions,
         ExcelFunctions excelFunctions,
         QuestController questController,
         QuestRegistry questRegistry,
     {
         _addonLifecycle = addonLifecycle;
         _dataManager = dataManager;
-        _gameFunctions = gameFunctions;
         _questFunctions = questFunctions;
+        _aetheryteFunctions = aetheryteFunctions;
         _excelFunctions = excelFunctions;
         _questController = questController;
         _questRegistry = questRegistry;
     private unsafe bool HandleTravelYesNo(AddonSelectYesno* addonSelectYesno,
         QuestController.QuestProgress currentQuest, string actualPrompt)
     {
-        if (_gameFunctions.ReturnRequestedAt >= DateTime.Now.AddSeconds(-2) && _returnRegex.IsMatch(actualPrompt))
+        if (_aetheryteFunctions.ReturnRequestedAt >= DateTime.Now.AddSeconds(-2) && _returnRegex.IsMatch(actualPrompt))
         {
             _logger.LogInformation("Automatically confirming return...");
             addonSelectYesno->AtkUnitBase.FireCallbackInt(0);
 
         return currentStep?.AetheryteShortcut != null;
     }
 
-    private List<ElementId> GetPriorityQuests()
-    {
-        List<ElementId> priorityQuests =
-        [
-            new QuestId(1157), // Garuda (Hard)
-            new QuestId(1158), // Titan (Hard)
-            ..QuestData.CrystalTowerQuests
-        ];
-
-        EClassJob classJob = (EClassJob?)_clientState.LocalPlayer?.ClassJob.Id ?? EClassJob.Adventurer;
-        ushort[] shadowbringersRoleQuestChapters = QuestData.AllRoleQuestChapters.Select(x => x[0]).ToArray();
-        if (classJob != EClassJob.Adventurer)
-        {
-            priorityQuests.AddRange(_questRegistry.GetKnownClassJobQuests(classJob)
-                .Where(x =>
-                {
-                    if (!_questRegistry.TryGetQuest(x.QuestId, out Quest? quest) ||
-                        quest.Info is not QuestInfo questInfo)
-                        return false;
-
-                    // if no shadowbringers role quest is complete, (at least one) is required
-                    if (shadowbringersRoleQuestChapters.Contains(questInfo.NewGamePlusChapter))
-                        return !QuestData.FinalShadowbringersRoleQuests.Any(_questFunctions.IsQuestComplete);
-
-                    // ignore all other role quests
-                    if (QuestData.AllRoleQuestChapters.Any(y => y.Contains(questInfo.NewGamePlusChapter)))
-                        return false;
-
-                    // even job quests for the later expacs (after role quests were introduced) might have skills locked
-                    // behind them, e.g. reaper and sage
-
-                    return true;
-                })
-                .Select(x => x.QuestId));
-        }
-
-        return priorityQuests;
-    }
-
     public bool TryPickPriorityQuest()
     {
         if (!IsInterruptible() || _nextQuest != null || _gatheringQuest != null || _simulatedQuest != null)
             return false;
 
+        ElementId? priorityQuestId = _questFunctions.GetNextPriorityQuestThatCanBeAccepted();
+        if (priorityQuestId == null)
+            return false;
+
         // don't start a second priority quest until the first one is resolved
-        List<ElementId> priorityQuests = GetPriorityQuests();
-        if (_startedQuest != null && priorityQuests.Contains(_startedQuest.Quest.Id))
+        if (_startedQuest != null && priorityQuestId == _startedQuest.Quest.Id)
             return false;
 
-        foreach (ElementId questId in priorityQuests)
+        if (_questRegistry.TryGetQuest(priorityQuestId, out var quest))
         {
-            if (!_questFunctions.IsReadyToAcceptQuest(questId) || !_questRegistry.TryGetQuest(questId, out var quest))
-                continue;
-
-            var firstStep = quest.FindSequence(0)?.FindStep(0);
-            if (firstStep == null)
-                continue;
-
-            if (firstStep.AetheryteShortcut is { } aetheryteShortcut)
-            {
-                if (_gameFunctions.IsAetheryteUnlocked(aetheryteShortcut))
-                {
-                    _logger.LogInformation("Priority quest is accessible via aetheryte {Aetheryte}", aetheryteShortcut);
-                    SetNextQuest(quest);
-
-                    _chatGui.Print(
-                        $"[Questionable] Picking up quest '{quest.Info.Name}' as a priority over current main story/side quests.");
-                    return true;
-                }
-                else
-                {
-                    _logger.LogWarning("Ignoring priority quest {QuestId} / {QuestName}, aetheryte locked", quest.Id,
-                        quest.Info.Name);
-                }
-            }
-
-            if (firstStep is { InteractionType: EInteractionType.UseItem, ItemId: UseItem.VesperBayAetheryteTicket })
-            {
-                _logger.LogInformation("Priority quest is accessible via vesper bay");
-                SetNextQuest(quest);
-
-                _chatGui.Print(
-                    $"[Questionable] Picking up quest '{quest.Info.Name}' as a priority over current main story/side quests.");
-                return true;
-            }
-            else
-                _logger.LogTrace("Ignoring priority quest {QuestId} / {QuestName}, as we don't know how to get there",
-                    questId, quest.Info.Name);
+            SetNextQuest(quest);
+            return true;
         }
 
         return false;
 
         }
     }
 
-    internal sealed class DoAttune(GameFunctions gameFunctions, ILogger<DoAttune> logger) : ITask
+    internal sealed class DoAttune(
+        AetheryteFunctions aetheryteFunctions,
+        GameFunctions gameFunctions,
+        ILogger<DoAttune> logger) : ITask
     {
         public EAetheryteLocation AetheryteLocation { get; set; }
 
 
         public bool Start()
         {
-            if (!gameFunctions.IsAetheryteUnlocked(AetheryteLocation))
+            if (!aetheryteFunctions.IsAetheryteUnlocked(AetheryteLocation))
             {
                 logger.LogInformation("Attuning to aethernet shard {AethernetShard}", AetheryteLocation);
                 gameFunctions.InteractWith((uint)AetheryteLocation, ObjectKind.Aetheryte);
         }
 
         public ETaskResult Update() =>
-            gameFunctions.IsAetheryteUnlocked(AetheryteLocation)
+            aetheryteFunctions.IsAetheryteUnlocked(AetheryteLocation)
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
 
 
         }
     }
 
-    internal sealed class DoAttune(GameFunctions gameFunctions, ILogger<DoAttune> logger) : ITask
+    internal sealed class DoAttune(
+        AetheryteFunctions aetheryteFunctions,
+        GameFunctions gameFunctions,
+        ILogger<DoAttune> logger) : ITask
     {
         public EAetheryteLocation AetheryteLocation { get; set; }
 
 
         public bool Start()
         {
-            if (!gameFunctions.IsAetheryteUnlocked(AetheryteLocation))
+            if (!aetheryteFunctions.IsAetheryteUnlocked(AetheryteLocation))
             {
                 logger.LogInformation("Attuning to aetheryte {Aetheryte}", AetheryteLocation);
                 gameFunctions.InteractWith((uint)AetheryteLocation);
         }
 
         public ETaskResult Update() =>
-            gameFunctions.IsAetheryteUnlocked(AetheryteLocation)
+            aetheryteFunctions.IsAetheryteUnlocked(AetheryteLocation)
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
 
 
 
     internal sealed class UseAethernetShortcut(
         ILogger<UseAethernetShortcut> logger,
-        GameFunctions gameFunctions,
+        AetheryteFunctions aetheryteFunctions,
         IClientState clientState,
         AetheryteData aetheryteData,
         LifestreamIpc lifestreamIpc,
                 }
 
                 if (SkipConditions.AetheryteLocked != null &&
-                    !gameFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteLocked.Value))
+                    !aetheryteFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteLocked.Value))
                 {
                     logger.LogInformation("Skipping aethernet shortcut because the target aetheryte is locked");
                     return false;
                 }
 
                 if (SkipConditions.AetheryteUnlocked != null &&
-                    gameFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteUnlocked.Value))
+                    aetheryteFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteUnlocked.Value))
                 {
                     logger.LogInformation("Skipping aethernet shortcut because the target aetheryte is unlocked");
                     return false;
                 }
             }
 
-            if (gameFunctions.IsAetheryteUnlocked(From) &&
-                gameFunctions.IsAetheryteUnlocked(To))
+            if (aetheryteFunctions.IsAetheryteUnlocked(From) &&
+                aetheryteFunctions.IsAetheryteUnlocked(To))
             {
                 ushort territoryType = clientState.TerritoryType;
                 Vector3 playerPosition = clientState.LocalPlayer!.Position;
 
 {
     internal sealed class Factory(
         IServiceProvider serviceProvider,
-        GameFunctions gameFunctions,
+        AetheryteFunctions aetheryteFunctions,
         AetheryteData aetheryteData) : ITaskFactory
     {
         public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
                 .With(step, step.AetheryteShortcut.Value, aetheryteData.TerritoryIds[step.AetheryteShortcut.Value]);
             return
             [
-                new WaitConditionTask(() => gameFunctions.CanTeleport(step.AetheryteShortcut.Value), "CanTeleport"),
+                new WaitConditionTask(() => aetheryteFunctions.CanTeleport(step.AetheryteShortcut.Value), "CanTeleport"),
                 task
             ];
         }
 
     internal sealed class UseAetheryteShortcut(
         ILogger<UseAetheryteShortcut> logger,
-        GameFunctions gameFunctions,
+        AetheryteFunctions aetheryteFunctions,
         IClientState clientState,
         IChatGui chatGui,
         AetheryteData aetheryteData) : ISkippableTask
                     }
 
                     if (skipConditions.AetheryteLocked != null &&
-                        !gameFunctions.IsAetheryteUnlocked(skipConditions.AetheryteLocked.Value))
+                        !aetheryteFunctions.IsAetheryteUnlocked(skipConditions.AetheryteLocked.Value))
                     {
                         logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (AetheryteLocked)");
                         return false;
                     }
 
                     if (skipConditions.AetheryteUnlocked != null &&
-                        gameFunctions.IsAetheryteUnlocked(skipConditions.AetheryteUnlocked.Value))
+                        aetheryteFunctions.IsAetheryteUnlocked(skipConditions.AetheryteUnlocked.Value))
                     {
                         logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (AetheryteUnlocked)");
                         return false;
                 }
             }
 
-            if (!gameFunctions.IsAetheryteUnlocked(TargetAetheryte))
+            if (!aetheryteFunctions.IsAetheryteUnlocked(TargetAetheryte))
             {
                 chatGui.PrintError($"[Questionable] Aetheryte {TargetAetheryte} is not unlocked.");
                 throw new TaskException("Aetheryte is not unlocked");
             }
-            else if (gameFunctions.TeleportAetheryte(TargetAetheryte))
+            else if (aetheryteFunctions.TeleportAetheryte(TargetAetheryte))
             {
                 logger.LogInformation("Travelling via aetheryte...");
                 return true;
 
 
     internal sealed class CheckSkip(
         ILogger<CheckSkip> logger,
+        AetheryteFunctions aetheryteFunctions,
         GameFunctions gameFunctions,
         QuestFunctions questFunctions,
         IClientState clientState) : ITask
                     DataId: not null,
                     InteractionType: EInteractionType.AttuneAetheryte or EInteractionType.AttuneAethernetShard
                 } &&
-                gameFunctions.IsAetheryteUnlocked((EAetheryteLocation)Step.DataId.Value))
+                aetheryteFunctions.IsAetheryteUnlocked((EAetheryteLocation)Step.DataId.Value))
             {
                 logger.LogInformation("Skipping step, as aetheryte/aethernet shard is unlocked");
                 return true;
             }
 
             if (SkipConditions.AetheryteLocked != null &&
-                !gameFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteLocked.Value))
+                !aetheryteFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteLocked.Value))
             {
                 logger.LogInformation("Skipping step, as aetheryte is locked");
                 return true;
             }
 
             if (SkipConditions.AetheryteUnlocked != null &&
-                gameFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteUnlocked.Value))
+                aetheryteFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteUnlocked.Value))
             {
                 logger.LogInformation("Skipping step, as aetheryte is unlocked");
                 return true;
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using Dalamud.Plugin.Services;
 using LLib.GameData;
         return _quests[elementId] ?? throw new ArgumentOutOfRangeException(nameof(elementId));
     }
 
+    public bool TryGetQuestInfo(ElementId elementId, [NotNullWhen(true)] out IQuestInfo? questInfo)
+    {
+        return _quests.TryGetValue(elementId, out questInfo);
+    }
+
     public List<IQuestInfo> GetAllByIssuerDataId(uint targetId)
     {
         return _quests.Values
 
--- /dev/null
+using System;
+using FFXIVClientStructs.FFXIV.Client.Game;
+using FFXIVClientStructs.FFXIV.Client.Game.UI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
+
+namespace Questionable.Functions;
+
+internal sealed unsafe class AetheryteFunctions
+{
+    private readonly IServiceProvider _serviceProvider;
+    private readonly ILogger<AetheryteFunctions> _logger;
+
+    public AetheryteFunctions(IServiceProvider serviceProvider, ILogger<AetheryteFunctions> logger)
+    {
+        _serviceProvider = serviceProvider;
+        _logger = logger;
+    }
+
+    public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
+
+    public bool IsAetheryteUnlocked(uint aetheryteId, out byte subIndex)
+    {
+        subIndex = 0;
+
+        var uiState = UIState.Instance();
+        return uiState != null && uiState->IsAetheryteUnlocked(aetheryteId);
+    }
+
+    public bool IsAetheryteUnlocked(EAetheryteLocation aetheryteLocation)
+    {
+        if (aetheryteLocation == EAetheryteLocation.IshgardFirmament)
+            return _serviceProvider.GetRequiredService<QuestFunctions>().IsQuestComplete(new QuestId(3672));
+        return IsAetheryteUnlocked((uint)aetheryteLocation, out _);
+    }
+
+    public bool CanTeleport(EAetheryteLocation aetheryteLocation)
+    {
+        if ((ushort)aetheryteLocation == PlayerState.Instance()->HomeAetheryteId &&
+            ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 8) == 0)
+            return true;
+
+        return ActionManager.Instance()->GetActionStatus(ActionType.Action, 5) == 0;
+    }
+
+    public bool TeleportAetheryte(uint aetheryteId)
+    {
+        _logger.LogDebug("Attempting to teleport to aetheryte {AetheryteId}", aetheryteId);
+        if (IsAetheryteUnlocked(aetheryteId, out var subIndex))
+        {
+            if (aetheryteId == PlayerState.Instance()->HomeAetheryteId &&
+                ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 8) == 0)
+            {
+                ReturnRequestedAt = DateTime.Now;
+                if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 8))
+                {
+                    _logger.LogInformation("Using 'return' for home aetheryte");
+                    return true;
+                }
+            }
+
+            if (ActionManager.Instance()->GetActionStatus(ActionType.Action, 5) == 0)
+            {
+                // fallback if return isn't available or (more likely) on a different aetheryte
+                _logger.LogInformation("Teleporting to aetheryte {AetheryteId}", aetheryteId);
+                return Telepo.Instance()->Teleport(aetheryteId, subIndex);
+            }
+        }
+
+        return false;
+    }
+
+    public bool TeleportAetheryte(EAetheryteLocation aetheryteLocation)
+        => TeleportAetheryte((uint)aetheryteLocation);
+}
 
             .AsReadOnly();
     }
 
-    public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
-
-    public bool IsAetheryteUnlocked(uint aetheryteId, out byte subIndex)
-    {
-        subIndex = 0;
-
-        var uiState = UIState.Instance();
-        return uiState != null && uiState->IsAetheryteUnlocked(aetheryteId);
-    }
-
-    public bool IsAetheryteUnlocked(EAetheryteLocation aetheryteLocation)
-    {
-        if (aetheryteLocation == EAetheryteLocation.IshgardFirmament)
-            return _questFunctions.IsQuestComplete(new QuestId(3672));
-        return IsAetheryteUnlocked((uint)aetheryteLocation, out _);
-    }
-
-    public bool CanTeleport(EAetheryteLocation aetheryteLocation)
-    {
-        if ((ushort)aetheryteLocation == PlayerState.Instance()->HomeAetheryteId &&
-            ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 8) == 0)
-            return true;
-
-        return ActionManager.Instance()->GetActionStatus(ActionType.Action, 5) == 0;
-    }
-
-    public bool TeleportAetheryte(uint aetheryteId)
-    {
-        _logger.LogDebug("Attempting to teleport to aetheryte {AetheryteId}", aetheryteId);
-        if (IsAetheryteUnlocked(aetheryteId, out var subIndex))
-        {
-            if (aetheryteId == PlayerState.Instance()->HomeAetheryteId &&
-                ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 8) == 0)
-            {
-                ReturnRequestedAt = DateTime.Now;
-                if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 8))
-                {
-                    _logger.LogInformation("Using 'return' for home aetheryte");
-                    return true;
-                }
-            }
-
-            if (ActionManager.Instance()->GetActionStatus(ActionType.Action, 5) == 0)
-            {
-                // fallback if return isn't available or (more likely) on a different aetheryte
-                _logger.LogInformation("Teleporting to aetheryte {AetheryteId}", aetheryteId);
-                return Telepo.Instance()->Teleport(aetheryteId, subIndex);
-            }
-        }
-
-        return false;
-    }
-
-    public bool TeleportAetheryte(EAetheryteLocation aetheryteLocation)
-        => TeleportAetheryte((uint)aetheryteLocation);
-
     public bool IsFlyingUnlocked(ushort territoryId)
     {
         if (_configuration.Advanced.NeverFly)
 
 using System;
+using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using Dalamud.Memory;
 using LLib.GameUI;
 using Lumina.Excel.GeneratedSheets;
 using Questionable.Controller;
+using Questionable.Controller.Steps.Interactions;
 using Questionable.Data;
 using Questionable.Model;
+using Questionable.Model.Common;
 using Questionable.Model.Questing;
 using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
 using Quest = Questionable.Model.Quest;
 {
     private readonly QuestRegistry _questRegistry;
     private readonly QuestData _questData;
+    private readonly AetheryteFunctions _aetheryteFunctions;
     private readonly Configuration _configuration;
     private readonly IDataManager _dataManager;
     private readonly IClientState _clientState;
     private readonly IGameGui _gameGui;
 
-    public QuestFunctions(QuestRegistry questRegistry, QuestData questData, Configuration configuration,
-        IDataManager dataManager, IClientState clientState, IGameGui gameGui)
+    public QuestFunctions(
+        QuestRegistry questRegistry,
+        QuestData questData,
+        AetheryteFunctions aetheryteFunctions,
+        Configuration configuration,
+        IDataManager dataManager,
+        IClientState clientState,
+        IGameGui gameGui)
     {
         _questRegistry = questRegistry;
         _questData = questData;
+        _aetheryteFunctions = aetheryteFunctions;
         _configuration = configuration;
         _dataManager = dataManager;
         _clientState = clientState;
         {
             // always prioritize accepting MSQ quests, to make sure we don't turn in one MSQ quest and then go off to do
             // side quests until the end of time.
-            var msqQuest = GetMainScenarioQuest(questManager);
-            if (msqQuest.CurrentQuest is { Value: not 0 } && _questRegistry.IsKnownQuest(msqQuest.CurrentQuest))
+            var msqQuest = GetMainScenarioQuest();
+            if (msqQuest.CurrentQuest != null && !_questRegistry.IsKnownQuest(msqQuest.CurrentQuest))
+                msqQuest = default;
+
+            if (msqQuest.CurrentQuest != null && !IsQuestAccepted(msqQuest.CurrentQuest))
                 return msqQuest;
 
             // Use the quests in the same order as they're shown in the to-do list, e.g. if the MSQ is the first item,
                     return (currentQuest, QuestManager.GetQuestSequence(currentQuest.Value));
             }
 
-            // if we know no quest of those currently in the to-do list, just do MSQ
-            return msqQuest;
+            ElementId? priorityQuest = GetNextPriorityQuestThatCanBeAccepted();
+            if (priorityQuest != null)
+            {
+                // if we have an accepted msq quest, and know of no quest of those currently in the to-do list...
+                // (1) try and find a priority quest to do
+                return (priorityQuest, QuestManager.GetQuestSequence(priorityQuest.Value));
+            }
+            else if (msqQuest.CurrentQuest != null)
+            {
+                // (2) just do a normal msq quest
+                return msqQuest;
+            }
         }
 
         return default;
     }
 
-    private (QuestId? CurrentQuest, byte Sequence) GetMainScenarioQuest(QuestManager* questManager)
+    private (QuestId? CurrentQuest, byte Sequence) GetMainScenarioQuest()
     {
         if (QuestManager.IsQuestComplete(3759)) // Memories Rekindled
         {
             return default;
 
         // if the MSQ is hidden, we generally ignore it
+        QuestManager* questManager = QuestManager.Instance();
         if (IsQuestAccepted(currentQuest) && questManager->GetQuestById(currentQuest.Value)->IsHidden)
             return default;
 
             return null;
     }
 
+    public ElementId? GetNextPriorityQuestThatCanBeAccepted()
+    {
+        return GetPriorityQuestsThatCanBeAccepted()
+            .FirstOrDefault(x =>
+            {
+                if (!_questRegistry.TryGetQuest(x, out Quest? quest))
+                    return false;
+
+                var firstStep = quest.FindSequence(0)?.FindStep(0);
+                if (firstStep == null)
+                    return false;
+
+                if (firstStep.AetheryteShortcut is { } aetheryteShortcut &&
+                    _aetheryteFunctions.IsAetheryteUnlocked(aetheryteShortcut))
+                    return true;
+
+                if (firstStep is
+                    { InteractionType: EInteractionType.UseItem, ItemId: UseItem.VesperBayAetheryteTicket })
+                    return true;
+
+                return false;
+            });
+    }
+
+    private List<ElementId> GetPriorityQuestsThatCanBeAccepted()
+    {
+        List<ElementId> priorityQuests =
+        [
+            new QuestId(1157), // Garuda (Hard)
+            new QuestId(1158), // Titan (Hard)
+            ..QuestData.CrystalTowerQuests
+        ];
+
+        EClassJob classJob = (EClassJob?)_clientState.LocalPlayer?.ClassJob.Id ?? EClassJob.Adventurer;
+        ushort[] shadowbringersRoleQuestChapters = QuestData.AllRoleQuestChapters.Select(x => x[0]).ToArray();
+        if (classJob != EClassJob.Adventurer)
+        {
+            priorityQuests.AddRange(_questRegistry.GetKnownClassJobQuests(classJob)
+                .Where(x =>
+                {
+                    if (!_questRegistry.TryGetQuest(x.QuestId, out Quest? quest) ||
+                        quest.Info is not QuestInfo questInfo)
+                        return false;
+
+                    // if no shadowbringers role quest is complete, (at least one) is required
+                    if (shadowbringersRoleQuestChapters.Contains(questInfo.NewGamePlusChapter))
+                        return !QuestData.FinalShadowbringersRoleQuests.Any(IsQuestComplete);
+
+                    // ignore all other role quests
+                    if (QuestData.AllRoleQuestChapters.Any(y => y.Contains(questInfo.NewGamePlusChapter)))
+                        return false;
+
+                    // even job quests for the later expacs (after role quests were introduced) might have skills locked
+                    // behind them, e.g. reaper and sage
+
+                    return true;
+                })
+                .Select(x => x.QuestId));
+        }
+
+        return priorityQuests
+            .Where(_questRegistry.IsKnownQuest)
+            .Where(IsReadyToAcceptQuest)
+            .ToList();
+    }
+
     public bool IsReadyToAcceptQuest(ElementId questId)
     {
         _questRegistry.TryGetQuest(questId, out var quest);
 
     public ushort Level { get; }
     public uint IssuerDataId { get; }
     public bool IsRepeatable { get; }
-    public ImmutableList<QuestId> PreviousQuests { get; }
+    public ImmutableList<QuestId> PreviousQuests { get; set; }
     public QuestJoin PreviousQuestJoin { get; }
     public ImmutableList<QuestId> QuestLocks { get; }
     public QuestJoin QuestLockJoin { get; }
         All = 1,
         AtLeastOne = 2,
     }
+
+    public void AddPreviousQuest(QuestId questId)
+    {
+        PreviousQuests = [..PreviousQuests, questId];
+    }
 }
 
 
     private static void AddBasicFunctionsAndData(ServiceCollection serviceCollection)
     {
+        serviceCollection.AddSingleton<AetheryteFunctions>();
         serviceCollection.AddSingleton<ExcelFunctions>();
         serviceCollection.AddSingleton<GameFunctions>();
         serviceCollection.AddSingleton<ChatFunctions>();
 
     private readonly GatheringController _gatheringController;
     private readonly QuestFunctions _questFunctions;
     private readonly ICommandManager _commandManager;
-    private readonly IDalamudPluginInterface _pluginInterface;
     private readonly Configuration _configuration;
     private readonly QuestRegistry _questRegistry;
     private readonly IChatGui _chatGui;
         GatheringController gatheringController,
         QuestFunctions questFunctions,
         ICommandManager commandManager,
-        IDalamudPluginInterface pluginInterface,
         Configuration configuration,
         QuestRegistry questRegistry,
         IChatGui chatGui)
         _gatheringController = gatheringController;
         _questFunctions = questFunctions;
         _commandManager = commandManager;
-        _pluginInterface = pluginInterface;
         _configuration = configuration;
         _questRegistry = questRegistry;
         _chatGui = chatGui;
 
 
             foreach (var q in quest.PreviousQuests)
             {
-                var qInfo = _questData.GetQuestInfo(q);
-                var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
-                if (!_questRegistry.IsKnownQuest(qInfo.QuestId))
-                    iconColor = ImGuiColors.DalamudGrey;
-
-                _uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
-
-                if (qInfo is QuestInfo qstInfo && (counter <= 2 || icon != FontAwesomeIcon.Check))
-                    DrawQuestUnlocks(qstInfo, counter + 1);
+                if (_questData.TryGetQuestInfo(q, out var qInfo))
+                {
+                    var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
+                    if (!_questRegistry.IsKnownQuest(qInfo.QuestId))
+                        iconColor = ImGuiColors.DalamudGrey;
+
+                    _uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
+
+                    if (qInfo is QuestInfo qstInfo && (counter <= 2 || icon != FontAwesomeIcon.Check))
+                        DrawQuestUnlocks(qstInfo, counter + 1);
+                }
+                else
+                {
+                    using var _ = ImRaii.Disabled();
+                    _uiUtils.ChecklistItem($"Unknown Quest ({q})", ImGuiColors.DalamudGrey, FontAwesomeIcon.Question);
+                }
             }
         }