From 0dbfdae66f3fbf436b078ca1d114bc159a70ed3e Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Tue, 19 Aug 2025 13:53:37 +0200 Subject: [PATCH] Show why available priority quests can't be accepted if 'Draw Additional Status Information' is enabled --- Questionable/Controller/QuestController.cs | 5 +- Questionable/Functions/QuestFunctions.cs | 73 ++++++++++++------- .../QuestComponents/ActiveQuestComponent.cs | 26 ++++++- 3 files changed, 74 insertions(+), 30 deletions(-) diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs index 13cfba17..73851794 100644 --- a/Questionable/Controller/QuestController.cs +++ b/Questionable/Controller/QuestController.cs @@ -875,7 +875,10 @@ internal sealed class QuestController : MiniTaskController if (!IsInterruptible() || _nextQuest != null || _gatheringQuest != null || _simulatedQuest != null) return false; - ElementId? priorityQuestId = _questFunctions.GetNextPriorityQuestsThatCanBeAccepted().FirstOrDefault(); + ElementId? priorityQuestId = _questFunctions.GetNextPriorityQuestsThatCanBeAccepted() + .Where(x => x.IsAvailable) + .Select(x => x.QuestId) + .FirstOrDefault(); if (priorityQuestId == null) return false; diff --git a/Questionable/Functions/QuestFunctions.cs b/Questionable/Functions/QuestFunctions.cs index dede0335..b0ef98a4 100644 --- a/Questionable/Functions/QuestFunctions.cs +++ b/Questionable/Functions/QuestFunctions.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; +using Dalamud.Game.Text; using Dalamud.Memory; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; @@ -233,7 +235,10 @@ internal sealed unsafe class QuestFunctions return new(firstTrackedQuest, firstTrackedSequence, msqQuest.State); } - ElementId? priorityQuest = GetNextPriorityQuestsThatCanBeAccepted().FirstOrDefault(); + ElementId? priorityQuest = GetNextPriorityQuestsThatCanBeAccepted() + .Where(x => x.IsAvailable) + .Select(x => x.QuestId) + .FirstOrDefault(); if (priorityQuest != null) { // if we have an accepted msq quest, and know of no quest of those currently in the to-do list... @@ -385,7 +390,7 @@ internal sealed unsafe class QuestFunctions return null; } - public List GetNextPriorityQuestsThatCanBeAccepted() + public List GetNextPriorityQuestsThatCanBeAccepted() { // all priority quests assume we're able to teleport to the beginning (and for e.g. class quests, the end) // ideally without having to wait 15m for Return. @@ -401,44 +406,55 @@ internal sealed unsafe class QuestFunctions return GetPriorityQuests() .Where(x => IsReadyToAcceptQuest(x)) - .Where(x => + .Select(x => { if (!_questRegistry.TryGetQuest(x, out Quest? quest)) - return false; + return new PriorityQuestInfo(x, "Unknown quest"); var firstStep = quest.FindSequence(0)?.FindStep(0); if (firstStep == null) - return false; + return new PriorityQuestInfo(x, "No sequence 0 with steps"); - return firstStep.IsTeleportableForPriorityQuests(); - }) - .Where(x => - { - if (!_questRegistry.TryGetQuest(x, out Quest? quest)) - return false; + if (!firstStep.IsTeleportableForPriorityQuests()) + return new PriorityQuestInfo(x, "Can't teleport to start"); if (gil < EstimateTeleportCosts(quest)) - return false; - - return quest.AllSteps().All(y => { - if (y.Step.AetheryteShortcut is { } aetheryteShortcut && - !_aetheryteFunctions.IsAetheryteUnlocked(aetheryteShortcut)) + return new PriorityQuestInfo(x, + string.Create(CultureInfo.InvariantCulture, + $"Not enough gil, estimated cost: {EstimateTeleportCosts(quest):N0}{SeIconChar.Gil.ToIconString()}")); + } + + EAetheryteLocation? firstLockedAetheryte = quest.AllSteps() + .Select(y => { - if (y.Step.SkipConditions?.AetheryteShortcutIf?.AetheryteLocked == aetheryteShortcut) + if (y.Step.AetheryteShortcut is { } aetheryteShortcut && + !_aetheryteFunctions.IsAetheryteUnlocked(aetheryteShortcut)) { - // _logger.LogTrace("Checking priority quest {QuestId}: aetheryte locked, but is listed as skippable", quest.Id); + if (y.Step.SkipConditions?.AetheryteShortcutIf?.AetheryteLocked == aetheryteShortcut) + { + // _logger.LogTrace("Checking priority quest {QuestId}: aetheryte locked, but is listed as skippable", quest.Id); + } + else + return aetheryteShortcut; + } + + if (y.Step.AethernetShortcut is { } aethernetShortcut) + { + if (!_aetheryteFunctions.IsAetheryteUnlocked(aethernetShortcut.From)) + return aethernetShortcut.From; + + if (!_aetheryteFunctions.IsAetheryteUnlocked(aethernetShortcut.To)) + return aethernetShortcut.To; } - else return false; - } - if (y.Step.AethernetShortcut is { } aethernetShortcut && - (!_aetheryteFunctions.IsAetheryteUnlocked(aethernetShortcut.From) || - !_aetheryteFunctions.IsAetheryteUnlocked(aethernetShortcut.To))) - return false; + return (EAetheryteLocation?)null; + }) + .FirstOrDefault(y => y != null); + if (firstLockedAetheryte != null) + return new PriorityQuestInfo(x, $"Aetheryte locked: {firstLockedAetheryte}"); - return true; - }); + return new PriorityQuestInfo(x); }) .ToList(); } @@ -872,3 +888,8 @@ public enum MainScenarioQuestState Complete, LoadingScreen, } + +internal sealed record PriorityQuestInfo(ElementId QuestId, string? UnavailableReason = null) +{ + public bool IsAvailable => UnavailableReason == null; +} diff --git a/Questionable/Windows/QuestComponents/ActiveQuestComponent.cs b/Questionable/Windows/QuestComponents/ActiveQuestComponent.cs index 64ce9678..c9857aa4 100644 --- a/Questionable/Windows/QuestComponents/ActiveQuestComponent.cs +++ b/Questionable/Windows/QuestComponents/ActiveQuestComponent.cs @@ -225,10 +225,14 @@ internal sealed partial class ActiveQuestComponent ImGui.Separator(); ImGui.Text("Available priority quests:"); - List priorityQuests = _questFunctions.GetNextPriorityQuestsThatCanBeAccepted(); - if (priorityQuests.Count > 0) + List priorityQuests = _questFunctions.GetNextPriorityQuestsThatCanBeAccepted(); + var availablePriorityQuests = priorityQuests + .Where(x => x.IsAvailable) + .Select(x => x.QuestId) + .ToList(); + if (availablePriorityQuests.Count > 0) { - foreach (var questId in priorityQuests) + foreach (var questId in availablePriorityQuests) { if (_questRegistry.TryGetQuest(questId, out var quest)) ImGui.BulletText($"{quest.Info.Name} ({questId})"); @@ -236,6 +240,22 @@ internal sealed partial class ActiveQuestComponent } else ImGui.BulletText("(none)"); + + if (_configuration.Advanced.AdditionalStatusInformation) + { + var unavailablePriorityQuests = priorityQuests + .Where(x => !x.IsAvailable) + .ToList(); + if (unavailablePriorityQuests.Count > 0) + { + ImGui.Text("Unavailable priority quests:"); + foreach (var (questId, reason) in unavailablePriorityQuests) + { + if (_questRegistry.TryGetQuest(questId, out var quest)) + ImGui.BulletText($"{quest.Info.Name} ({questId}) - {reason}"); + } + } + } } } } -- 2.20.1