Allied Society daily 'quest', part 1
authorLiza Carvelli <liza@carvel.li>
Sat, 21 Dec 2024 14:04:31 +0000 (15:04 +0100)
committerLiza Carvelli <liza@carvel.li>
Sat, 21 Dec 2024 14:04:31 +0000 (15:04 +0100)
QuestPaths/3.x - Heavensward/Allied Societies/Vanu Vanu/A6_Vanu Vanu (all).json [new file with mode: 0644]
QuestPaths/3.x - Heavensward/Allied Societies/Vanu Vanu/Dailies/2172_Pussyfooting About.json
QuestPaths/3.x - Heavensward/Allied Societies/Vath/A7_Vath.json [new file with mode: 0644]
Questionable.Model/Questing/ElementId.cs
Questionable/Controller/CommandHandler.cs
Questionable/Data/QuestData.cs
Questionable/Functions/QuestFunctions.cs
Questionable/Model/AlliedSocietyDailyInfo.cs [new file with mode: 0644]
Questionable/Windows/PriorityWindow.cs

diff --git a/QuestPaths/3.x - Heavensward/Allied Societies/Vanu Vanu/A6_Vanu Vanu (all).json b/QuestPaths/3.x - Heavensward/Allied Societies/Vanu Vanu/A6_Vanu Vanu (all).json
new file mode 100644 (file)
index 0000000..036c298
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+  "Author": "liza",
+  "QuestSequence": [
+    {
+      "Sequence": 0,
+      "Steps": [
+        {
+          "DataId": 1016089,
+          "Position": {
+            "X": -799.46594,
+            "Y": -133.2695,
+            "Z": -404.1352
+          },
+          "StopDistance": 3,
+          "TerritoryId": 401,
+          "InteractionType": "WalkTo",
+          "AetheryteShortcut": "The Sea of Clouds - Ok' Zundu",
+          "Fly": true,
+          "SkipConditions": {
+            "AetheryteShortcutIf": {
+              "InSameTerritory": true
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
index fab39d1f316810e7b3b0cc0abb0f695b62ab2628..e12ff08c88cf130101cab031e325d25c9d5ae1f6 100644 (file)
       "Sequence": 2,
       "Steps": [
         {
-          "DataId": 1016089,
           "Position": {
             "X": -799.46594,
             "Y": -133.2695,
             "Z": -404.1352
           },
           "TerritoryId": 401,
-          "InteractionType": "Interact",
+          "InteractionType": "WalkTo",
           "AetheryteShortcut": "The Sea of Clouds - Ok' Zundu",
           "Fly": true
+        },
+        {
+          "DataId": 1016089,
+          "Position": {
+            "X": -799.46594,
+            "Y": -133.2695,
+            "Z": -404.1352
+          },
+          "TerritoryId": 401,
+          "InteractionType": "Interact",
+          "Mount": false
         }
       ]
     },
diff --git a/QuestPaths/3.x - Heavensward/Allied Societies/Vath/A7_Vath.json b/QuestPaths/3.x - Heavensward/Allied Societies/Vath/A7_Vath.json
new file mode 100644 (file)
index 0000000..6ffe879
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+  "Author": "liza",
+  "QuestSequence": [
+    {
+      "Sequence": 0,
+      "Steps": [
+        {
+          "Position": {
+            "X": 58.39701,
+            "Y": -48.000008,
+            "Z": -172.36507
+          },
+          "TerritoryId": 398,
+          "InteractionType": "WalkTo",
+          "AetheryteShortcut": "The Dravanian Forelands - Anyx Trine",
+          "Fly": true,
+          "SkipConditions": {
+            "AetheryteShortcutIf": {
+              "InSameTerritory": true
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
index 396c3524b75896c0e93c1087550efb41dfb33b03..a553ff0d3448939c1e9bd624db22fa1daefdf0ee 100644 (file)
@@ -56,6 +56,19 @@ public abstract class ElementId : IComparable<ElementId>, IEquatable<ElementId>
             return new LeveId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
         else if (value.StartsWith("S"))
             return new SatisfactionSupplyNpcId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
+        else if (value.StartsWith("A"))
+        {
+            value = value.Substring(1);
+            string[] parts = value.Split('x');
+            if (parts.Length == 2)
+            {
+                return new AlliedSocietyDailyId(
+                    byte.Parse(parts[0], CultureInfo.InvariantCulture),
+                    byte.Parse(parts[1], CultureInfo.InvariantCulture));
+            }
+            else
+                return new AlliedSocietyDailyId(byte.Parse(value, CultureInfo.InvariantCulture));
+        }
         else
             return new QuestId(ushort.Parse(value, CultureInfo.InvariantCulture));
     }
@@ -70,7 +83,8 @@ public abstract class ElementId : IComparable<ElementId>, IEquatable<ElementId>
         catch (Exception)
         {
             elementId = null;
-            return false;
+            //return false;
+            throw;
         }
     }
 }
@@ -98,3 +112,14 @@ public sealed class SatisfactionSupplyNpcId(ushort value) : ElementId(value)
         return "S" + Value.ToString(CultureInfo.InvariantCulture);
     }
 }
+
+public sealed class AlliedSocietyDailyId(byte alliedSociety, byte rank = 0) : ElementId((ushort)(alliedSociety * 10 + rank))
+{
+    public byte AlliedSociety { get; } = alliedSociety;
+    public byte Rank { get; } = rank;
+
+    public override string ToString()
+    {
+        return "A" + AlliedSociety + "x" + Rank;
+    }
+}
index c6085a18dbf97647b5c3c0e80501c76997a16091..1c2bed2bd2b96f0c390f2317e8e022d803178dcd 100644 (file)
@@ -4,6 +4,7 @@ using Dalamud.Game.ClientState.Objects;
 using Dalamud.Game.Command;
 using Dalamud.Plugin.Services;
 using Lumina.Excel.Sheets;
+using Microsoft.Extensions.Logging;
 using Questionable.Functions;
 using Questionable.Model.Questing;
 using Questionable.Windows;
index 5d7b7022975d5924783b2e245245ca08e84dab02..3cb2f295a482aa421a8cf078c529e73ed178316e 100644 (file)
@@ -68,6 +68,29 @@ internal sealed class QuestData
                 .Where(x => x.LevelLevemete.RowId != 0)
                 .Select(x => new LeveInfo(x)),
         ];
+
+        quests.AddRange(
+            dataManager.GetExcelSheet<BeastTribe>()
+                .Where(x => x.RowId > 0 && !x.Name.IsEmpty)
+                .SelectMany(x =>
+                {
+                    if (x.RowId < 5)
+                    {
+                        return ((IEnumerable<byte>)
+                            [
+                                0,
+                                ..quests.Where(y => y.AlliedSociety == (EAlliedSociety)x.RowId && y.IsRepeatable)
+                                    .Cast<QuestInfo>()
+                                    .Select(y => (byte)y.AlliedSocietyRank).Distinct()
+                            ])
+                            .Select(rank => new AlliedSocietyDailyInfo(x, rank));
+                    }
+                    else
+                    {
+                        return [new AlliedSocietyDailyInfo(x, 0)];
+                    }
+                }));
+
         _quests = quests.ToDictionary(x => x.QuestId, x => x);
 
         // workaround because the game doesn't require completion of the CT questline through normal means
index bb206a1b99d945a72f6a0c7bceaf1f4bf37089b3..fc5d7935c1c8d43b76ed1955a6b191cb58eecc3c 100644 (file)
@@ -14,7 +14,6 @@ using LLib.GameData;
 using LLib.GameUI;
 using Lumina.Excel.Sheets;
 using Questionable.Controller;
-using Questionable.Controller.Steps.Interactions;
 using Questionable.Data;
 using Questionable.Model;
 using Questionable.Model.Questing;
@@ -487,6 +486,8 @@ internal sealed unsafe class QuestFunctions
             return IsQuestAccepted(leveId);
         else if (elementId is SatisfactionSupplyNpcId)
             return false;
+        else if (elementId is AlliedSocietyDailyId)
+            return false;
         else
             throw new ArgumentOutOfRangeException(nameof(elementId));
     }
@@ -517,6 +518,8 @@ internal sealed unsafe class QuestFunctions
             return IsQuestComplete(leveId);
         else if (elementId is SatisfactionSupplyNpcId)
             return false;
+        else if (elementId is AlliedSocietyDailyId)
+            return false;
         else
             throw new ArgumentOutOfRangeException(nameof(elementId));
     }
@@ -540,6 +543,8 @@ internal sealed unsafe class QuestFunctions
             return IsQuestLocked(leveId);
         else if (elementId is SatisfactionSupplyNpcId satisfactionSupplyNpcId)
             return IsQuestLocked(satisfactionSupplyNpcId);
+        else if (elementId is AlliedSocietyDailyId alliedSocietyDailyId)
+            return IsQuestLocked(alliedSocietyDailyId);
         else
             throw new ArgumentOutOfRangeException(nameof(elementId));
     }
@@ -579,6 +584,13 @@ internal sealed unsafe class QuestFunctions
         return !HasCompletedPreviousQuests(questInfo, null);
     }
 
+    private bool IsQuestLocked(AlliedSocietyDailyId alliedSocietyDailyId)
+    {
+        PlayerState* playerState = PlayerState.Instance();
+        byte currentRank = playerState->GetBeastTribeRank(alliedSocietyDailyId.AlliedSociety);
+        return currentRank == 0 || currentRank < alliedSocietyDailyId.Rank;
+    }
+
     public bool IsDailyAlliedSocietyQuest(QuestId questId)
     {
         var questInfo = (QuestInfo)_questData.GetQuestInfo(questId);
diff --git a/Questionable/Model/AlliedSocietyDailyInfo.cs b/Questionable/Model/AlliedSocietyDailyInfo.cs
new file mode 100644 (file)
index 0000000..0f20360
--- /dev/null
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using LLib.GameData;
+using Lumina.Excel.Sheets;
+using Questionable.Data;
+using Questionable.Model.Questing;
+
+namespace Questionable.Model;
+
+internal sealed class AlliedSocietyDailyInfo : IQuestInfo
+{
+    public AlliedSocietyDailyInfo(BeastTribe beastTribe, byte rank)
+    {
+        QuestId = new AlliedSocietyDailyId((byte)beastTribe.RowId, rank);
+        Name = beastTribe.Name.ToString();
+        ClassJobs = (EAlliedSociety)beastTribe.RowId switch
+        {
+            EAlliedSociety.Amaljaa or EAlliedSociety.Sylphs or EAlliedSociety.Kobolds or EAlliedSociety.Sahagin or
+                EAlliedSociety.VanuVanu or EAlliedSociety.Vath or
+                EAlliedSociety.Kojin or EAlliedSociety.Ananta or
+                EAlliedSociety.Pixies or
+                EAlliedSociety.Arkasodara or
+                EAlliedSociety.Pelupelu =>
+                [
+                    ..ClassJobUtils.AsIndividualJobs(EExtendedClassJob.DoW),
+                    ..ClassJobUtils.AsIndividualJobs(EExtendedClassJob.DoM)
+                ],
+            EAlliedSociety.Ixal or EAlliedSociety.Moogles or EAlliedSociety.Dwarves or EAlliedSociety.Loporrits =>
+                ClassJobUtils.AsIndividualJobs(EExtendedClassJob.DoH).ToList(),
+
+            EAlliedSociety.Qitari or EAlliedSociety.Omicrons =>
+                ClassJobUtils.AsIndividualJobs(EExtendedClassJob.DoL).ToList(),
+
+            EAlliedSociety.Namazu =>
+            [
+                ..ClassJobUtils.AsIndividualJobs(EExtendedClassJob.DoH),
+                ..ClassJobUtils.AsIndividualJobs(EExtendedClassJob.DoL)
+            ],
+
+            _ => throw new ArgumentOutOfRangeException(nameof(beastTribe))
+        };
+        Expansion = (EExpansionVersion)beastTribe.Expansion.RowId;
+    }
+
+    public ElementId QuestId { get; }
+    public string Name { get; }
+    public uint IssuerDataId => 0;
+    public ImmutableList<PreviousQuestInfo> PreviousQuests { get; } = [];
+    public EQuestJoin PreviousQuestJoin => EQuestJoin.All;
+    public bool IsRepeatable => true;
+    public ushort Level => 1;
+    public EAlliedSociety AlliedSociety => EAlliedSociety.None;
+    public uint? JournalGenre => null;
+    public ushort SortKey => 0;
+    public bool IsMainScenarioQuest => false;
+    public IReadOnlyList<EClassJob> ClassJobs { get; }
+    public EExpansionVersion Expansion { get; }
+}
index 08bd35e5979187eb096b0bbe4976fbfa9e7d4b6b..8b4d5202a2f11d4b186ad5d065861cbf74a32691 100644 (file)
@@ -106,7 +106,7 @@ internal sealed class PriorityWindow : LWindow
             if (!string.IsNullOrEmpty(_searchString))
             {
                 foundQuests = _questRegistry.AllQuests
-                    .Where(x => x.Id is not SatisfactionSupplyNpcId)
+                    .Where(x => x.Id is not SatisfactionSupplyNpcId and not AlliedSocietyDailyId)
                     .Where(x => x.Info.Name.Contains(_searchString, StringComparison.CurrentCultureIgnoreCase))
                     .Where(x => !_questFunctions.IsQuestUnobtainable(x.Id));
             }