Improve logic for detecting side-quests based off of the quest/to-do list v0.18
authorLiza Carvelli <liza@carvel.li>
Tue, 18 Jun 2024 22:17:51 +0000 (00:17 +0200)
committerLiza Carvelli <liza@carvel.li>
Tue, 18 Jun 2024 22:17:51 +0000 (00:17 +0200)
QuestPaths/Endwalker/MSQ/D-Thavnair2/4410_The Blasphemy Unmasked.json
QuestPaths/Endwalker/MSQ/D-Thavnair2/4415_Warm Hearts, Rekindled Hopes.json
Questionable/GameFunctions.cs
Questionable/Windows/QuestWindow.cs

index a23d73f86622b627cc7b428d1b349f51316d7f79..6d23d99794692464bda8fc585685be2c290c3d40 100644 (file)
     {
       "Sequence": 3,
       "Steps": [
+        {
+          "Position": {
+            "X": 81.33277,
+            "Y": 1.8631814,
+            "Z": -96.56102
+          },
+          "TerritoryId": 963,
+          "InteractionType": "WalkTo",
+          "DisableNavmesh": true,
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            -128
+          ],
+          "Sprint": true
+        },
         {
           "DataId": 1037314,
           "Position": {
index 1b19646c41da094c5fe468401f20c97da7894451..08e581a06fdd7997ef792f5659609b1c0bc55c67 100644 (file)
@@ -99,8 +99,7 @@
           "TerritoryId": 957,
           "InteractionType": "WalkTo",
           "DisableNavmesh": true,
-          "Mount": true,
-          "Comment": "FIXME Returning to the surface means navmesh won't move anymore, but the path is still 'running'"
+          "Mount": true
         },
         {
           "Position": {
index c311869764f417e81d977a3860177cbb325e813c..cd1588c2c168d26c895597ca4d6c31a97fb51d47 100644 (file)
@@ -148,29 +148,47 @@ internal sealed unsafe class GameFunctions
 
     public (ushort CurrentQuest, byte Sequence) GetCurrentQuestInternal()
     {
-        ushort currentQuest;
-
-        // if any quest that is currently tracked (i.e. in the to-do list) exists as mapped quest, we use that
         var questManager = QuestManager.Instance();
         if (questManager != null)
         {
-            foreach (var tracked in questManager->TrackedQuestsSpan)
+            // 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 = GetMainStoryQuest(questManager);
+            if (msqQuest.CurrentQuest != 0 && !questManager->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,
+            // do the MSQ; if a side quest is the first item do that side quest.
+            //
+            // If no quests are marked as 'priority', accepting a new quest adds it to the top of the list.
+            for (int i = questManager->TrackedQuestsSpan.Length - 1; i >= 0; --i)
             {
-                switch (tracked.QuestType)
+                ushort currentQuest;
+                var trackedQuest = questManager->TrackedQuestsSpan[i];
+                switch (trackedQuest.QuestType)
                 {
                     default:
                         continue;
 
                     case 1: // normal quest
-                        currentQuest = questManager->NormalQuestsSpan[tracked.Index].QuestId;
+                        currentQuest = questManager->NormalQuestsSpan[trackedQuest.Index].QuestId;
                         break;
                 }
 
                 if (_questRegistry.IsKnownQuest(currentQuest))
                     return (currentQuest, QuestManager.GetQuestSequence(currentQuest));
             }
+
+            // if we know no quest of those currently in the to-do list, just do MSQ
+            return msqQuest;
         }
 
+        return default;
+    }
+
+    // TODO This doesn't work with unaccepted quests in NG+, only accepted quests
+    private (ushort CurrentQuest, byte Sequence) GetMainStoryQuest(QuestManager* questManager)
+    {
         var scenarioTree = AgentScenarioTree.Instance();
         if (scenarioTree == null)
             return default;
@@ -178,10 +196,14 @@ internal sealed unsafe class GameFunctions
         if (scenarioTree->Data == null)
             return default;
 
-        currentQuest = scenarioTree->Data->CurrentScenarioQuest;
+        ushort currentQuest = scenarioTree->Data->CurrentScenarioQuest;
         if (currentQuest == 0)
             return default;
 
+        // if the MSQ is hidden, we generally ignore it
+        if (questManager->IsQuestAccepted(currentQuest) && questManager->GetQuestById(currentQuest)->IsHidden)
+            return default;
+
         return (currentQuest, QuestManager.GetQuestSequence(currentQuest));
     }
 
index 11672649e7cfc1295617afe4297fa2ac264e0ac5..eb65373be6fa81f34467181d33d4a4caa2e05096 100644 (file)
@@ -36,6 +36,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
     private readonly GameUiController _gameUiController;
     private readonly Configuration _configuration;
     private readonly NavmeshIpc _navmeshIpc;
+    private readonly QuestRegistry _questRegistry;
     private readonly ILogger<QuestWindow> _logger;
 
     public QuestWindow(DalamudPluginInterface pluginInterface,
@@ -48,6 +49,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
         GameUiController gameUiController,
         Configuration configuration,
         NavmeshIpc navmeshIpc,
+        QuestRegistry questRegistry,
         ILogger<QuestWindow> logger)
         : base("Questionable###Questionable", ImGuiWindowFlags.AlwaysAutoResize)
     {
@@ -61,6 +63,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
         _gameUiController = gameUiController;
         _configuration = configuration;
         _navmeshIpc = navmeshIpc;
+        _questRegistry = questRegistry;
         _logger = logger;
 
 #if DEBUG
@@ -206,11 +209,11 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
         var q = _gameFunctions.GetCurrentQuest();
         ImGui.Text($"Current Quest: {q.CurrentQuest} → {q.Sequence}");
 
+#if false
         var questManager = QuestManager.Instance();
         if (questManager != null)
         {
-            // unsure how these are sorted
-            for (int i = 0; i < 1 /*questManager->TrackedQuestsSpan.Length*/; ++i)
+            for (int i = questManager->TrackedQuestsSpan.Length - 1; i >= 0; --i)
             {
                 var trackedQuest = questManager->TrackedQuestsSpan[i];
                 switch (trackedQuest.QuestType)
@@ -220,13 +223,15 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
                         break;
 
                     case 1:
+                        _questRegistry.TryGetQuest(questManager->NormalQuestsSpan[trackedQuest.Index].QuestId,
+                            out var quest);
                         ImGui.Text(
-                            $"Tracked quest: {questManager->NormalQuestsSpan[trackedQuest.Index].QuestId}, {trackedQuest.Index}");
+                            $"Tracked quest: {questManager->NormalQuestsSpan[trackedQuest.Index].QuestId}, {trackedQuest.Index}: {quest?.Name}");
                         break;
                 }
             }
         }
-
+#endif
 
         if (_targetManager.Target != null)
         {