From 7072df6fcfc1f458852a0bb8c2fba3b64c376074 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Thu, 11 Jul 2024 02:56:42 +0200 Subject: [PATCH] Rework logic for next/simulated quests., handle dialogue choices when starting quests, tuliyollal/solution nine side quests --- QuestPathGenerator/QuestSourceGenerator.cs | 24 ++- .../4860_A New World to Explore.json | 6 +- .../Healer/4824_In the Sting of Things.json | 9 +- .../4825_Causing Problems on Purpose.json | 9 +- .../Healer/4826_Living among the Deadly.json | 9 +- .../4827_Taste of a Toxin Paradise.json | 10 +- .../Healer/4828_Downed by the River.json | 151 ++++++++++++++ .../Healer/4829_An Antidote for Anarchy.json | 25 +++ .../MagicalRanged/4842_Power Forgotten.json | 3 +- .../4843_A Brand of Justice.json | 3 +- .../4844_The Seeds of Popularity.json | 3 +- .../4836_To Steal a Steelhog.json | 100 ++++++++- .../PhysicalRanged/4837_Bandits Abound.json | 25 +++ .../Tank/4818_The Narwhal Beckons.json | 3 +- .../Tank/4819_Sleepless in Ishgard.json | 3 +- .../Tank/4820_Between Sleep and Death.json | 3 +- .../Tank/4821_Beacon in the Darkness.json | 3 +- .../Tank/4822_Awakened, not Stirred.json | 3 +- .../5164_Credits Where Credits are Due.json | 88 ++++++++ .../Solution Nine/5165_True Vue Blues.json | 133 ++++++++++++ .../5166_Problem Child, Solution Nine.json | 94 +++++++++ .../Solution Nine/5167_An Usher's Lament.json | 92 +++++++++ .../Solution Nine/5168_Towering Troubles.json | 95 +++++++++ .../5169_A Fitting Send-off.json | 87 ++++++++ .../5170_Rousing Mister Robot.json | 100 +++++++++ .../5171_Of Lightning Rings and Romance.json | 51 +++++ ...72_Warrior of Light, Wrangler of Cats.json | 73 +++++++ ...5017_The Many Multitudes fo Mamool Ja.json | 51 +++++ .../Tuliyollal/5018_How Far He'll Go.json | 81 ++++++++ .../Tuliyollal/5019_Culinary Import.json | 82 ++++++++ .../Tuliyollal/5020_Curious Corn.json | 126 +++++++++++ .../Tuliyollal/5021_Painter on the Hill.json | 51 +++++ .../5022_For Whom the Drum Beats.json | 91 ++++++++ ...23_Special Orders, Special Deliveries.json | 85 ++++++++ .../5024_Mulch Ado About Nothing.json | 78 +++++++ ...25_Ferocious Beasts on Foreign Shores.json | 66 ++++++ .../5026_Once a Riddler, Now a Ruler.json | 94 +++++++++ .../Tuliyollal/5027_A Trying Breed.json | 87 ++++++++ .../5028_Of Crates and Correspondence.json | 140 +++++++++++++ .../Tuliyollal/5029_Guilding the Lily.json | 119 +++++++++++ .../Urqopacha/5052_To Catch a Merchant.json | 86 ++++++++ .../Urqopacha/5053_Finding Bait.json | 132 ++++++++++++ .../Urqopacha/5054_Springing the Trap.json | 92 +++++++++ QuestPaths/quest-v1.json | 32 ++- .../V1/Converter/EmoteConverter.cs | 1 + Questionable.Model/V1/EEmote.cs | 1 + .../V1/{QuestData.cs => QuestRoot.cs} | 2 +- Questionable.Model/V1/QuestStep.cs | 7 +- Questionable/Controller/CommandHandler.cs | 155 ++++++++++++++ Questionable/Controller/GameUiController.cs | 84 +++++--- Questionable/Controller/QuestController.cs | 195 +++++++++++------- Questionable/Controller/QuestRegistry.cs | 37 ++-- .../Controller/Steps/BaseFactory/WaitAtEnd.cs | 4 +- .../Controller/Steps/BaseTasks/NextQuest.cs | 54 +++++ Questionable/DalamudInitializer.cs | 93 ++------- Questionable/Data/QuestData.cs | 58 ++++++ Questionable/GameFunctions.cs | 32 ++- Questionable/Model/Quest.cs | 7 +- Questionable/Model/QuestInfo.cs | 19 ++ Questionable/QuestionablePlugin.cs | 5 +- Questionable/Windows/DebugOverlay.cs | 2 +- Questionable/Windows/QuestWindow.cs | 81 ++++---- 62 files changed, 3252 insertions(+), 283 deletions(-) create mode 100644 QuestPaths/Dawntrail/RoleQuests/Healer/4828_Downed by the River.json create mode 100644 QuestPaths/Dawntrail/RoleQuests/Healer/4829_An Antidote for Anarchy.json create mode 100644 QuestPaths/Dawntrail/RoleQuests/PhysicalRanged/4837_Bandits Abound.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5164_Credits Where Credits are Due.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5165_True Vue Blues.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5166_Problem Child, Solution Nine.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5167_An Usher's Lament.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5168_Towering Troubles.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5169_A Fitting Send-off.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5170_Rousing Mister Robot.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5171_Of Lightning Rings and Romance.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Solution Nine/5172_Warrior of Light, Wrangler of Cats.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5017_The Many Multitudes fo Mamool Ja.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5018_How Far He'll Go.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5019_Culinary Import.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5020_Curious Corn.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5021_Painter on the Hill.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5022_For Whom the Drum Beats.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5023_Special Orders, Special Deliveries.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5024_Mulch Ado About Nothing.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5025_Ferocious Beasts on Foreign Shores.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5026_Once a Riddler, Now a Ruler.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5027_A Trying Breed.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5028_Of Crates and Correspondence.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Tuliyollal/5029_Guilding the Lily.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Urqopacha/5052_To Catch a Merchant.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Urqopacha/5053_Finding Bait.json create mode 100644 QuestPaths/Dawntrail/SideQuests/Urqopacha/5054_Springing the Trap.json rename Questionable.Model/V1/{QuestData.cs => QuestRoot.cs} (92%) create mode 100644 Questionable/Controller/CommandHandler.cs create mode 100644 Questionable/Controller/Steps/BaseTasks/NextQuest.cs create mode 100644 Questionable/Data/QuestData.cs create mode 100644 Questionable/Model/QuestInfo.cs diff --git a/QuestPathGenerator/QuestSourceGenerator.cs b/QuestPathGenerator/QuestSourceGenerator.cs index be47a0be..d30a0fa6 100644 --- a/QuestPathGenerator/QuestSourceGenerator.cs +++ b/QuestPathGenerator/QuestSourceGenerator.cs @@ -38,7 +38,7 @@ public class QuestSourceGenerator : ISourceGenerator public void Execute(GeneratorExecutionContext context) { - List<(ushort, QuestData)> quests = []; + List<(ushort, QuestRoot)> quests = []; // Find schema definition AdditionalText jsonSchemaFile = @@ -75,7 +75,7 @@ public class QuestSourceGenerator : ISourceGenerator context.ReportDiagnostic(error); } - var quest = questNode.Deserialize()!; + var quest = questNode.Deserialize()!; quests.Add((id, quest)); } @@ -196,7 +196,7 @@ public class QuestSourceGenerator : ISourceGenerator context.AddSource("AssemblyQuestLoader.g.cs", code.ToFullString()); } - private static IEnumerable CreateQuestInitializer(ushort questId, QuestData quest) + private static IEnumerable CreateQuestInitializer(ushort questId, QuestRoot quest) { return new SyntaxNodeOrToken[] { @@ -210,23 +210,23 @@ public class QuestSourceGenerator : ISourceGenerator Literal(questId)), Token(SyntaxKind.CommaToken), ObjectCreationExpression( - IdentifierName(nameof(QuestData))) + IdentifierName(nameof(QuestRoot))) .WithInitializer( InitializerExpression( SyntaxKind.ObjectInitializerExpression, SeparatedList( SyntaxNodeList( - Assignment(nameof(QuestData.Author), quest.Author, null) + Assignment(nameof(QuestRoot.Author), quest.Author, null) .AsSyntaxNodeOrToken(), - AssignmentList(nameof(QuestData.Contributors), quest.Contributors) + AssignmentList(nameof(QuestRoot.Contributors), quest.Contributors) .AsSyntaxNodeOrToken(), - Assignment(nameof(QuestData.Comment), quest.Comment, null) + Assignment(nameof(QuestRoot.Comment), quest.Comment, null) .AsSyntaxNodeOrToken(), - AssignmentList(nameof(QuestData.TerritoryBlacklist), + AssignmentList(nameof(QuestRoot.TerritoryBlacklist), quest.TerritoryBlacklist).AsSyntaxNodeOrToken(), AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, - IdentifierName(nameof(QuestData.QuestSequence)), + IdentifierName(nameof(QuestRoot.QuestSequence)), CreateQuestSequence(quest.QuestSequence)) )))) })), @@ -354,7 +354,11 @@ public class QuestSourceGenerator : ISourceGenerator .AsSyntaxNodeOrToken(), AssignmentList(nameof(QuestStep.PointMenuChoices), step.PointMenuChoices) .AsSyntaxNodeOrToken(), - Assignment(nameof(QuestStep.QuestId), step.QuestId, emptyStep.QuestId) + Assignment(nameof(QuestStep.PickupQuestId), step.PickupQuestId, emptyStep.PickupQuestId) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.TurnInQuestId), step.TurnInQuestId, emptyStep.TurnInQuestId) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.NextQuestId), step.NextQuestId, emptyStep.NextQuestId) .AsSyntaxNodeOrToken()))))), Token(SyntaxKind.CommaToken), }.ToArray()))); diff --git a/QuestPaths/Dawntrail/MSQ/A-Kozama'uka1-Urqopacha1/4860_A New World to Explore.json b/QuestPaths/Dawntrail/MSQ/A-Kozama'uka1-Urqopacha1/4860_A New World to Explore.json index 9164879e..92d34861 100644 --- a/QuestPaths/Dawntrail/MSQ/A-Kozama'uka1-Urqopacha1/4860_A New World to Explore.json +++ b/QuestPaths/Dawntrail/MSQ/A-Kozama'uka1-Urqopacha1/4860_A New World to Explore.json @@ -14,7 +14,11 @@ }, "StopDistance": 7, "TerritoryId": 962, - "InteractionType": "AcceptQuest" + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Old Sharlayan", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] } ] }, diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4824_In the Sting of Things.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4824_In the Sting of Things.json index f1687aa7..c7d85280 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Healer/4824_In the Sting of Things.json +++ b/QuestPaths/Dawntrail/RoleQuests/Healer/4824_In the Sting of Things.json @@ -13,7 +13,11 @@ "Z": 201.28174 }, "TerritoryId": 1185, - "InteractionType": "AcceptQuest" + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Tuliyollal", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] } ] }, @@ -187,7 +191,8 @@ "Z": 111.436646 }, "TerritoryId": 129, - "InteractionType": "CompleteQuest" + "InteractionType": "CompleteQuest", + "NextQuestId": 4825 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4825_Causing Problems on Purpose.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4825_Causing Problems on Purpose.json index b3fdcec6..1c7732ec 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Healer/4825_Causing Problems on Purpose.json +++ b/QuestPaths/Dawntrail/RoleQuests/Healer/4825_Causing Problems on Purpose.json @@ -13,7 +13,11 @@ "Z": 111.436646 }, "TerritoryId": 129, - "InteractionType": "AcceptQuest" + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Limsa Lominsa", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] } ] }, @@ -115,7 +119,8 @@ }, "TerritoryId": 129, "InteractionType": "CompleteQuest", - "AetheryteShortcut": "Limsa Lominsa" + "AetheryteShortcut": "Limsa Lominsa", + "NextQuestId": 4826 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4826_Living among the Deadly.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4826_Living among the Deadly.json index 30debaf9..7babb173 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Healer/4826_Living among the Deadly.json +++ b/QuestPaths/Dawntrail/RoleQuests/Healer/4826_Living among the Deadly.json @@ -13,7 +13,11 @@ "Z": 111.436646 }, "TerritoryId": 129, - "InteractionType": "AcceptQuest" + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Limsa Lominsa", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] } ] }, @@ -99,7 +103,8 @@ }, "TerritoryId": 129, "InteractionType": "CompleteQuest", - "AetheryteShortcut": "Limsa Lominsa" + "AetheryteShortcut": "Limsa Lominsa", + "NextQuestId": 4827 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4827_Taste of a Toxin Paradise.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4827_Taste of a Toxin Paradise.json index b49047ec..2ef9ff33 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Healer/4827_Taste of a Toxin Paradise.json +++ b/QuestPaths/Dawntrail/RoleQuests/Healer/4827_Taste of a Toxin Paradise.json @@ -13,7 +13,11 @@ "Z": 111.436646 }, "TerritoryId": 129, - "InteractionType": "AcceptQuest" + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Limsa Lominsa", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] } ] }, @@ -137,7 +141,9 @@ "Z": 111.436646 }, "TerritoryId": 129, - "InteractionType": "CompleteQuest" + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Limsa Lominsa", + "NextQuestId": 4828 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4828_Downed by the River.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4828_Downed by the River.json new file mode 100644 index 00000000..e2e5a62a --- /dev/null +++ b/QuestPaths/Dawntrail/RoleQuests/Healer/4828_Downed by the River.json @@ -0,0 +1,151 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1046290, + "Position": { + "X": -114.091736, + "Y": 20, + "Z": 111.436646 + }, + "TerritoryId": 129, + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Limsa Lominsa", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1046317, + "Position": { + "X": 570.18384, + "Y": 20.721313, + "Z": 451.34656 + }, + "TerritoryId": 137, + "InteractionType": "Interact", + "AetheryteShortcut": "Eastern La Noscea - Costa Del Sol", + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_KINGBA241_04828_Q1_000_013", + "Answer": "TEXT_KINGBA241_04828_A1_000_001" + } + ] + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1046317, + "Position": { + "X": 570.18384, + "Y": 20.721313, + "Z": 451.34656 + }, + "TerritoryId": 137, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 2013561, + "Position": { + "X": -220.44714, + "Y": 35.78235, + "Z": 268.05518 + }, + "StopDistance": 0.25, + "TerritoryId": 137, + "InteractionType": "Combat", + "EnemySpawnType": "AfterInteraction", + "KillEnemyDataIds": [ + 17614, + 17615, + 17616 + ], + "AetheryteShortcut": "Eastern La Noscea - Wineport", + "Fly": true, + "Comment": "two waves of enemies" + } + ] + }, + { + "Sequence": 4, + "Steps": [ + { + "DataId": 1046318, + "Position": { + "X": -98.95477, + "Y": 34.20764, + "Z": 220.44702 + }, + "TerritoryId": 137, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 5, + "Steps": [ + { + "DataId": 1046318, + "Position": { + "X": -98.95477, + "Y": 34.20764, + "Z": 220.44702 + }, + "TerritoryId": 137, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 6, + "Steps": [ + { + "DataId": 1046317, + "Position": { + "X": 570.18384, + "Y": 20.721313, + "Z": 451.34656 + }, + "TerritoryId": 137, + "InteractionType": "Interact", + "AetheryteShortcut": "Eastern La Noscea - Costa Del Sol" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1046290, + "Position": { + "X": -114.091736, + "Y": 20, + "Z": 111.436646 + }, + "TerritoryId": 129, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Limsa Lominsa", + "NextQuestId": 4829 + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/RoleQuests/Healer/4829_An Antidote for Anarchy.json b/QuestPaths/Dawntrail/RoleQuests/Healer/4829_An Antidote for Anarchy.json new file mode 100644 index 00000000..a994b992 --- /dev/null +++ b/QuestPaths/Dawntrail/RoleQuests/Healer/4829_An Antidote for Anarchy.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1046290, + "Position": { + "X": -114.091736, + "Y": 20, + "Z": 111.436646 + }, + "TerritoryId": 129, + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Limsa Lominsa", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4842_Power Forgotten.json b/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4842_Power Forgotten.json index 2cb7c7f4..124f6879 100644 --- a/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4842_Power Forgotten.json +++ b/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4842_Power Forgotten.json @@ -75,7 +75,8 @@ "Z": 628.3817 }, "TerritoryId": 957, - "InteractionType": "CompleteQuest" + "InteractionType": "CompleteQuest", + "NextQuestId": 4843 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4843_A Brand of Justice.json b/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4843_A Brand of Justice.json index 6ca94200..7b194cf4 100644 --- a/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4843_A Brand of Justice.json +++ b/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4843_A Brand of Justice.json @@ -133,7 +133,8 @@ "Z": 628.3817 }, "TerritoryId": 957, - "InteractionType": "CompleteQuest" + "InteractionType": "CompleteQuest", + "NextQuestId": 4844 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4844_The Seeds of Popularity.json b/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4844_The Seeds of Popularity.json index 4ca3c1bc..d1926e08 100644 --- a/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4844_The Seeds of Popularity.json +++ b/QuestPaths/Dawntrail/RoleQuests/MagicalRanged/4844_The Seeds of Popularity.json @@ -155,7 +155,8 @@ "Z": 628.3817 }, "TerritoryId": 957, - "InteractionType": "CompleteQuest" + "InteractionType": "CompleteQuest", + "NextQuestId": 4845 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/PhysicalRanged/4836_To Steal a Steelhog.json b/QuestPaths/Dawntrail/RoleQuests/PhysicalRanged/4836_To Steal a Steelhog.json index ecb16d8c..064e3e2e 100644 --- a/QuestPaths/Dawntrail/RoleQuests/PhysicalRanged/4836_To Steal a Steelhog.json +++ b/QuestPaths/Dawntrail/RoleQuests/PhysicalRanged/4836_To Steal a Steelhog.json @@ -13,7 +13,105 @@ "Z": 203.38745 }, "TerritoryId": 1185, - "InteractionType": "AcceptQuest" + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Tuliyollal", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1020193, + "Position": { + "X": 26.108154, + "Y": 0, + "Z": 25.253662 + }, + "TerritoryId": 635, + "InteractionType": "Interact", + "AetheryteShortcut": "Rhalgr's Reach", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "DataId": 1019483, + "Position": { + "X": -37.521973, + "Y": 1.2530026, + "Z": 36.301147 + }, + "TerritoryId": 635, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + }, + { + "DataId": 1019484, + "Position": { + "X": -66.5141, + "Y": -5.9571908E-15, + "Z": -22.964844 + }, + "TerritoryId": 635, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1048311, + "Position": { + "X": 460.86804, + "Y": 41.92747, + "Z": 335.10327 + }, + "TerritoryId": 612, + "InteractionType": "Interact", + "AetheryteShortcut": "Fringes - Peering Stones", + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1048309, + "Position": { + "X": 19.638367, + "Y": -0.11837298, + "Z": 47.226562 + }, + "TerritoryId": 635, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Rhalgr's Reach", + "NextQuestId": 4837 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/PhysicalRanged/4837_Bandits Abound.json b/QuestPaths/Dawntrail/RoleQuests/PhysicalRanged/4837_Bandits Abound.json new file mode 100644 index 00000000..e494b3b3 --- /dev/null +++ b/QuestPaths/Dawntrail/RoleQuests/PhysicalRanged/4837_Bandits Abound.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1048309, + "Position": { + "X": 19.638367, + "Y": -0.11837298, + "Z": 47.226562 + }, + "TerritoryId": 635, + "InteractionType": "AcceptQuest", + "AetheryteShortcut": "Rhalgr's Reach", + "SkipIf": [ + "AetheryteShortcutIfInSameTerritory" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/RoleQuests/Tank/4818_The Narwhal Beckons.json b/QuestPaths/Dawntrail/RoleQuests/Tank/4818_The Narwhal Beckons.json index 6a16c2ba..877b223e 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Tank/4818_The Narwhal Beckons.json +++ b/QuestPaths/Dawntrail/RoleQuests/Tank/4818_The Narwhal Beckons.json @@ -96,7 +96,8 @@ "Z": -48.05072 }, "TerritoryId": 418, - "InteractionType": "CompleteQuest" + "InteractionType": "CompleteQuest", + "NextQuestId": 4819 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/Tank/4819_Sleepless in Ishgard.json b/QuestPaths/Dawntrail/RoleQuests/Tank/4819_Sleepless in Ishgard.json index 5e765989..e49d9cd6 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Tank/4819_Sleepless in Ishgard.json +++ b/QuestPaths/Dawntrail/RoleQuests/Tank/4819_Sleepless in Ishgard.json @@ -113,7 +113,8 @@ "Z": -48.05072 }, "TerritoryId": 418, - "InteractionType": "CompleteQuest" + "InteractionType": "CompleteQuest", + "NextQuestId": 4820 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/Tank/4820_Between Sleep and Death.json b/QuestPaths/Dawntrail/RoleQuests/Tank/4820_Between Sleep and Death.json index 2903bae8..fc6aea2f 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Tank/4820_Between Sleep and Death.json +++ b/QuestPaths/Dawntrail/RoleQuests/Tank/4820_Between Sleep and Death.json @@ -143,7 +143,8 @@ "AethernetShortcut": [ "[Ishgard] Aetheryte Plaza", "[Ishgard] The Forgotten Knight" - ] + ], + "NextQuestId": 4821 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/Tank/4821_Beacon in the Darkness.json b/QuestPaths/Dawntrail/RoleQuests/Tank/4821_Beacon in the Darkness.json index b0e9cde3..35e95ca1 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Tank/4821_Beacon in the Darkness.json +++ b/QuestPaths/Dawntrail/RoleQuests/Tank/4821_Beacon in the Darkness.json @@ -115,7 +115,8 @@ }, "StopDistance": 5, "TerritoryId": 418, - "InteractionType": "CompleteQuest" + "InteractionType": "CompleteQuest", + "NextQuestId": 4822 } ] } diff --git a/QuestPaths/Dawntrail/RoleQuests/Tank/4822_Awakened, not Stirred.json b/QuestPaths/Dawntrail/RoleQuests/Tank/4822_Awakened, not Stirred.json index 67b2101a..b70b42ba 100644 --- a/QuestPaths/Dawntrail/RoleQuests/Tank/4822_Awakened, not Stirred.json +++ b/QuestPaths/Dawntrail/RoleQuests/Tank/4822_Awakened, not Stirred.json @@ -137,7 +137,8 @@ "Prompt": "TEXT_KINGBA141_04822_Q3_000_000", "Answer": "TEXT_KINGBA141_04822_A3_000_002" } - ] + ], + "NextQuestId": 4823 } ] } diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5164_Credits Where Credits are Due.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5164_Credits Where Credits are Due.json new file mode 100644 index 00000000..ae3a51ab --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5164_Credits Where Credits are Due.json @@ -0,0 +1,88 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051388, + "Position": { + "X": -185.47345, + "Y": 0.66, + "Z": -42.648987 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "Position": { + "X": -445.9273, + "Y": 13.999773, + "Z": 192.44756 + }, + "TerritoryId": 1186, + "InteractionType": "WalkTo", + "AethernetShortcut": [ + "[Solution Nine] Nexus Arcade", + "[Solution Nine] Residential Sector" + ] + }, + { + "DataId": 1050492, + "Position": { + "X": -446.25012, + "Y": 13.999722, + "Z": 195.23914 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "Position": { + "X": -445.1605, + "Y": 13.999999, + "Z": 162.65598 + }, + "TerritoryId": 1186, + "InteractionType": "WalkTo" + }, + { + "Position": { + "X": -440.51483, + "Y": 13.999909, + "Z": 154.73817 + }, + "TerritoryId": 1186, + "InteractionType": "WalkTo", + "DisableNavmesh": true, + "Comment": "For some reason navmesh wanders off through the whole area" + }, + { + "DataId": 1051388, + "Position": { + "X": -185.47345, + "Y": 0.66, + "Z": -42.648987 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Solution Nine] Residential Sector", + "[Solution Nine] Nexus Arcade" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5165_True Vue Blues.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5165_True Vue Blues.json new file mode 100644 index 00000000..d618ecbb --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5165_True Vue Blues.json @@ -0,0 +1,133 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051389, + "Position": { + "X": 501.12158, + "Y": 59.999813, + "Z": 163.83606 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest", + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_KINGZG002_05165_Q1_000_000", + "Answer": "TEXT_KINGZG002_05165_A1_000_002" + } + ] + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2014339, + "Position": { + "X": 483.08533, + "Y": 61.386963, + "Z": 196.09363 + }, + "TerritoryId": 1186, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "DataId": 2014340, + "Position": { + "X": 444.3275, + "Y": 61.356445, + "Z": 242.38953 + }, + "TerritoryId": 1186, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] + }, + { + "DataId": 2014338, + "Position": { + "X": 456.2904, + "Y": 61.41748, + "Z": 68.89441 + }, + "TerritoryId": 1186, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1051390, + "Position": { + "X": 500.08386, + "Y": 59.999813, + "Z": 161.4862 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1051391, + "Position": { + "X": 497.94763, + "Y": 59.99981, + "Z": 160.84534 + }, + "TerritoryId": 1186, + "InteractionType": "Emote", + "Emote": "doubt" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051390, + "Position": { + "X": 500.08386, + "Y": 59.999813, + "Z": 161.4862 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5166_Problem Child, Solution Nine.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5166_Problem Child, Solution Nine.json new file mode 100644 index 00000000..6f08f790 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5166_Problem Child, Solution Nine.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051393, + "Position": { + "X": -315.4193, + "Y": 14, + "Z": 155.7793 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051393, + "Position": { + "X": -315.4193, + "Y": 14, + "Z": 155.7793 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1051393, + "Position": { + "X": -206.10861, + "Y": 2.1999774, + "Z": 213.03752 + }, + "StopDistance": 0.25, + "TerritoryId": 1186, + "InteractionType": "Interact", + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_KINGZG003_05166_Q2_000_000", + "Answer": "TEXT_KINGZG003_05166_A2_000_001" + } + ] + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1051393, + "Position": { + "X": -23.899967, + "Y": -5.8484287, + "Z": 230.24123 + }, + "StopDistance": 0.25, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051395, + "Position": { + "X": -3.55542, + "Y": 0.0005434508, + "Z": -85.89307 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Solution Nine] Information Center", + "[Solution Nine] Aetheryte Plaza" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5167_An Usher's Lament.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5167_An Usher's Lament.json new file mode 100644 index 00000000..530bdf00 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5167_An Usher's Lament.json @@ -0,0 +1,92 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051396, + "Position": { + "X": -346.7918, + "Y": 9.519508, + "Z": 18.20398 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "Position": { + "X": -306.07123, + "Y": 10.014936, + "Z": 1.7894125 + }, + "TerritoryId": 1186, + "InteractionType": "WalkTo" + }, + { + "DataId": 1049242, + "Position": { + "X": -303.7309, + "Y": 10.014902, + "Z": 1.7241821 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "Position": { + "X": 292.01248, + "Y": 51.401478, + "Z": 200.04631 + }, + "TerritoryId": 1186, + "InteractionType": "WalkTo", + "AethernetShortcut": [ + "[Solution Nine] Residential Sector", + "[Solution Nine] Neon Stein" + ] + }, + { + "DataId": 1048081, + "Position": { + "X": 292.16443, + "Y": 51.401485, + "Z": 202.65503 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051396, + "Position": { + "X": -346.7918, + "Y": 9.519508, + "Z": 18.20398 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Solution Nine] Neon Stein", + "[Solution Nine] Residential Sector" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5168_Towering Troubles.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5168_Towering Troubles.json new file mode 100644 index 00000000..2a487b98 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5168_Towering Troubles.json @@ -0,0 +1,95 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051397, + "Position": { + "X": -40.024475, + "Y": 38.80659, + "Z": -459.49493 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051400, + "Position": { + "X": -56.351562, + "Y": 38.0566, + "Z": -347.1275 + }, + "TerritoryId": 1186, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] + }, + { + "DataId": 1051399, + "Position": { + "X": -26.230347, + "Y": 38.056416, + "Z": -310.81104 + }, + "TerritoryId": 1186, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "DataId": 1051398, + "Position": { + "X": 38.254395, + "Y": 38.0566, + "Z": -357.99194 + }, + "TerritoryId": 1186, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051397, + "Position": { + "X": -40.024475, + "Y": 38.80659, + "Z": -459.49493 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5169_A Fitting Send-off.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5169_A Fitting Send-off.json new file mode 100644 index 00000000..881c7beb --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5169_A Fitting Send-off.json @@ -0,0 +1,87 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051401, + "Position": { + "X": 383.90173, + "Y": 60, + "Z": 56.839844 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "Position": { + "X": 292.01248, + "Y": 51.401478, + "Z": 200.04631 + }, + "TerritoryId": 1186, + "InteractionType": "WalkTo", + "AethernetShortcut": [ + "[Solution Nine] True Vue", + "[Solution Nine] Neon Stein" + ] + }, + { + "DataId": 1048081, + "Position": { + "X": 292.16443, + "Y": 51.401485, + "Z": 202.65503 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1051402, + "Position": { + "X": -205.58484, + "Y": 1.9200057, + "Z": 7.583679 + }, + "TerritoryId": 1186, + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Solution Nine] Neon Stein", + "[Solution Nine] Nexus Arcade" + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051403, + "Position": { + "X": 13.992493, + "Y": -5.845003, + "Z": 269.91675 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Solution Nine] Nexus Arcade", + "[Solution Nine] Information Center" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5170_Rousing Mister Robot.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5170_Rousing Mister Robot.json new file mode 100644 index 00000000..bd25e0ec --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5170_Rousing Mister Robot.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051404, + "Position": { + "X": -157.33582, + "Y": 2.1999714, + "Z": 229.69409 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "Position": { + "X": 322.70325, + "Y": 52.41666, + "Z": 211.24818 + }, + "TerritoryId": 1186, + "InteractionType": "WalkTo", + "AethernetShortcut": [ + "[Solution Nine] Information Center", + "[Solution Nine] True Vue" + ] + }, + { + "DataId": 1049154, + "Position": { + "X": 320.27148, + "Y": 52.41666, + "Z": 211.32214 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1051405, + "Position": { + "X": -156.2677, + "Y": 2.199971, + "Z": 231.79968 + }, + "TerritoryId": 1186, + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Solution Nine] True Vue", + "[Solution Nine] Information Center" + ] + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1051409, + "Position": { + "X": -157.24426, + "Y": 2.1999712, + "Z": 231.21985 + }, + "StopDistance": 4, + "TerritoryId": 1186, + "InteractionType": "Emote", + "Emote": "psych" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051405, + "Position": { + "X": -156.2677, + "Y": 2.199971, + "Z": 231.79968 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5171_Of Lightning Rings and Romance.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5171_Of Lightning Rings and Romance.json new file mode 100644 index 00000000..f958ce86 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5171_Of Lightning Rings and Romance.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051407, + "Position": { + "X": -182.29956, + "Y": 1.1335879E-05, + "Z": 45.059814 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2014345, + "Position": { + "X": -206.92761, + "Y": 0.25933838, + "Z": -106.58429 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051407, + "Position": { + "X": -182.29956, + "Y": 1.1335879E-05, + "Z": 45.059814 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Solution Nine/5172_Warrior of Light, Wrangler of Cats.json b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5172_Warrior of Light, Wrangler of Cats.json new file mode 100644 index 00000000..2bf6e87d --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Solution Nine/5172_Warrior of Light, Wrangler of Cats.json @@ -0,0 +1,73 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051069, + "Position": { + "X": -313.43555, + "Y": 4.6900077, + "Z": -36.57599 + }, + "TerritoryId": 1186, + "InteractionType": "AcceptQuest", + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_KINGZG009_05172_Q1_000_000", + "Answer": "TEXT_KINGZG009_05172_A1_000_003" + } + ] + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2014292, + "Position": { + "X": -375.93652, + "Y": 13.992493, + "Z": 144.06042 + }, + "TerritoryId": 1186, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "Position": { + "X": -375.93652, + "Y": 13.992493, + "Z": 144.06042 + }, + "TerritoryId": 1186, + "InteractionType": "Instruction", + "Comment": "Catch the cat. Good luck!" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051069, + "Position": { + "X": -313.43555, + "Y": 4.6900077, + "Z": -36.57599 + }, + "TerritoryId": 1186, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5017_The Many Multitudes fo Mamool Ja.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5017_The Many Multitudes fo Mamool Ja.json new file mode 100644 index 00000000..1a253674 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5017_The Many Multitudes fo Mamool Ja.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1050728, + "Position": { + "X": 50.125854, + "Y": 47, + "Z": -330.40363 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1050729, + "Position": { + "X": 187.51807, + "Y": 42.40001, + "Z": -350.36243 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1050728, + "Position": { + "X": 50.125854, + "Y": 47, + "Z": -330.40363 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5018_How Far He'll Go.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5018_How Far He'll Go.json new file mode 100644 index 00000000..60fec5ad --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5018_How Far He'll Go.json @@ -0,0 +1,81 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1048459, + "Position": { + "X": -394.43048, + "Y": 11, + "Z": 3.5552979 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1050733, + "Position": { + "X": -16.83075, + "Y": -10.000011, + "Z": 93.12573 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Tuliyollal] Dirigible Landing", + "[Tuliyollal] Bayside Bevy Marketplace" + ] + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1050734, + "Position": { + "X": 79.301025, + "Y": -17.964588, + "Z": 199.57275 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_KINGZA002_05018_Q1_000_000", + "Answer": "TEXT_KINGZA002_05018_A1_000_003" + } + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1048459, + "Position": { + "X": -394.43048, + "Y": 11, + "Z": 3.5552979 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Tuliyollal] Bayside Bevy Marketplace", + "[Tuliyollal] Dirigible Landing" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5019_Culinary Import.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5019_Culinary Import.json new file mode 100644 index 00000000..f2607d95 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5019_Culinary Import.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1050735, + "Position": { + "X": 121.11084, + "Y": -17.972874, + "Z": 74.021484 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1050736, + "Position": { + "X": 31.47937, + "Y": -17.9643, + "Z": 167.46765 + }, + "StopDistance": 2, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 2014232, + "Position": { + "X": 33.310425, + "Y": -16.678162, + "Z": 167.98657 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1050737, + "Position": { + "X": 87.57141, + "Y": -14, + "Z": 52.384155 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1050737, + "Position": { + "X": 87.57141, + "Y": -14, + "Z": 52.384155 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5020_Curious Corn.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5020_Curious Corn.json new file mode 100644 index 00000000..09f7c33f --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5020_Curious Corn.json @@ -0,0 +1,126 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051038, + "Position": { + "X": 153.94824, + "Y": -17.9645, + "Z": 95.384155 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051025, + "Position": { + "X": 153.94824, + "Y": -17.9645, + "Z": 95.384155 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1051026, + "Position": { + "X": 109.910645, + "Y": -14, + "Z": 39.505493 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + }, + { + "DataId": 1051027, + "Position": { + "X": 12.527649, + "Y": -10.00001, + "Z": 35.599243 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "DataId": 1051028, + "Position": { + "X": -69.77954, + "Y": -10.00001, + "Z": 119.79846 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1051025, + "Position": { + "X": -124.85585, + "Y": -5.0000095, + "Z": 130.06908 + }, + "StopDistance": 0.25, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051029, + "Position": { + "X": -249.77496, + "Y": -9.2517585E-06, + "Z": 138.01782 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5021_Painter on the Hill.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5021_Painter on the Hill.json new file mode 100644 index 00000000..5c480bb6 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5021_Painter on the Hill.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051030, + "Position": { + "X": -227.64941, + "Y": 40.075134, + "Z": -25.436829 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051030, + "Position": { + "X": -227.64941, + "Y": 40.075134, + "Z": -25.436829 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051031, + "Position": { + "X": -56.6568, + "Y": 52.057556, + "Z": 20.767456 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5022_For Whom the Drum Beats.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5022_For Whom the Drum Beats.json new file mode 100644 index 00000000..ad9138c5 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5022_For Whom the Drum Beats.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051032, + "Position": { + "X": -259.05243, + "Y": 2.127327E-05, + "Z": 116.13635 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051033, + "Position": { + "X": -282.49036, + "Y": 14.508167, + "Z": 178.72888 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "Position": { + "X": -290.98425, + "Y": -9.417534E-06, + "Z": 179.37634 + }, + "TerritoryId": 1185, + "InteractionType": "WalkTo", + "DisableNavmesh": true + }, + { + "DataId": 2014234, + "Position": { + "X": -272.84656, + "Y": -0.015319824, + "Z": 191.39392 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1051033, + "Position": { + "X": -282.49036, + "Y": 14.508167, + "Z": 178.72888 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051032, + "Position": { + "X": -259.05243, + "Y": 2.127327E-05, + "Z": 116.13635 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5023_Special Orders, Special Deliveries.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5023_Special Orders, Special Deliveries.json new file mode 100644 index 00000000..5d811b6c --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5023_Special Orders, Special Deliveries.json @@ -0,0 +1,85 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051034, + "Position": { + "X": 15.457336, + "Y": -10.00001, + "Z": 64.43884 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051036, + "Position": { + "X": -134.72198, + "Y": 45.35523, + "Z": 52.20105 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Tuliyollal] Aetheryte Plaza", + "[Tuliyollal] The Resplendent Quarter" + ], + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "DataId": 1051035, + "Position": { + "X": -104.38696, + "Y": 54.09999, + "Z": 19.33313 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051034, + "Position": { + "X": 15.457336, + "Y": -10.00001, + "Z": 64.43884 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Tuliyollal] The Resplendent Quarter", + "[Tuliyollal] Aetheryte Plaza" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5024_Mulch Ado About Nothing.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5024_Mulch Ado About Nothing.json new file mode 100644 index 00000000..b421e1aa --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5024_Mulch Ado About Nothing.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1048509, + "Position": { + "X": 181.44495, + "Y": 40.99999, + "Z": -66.63617 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1048431, + "Position": { + "X": 88.48706, + "Y": -14, + "Z": 56.687256 + }, + "StopDistance": 4, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "DataId": 1051037, + "Position": { + "X": -35.29419, + "Y": -17.97287, + "Z": 169.02417 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1048509, + "Position": { + "X": 181.44495, + "Y": 40.99999, + "Z": -66.63617 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest" + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5025_Ferocious Beasts on Foreign Shores.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5025_Ferocious Beasts on Foreign Shores.json new file mode 100644 index 00000000..976bfbb0 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5025_Ferocious Beasts on Foreign Shores.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051039, + "Position": { + "X": 17.593567, + "Y": -17.942173, + "Z": 153.06323 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest", + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_KINGZA009_05025_Q1_000_000", + "Answer": "TEXT_KINGZA009_05025_A1_000_001" + } + ] + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051040, + "Position": { + "X": -169.93976, + "Y": 59.999985, + "Z": -54.703613 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Tuliyollal] Bayside Bevy Marketplace", + "[Tuliyollal] The Resplendent Quarter" + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051039, + "Position": { + "X": 17.593567, + "Y": -17.942173, + "Z": 153.06323 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Tuliyollal] The Resplendent Quarter", + "[Tuliyollal] Bayside Bevy Marketplace" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5026_Once a Riddler, Now a Ruler.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5026_Once a Riddler, Now a Ruler.json new file mode 100644 index 00000000..0b10edcd --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5026_Once a Riddler, Now a Ruler.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051042, + "Position": { + "X": 100.38904, + "Y": 47, + "Z": -230.27393 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2014235, + "Position": { + "X": -80.094604, + "Y": -19.333252, + "Z": 179.49182 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Tuliyollal] Brightploom Post", + "[Tuliyollal] The For'ard Cabins" + ] + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 2014236, + "Position": { + "X": -154.34503, + "Y": -14.084106, + "Z": 548.24133 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 2014237, + "Position": { + "X": -124.77307, + "Y": -5.0202637, + "Z": 113.11511 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "AetheryteShortcut": "Tuliyollal", + "AethernetShortcut": [ + "[Tuliyollal] Aetheryte Plaza", + "[Tuliyollal] The For'ard Cabins" + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051042, + "Position": { + "X": 100.38904, + "Y": 47, + "Z": -230.27393 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Tuliyollal] The For'ard Cabins", + "[Tuliyollal] Brightploom Post" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5027_A Trying Breed.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5027_A Trying Breed.json new file mode 100644 index 00000000..1c55ba5b --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5027_A Trying Breed.json @@ -0,0 +1,87 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051043, + "Position": { + "X": -276.99707, + "Y": 1.9748375E-05, + "Z": 71.97681 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051044, + "Position": { + "X": -307.6372, + "Y": 3, + "Z": -50.8584 + }, + "TerritoryId": 1185, + "InteractionType": "Emote", + "Emote": "soothe", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + }, + { + "DataId": 1051045, + "Position": { + "X": -49.42401, + "Y": -2.7939677E-09, + "Z": 16.647583 + }, + "TerritoryId": 1185, + "InteractionType": "Emote", + "Emote": "soothe", + "AethernetShortcut": [ + "[Tuliyollal] Dirigible Landing", + "[Tuliyollal] Aetheryte Plaza" + ], + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051043, + "Position": { + "X": -276.99707, + "Y": 1.9748375E-05, + "Z": 71.97681 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Tuliyollal] Aetheryte Plaza", + "[Tuliyollal] The Resplendent Quarter" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5028_Of Crates and Correspondence.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5028_Of Crates and Correspondence.json new file mode 100644 index 00000000..bcda4f90 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5028_Of Crates and Correspondence.json @@ -0,0 +1,140 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051046, + "Position": { + "X": 69.38269, + "Y": -14, + "Z": 91.081055 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2014238, + "Position": { + "X": 75.12012, + "Y": -17.990417, + "Z": 154.52808 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + }, + { + "DataId": 2014239, + "Position": { + "X": 64.530396, + "Y": -17.990417, + "Z": 189.56274 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "DataId": 2014240, + "Position": { + "X": 131.91418, + "Y": -17.990417, + "Z": 112.2301 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1051046, + "Position": { + "X": 69.38269, + "Y": -14, + "Z": 91.081055 + }, + "TerritoryId": 1185, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1051048, + "Position": { + "X": -227.95453, + "Y": 40.04104, + "Z": -42.648987 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Tuliyollal] Aetheryte Plaza", + "[Tuliyollal] The Resplendent Quarter" + ], + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_KINGZA012_05028_Q1_000_000", + "Answer": "TEXT_KINGZA012_05028_A1_000_002" + } + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051046, + "Position": { + "X": 69.38269, + "Y": -14, + "Z": 91.081055 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Tuliyollal] The Resplendent Quarter", + "[Tuliyollal] Aetheryte Plaza" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5029_Guilding the Lily.json b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5029_Guilding the Lily.json new file mode 100644 index 00000000..2670e2f7 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Tuliyollal/5029_Guilding the Lily.json @@ -0,0 +1,119 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1048537, + "Position": { + "X": -61.753296, + "Y": 99.99999, + "Z": -188.98297 + }, + "TerritoryId": 1185, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1046521, + "Position": { + "X": -46.616333, + "Y": -17.97287, + "Z": 180.3158 + }, + "StopDistance": 5, + "TerritoryId": 1185, + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Tuliyollal] Vollok Shoonsa", + "[Tuliyollal] Bayside Bevy Marketplace" + ] + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1048547, + "Position": { + "X": -39.35309, + "Y": -17.972866, + "Z": 198.38245 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] + }, + { + "DataId": 1048418, + "Position": { + "X": -54.154297, + "Y": -17.972868, + "Z": 198.44348 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "DataId": 1048546, + "Position": { + "X": -60.83777, + "Y": -17.972868, + "Z": 197.07019 + }, + "TerritoryId": 1185, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1048537, + "Position": { + "X": -61.753296, + "Y": 99.99999, + "Z": -188.98297 + }, + "TerritoryId": 1185, + "InteractionType": "CompleteQuest", + "AethernetShortcut": [ + "[Tuliyollal] Bayside Bevy Marketplace", + "[Tuliyollal] Vollok Shoonsa" + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Urqopacha/5052_To Catch a Merchant.json b/QuestPaths/Dawntrail/SideQuests/Urqopacha/5052_To Catch a Merchant.json new file mode 100644 index 00000000..72e728c1 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Urqopacha/5052_To Catch a Merchant.json @@ -0,0 +1,86 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051195, + "Position": { + "X": 383.29138, + "Y": -154.50243, + "Z": -420.49292 + }, + "TerritoryId": 1187, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051199, + "Position": { + "X": 442.95398, + "Y": -116.815155, + "Z": -178.63745 + }, + "TerritoryId": 1187, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1051200, + "Position": { + "X": 563.83606, + "Y": -49.515564, + "Z": 49.54602 + }, + "TerritoryId": 1187, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1048773, + "Position": { + "X": 576.2875, + "Y": -49.16388, + "Z": 37.00305 + }, + "TerritoryId": 1187, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051195, + "Position": { + "X": 383.29138, + "Y": -154.50243, + "Z": -420.49292 + }, + "TerritoryId": 1187, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Urqopacha - Wachunpelo", + "Fly": true, + "NextQuestId": 5053 + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Urqopacha/5053_Finding Bait.json b/QuestPaths/Dawntrail/SideQuests/Urqopacha/5053_Finding Bait.json new file mode 100644 index 00000000..b2c083b7 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Urqopacha/5053_Finding Bait.json @@ -0,0 +1,132 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051195, + "Position": { + "X": 383.29138, + "Y": -154.50243, + "Z": -420.49292 + }, + "TerritoryId": 1187, + "InteractionType": "AcceptQuest", + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_KINGZB203_05053_Q1_000_000", + "Answer": "TEXT_KINGZB203_05053_A1_000_003" + } + ] + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1048629, + "Position": { + "X": 353.35315, + "Y": -158.06824, + "Z": -423.72784 + }, + "TerritoryId": 1187, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] + }, + { + "DataId": 1051202, + "Position": { + "X": 338.55188, + "Y": -160.20284, + "Z": -450.9804 + }, + "TerritoryId": 1187, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ], + "Fly": true + }, + { + "DataId": 1051203, + "Position": { + "X": 283.22266, + "Y": -168.30641, + "Z": -452.26215 + }, + "TerritoryId": 1187, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ], + "Fly": true + }, + { + "DataId": 1048660, + "Position": { + "X": 246.3263, + "Y": -168.27, + "Z": -487.20532 + }, + "TerritoryId": 1187, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 16 + ], + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051195, + "Position": { + "X": 383.29138, + "Y": -154.50243, + "Z": -420.49292 + }, + "TerritoryId": 1187, + "InteractionType": "CompleteQuest", + "Fly": true, + "NextQuestId": 5054, + "DialogueChoices": [ + { + "Type": "YesNo", + "Prompt": "TEXT_KINGZB203_05053_Q2_000_000", + "Yes": true + } + ] + } + ] + } + ] +} diff --git a/QuestPaths/Dawntrail/SideQuests/Urqopacha/5054_Springing the Trap.json b/QuestPaths/Dawntrail/SideQuests/Urqopacha/5054_Springing the Trap.json new file mode 100644 index 00000000..d336dec7 --- /dev/null +++ b/QuestPaths/Dawntrail/SideQuests/Urqopacha/5054_Springing the Trap.json @@ -0,0 +1,92 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1051195, + "Position": { + "X": 383.29138, + "Y": -154.50243, + "Z": -420.49292 + }, + "TerritoryId": 1187, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1051200, + "Position": { + "X": 563.83606, + "Y": -49.515564, + "Z": 49.54602 + }, + "TerritoryId": 1187, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 2014271, + "Position": { + "X": 560.9064, + "Y": -54.459473, + "Z": 22.781677 + }, + "TerritoryId": 1187, + "InteractionType": "Interact", + "Fly": true, + "DialogueChoices": [ + { + "Type": "YesNo", + "Prompt": "TEXT_KINGZB204_05054_Q1_000_000", + "Yes": true + } + ] + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1051204, + "Position": { + "X": 562.7374, + "Y": -54.457336, + "Z": 21.530457 + }, + "StopDistance": 5, + "TerritoryId": 1187, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1051195, + "Position": { + "X": 383.29138, + "Y": -154.50243, + "Z": -420.49292 + }, + "TerritoryId": 1187, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Urqopacha - Wachunpelo" + } + ] + } + ] +} diff --git a/QuestPaths/quest-v1.json b/QuestPaths/quest-v1.json index ee2996d4..32f9de52 100644 --- a/QuestPaths/quest-v1.json +++ b/QuestPaths/quest-v1.json @@ -651,7 +651,8 @@ "cheer", "happy", "poke", - "flex" + "flex", + "soothe" ] } } @@ -948,7 +949,7 @@ }, "then": { "properties": { - "QuestId": { + "PickUpQuestId": { "type": [ "null", "number" @@ -957,6 +958,33 @@ } } } + }, + { + "if": { + "properties": { + "InteractionType": { + "const": "CompleteQuest" + } + } + }, + "then": { + "properties": { + "TurnInQuestId": { + "type": [ + "null", + "number" + ], + "description": "Determines the quest which should be turned in. If empty/null, turns in the quest corresponding to the file name." + }, + "NextQuestId": { + "type": [ + "null", + "number" + ], + "description": "For quest chains (e.g. DT healer role quests) Which quest to do next, given that you meet the required level." + } + } + } } ] } diff --git a/Questionable.Model/V1/Converter/EmoteConverter.cs b/Questionable.Model/V1/Converter/EmoteConverter.cs index 830f57a0..04202daa 100644 --- a/Questionable.Model/V1/Converter/EmoteConverter.cs +++ b/Questionable.Model/V1/Converter/EmoteConverter.cs @@ -18,5 +18,6 @@ public sealed class EmoteConverter() : EnumConverter(Values) { EEmote.Happy, "happy" }, { EEmote.Poke, "poke" }, { EEmote.Flex, "flex" }, + { EEmote.Soothe, "soothe" }, }; } diff --git a/Questionable.Model/V1/EEmote.cs b/Questionable.Model/V1/EEmote.cs index e209feda..502ed7ca 100644 --- a/Questionable.Model/V1/EEmote.cs +++ b/Questionable.Model/V1/EEmote.cs @@ -20,4 +20,5 @@ public enum EEmote Happy = 48, Poke = 28, Flex = 139, + Soothe = 35, } diff --git a/Questionable.Model/V1/QuestData.cs b/Questionable.Model/V1/QuestRoot.cs similarity index 92% rename from Questionable.Model/V1/QuestData.cs rename to Questionable.Model/V1/QuestRoot.cs index 111bd257..880e34ce 100644 --- a/Questionable.Model/V1/QuestData.cs +++ b/Questionable.Model/V1/QuestRoot.cs @@ -2,7 +2,7 @@ namespace Questionable.Model.V1; -public sealed class QuestData +public sealed class QuestRoot { public string Author { get; set; } = null!; public List Contributors { get; set; } = new(); diff --git a/Questionable.Model/V1/QuestStep.cs b/Questionable.Model/V1/QuestStep.cs index 377c9cd7..e7c4906f 100644 --- a/Questionable.Model/V1/QuestStep.cs +++ b/Questionable.Model/V1/QuestStep.cs @@ -53,7 +53,12 @@ public sealed class QuestStep public IList CompletionQuestVariablesFlags { get; set; } = new List(); public IList DialogueChoices { get; set; } = new List(); public IList PointMenuChoices { get; set; } = new List(); - public ushort? QuestId { get; set; } + + // TODO: Not implemented + public ushort? PickupQuestId { get; set; } + + public ushort? TurnInQuestId { get; set; } + public ushort? NextQuestId { get; set; } [JsonConstructor] public QuestStep() diff --git a/Questionable/Controller/CommandHandler.cs b/Questionable/Controller/CommandHandler.cs new file mode 100644 index 00000000..6cc495bb --- /dev/null +++ b/Questionable/Controller/CommandHandler.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.Command; +using Dalamud.Plugin.Services; +using Questionable.Data; +using Questionable.Model; +using Questionable.Windows; + +namespace Questionable.Controller; + +internal sealed class CommandHandler : IDisposable +{ + private readonly ICommandManager _commandManager; + private readonly IChatGui _chatGui; + private readonly QuestController _questController; + private readonly MovementController _movementController; + private readonly QuestRegistry _questRegistry; + private readonly QuestData _questData; + private readonly ConfigWindow _configWindow; + private readonly DebugOverlay _debugOverlay; + private readonly QuestWindow _questWindow; + + public CommandHandler(ICommandManager commandManager, IChatGui chatGui, QuestController questController, + MovementController movementController, QuestRegistry questRegistry, QuestData questData, + ConfigWindow configWindow, DebugOverlay debugOverlay, QuestWindow questWindow) + { + _commandManager = commandManager; + _chatGui = chatGui; + _questController = questController; + _movementController = movementController; + _questRegistry = questRegistry; + _questData = questData; + _configWindow = configWindow; + _debugOverlay = debugOverlay; + _questWindow = questWindow; + + _commandManager.AddHandler("/qst", new CommandInfo(ProcessCommand) + { + HelpMessage = "Opens the Questing window" + }); + } + + private void ProcessCommand(string command, string arguments) + { + string[] parts = arguments.Split(' '); + switch (parts[0]) + { + case "c": + case "config": + _configWindow.Toggle(); + break; + + case "start": + _questController.ExecuteNextStep(true); + break; + + case "stop": + _movementController.Stop(); + _questController.Stop("Stop command"); + break; + + case "do": + ConfigureDebugOverlay(parts.Skip(1).ToArray()); + break; + + case "next": + SetNextQuest(parts.Skip(1).ToArray()); + break; + + case "sim": + SetSimulatedQuest(parts.Skip(1).ToArray()); + break; + + case "which": + _questData.ShowQuestsIssuedByTarget(); + break; + + default: + _questWindow.Toggle(); + break; + } + } + + private void ConfigureDebugOverlay(string[] arguments) + { + if (!_debugOverlay.DrawConditions()) + { + _chatGui.PrintError("[Questionable] You don't have the debug overlay enabled."); + return; + } + + if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId)) + { + if (_questRegistry.IsKnownQuest(questId)) + { + _debugOverlay.HighlightedQuest = questId; + _chatGui.Print($"[Questionable] Set highlighted quest to {questId}."); + } + else + _chatGui.PrintError($"[Questionable] Unknown quest {questId}."); + } + else + { + _debugOverlay.HighlightedQuest = null; + _chatGui.Print("[Questionable] Cleared highlighted quest."); + } + } + + private void SetNextQuest(string[] arguments) + { + if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId)) + { + if (_questRegistry.TryGetQuest(questId, out Quest? quest)) + { + _questController.SetNextQuest(quest); + _chatGui.Print($"[Questionable] Set next quest to {questId}."); + } + else + { + _chatGui.PrintError($"[Questionable] Unknown quest {questId}."); + } + } + else + { + _questController.SetNextQuest(null); + _chatGui.Print("[Questionable] Cleared next quest."); + } + } + + private void SetSimulatedQuest(string[] arguments) + { + if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId)) + { + if (_questRegistry.TryGetQuest(questId, out Quest? quest)) + { + _questController.SimulateQuest(quest); + _chatGui.Print($"[Questionable] Simulating quest {questId}."); + } + else + _chatGui.PrintError($"[Questionable] Unknown quest {questId}."); + } + else + { + _questController.SimulateQuest(null); + _chatGui.Print("[Questionable] Cleared simulated quest."); + } + } + + public void Dispose() + { + _commandManager.RemoveHandler("/qst"); + } +} diff --git a/Questionable/Controller/GameUiController.cs b/Questionable/Controller/GameUiController.cs index 0784cca4..29071202 100644 --- a/Questionable/Controller/GameUiController.cs +++ b/Questionable/Controller/GameUiController.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using Dalamud.Game.Addon.Lifecycle; @@ -13,6 +12,7 @@ using LLib; using LLib.GameUI; using Lumina.Excel.GeneratedSheets; using Microsoft.Extensions.Logging; +using Questionable.Data; using Questionable.Model.V1; using Quest = Questionable.Model.Quest; using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; @@ -25,19 +25,23 @@ internal sealed class GameUiController : IDisposable private readonly IDataManager _dataManager; private readonly GameFunctions _gameFunctions; private readonly QuestController _questController; + private readonly QuestRegistry _questRegistry; + private readonly QuestData _questData; private readonly IGameGui _gameGui; private readonly ITargetManager _targetManager; private readonly ILogger _logger; private readonly Regex _returnRegex; public GameUiController(IAddonLifecycle addonLifecycle, IDataManager dataManager, GameFunctions gameFunctions, - QuestController questController, IGameGui gameGui, ITargetManager targetManager, IPluginLog pluginLog, - ILogger logger) + QuestController questController, QuestRegistry questRegistry, QuestData questData, IGameGui gameGui, + ITargetManager targetManager, IPluginLog pluginLog, ILogger logger) { _addonLifecycle = addonLifecycle; _dataManager = dataManager; _gameFunctions = gameFunctions; _questController = questController; + _questRegistry = questRegistry; + _questData = questData; _gameGui = gameGui; _targetManager = targetManager; _logger = logger; @@ -162,7 +166,7 @@ internal sealed class GameUiController : IDisposable if (currentQuest != null && actualPrompt == null) { // it is possible for this to be a quest selection - string questName = currentQuest.Quest.Name; + string questName = currentQuest.Quest.Info.Name; int questSelection = answers.FindIndex(x => GameStringEquals(questName, x)); if (questSelection >= 0) addonSelectIconString->AtkUnitBase.FireCallbackInt(questSelection); @@ -172,33 +176,55 @@ internal sealed class GameUiController : IDisposable private int? HandleListChoice(string? actualPrompt, List answers, bool checkAllSteps) { + List dialogueChoices = []; var currentQuest = _questController.CurrentQuest; - if (currentQuest == null) + if (currentQuest != null) { - _logger.LogInformation("Ignoring list choice, no active quest"); - return null; - } - - var quest = currentQuest.Quest; - IList dialogueChoices; - if (checkAllSteps) - { - var sequence = quest.FindSequence(currentQuest.Sequence); - dialogueChoices = sequence?.Steps.SelectMany(x => x.DialogueChoices).ToList() ?? new List(); + var quest = currentQuest.Quest; + if (checkAllSteps) + { + var sequence = quest.FindSequence(currentQuest.Sequence); + var choices = sequence?.Steps.SelectMany(x => x.DialogueChoices); + if (choices != null) + dialogueChoices.AddRange(choices.Select(x => new DialogueChoiceInfo(quest, x))); + } + else + { + var step = quest.FindSequence(currentQuest.Sequence)?.FindStep(currentQuest.Step); + if (step == null) + _logger.LogInformation("Ignoring current quest dialogue choices, no active step"); + else + dialogueChoices.AddRange(step.DialogueChoices.Select(x => new DialogueChoiceInfo(quest, x))); + } } else - { - var step = quest.FindSequence(currentQuest.Sequence)?.FindStep(currentQuest.Step); - if (step == null) - { - _logger.LogInformation("Ignoring list choice, no active step"); - return null; - } + _logger.LogInformation("Ignoring current quest dialogue choices, no active quest"); - dialogueChoices = step.DialogueChoices; + // add all quests that start with the targeted npc + var target = _targetManager.Target; + if (target != null) + { + foreach (var questInfo in _questData.GetAllByIssuerDataId(target.DataId)) + { + if (_gameFunctions.IsReadyToAcceptQuest(questInfo.QuestId) && + _questRegistry.TryGetQuest(questInfo.QuestId, out Quest? knownQuest)) + { + var questChoices = knownQuest.FindSequence(0)?.Steps + .SelectMany(x => x.DialogueChoices) + .ToList(); + if (questChoices != null && questChoices.Count > 0) + { + _logger.LogInformation("Adding {Count} dialogue choices from not accepted quest {QuestName}", questChoices.Count, questInfo.Name); + dialogueChoices.AddRange(questChoices.Select(x => new DialogueChoiceInfo(knownQuest, x))); + } + } + } } - foreach (var dialogueChoice in dialogueChoices) + if (dialogueChoices.Count == 0) + return null; + + foreach (var (quest, dialogueChoice) in dialogueChoices) { if (dialogueChoice.Type != EDialogChoiceType.List) continue; @@ -433,13 +459,7 @@ internal sealed class GameUiController : IDisposable }; addonPointMenu->FireCallback(2, selectChoice); - _questController.CurrentQuest = currentQuest with - { - StepProgress = currentQuest.StepProgress with - { - PointMenuCounter = counter + 1, - } - }; + currentQuest.IncreasePointMenuCounter(); } private unsafe void CreditPostSetup(AddonEvent type, AddonArgs args) @@ -519,4 +539,6 @@ internal sealed class GameUiController : IDisposable _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "CutSceneSelectString", CutsceneSelectStringPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup); } + + private sealed record DialogueChoiceInfo(Quest Quest, DialogueChoice DialogueChoice); } diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs index 07f21d72..68b984af 100644 --- a/Questionable/Controller/QuestController.cs +++ b/Questionable/Controller/QuestController.cs @@ -25,6 +25,10 @@ internal sealed class QuestController private readonly object _lock = new(); + + private QuestProgress? _startedQuest; + private QuestProgress? _nextQuest; + private QuestProgress? _simulatedQuest; private readonly Queue _taskQueue = new(); private ITask? _currentTask; private bool _automatic; @@ -51,9 +55,22 @@ internal sealed class QuestController _taskFactories = taskFactories.ToList().AsReadOnly(); } + public QuestProgress? CurrentQuest + { + get + { + if (_simulatedQuest != null) + return _simulatedQuest; + else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.QuestId)) + return _nextQuest; + else + return _startedQuest; + } + } + + public QuestProgress? SimulatedQuest => _simulatedQuest; + public QuestProgress? NextQuest => _nextQuest; - public QuestProgress? CurrentQuest { get; set; } - public SimulationProgress? SimulatedQuest { get; set; } public string? DebugState { get; private set; } public string? Comment { get; private set; } @@ -61,7 +78,10 @@ internal sealed class QuestController { lock (_lock) { - CurrentQuest = null; + _startedQuest = null; + _nextQuest = null; + _simulatedQuest = null; + DebugState = null; _questRegistry.Reload(); @@ -81,7 +101,7 @@ internal sealed class QuestController } } - if (CurrentQuest != null && CurrentQuest.Quest.Data.TerritoryBlacklist.Contains(_clientState.TerritoryType)) + if (CurrentQuest != null && CurrentQuest.Quest.Root.TerritoryBlacklist.Contains(_clientState.TerritoryType)) return; // not verified to work @@ -92,10 +112,7 @@ internal sealed class QuestController lock (_lock) { _logger.LogWarning("Quest accept apparently didn't work out, resetting progress"); - CurrentQuest = CurrentQuest with - { - Step = 0 - }; + CurrentQuest.SetStep(0); } ExecuteNextStep(true); @@ -111,50 +128,64 @@ internal sealed class QuestController { DebugState = null; - ushort currentQuestId; - byte currentSequence; - if (SimulatedQuest != null) + byte currentSequence = 0; + if (_simulatedQuest != null) { - currentQuestId = SimulatedQuest.Quest.QuestId; - currentSequence = SimulatedQuest.Sequence; + currentSequence = _simulatedQuest.Sequence; } - else - (currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest(); - - if (currentQuestId == 0) + else if (_nextQuest != null) { - if (CurrentQuest != null) + // if the quest is accepted, we no longer track it + if (_gameFunctions.IsQuestAcceptedOrComplete(_nextQuest.Quest.QuestId)) { - _logger.LogInformation("No current quest, resetting data"); - CurrentQuest = null; - Stop("Resetting current quest"); + _nextQuest = null; + currentSequence = 0; + } + else + { + currentSequence = _nextQuest.Sequence; // by definition, this should always be 0 } } - else if (CurrentQuest == null || CurrentQuest.Quest.QuestId != currentQuestId) + + if (_simulatedQuest == null && _nextQuest == null) { - if (_questRegistry.TryGetQuest(currentQuestId, out var quest)) + (ushort currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest(); + if (currentQuestId == 0) { - _logger.LogInformation("New quest: {QuestName}", quest.Name); - CurrentQuest = new QuestProgress(quest, currentSequence, 0); - - bool continueAutomatically = _configuration.General.AutoAcceptNextQuest; - - if (_clientState.LocalPlayer?.Level < quest.Level) - continueAutomatically = false; - - Stop("Different Quest", continueAutomatically); + if (_startedQuest != null) + { + _logger.LogInformation("No current quest, resetting data"); + _startedQuest = null; + Stop("Resetting current quest"); + } } - else if (CurrentQuest != null) + else if (_startedQuest == null || _startedQuest.Quest.QuestId != currentQuestId) { - _logger.LogInformation("No active quest anymore? Not sure what happened..."); - CurrentQuest = null; - Stop("No active Quest"); - } + if (_questRegistry.TryGetQuest(currentQuestId, out var quest)) + { + _logger.LogInformation("New quest: {QuestName}", quest.Info.Name); + _startedQuest = new QuestProgress(quest, currentSequence); - return; + bool continueAutomatically = _configuration.General.AutoAcceptNextQuest; + + if (_clientState.LocalPlayer?.Level < quest.Info.Level) + continueAutomatically = false; + + Stop("Different Quest", continueAutomatically); + } + else if (_startedQuest != null) + { + _logger.LogInformation("No active quest anymore? Not sure what happened..."); + _startedQuest = null; + Stop("No active Quest"); + } + + return; + } } - if (CurrentQuest == null) + var questToRun = CurrentQuest; + if (questToRun == null) { DebugState = "No quest active"; Comment = null; @@ -179,14 +210,14 @@ internal sealed class QuestController return; } - if (CurrentQuest.Sequence != currentSequence) + if (questToRun.Sequence != currentSequence) { - CurrentQuest = CurrentQuest with { Sequence = currentSequence, Step = 0 }; + questToRun.SetSequence(currentSequence); Stop("New sequence", continueIfAutomatic: true); } - var q = CurrentQuest.Quest; - var sequence = q.FindSequence(CurrentQuest.Sequence); + var q = questToRun.Quest; + var sequence = q.FindSequence(questToRun.Sequence); if (sequence == null) { DebugState = "Sequence not found"; @@ -195,7 +226,7 @@ internal sealed class QuestController return; } - if (CurrentQuest.Step == 255) + if (questToRun.Step == 255) { DebugState = "Step completed"; Comment = null; @@ -204,7 +235,7 @@ internal sealed class QuestController return; } - if (CurrentQuest.Step >= sequence.Steps.Count) + if (questToRun.Step >= sequence.Steps.Count) { DebugState = "Step not found"; Comment = null; @@ -212,9 +243,9 @@ internal sealed class QuestController return; } - var step = sequence.Steps[CurrentQuest.Step]; + var step = sequence.Steps[questToRun.Step]; DebugState = null; - Comment = step.Comment ?? sequence.Comment ?? q.Data.Comment; + Comment = step.Comment ?? sequence.Comment ?? q.Root.Comment; } } @@ -262,21 +293,9 @@ internal sealed class QuestController _logger.LogInformation("Increasing step count from {CurrentValue}", CurrentQuest.Step); if (CurrentQuest.Step + 1 < seq.Steps.Count) - { - CurrentQuest = CurrentQuest with - { - Step = CurrentQuest.Step + 1, - StepProgress = new(DateTime.Now), - }; - } + CurrentQuest.SetStep(CurrentQuest.Step + 1); else - { - CurrentQuest = CurrentQuest with - { - Step = 255, - StepProgress = new(DateTime.Now), - }; - } + CurrentQuest.SetStep(255); } if (shouldContinue && _automatic) @@ -310,14 +329,26 @@ internal sealed class QuestController _logger.LogInformation("Stopping automatic questing"); _automatic = false; } + + _nextQuest = null; } public void SimulateQuest(Quest? quest) { + _logger.LogInformation("SimulateQuest: {QuestId}", quest?.QuestId); if (quest != null) - SimulatedQuest = new SimulationProgress(quest, 0); + _simulatedQuest = new QuestProgress(quest); else - SimulatedQuest = null; + _simulatedQuest = null; + } + + public void SetNextQuest(Quest? quest) + { + _logger.LogInformation("NextQuest: {QuestId}", quest?.QuestId); + if (quest != null) + _nextQuest = new QuestProgress(quest); + else + _nextQuest = null; } private void UpdateCurrentTask() @@ -470,20 +501,40 @@ internal sealed class QuestController public bool IsRunning => _currentTask != null || _taskQueue.Count > 0; - public sealed record QuestProgress( - Quest Quest, - byte Sequence, - int Step, - StepProgress StepProgress) + public sealed class QuestProgress { - public QuestProgress(Quest quest, byte sequence, int step) - : this(quest, sequence, step, new StepProgress(DateTime.Now)) + public Quest Quest { get; } + public byte Sequence { get; private set; } + public int Step { get; private set; } + public StepProgress StepProgress { get; private set; } = new(DateTime.Now); + + public QuestProgress(Quest quest, byte sequence = 0) { + Quest = quest; + SetSequence(sequence); + } + + public void SetSequence(byte sequence) + { + Sequence = sequence; + SetStep(0); + } + + public void SetStep(int step) + { + Step = step; + StepProgress = new StepProgress(DateTime.Now); + } + + public void IncreasePointMenuCounter() + { + StepProgress = StepProgress with + { + PointMenuCounter = StepProgress.PointMenuCounter + 1, + }; } } - public sealed record SimulationProgress(Quest Quest, byte Sequence); - public void Skip(ushort questQuestId, byte currentQuestSequence) { lock (_lock) diff --git a/Questionable/Controller/QuestRegistry.cs b/Questionable/Controller/QuestRegistry.cs index cfb61097..779c5258 100644 --- a/Questionable/Controller/QuestRegistry.cs +++ b/Questionable/Controller/QuestRegistry.cs @@ -8,6 +8,7 @@ using System.Text.Json; using Dalamud.Plugin; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; +using Questionable.Data; using Questionable.Model; using Questionable.Model.V1; @@ -16,16 +17,16 @@ namespace Questionable.Controller; internal sealed class QuestRegistry { private readonly IDalamudPluginInterface _pluginInterface; - private readonly IDataManager _dataManager; + private readonly QuestData _questData; private readonly ILogger _logger; private readonly Dictionary _quests = new(); - public QuestRegistry(IDalamudPluginInterface pluginInterface, IDataManager dataManager, + public QuestRegistry(IDalamudPluginInterface pluginInterface, QuestData questData, ILogger logger) { _pluginInterface = pluginInterface; - _dataManager = dataManager; + _questData = questData; _logger = logger; } @@ -47,7 +48,7 @@ internal sealed class QuestRegistry _quests[questId] = quest; } #else - DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation?.Directory?.Parent?.Parent; + DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent; if (solutionDirectory != null) { DirectoryInfo pathProjectDirectory = @@ -64,22 +65,14 @@ internal sealed class QuestRegistry LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "Quests"))); - foreach (var (questId, quest) in _quests) - { - var questData = - _dataManager.GetExcelSheet()!.GetRow((uint)questId + 0x10000); - if (questData == null) - continue; - - quest.Name = questData.Name.ToString(); - quest.Level = questData.ClassJobLevel0; - #if !RELEASE - int missingSteps = quest.Data.QuestSequence.Where(x => x.Sequence < 255).Max(x => x.Sequence) - quest.Data.QuestSequence.Count(x => x.Sequence < 255) + 1; + foreach (var quest in _quests.Values) + { + int missingSteps = quest.Root.QuestSequence.Where(x => x.Sequence < 255).Max(x => x.Sequence) - quest.Root.QuestSequence.Count(x => x.Sequence < 255) + 1; if (missingSteps != 0) - _logger.LogWarning("Quest has missing steps: {QuestId} / {QuestName} → {Count}", quest.QuestId, quest.Name, missingSteps); -#endif + _logger.LogWarning("Quest has missing steps: {QuestId} / {QuestName} → {Count}", quest.QuestId, quest.Info.Name, missingSteps); } +#endif _logger.LogInformation("Loaded {Count} quests", _quests.Count); } @@ -88,12 +81,12 @@ internal sealed class QuestRegistry private void LoadQuestFromStream(string fileName, Stream stream) { _logger.LogTrace("Loading quest from '{FileName}'", fileName); - var (questId, name) = ExtractQuestDataFromName(fileName); + var questId = ExtractQuestIdFromName(fileName); Quest quest = new Quest { QuestId = questId, - Name = name, - Data = JsonSerializer.Deserialize(stream)!, + Root = JsonSerializer.Deserialize(stream)!, + Info = _questData.GetQuestInfo(questId), }; _quests[questId] = quest; } @@ -124,13 +117,13 @@ internal sealed class QuestRegistry LoadFromDirectory(childDirectory); } - private static (ushort QuestId, string Name) ExtractQuestDataFromName(string resourceName) + private static ushort ExtractQuestIdFromName(string resourceName) { string name = resourceName.Substring(0, resourceName.Length - ".json".Length); name = name.Substring(name.LastIndexOf('.') + 1); string[] parts = name.Split('_', 2); - return (ushort.Parse(parts[0], CultureInfo.InvariantCulture), parts[1]); + return ushort.Parse(parts[0], CultureInfo.InvariantCulture); } public bool IsKnownQuest(ushort questId) => _quests.ContainsKey(questId); diff --git a/Questionable/Controller/Steps/BaseFactory/WaitAtEnd.cs b/Questionable/Controller/Steps/BaseFactory/WaitAtEnd.cs index 4c74c592..1147458b 100644 --- a/Questionable/Controller/Steps/BaseFactory/WaitAtEnd.cs +++ b/Questionable/Controller/Steps/BaseFactory/WaitAtEnd.cs @@ -107,14 +107,14 @@ internal static class WaitAtEnd case EInteractionType.AcceptQuest: return [ - serviceProvider.GetRequiredService().With(step.QuestId ?? quest.QuestId), + serviceProvider.GetRequiredService().With(step.PickupQuestId ?? quest.QuestId), serviceProvider.GetRequiredService() ]; case EInteractionType.CompleteQuest: return [ - serviceProvider.GetRequiredService().With(step.QuestId ?? quest.QuestId), + serviceProvider.GetRequiredService().With(step.TurnInQuestId ?? quest.QuestId), serviceProvider.GetRequiredService() ]; diff --git a/Questionable/Controller/Steps/BaseTasks/NextQuest.cs b/Questionable/Controller/Steps/BaseTasks/NextQuest.cs new file mode 100644 index 00000000..0790d75b --- /dev/null +++ b/Questionable/Controller/Steps/BaseTasks/NextQuest.cs @@ -0,0 +1,54 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Questionable.Model; +using Questionable.Model.V1; + +namespace Questionable.Controller.Steps.BaseTasks; + +internal static class NextQuest +{ + internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory + { + public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) + { + if (step.InteractionType != EInteractionType.CompleteQuest) + return null; + + if (step.NextQuestId == null) + return null; + + return serviceProvider.GetRequiredService() + .With(step.NextQuestId.Value); + } + } + + internal sealed class SetQuest(QuestRegistry questRegistry, QuestController questController, ILogger logger) : ITask + { + public ushort NextQuestId { get; set; } + + public ITask With(ushort nextQuestId) + { + NextQuestId = nextQuestId; + return this; + } + + public bool Start() + { + if (questRegistry.TryGetQuest(NextQuestId, out Quest? quest)) + { + logger.LogInformation("Setting next quest to {QuestId}: '{QuestName}'", NextQuestId, quest.Info.Name); + questController.SetNextQuest(quest); + } + else + { + logger.LogInformation("Next quest with id {QuestId} not found", NextQuestId); + questController.SetNextQuest(null); + } + + return true; + } + + public ETaskResult Update() => ETaskResult.TaskComplete; + } +} diff --git a/Questionable/DalamudInitializer.cs b/Questionable/DalamudInitializer.cs index caa490f1..cde25ef6 100644 --- a/Questionable/DalamudInitializer.cs +++ b/Questionable/DalamudInitializer.cs @@ -1,12 +1,8 @@ using System; -using Dalamud.Game.Command; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; -using Microsoft.Extensions.Logging; using Questionable.Controller; -using Questionable.Data; -using Questionable.Model; using Questionable.Windows; namespace Questionable; @@ -15,35 +11,33 @@ internal sealed class DalamudInitializer : IDisposable { private readonly IDalamudPluginInterface _pluginInterface; private readonly IFramework _framework; - private readonly ICommandManager _commandManager; private readonly QuestController _questController; private readonly MovementController _movementController; private readonly NavigationShortcutController _navigationShortcutController; - private readonly IChatGui _chatGui; private readonly WindowSystem _windowSystem; private readonly QuestWindow _questWindow; - private readonly DebugOverlay _debugOverlay; private readonly ConfigWindow _configWindow; - private readonly QuestRegistry _questRegistry; - public DalamudInitializer(IDalamudPluginInterface pluginInterface, IFramework framework, - ICommandManager commandManager, QuestController questController, MovementController movementController, - GameUiController gameUiController, NavigationShortcutController navigationShortcutController, IChatGui chatGui, - WindowSystem windowSystem, QuestWindow questWindow, DebugOverlay debugOverlay, ConfigWindow configWindow, - QuestRegistry questRegistry) + public DalamudInitializer( + IDalamudPluginInterface pluginInterface, + IFramework framework, + QuestController questController, + MovementController movementController, + GameUiController gameUiController, + NavigationShortcutController navigationShortcutController, + WindowSystem windowSystem, + QuestWindow questWindow, + DebugOverlay debugOverlay, + ConfigWindow configWindow) { _pluginInterface = pluginInterface; _framework = framework; - _commandManager = commandManager; _questController = questController; _movementController = movementController; _navigationShortcutController = navigationShortcutController; - _chatGui = chatGui; _windowSystem = windowSystem; _questWindow = questWindow; - _debugOverlay = debugOverlay; _configWindow = configWindow; - _questRegistry = questRegistry; _windowSystem.AddWindow(questWindow); _windowSystem.AddWindow(configWindow); @@ -53,11 +47,6 @@ internal sealed class DalamudInitializer : IDisposable _pluginInterface.UiBuilder.OpenMainUi += _questWindow.Toggle; _pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle; _framework.Update += FrameworkUpdate; - _commandManager.AddHandler("/qst", new CommandInfo(ProcessCommand) - { - HelpMessage = "Opens the Questing window" - }); - _framework.RunOnTick(gameUiController.HandleCurrentDialogueChoices, TimeSpan.FromMilliseconds(200)); } @@ -76,68 +65,10 @@ internal sealed class DalamudInitializer : IDisposable } } - private void ProcessCommand(string command, string arguments) - { - if (arguments is "c" or "config") - _configWindow.Toggle(); - if (arguments is "start") - _questController.ExecuteNextStep(true); - else if (arguments is "stop") - { - _movementController.Stop(); - _questController.Stop("Stop command"); - } - else if (arguments.StartsWith("do", StringComparison.Ordinal)) - { - if (!_debugOverlay.DrawConditions()) - { - _chatGui.PrintError("[Questionable] You don't have the debug overlay enabled."); - return; - } - - if (arguments.Length >= 4 && ushort.TryParse(arguments.AsSpan(3), out ushort questId)) - { - if (_questRegistry.IsKnownQuest(questId)) - { - _debugOverlay.HighlightedQuest = questId; - _chatGui.Print($"[Questionable] Set highlighted quest to {questId}."); - } - else - _chatGui.PrintError($"[Questionable] Unknown quest {questId}."); - } - else - { - _debugOverlay.HighlightedQuest = null; - _chatGui.Print("[Questionable] Cleared highlighted quest."); - } - } - else if (arguments.StartsWith("sim", StringComparison.InvariantCulture)) - { - string[] parts = arguments.Split(' '); - if (parts.Length == 2 && ushort.TryParse(parts[1], out ushort questId)) - { - if (_questRegistry.TryGetQuest(questId, out Quest? quest)) - { - _questController.SimulateQuest(quest); - _chatGui.Print($"[Questionable] Simulating quest {questId}."); - } - else - _chatGui.PrintError($"[Questionable] Unknown quest {questId}."); - } - else - { - _questController.SimulateQuest(null); - _chatGui.Print("[Questionable] Cleared simulated quest."); - } - } - else if (string.IsNullOrEmpty(arguments)) - _questWindow.Toggle(); - } - public void Dispose() { - _commandManager.RemoveHandler("/qst"); _framework.Update -= FrameworkUpdate; + _pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle; _pluginInterface.UiBuilder.OpenMainUi -= _questWindow.Toggle; _pluginInterface.UiBuilder.Draw -= _windowSystem.Draw; diff --git a/Questionable/Data/QuestData.cs b/Questionable/Data/QuestData.cs new file mode 100644 index 00000000..3a2c5682 --- /dev/null +++ b/Questionable/Data/QuestData.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Plugin.Services; +using Questionable.Model; +using Quest = Lumina.Excel.GeneratedSheets.Quest; + +namespace Questionable.Data; + +internal sealed class QuestData +{ + private readonly ITargetManager _targetManager; + private readonly IChatGui _chatGui; + + private readonly ImmutableDictionary _quests; + + public QuestData(IDataManager dataManager, ITargetManager targetManager, IChatGui chatGui) + { + _targetManager = targetManager; + _chatGui = chatGui; + + _quests = dataManager.GetExcelSheet()! + .Where(x => x.RowId > 0) + .Where(x => x.IssuerLocation.Row > 0) + .Select(x => new QuestInfo(x)) + .ToImmutableDictionary(x => x.QuestId, x => x); + } + + public QuestInfo GetQuestInfo(ushort questId) + { + return _quests[questId] ?? throw new ArgumentOutOfRangeException(nameof(questId)); + } + + public List GetAllByIssuerDataId(uint targetId) + { + return _quests.Values + .Where(x => x.IssuerDataId == targetId) + .ToList(); + } + + public void ShowQuestsIssuedByTarget() + { + var targetId = _targetManager.Target?.DataId; + if (targetId == null) + { + _chatGui.PrintError("[Questionable] No target selected."); + return; + } + + List quests = GetAllByIssuerDataId(targetId.Value); + _chatGui.Print($"{quests.Count} quest(s) issued by target {_targetManager.Target?.Name}:"); + + foreach (QuestInfo quest in quests) + _chatGui.Print($" {quest.QuestId}: {quest.Name}"); + } +} diff --git a/Questionable/GameFunctions.cs b/Questionable/GameFunctions.cs index 5e42ec4e..73118965 100644 --- a/Questionable/GameFunctions.cs +++ b/Questionable/GameFunctions.cs @@ -206,7 +206,10 @@ internal sealed unsafe class GameFunctions return default; // if we're not at a high enough level to continue, we also ignore it - if (_questRegistry.TryGetQuest(currentQuest, out Quest? quest) && quest.Level > (_clientState.LocalPlayer?.Level ?? 0)) + var currentLevel = _clientState.LocalPlayer?.Level ?? 0; + if (currentLevel != 0 && + _questRegistry.TryGetQuest(currentQuest, out Quest? quest) + && quest.Info.Level > currentLevel) return default; return (currentQuest, QuestManager.GetQuestSequence(currentQuest)); @@ -218,6 +221,30 @@ internal sealed unsafe class GameFunctions return questWork != null ? *questWork : null; } + public bool IsReadyToAcceptQuest(ushort questId) + { + if (IsQuestAcceptedOrComplete(questId)) + return false; + + // if we're not at a high enough level to continue, we also ignore it + var currentLevel = _clientState.LocalPlayer?.Level ?? 0; + if (currentLevel != 0 && + _questRegistry.TryGetQuest(questId, out Quest? quest) && + quest.Info.Level > currentLevel) + return false; + + return true; + } + + public bool IsQuestAcceptedOrComplete(ushort questId) + { + if (QuestManager.IsQuestComplete(questId)) + return true; + + QuestManager* questManager = QuestManager.Instance(); + return questManager->IsQuestAccepted(questId); + } + public bool IsAetheryteUnlocked(uint aetheryteId, out byte subIndex) { subIndex = 0; @@ -548,6 +575,9 @@ internal sealed unsafe class GameFunctions public bool IsOccupied() { + if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null) + return true; + if (IsLoadingScreenVisible()) return true; diff --git a/Questionable/Model/Quest.cs b/Questionable/Model/Quest.cs index ac6d408a..e70911ae 100644 --- a/Questionable/Model/Quest.cs +++ b/Questionable/Model/Quest.cs @@ -6,10 +6,9 @@ namespace Questionable.Model; internal sealed class Quest { public required ushort QuestId { get; init; } - public required string Name { get; set; } - public ushort Level { get; set; } - public required QuestData Data { get; init; } + public required QuestRoot Root { get; init; } + public required QuestInfo Info { get; init; } public QuestSequence? FindSequence(byte currentSequence) - => Data.QuestSequence.SingleOrDefault(seq => seq.Sequence == currentSequence); + => Root.QuestSequence.SingleOrDefault(seq => seq.Sequence == currentSequence); } diff --git a/Questionable/Model/QuestInfo.cs b/Questionable/Model/QuestInfo.cs new file mode 100644 index 00000000..67301801 --- /dev/null +++ b/Questionable/Model/QuestInfo.cs @@ -0,0 +1,19 @@ +using ExcelQuest = Lumina.Excel.GeneratedSheets.Quest; + +namespace Questionable.Model; + +internal sealed class QuestInfo +{ + public QuestInfo(ExcelQuest quest) + { + QuestId = (ushort)(quest.RowId & 0xFFFF); + Name = quest.Name.ToString(); + Level = quest.ClassJobLevel0; + IssuerDataId = quest.IssuerStart; + } + + public ushort QuestId { get; } + public string Name { get; } + public ushort Level { get; } + public uint IssuerDataId { get; } +} diff --git a/Questionable/QuestionablePlugin.cs b/Questionable/QuestionablePlugin.cs index 75a1c295..e7b943da 100644 --- a/Questionable/QuestionablePlugin.cs +++ b/Questionable/QuestionablePlugin.cs @@ -68,6 +68,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -110,6 +111,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin WaitAtEnd.WaitObjectAtPosition>(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTaskWithFactory(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -120,11 +122,12 @@ public sealed class QuestionablePlugin : IDalamudPlugin serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); _serviceProvider = serviceCollection.BuildServiceProvider(); _serviceProvider.GetRequiredService().Reload(); - _serviceProvider.GetRequiredService(); + _serviceProvider.GetRequiredService(); _serviceProvider.GetRequiredService(); } diff --git a/Questionable/Windows/DebugOverlay.cs b/Questionable/Windows/DebugOverlay.cs index 60e26508..bbbc4302 100644 --- a/Questionable/Windows/DebugOverlay.cs +++ b/Questionable/Windows/DebugOverlay.cs @@ -76,7 +76,7 @@ internal sealed class DebugOverlay : Window if (HighlightedQuest == null || !_questRegistry.TryGetQuest(HighlightedQuest.Value, out var quest)) return; - foreach (var sequence in quest.Data.QuestSequence) + foreach (var sequence in quest.Root.QuestSequence) { for (int i = 0; i < sequence.Steps.Count; ++i) { diff --git a/Questionable/Windows/QuestWindow.cs b/Questionable/Windows/QuestWindow.cs index 17f40084..2a47215a 100644 --- a/Questionable/Windows/QuestWindow.cs +++ b/Questionable/Windows/QuestWindow.cs @@ -41,6 +41,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig private readonly Configuration _configuration; private readonly NavmeshIpc _navmeshIpc; private readonly QuestRegistry _questRegistry; + private readonly QuestData _questData; private readonly TerritoryData _territoryData; private readonly ILogger _logger; @@ -56,6 +57,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig Configuration configuration, NavmeshIpc navmeshIpc, QuestRegistry questRegistry, + QuestData questData, TerritoryData territoryData, ILogger logger) : base("Questionable###Questionable", ImGuiWindowFlags.AlwaysAutoResize) @@ -72,6 +74,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig _configuration = configuration; _navmeshIpc = navmeshIpc; _questRegistry = questRegistry; + _questData = questData; _territoryData = territoryData; _logger = logger; @@ -99,7 +102,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig return false; var currentQuest = _questController.CurrentQuest; - return currentQuest == null || !currentQuest.Quest.Data.TerritoryBlacklist.Contains(_clientState.TerritoryType); + return currentQuest == null || !currentQuest.Quest.Root.TerritoryBlacklist.Contains(_clientState.TerritoryType); } public override void Draw() @@ -114,12 +117,12 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig DrawRemainingTasks(); } - private unsafe void DrawQuest() + private void DrawQuest() { var currentQuest = _questController.CurrentQuest; if (currentQuest != null) { - ImGui.TextUnformatted($"Quest: {currentQuest.Quest.Name} / {currentQuest.Sequence} / {currentQuest.Step}"); + ImGui.TextUnformatted($"Quest: {currentQuest.Quest.Info.Name} / {currentQuest.Sequence} / {currentQuest.Step}"); ImGui.BeginDisabled(); var questWork = _gameFunctions.GetQuestEx(currentQuest.Quest.QuestId); @@ -144,7 +147,12 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig ImGui.Text($"QW: {vars.Trim()}"); } else - ImGui.TextUnformatted("(Not accepted)"); + { + if (currentQuest.Quest.QuestId == _questController.NextQuest?.Quest.QuestId) + ImGui.TextUnformatted("(Next quest in story line not accepted)"); + else + ImGui.TextUnformatted("(Not accepted)"); + } ImGui.TextUnformatted(_questController.DebugState ?? "--"); ImGui.EndDisabled(); @@ -158,6 +166,10 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig ImGui.BeginDisabled(_questController.IsRunning); if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { + // if we haven't accepted this quest, mark it as next quest so that we can optionally use aetherytes to travel + if (questWork == null) + _questController.SetNextQuest(currentQuest.Quest); + _questController.ExecuteNextStep(true); } @@ -210,84 +222,75 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig if (_questController.SimulatedQuest != null) { + var simulatedQuest = _questController.SimulatedQuest; + ImGui.Separator(); ImGui.TextColored(ImGuiColors.DalamudRed, "Quest sim active (experimental)"); - ImGui.Text($"Sequence: {_questController.SimulatedQuest.Sequence}"); + ImGui.Text($"Sequence: {simulatedQuest.Sequence}"); - ImGui.BeginDisabled(_questController.SimulatedQuest.Sequence == 0); + ImGui.BeginDisabled(simulatedQuest.Sequence == 0); if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus)) { _movementController.Stop(); _questController.Stop("Sim-"); - byte oldSequence = _questController.SimulatedQuest.Sequence; - byte newSequence = _questController.SimulatedQuest.Quest.Data.QuestSequence + byte oldSequence = simulatedQuest.Sequence; + byte newSequence = simulatedQuest.Quest.Root.QuestSequence .Select(x => (byte)x.Sequence) .LastOrDefault(x => x < oldSequence, byte.MinValue); - _questController.SimulatedQuest = _questController.SimulatedQuest with - { - Sequence = newSequence, - }; + _questController.SimulatedQuest.SetSequence(newSequence); } ImGui.EndDisabled(); ImGui.SameLine(); - ImGui.BeginDisabled(_questController.SimulatedQuest.Sequence >= 255); + ImGui.BeginDisabled(simulatedQuest.Sequence >= 255); if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) { _movementController.Stop(); _questController.Stop("Sim+"); - byte oldSequence = _questController.SimulatedQuest.Sequence; - byte newSequence = _questController.SimulatedQuest.Quest.Data.QuestSequence + byte oldSequence = simulatedQuest.Sequence; + byte newSequence = simulatedQuest.Quest.Root.QuestSequence .Select(x => (byte)x.Sequence) .FirstOrDefault(x => x > oldSequence, byte.MaxValue); - _questController.SimulatedQuest = _questController.SimulatedQuest with - { - Sequence = newSequence, - }; + simulatedQuest.SetSequence(newSequence); } ImGui.EndDisabled(); - var simulatedSequence = - _questController.SimulatedQuest.Quest.FindSequence(_questController.SimulatedQuest.Sequence); + var simulatedSequence = simulatedQuest.Quest.FindSequence(simulatedQuest.Sequence); if (simulatedSequence != null) { using var _ = ImRaii.PushId("SimulatedStep"); - ImGui.Text($"Step: {currentQuest.Step} / {simulatedSequence.Steps.Count - 1}"); + ImGui.Text($"Step: {simulatedQuest.Step} / {simulatedSequence.Steps.Count - 1}"); - ImGui.BeginDisabled(currentQuest.Step == 0); + ImGui.BeginDisabled(simulatedQuest.Step == 0); if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus)) { _movementController.Stop(); _questController.Stop("SimStep-"); - _questController.CurrentQuest = currentQuest with - { - Step = Math.Min(currentQuest.Step - 1, simulatedSequence.Steps.Count - 1), - }; + simulatedQuest.SetStep(Math.Min(simulatedQuest.Step - 1, + simulatedSequence.Steps.Count - 1)); } ImGui.EndDisabled(); ImGui.SameLine(); - ImGui.BeginDisabled(currentQuest.Step >= simulatedSequence.Steps.Count); + ImGui.BeginDisabled(simulatedQuest.Step >= simulatedSequence.Steps.Count); if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) { _movementController.Stop(); _questController.Stop("SimStep+"); - _questController.CurrentQuest = currentQuest with - { - Step = currentQuest.Step == simulatedSequence.Steps.Count - 1 + simulatedQuest.SetStep( + simulatedQuest.Step == simulatedSequence.Steps.Count - 1 ? 255 - : (currentQuest.Step + 1), - }; + : (simulatedQuest.Step + 1)); } ImGui.EndDisabled(); @@ -375,7 +378,13 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig ImGui.EndDisabled(); ImGui.SameLine(); - if (ImGui.Button("Interact")) + ImGui.BeginDisabled(gameObject->NamePlateIconId == 0); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) + _questData.ShowQuestsIssuedByTarget(); + ImGui.EndDisabled(); + + ImGui.SameLine(); + if (ImGuiComponents.IconButton(FontAwesomeIcon.MousePointer)) { ulong result = TargetSystem.Instance()->InteractWithObject( (GameObject*)_targetManager.Target.Address, false); @@ -384,7 +393,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig ImGui.SameLine(); - ImGui.Button("Copy"); + ImGuiComponents.IconButton(FontAwesomeIcon.Copy); if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) { string interactionType = gameObject->NamePlateIconId switch @@ -414,7 +423,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig } else { - ImGui.Button($"Copy"); + ImGuiComponents.IconButton(FontAwesomeIcon.Copy); if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) { ImGui.SetClipboardText($$"""