Handle quests that are unlocked when a previous quest isn't complete yet
authorLiza Carvelli <liza@carvel.li>
Sun, 25 Aug 2024 12:45:08 +0000 (14:45 +0200)
committerLiza Carvelli <liza@carvel.li>
Sun, 25 Aug 2024 12:45:08 +0000 (14:45 +0200)
Questionable/Data/QuestData.cs
Questionable/Functions/QuestFunctions.cs
Questionable/Model/LeveInfo.cs
Questionable/Model/QuestInfo.cs
Questionable/Model/QuestInfoUtils.cs
Questionable/Windows/QuestComponents/QuestTooltipComponent.cs

index 3a5f64acc8fa1febc5cfe2fae15382ce4688c657..c2a8aba394eec7a8e11b32342a635186a29272ac 100644 (file)
@@ -8,7 +8,8 @@ using LLib.GameData;
 using Lumina.Excel.GeneratedSheets;
 using Questionable.Model;
 using Questionable.Model.Questing;
-using Quest = Lumina.Excel.GeneratedSheets.Quest;
+using Leve = Lumina.Excel.GeneratedSheets2.Leve;
+using Quest = Lumina.Excel.GeneratedSheets2.Quest;
 
 namespace Questionable.Data;
 
@@ -62,7 +63,7 @@ internal sealed class QuestData
 
         // workaround because the game doesn't require completion of the CT questline through normal means
         QuestInfo aTimeToEveryPurpose = (QuestInfo)_quests[new QuestId(425)];
-        aTimeToEveryPurpose.AddPreviousQuest(new QuestId(495));
+        aTimeToEveryPurpose.AddPreviousQuest(new QuestInfo.PreviousQuestInfo(new QuestId(495)));
     }
 
     public IQuestInfo GetQuestInfo(ElementId elementId)
index 7e360cd69760a0cac88d82b8a319d01730829f64..15a2dec1c2b21f83befb07cc00a13927902a7aec 100644 (file)
@@ -494,7 +494,8 @@ internal sealed unsafe class QuestFunctions
         if (questInfo.PreviousQuests.Count == 0)
             return true;
 
-        var completedQuests = questInfo.PreviousQuests.Count(x => IsQuestComplete(x) || x.Equals(extraCompletedQuest));
+        var completedQuests = questInfo.PreviousQuests.Count(x =>
+            HasEnoughProgressOnPreviousQuest(x) || x.QuestId.Equals(extraCompletedQuest));
         if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.All &&
             questInfo.PreviousQuests.Count == completedQuests)
             return true;
@@ -504,6 +505,20 @@ internal sealed unsafe class QuestFunctions
             return false;
     }
 
+    private bool HasEnoughProgressOnPreviousQuest(QuestInfo.PreviousQuestInfo previousQuestInfo)
+    {
+        if (IsQuestComplete(previousQuestInfo.QuestId))
+            return true;
+
+        if (previousQuestInfo.Sequence != 0 && IsQuestAccepted(previousQuestInfo.QuestId))
+        {
+            var progress = GetQuestProgressInfo(previousQuestInfo.QuestId);
+            return progress != null && progress.Sequence >= previousQuestInfo.Sequence;
+        }
+
+        return false;
+    }
+
     private static bool HasCompletedPreviousInstances(QuestInfo questInfo)
     {
         if (questInfo.PreviousInstanceContent.Count == 0)
index b446da9a77fea4ffd0ee787baf7b19c096546c3b..d50ac82e4aa1bd047b99dadf269eba5a03b1c0fd 100644 (file)
@@ -1,6 +1,6 @@
 using System.Collections.Generic;
 using LLib.GameData;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.GeneratedSheets2;
 using Questionable.Model.Questing;
 
 namespace Questionable.Model;
@@ -14,7 +14,7 @@ internal sealed class LeveInfo : IQuestInfo
         Level = leve.ClassJobLevel;
         JournalGenre = leve.JournalGenre.Row;
         SortKey = QuestId.Value;
-        IssuerDataId = leve.LevelLevemete.Value!.Object;
+        IssuerDataId = leve.LevelLevemete.Value!.Object.Row;
         ClassJobs = QuestInfoUtils.AsList(leve.ClassJobCategory.Value!);
         Expansion = (EExpansionVersion)leve.LevelLevemete.Value.Territory.Value!.ExVersion.Row;
     }
index 2c9c295174b835fd8a57758fc8da99d4f2ad4b0d..d78838fc95245fc31eca33047795c04695c4e982 100644 (file)
@@ -5,7 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent;
 using JetBrains.Annotations;
 using LLib.GameData;
 using Questionable.Model.Questing;
-using ExcelQuest = Lumina.Excel.GeneratedSheets.Quest;
+using ExcelQuest = Lumina.Excel.GeneratedSheets2.Quest;
 
 namespace Questionable.Model;
 
@@ -31,13 +31,18 @@ internal sealed class QuestInfo : IQuestInfo
         };
 
         Name = $"{quest.Name}{suffix}";
-        Level = quest.ClassJobLevel0;
-        IssuerDataId = quest.IssuerStart;
+        Level = quest.ClassJobLevel[0];
+        IssuerDataId = quest.IssuerStart.Row;
         IsRepeatable = quest.IsRepeatable;
-        PreviousQuests = quest.PreviousQuest
-            .Select(x => new QuestId((ushort)(x.Row & 0xFFFF)))
-            .Where(x => x.Value != 0)
-            .ToImmutableList();
+        PreviousQuests =
+            new List<PreviousQuestInfo>
+                {
+                    new(new QuestId((ushort)(quest.PreviousQuest[0].Row & 0xFFFF)), quest.Unknown7),
+                    new(new QuestId((ushort)(quest.PreviousQuest[1].Row & 0xFFFF))),
+                    new(new QuestId((ushort)(quest.PreviousQuest[1].Row & 0xFFFF)))
+                }
+                .Where(x => x.QuestId.Value != 0)
+                .ToImmutableList();
         PreviousQuestJoin = (QuestJoin)quest.PreviousQuestJoin;
         QuestLocks = quest.QuestLock
             .Select(x => new QuestId((ushort)(x.Row & 0xFFFFF)))
@@ -47,7 +52,7 @@ internal sealed class QuestInfo : IQuestInfo
         JournalGenre = quest.JournalGenre?.Row;
         SortKey = quest.SortKey;
         IsMainScenarioQuest = quest.JournalGenre?.Value?.JournalCategory?.Value?.JournalSection?.Row is 0 or 1;
-        CompletesInstantly = quest.ToDoCompleteSeq[0] == 0;
+        CompletesInstantly = quest.TodoParams[0].ToDoCompleteSeq == 0;
         PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList();
         PreviousInstanceContentJoin = (QuestJoin)quest.InstanceContentJoin;
         GrandCompany = (GrandCompany)quest.GrandCompany.Row;
@@ -64,7 +69,7 @@ internal sealed class QuestInfo : IQuestInfo
     public ushort Level { get; }
     public uint IssuerDataId { get; }
     public bool IsRepeatable { get; }
-    public ImmutableList<QuestId> PreviousQuests { get; set; }
+    public ImmutableList<PreviousQuestInfo> PreviousQuests { get; set; }
     public QuestJoin PreviousQuestJoin { get; }
     public ImmutableList<QuestId> QuestLocks { get; }
     public QuestJoin QuestLockJoin { get; }
@@ -89,8 +94,10 @@ internal sealed class QuestInfo : IQuestInfo
         AtLeastOne = 2,
     }
 
-    public void AddPreviousQuest(QuestId questId)
+    public void AddPreviousQuest(PreviousQuestInfo questId)
     {
         PreviousQuests = [..PreviousQuests, questId];
     }
+
+    public sealed record PreviousQuestInfo(QuestId QuestId, byte Sequence = 0);
 }
index 688458840ba184cd6f04648090a0cc8d18d7c0e8..4f44fd4aa27b532ff60e09e923c160a229d0af23 100644 (file)
@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using LLib.GameData;
-using Lumina.Excel.GeneratedSheets;
+using Lumina.Excel.GeneratedSheets2;
 
 namespace Questionable.Model;
 
@@ -57,8 +57,8 @@ internal static class QuestInfoUtils
                 { EClassJob.Dancer, classJobCategory.DNC },
                 { EClassJob.Reaper, classJobCategory.RPR },
                 { EClassJob.Sage, classJobCategory.SGE },
-                { EClassJob.Viper, classJobCategory.VPR },
-                { EClassJob.Pictomancer, classJobCategory.PCT }
+                { EClassJob.Viper, classJobCategory.Unknown1 },
+                { EClassJob.Pictomancer, classJobCategory.Unknown2 }
             }
             .Where(y => y.Value)
             .Select(y => y.Key)
index d181d45a82347111191de5a111bb2ea3a899258e..cda7636c2af5b31de3b5804bad13768c5867dbb6 100644 (file)
@@ -19,19 +19,22 @@ internal sealed class QuestTooltipComponent
     private readonly TerritoryData _territoryData;
     private readonly QuestFunctions _questFunctions;
     private readonly UiUtils _uiUtils;
+    private readonly Configuration _configuration;
 
     public QuestTooltipComponent(
         QuestRegistry questRegistry,
         QuestData questData,
         TerritoryData territoryData,
         QuestFunctions questFunctions,
-        UiUtils uiUtils)
+        UiUtils uiUtils,
+        Configuration configuration)
     {
         _questRegistry = questRegistry;
         _questData = questData;
         _territoryData = territoryData;
         _questFunctions = questFunctions;
         _uiUtils = uiUtils;
+        _configuration = configuration;
     }
 
     public void Draw(IQuestInfo quest)
@@ -105,13 +108,13 @@ internal sealed class QuestTooltipComponent
 
             foreach (var q in quest.PreviousQuests)
             {
-                if (_questData.TryGetQuestInfo(q, out var qInfo))
+                if (_questData.TryGetQuestInfo(q.QuestId, out var qInfo))
                 {
-                    var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q);
+                    var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q.QuestId);
                     if (!_questRegistry.IsKnownQuest(qInfo.QuestId))
                         iconColor = ImGuiColors.DalamudGrey;
 
-                    _uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
+                    _uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo, _questFunctions.IsQuestComplete(q.QuestId) ? byte.MinValue : q.Sequence), iconColor, icon);
 
                     if (qInfo is QuestInfo qstInfo && (counter <= 2 || icon != FontAwesomeIcon.Check))
                         DrawQuestUnlocks(qstInfo, counter + 1);
@@ -188,11 +191,17 @@ internal sealed class QuestTooltipComponent
             ImGui.Unindent();
     }
 
-    private static string FormatQuestUnlockName(IQuestInfo questInfo)
+    private string FormatQuestUnlockName(IQuestInfo questInfo, byte sequence = 0)
     {
+        string name = questInfo.Name;
+        if (_configuration.Advanced.AdditionalStatusInformation && sequence != 0)
+            name += $" {SeIconChar.ItemLevel.ToIconString()}";
+
         if (questInfo.IsMainScenarioQuest)
-            return $"{questInfo.Name} ({questInfo.QuestId}, MSQ)";
+            name += $" ({questInfo.QuestId}, MSQ)";
         else
-            return $"{questInfo.Name} ({questInfo.QuestId})";
+            name += $" {questInfo.Name} ({questInfo.QuestId})";
+
+        return name;
     }
 }