From b4be834f13f8e3f74505ee8c94ea6cebd51cf6dd Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Sun, 14 Jul 2024 03:42:30 +0200 Subject: [PATCH] Very experimental combat implementation (requires RSR) --- QuestPathGenerator/QuestSourceGenerator.cs | 3 + .../Pixies/Dailies/3690_A Flowery Frolic.json | 56 +++++ .../Dailies/3691_Sharing Is Caring.json | 57 +++++ .../3692_The Aesthetician of Il Mheg.json | 106 ++++++++ .../Pixies/Dailies/3693_Sweet as Honey.json | 57 +++++ .../Dailies/3694_I Dream of Shinies.json | 75 ++++++ .../Dailies/3695_The Wonder of Witchweed.json | 58 +++++ .../Dailies/3696_Of Marvelous Mallow.json | 81 ++++++ .../Dailies/3697_The Moss Fungus Menace.json | 59 +++++ .../3703_Raiders of the Lost Pork.json | 53 ++++ .../Dailies/3704_Pebble without a Cause.json | 59 +++++ .../Dailies/3709_No Accounting for Taste.json | 57 +++++ .../Tribal/Pixies/Dailies/~template.json | 43 ++++ .../Progress/3685_The Heart's Oasis.json | 231 ++++++++++++++++++ .../Unlock/3683_Manic Pixie Dream Realm.json | 214 ++++++++++++++++ .../Dailies/4552_Deal by the Docks.json | 70 ++++++ .../Dailies/4553_Do It for the Vine.json | 58 +++++ .../Dailies/4554_Missing Rider.json | 110 +++++++++ .../Dailies/4557_Gulal Generosity.json | 175 +++++++++++++ .../Dailies/4558_Essential Eggredients.json | 70 ++++++ .../Dailies/4559_Darling Defender.json | 73 ++++++ .../Dailies/4564_Olfactory Warfare.json | 2 +- .../Dailies/4565_Ridin' Hazards.json | 2 +- .../Arkasodara/Dailies/4566_Hippo Scrub.json | 2 +- .../Dailies/4567_Vanaspati's Blessing.json | 2 +- ...Hells Hath No Fury as a Hippo Scorned.json | 2 +- .../Arkasodara/Dailies/4569_Tusk Trouble.json | 2 +- .../Tribal/Arkasodara/Dailies/~template.json | 43 ++++ QuestPaths/quest-v1.json | 6 +- Questionable/Controller/CombatController.cs | 155 ++++++++++++ .../Controller/CombatModules/ICombatModule.cs | 14 ++ .../RotationSolverRebornModule.cs | 98 ++++++++ Questionable/Controller/GameUiController.cs | 15 +- Questionable/Controller/QuestController.cs | 6 + Questionable/Controller/QuestRegistry.cs | 45 +++- .../Controller/Steps/Interactions/Combat.cs | 114 +++++++-- .../Controller/Steps/Shared/SkipCondition.cs | 4 +- .../Controller/Steps/Shared/WaitAtEnd.cs | 15 +- .../Utils/QuestWorkUtils.cs} | 17 +- Questionable/Data/QuestData.cs | 24 +- Questionable/Model/EMovementType.cs | 1 + Questionable/QuestionablePlugin.cs | 6 +- Questionable/Windows/QuestWindow.cs | 17 +- 43 files changed, 2298 insertions(+), 59 deletions(-) create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3690_A Flowery Frolic.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3691_Sharing Is Caring.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3692_The Aesthetician of Il Mheg.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3693_Sweet as Honey.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3694_I Dream of Shinies.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3695_The Wonder of Witchweed.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3696_Of Marvelous Mallow.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3697_The Moss Fungus Menace.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3703_Raiders of the Lost Pork.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3704_Pebble without a Cause.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3709_No Accounting for Taste.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/~template.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Progress/3685_The Heart's Oasis.json create mode 100644 QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Unlock/3683_Manic Pixie Dream Realm.json create mode 100644 QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4552_Deal by the Docks.json create mode 100644 QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4553_Do It for the Vine.json create mode 100644 QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4554_Missing Rider.json create mode 100644 QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4557_Gulal Generosity.json create mode 100644 QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4558_Essential Eggredients.json create mode 100644 QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4559_Darling Defender.json create mode 100644 QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/~template.json create mode 100644 Questionable/Controller/CombatController.cs create mode 100644 Questionable/Controller/CombatModules/ICombatModule.cs create mode 100644 Questionable/Controller/CombatModules/RotationSolverRebornModule.cs rename Questionable/{Model/V1/QuestStepExtensions.cs => Controller/Utils/QuestWorkUtils.cs} (64%) diff --git a/QuestPathGenerator/QuestSourceGenerator.cs b/QuestPathGenerator/QuestSourceGenerator.cs index 595cdb880..66f060ec9 100644 --- a/QuestPathGenerator/QuestSourceGenerator.cs +++ b/QuestPathGenerator/QuestSourceGenerator.cs @@ -55,6 +55,9 @@ public class QuestSourceGenerator : ISourceGenerator continue; string name = Path.GetFileName(additionalFile.Path); + if (!name.Contains('_')) + continue; + ushort id = ushort.Parse(name.Substring(0, name.IndexOf('_'))); var text = additionalFile.GetText(); diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3690_A Flowery Frolic.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3690_A Flowery Frolic.json new file mode 100644 index 000000000..7dc3f163b --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3690_A Flowery Frolic.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2010859, + "Position": { + "X": -743.86206, + "Y": 78.96533, + "Z": 457.14502 + }, + "TerritoryId": 816, + "InteractionType": "Combat", + "EnemySpawnType": "AfterInteraction", + "KillEnemyDataIds": [ + 11443 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3691_Sharing Is Caring.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3691_Sharing Is Caring.json new file mode 100644 index 000000000..80b35d7f6 --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3691_Sharing Is Caring.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1032140, + "Position": { + "X": -459.89166, + "Y": 42.65948, + "Z": -305.71454 + }, + "TerritoryId": 816, + "InteractionType": "Say", + "ChatMessage": { + "Key": "TEXT_BANPIX103_03691_SAYTODO_000_030" + }, + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3692_The Aesthetician of Il Mheg.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3692_The Aesthetician of Il Mheg.json new file mode 100644 index 000000000..330fd1059 --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3692_The Aesthetician of Il Mheg.json @@ -0,0 +1,106 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Comment": "TODO Has multiple possible targets, unsure if QW works", + "Steps": [ + { + "Position": { + "X": -342.05676, + "Y": 37.5036, + "Z": 434.53723 + }, + "TerritoryId": 816, + "InteractionType": "WalkTo", + "Fly": true, + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + -128 + ] + }, + { + "DataId": 2010860, + "Position": { + "X": -340.38306, + "Y": 38.77307, + "Z": 434.07336 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + }, + { + "DataId": 2010861, + "Position": { + "X": -332.44836, + "Y": 44.47998, + "Z": 462.15002 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "SkipIf": [ + "NotTargetable" + ] + }, + { + "DataId": 2010863, + "Position": { + "X": -293.08008, + "Y": 42.70996, + "Z": 463.61487 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "SkipIf": [ + "NotTargetable" + ], + "IgnoreDistanceToObject": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3693_Sweet as Honey.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3693_Sweet as Honey.json new file mode 100644 index 000000000..6ddd63784 --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3693_Sweet as Honey.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2010864, + "Position": { + "X": 3.8909912, + "Y": 13.90094, + "Z": 637.23206 + }, + "TerritoryId": 816, + "InteractionType": "Combat", + "EnemySpawnType": "AfterInteraction", + "KillEnemyDataIds": [ + 11444 + ], + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3694_I Dream of Shinies.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3694_I Dream of Shinies.json new file mode 100644 index 000000000..bd3cf85cd --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3694_I Dream of Shinies.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "Position": { + "X": -338.70084, + "Y": 9.922639, + "Z": -181.90271 + }, + "TerritoryId": 816, + "InteractionType": "WalkTo", + "Fly": true + }, + { + "Position": { + "X": -332.49713, + "Y": -44.52391, + "Z": -242.4296 + }, + "TerritoryId": 816, + "InteractionType": "WalkTo", + "Fly": true, + "DisableNavmesh": true + }, + { + "DataId": 2010866, + "Position": { + "X": -335.59174, + "Y": -54.154297, + "Z": -293.41577 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3695_The Wonder of Witchweed.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3695_The Wonder of Witchweed.json new file mode 100644 index 000000000..900ec0281 --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3695_The Wonder of Witchweed.json @@ -0,0 +1,58 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2010868, + "Position": { + "X": -504.32596, + "Y": 77.22583, + "Z": -410.11676 + }, + "TerritoryId": 816, + "InteractionType": "Combat", + "EnemySpawnType": "AfterInteraction", + "KillEnemyDataIds": [ + 11445 + ], + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3696_Of Marvelous Mallow.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3696_Of Marvelous Mallow.json new file mode 100644 index 000000000..a689011c6 --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3696_Of Marvelous Mallow.json @@ -0,0 +1,81 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2010888, + "Position": { + "X": -171.58777, + "Y": 5.2338257, + "Z": -252.88782 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "Fly": true, + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + }, + { + "DataId": 2010889, + "Position": { + "X": -170.1839, + "Y": 4.9591064, + "Z": -283.0396 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "Fly": true, + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3697_The Moss Fungus Menace.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3697_The Moss Fungus Menace.json new file mode 100644 index 000000000..ef2fc0f5c --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3697_The Moss Fungus Menace.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2010890, + "Position": { + "X": 10.0251465, + "Y": 32.944214, + "Z": -608.6061 + }, + "TerritoryId": 816, + "InteractionType": "Combat", + "EnemySpawnType": "AfterInteraction", + "KillEnemyDataIds": [ + 11446 + ], + "AetheryteShortcut": "Il Mheg - Wolekdorf", + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3703_Raiders of the Lost Pork.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3703_Raiders of the Lost Pork.json new file mode 100644 index 000000000..5f4962f31 --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3703_Raiders of the Lost Pork.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1032167, + "Position": { + "X": -416.52557, + "Y": 20.303928, + "Z": 221.75928 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3704_Pebble without a Cause.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3704_Pebble without a Cause.json new file mode 100644 index 000000000..940bafba5 --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3704_Pebble without a Cause.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2010901, + "Position": { + "X": -413.96204, + "Y": -0.10687256, + "Z": -55.77173 + }, + "TerritoryId": 816, + "InteractionType": "Combat", + "EnemySpawnType": "AfterInteraction", + "KillEnemyDataIds": [ + 11451 + ], + "Fly": true, + "Comment": "TODO Combat is optional, check where we should walk" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3709_No Accounting for Taste.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3709_No Accounting for Taste.json new file mode 100644 index 000000000..774fe043b --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/3709_No Accounting for Taste.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2010909, + "Position": { + "X": -242.08441, + "Y": 0.83917236, + "Z": 223.80408 + }, + "TerritoryId": 816, + "InteractionType": "Combat", + "EnemySpawnType": "AfterInteraction", + "KillEnemyDataIds": [ + 11450 + ], + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/~template.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/~template.json new file mode 100644 index 000000000..7b587be66 --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Dailies/~template.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031809, + "Position": { + "X": -454.3069, + "Y": 71.43217, + "Z": 575.1278 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Progress/3685_The Heart's Oasis.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Progress/3685_The Heart's Oasis.json new file mode 100644 index 000000000..39263b7fc --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Progress/3685_The Heart's Oasis.json @@ -0,0 +1,231 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031806, + "Position": { + "X": -464.59143, + "Y": 71.76874, + "Z": 573.8766 + }, + "TerritoryId": 816, + "InteractionType": "AcceptQuest", + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_BANPIX003_03685_Q1_000_000", + "Answer": "TEXT_BANPIX003_03685_A1_000_003" + } + ] + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1031892, + "Position": { + "X": -461.5702, + "Y": 72.51754, + "Z": 586.48047 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "TargetTerritoryId": 891 + }, + { + "DataId": 1031866, + "Position": { + "X": 78.690796, + "Y": 0.15887591, + "Z": 50.0343 + }, + "TerritoryId": 891, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1031865, + "Position": { + "X": 76.37134, + "Y": 0.15887591, + "Z": 51.987427 + }, + "TerritoryId": 891, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 1031867, + "Position": { + "X": 303.15088, + "Y": 1.4685827, + "Z": -313.34406 + }, + "TerritoryId": 815, + "InteractionType": "Interact", + "AetheryteShortcut": "Amh Araeng - Mord Souq", + "Fly": true + } + ] + }, + { + "Sequence": 4, + "Steps": [ + { + "Position": { + "X": 201.67809, + "Y": 7.1558266, + "Z": -137.17564 + }, + "TerritoryId": 815, + "InteractionType": "WalkTo", + "Fly": true + }, + { + "DataId": 1031869, + "Position": { + "X": 201.06812, + "Y": 7.1558266, + "Z": -138.81134 + }, + "TerritoryId": 815, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 5, + "Steps": [ + { + "Position": { + "X": 355.25076, + "Y": -19.54202, + "Z": -4.2170615 + }, + "StopDistance": 0.5, + "TerritoryId": 815, + "InteractionType": "Combat", + "EnemySpawnType": "AutoOnEnterArea", + "KillEnemyDataIds": [ + 11439 + ] + } + ] + }, + { + "Sequence": 6, + "Steps": [ + { + "DataId": 1031871, + "Position": { + "X": 350.05713, + "Y": -18.544811, + "Z": -15.793152 + }, + "StopDistance": 7, + "TerritoryId": 815, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 7, + "Steps": [ + { + "Position": { + "X": 201.67809, + "Y": 7.1558266, + "Z": -137.17564 + }, + "TerritoryId": 815, + "InteractionType": "WalkTo", + "Fly": true + }, + { + "DataId": 1031869, + "Position": { + "X": 201.06812, + "Y": 7.1558266, + "Z": -138.81134 + }, + "TerritoryId": 815, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 8, + "Steps": [ + { + "DataId": 1031892, + "Position": { + "X": -461.5702, + "Y": 72.51754, + "Z": 586.48047 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "TargetTerritoryId": 891, + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true + }, + { + "DataId": 1032030, + "Position": { + "X": 90.40967, + "Y": 40.45613, + "Z": -105.48566 + }, + "TerritoryId": 891, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 9, + "Steps": [ + { + "DataId": 1032352, + "Position": { + "X": 95.994385, + "Y": 38.906254, + "Z": -89.37213 + }, + "TerritoryId": 891, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031806, + "Position": { + "X": -464.59143, + "Y": 71.76874, + "Z": 573.8766 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Unlock/3683_Manic Pixie Dream Realm.json b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Unlock/3683_Manic Pixie Dream Realm.json new file mode 100644 index 000000000..302a217fe --- /dev/null +++ b/QuestPaths/5.x - Shadowbringers/Tribal/Pixies/Unlock/3683_Manic Pixie Dream Realm.json @@ -0,0 +1,214 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1031839, + "Position": { + "X": 95.628296, + "Y": 1.490116E-08, + "Z": 204.11987 + }, + "TerritoryId": 819, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1031843, + "Position": { + "X": 152.48328, + "Y": 0.21766767, + "Z": 655.4512 + }, + "TerritoryId": 813, + "InteractionType": "Interact", + "AetheryteShortcut": "Lakeland - Fort Jobb", + "Fly": true + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1031845, + "Position": { + "X": -200.09155, + "Y": 2.2048368, + "Z": 737.8804 + }, + "TerritoryId": 813, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "Position": { + "X": 234.24565, + "Y": 10.83118, + "Z": 738.46594 + }, + "StopDistance": 1, + "TerritoryId": 813, + "InteractionType": "Combat", + "EnemySpawnType": "AutoOnEnterArea", + "KillEnemyDataIds": [ + 11437 + ], + "Fly": true + } + ] + }, + { + "Sequence": 4, + "Steps": [ + { + "DataId": 1031847, + "Position": { + "X": 231.92188, + "Y": 9.887029, + "Z": 726.74133 + }, + "StopDistance": 7, + "TerritoryId": 813, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 5, + "Steps": [ + { + "DataId": 1031846, + "Position": { + "X": -204.76086, + "Y": 2.4649847, + "Z": 739.9557 + }, + "TerritoryId": 813, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 6, + "Steps": [ + { + "DataId": 1031808, + "Position": { + "X": -461.53967, + "Y": 72.51729, + "Z": 586.48047 + }, + "TerritoryId": 816, + "InteractionType": "Interact", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true, + "DialogueChoices": [ + { + "Type": "YesNo", + "Prompt": "TEXT_BANPIX001_03683_EVENTAREA_WARP_000_133", + "Yes": true + } + ] + } + ] + }, + { + "Sequence": 7, + "Steps": [ + { + "Position": { + "X": 0, + "Y": 0, + "Z": 0 + }, + "TerritoryId": 1, + "InteractionType": "WalkTo", + "Comment": "Filler" + } + ] + }, + { + "Sequence": 8, + "Steps": [ + { + "DataId": 1031850, + "Position": { + "X": 55.588623, + "Y": -1.6532946, + "Z": 48.599854 + }, + "TerritoryId": 889, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 9, + "Steps": [ + { + "Position": { + "X": 0, + "Y": 0, + "Z": 0 + }, + "TerritoryId": 1, + "InteractionType": "WalkTo", + "Comment": "Filler" + } + ] + }, + { + "Sequence": 10, + "Steps": [ + { + "DataId": 1032348, + "Position": { + "X": -18.295654, + "Y": 6.8822618, + "Z": -67.338135 + }, + "TerritoryId": 890, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1031806, + "Position": { + "X": -464.59143, + "Y": 71.76874, + "Z": 573.8766 + }, + "TerritoryId": 816, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Il Mheg - Lydha Lran", + "Fly": true, + "DialogueChoices": [ + { + "Type": "List", + "Prompt": "TEXT_BANPIX001_03683_Q3_000_000", + "Answer": "TEXT_BANPIX001_03683_A3_000_001" + } + ] + } + ] + } + ] +} diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4552_Deal by the Docks.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4552_Deal by the Docks.json new file mode 100644 index 000000000..3d6aba36c --- /dev/null +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4552_Deal by the Docks.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1037644, + "Position": { + "X": -293.72095, + "Y": 1.4600283, + "Z": 551.0491 + }, + "TerritoryId": 957, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1042366, + "Position": { + "X": 185.56494, + "Y": 1.8742322, + "Z": 760.4332 + }, + "TerritoryId": 957, + "InteractionType": "Interact", + "AetheryteShortcut": "Thavnair - Yedlihmad", + "Fly": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "CompleteQuest", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4553_Do It for the Vine.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4553_Do It for the Vine.json new file mode 100644 index 000000000..9f22b8962 --- /dev/null +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4553_Do It for the Vine.json @@ -0,0 +1,58 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 2012850, + "Position": { + "X": -399.74066, + "Y": 58.91504, + "Z": -354.97064 + }, + "TerritoryId": 957, + "InteractionType": "Combat", + "EnemySpawnType": "AfterInteraction", + "KillEnemyDataIds": [ + 14674, + 14675 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Thavnair - Yedlihmad", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4554_Missing Rider.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4554_Missing Rider.json new file mode 100644 index 000000000..f9767cec8 --- /dev/null +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4554_Missing Rider.json @@ -0,0 +1,110 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1042367, + "Position": { + "X": 507.01135, + "Y": 12.589098, + "Z": -482.96332 + }, + "TerritoryId": 957, + "InteractionType": "Interact", + "AetheryteShortcut": "Thavnair - Palaka's Stand", + "Fly": true + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 2012851, + "Position": { + "X": 427.4204, + "Y": 18.44812, + "Z": -451.37714 + }, + "TerritoryId": 957, + "InteractionType": "Interact", + "Fly": true + } + ] + }, + { + "Sequence": 3, + "Steps": [ + { + "DataId": 2012851, + "Position": { + "X": 427.4204, + "Y": 18.44812, + "Z": -451.37714 + }, + "TerritoryId": 957, + "InteractionType": "Interact", + "SkipIf": [ + "NotTargetable" + ] + }, + { + "Position": { + "X": 436.76804, + "Y": 21.84539, + "Z": -466.46533 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo", + "DisableNavmesh": true + }, + { + "DataId": 1042367, + "Position": { + "X": 507.01135, + "Y": 12.589098, + "Z": -482.96332 + }, + "TerritoryId": 957, + "InteractionType": "Interact", + "DisableNavmesh": true + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Thavnair - Yedlihmad", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4557_Gulal Generosity.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4557_Gulal Generosity.json new file mode 100644 index 000000000..f2476fc2b --- /dev/null +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4557_Gulal Generosity.json @@ -0,0 +1,175 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1042302, + "Position": { + "X": -102.43384, + "Y": 40.00001, + "Z": 331.89893 + }, + "TerritoryId": 957, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "Position": { + "X": 436.3157, + "Y": 3.1168795, + "Z": -241.61731 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo", + "Fly": true, + "Land": true, + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + -32 + ] + }, + { + "DataId": 1042372, + "Position": { + "X": 441.062, + "Y": 3.5405273, + "Z": -238.78845 + }, + "StopDistance": 5, + "TerritoryId": 957, + "InteractionType": "Action", + "Action": "Yellow Gulal", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] + }, + { + "Position": { + "X": 378.90213, + "Y": 3.1168797, + "Z": -226.82733 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo", + "Fly": true, + "Land": true, + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + -64 + ] + }, + { + "DataId": 1042371, + "Position": { + "X": 376.7605, + "Y": 3.3540652, + "Z": -225.33002 + }, + "StopDistance": 5, + "TerritoryId": 957, + "InteractionType": "Action", + "Action": "Blue Gulal", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ] + }, + { + "Position": { + "X": 404.06558, + "Y": 13.027411, + "Z": -307.05457 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo", + "Fly": true, + "Land": true, + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + -128 + ] + }, + { + "DataId": 1042373, + "Position": { + "X": 406.1189, + "Y": 13.0274105, + "Z": -311.0857 + }, + "StopDistance": 5, + "TerritoryId": 957, + "InteractionType": "Action", + "Action": "Red Gulal", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ] + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Thavnair - Yedlihmad", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4558_Essential Eggredients.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4558_Essential Eggredients.json new file mode 100644 index 000000000..92f8f2953 --- /dev/null +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4558_Essential Eggredients.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "DataId": 1042376, + "Position": { + "X": 205.34058, + "Y": 63.981293, + "Z": -640.4059 + }, + "TerritoryId": 957, + "InteractionType": "Interact", + "AetheryteShortcut": "Thavnair - Palaka's Stand", + "Fly": true + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 2012908, + "Position": { + "X": 208.23987, + "Y": 65.62903, + "Z": -642.1149 + }, + "TerritoryId": 957, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Thavnair - Yedlihmad", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4559_Darling Defender.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4559_Darling Defender.json new file mode 100644 index 000000000..44068d32a --- /dev/null +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4559_Darling Defender.json @@ -0,0 +1,73 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + { + "Position": { + "X": -605.46277, + "Y": 2.1626635, + "Z": -336.87347 + }, + "StopDistance": 0.25, + "TerritoryId": 957, + "InteractionType": "Combat", + "EnemySpawnType": "AutoOnEnterArea", + "KillEnemyDataIds": [ + 14676 + ] + } + ] + }, + { + "Sequence": 2, + "Steps": [ + { + "DataId": 1042378, + "Position": { + "X": -605.46277, + "Y": 2.1626635, + "Z": -336.87347 + }, + "StopDistance": 5, + "TerritoryId": 957, + "InteractionType": "Interact" + } + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "CompleteQuest", + "AetheryteShortcut": "Thavnair - Yedlihmad", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4564_Olfactory Warfare.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4564_Olfactory Warfare.json index 1c2b49974..acfd58ad7 100644 --- a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4564_Olfactory Warfare.json +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4564_Olfactory Warfare.json @@ -13,7 +13,7 @@ "Z": 321.06494 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "AcceptQuest" } ] }, diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4565_Ridin' Hazards.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4565_Ridin' Hazards.json index c2578b9a9..8425fa74b 100644 --- a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4565_Ridin' Hazards.json +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4565_Ridin' Hazards.json @@ -13,7 +13,7 @@ "Z": 321.06494 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "AcceptQuest" } ] }, diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4566_Hippo Scrub.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4566_Hippo Scrub.json index 25dd87c31..5bb38148e 100644 --- a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4566_Hippo Scrub.json +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4566_Hippo Scrub.json @@ -13,7 +13,7 @@ "Z": 321.06494 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "AcceptQuest" } ] }, diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4567_Vanaspati's Blessing.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4567_Vanaspati's Blessing.json index 18b9b83de..d486bec6c 100644 --- a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4567_Vanaspati's Blessing.json +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4567_Vanaspati's Blessing.json @@ -13,7 +13,7 @@ "Z": 321.06494 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "AcceptQuest" } ] }, diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4568_Hells Hath No Fury as a Hippo Scorned.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4568_Hells Hath No Fury as a Hippo Scorned.json index 0a0aaa800..477fae195 100644 --- a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4568_Hells Hath No Fury as a Hippo Scorned.json +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4568_Hells Hath No Fury as a Hippo Scorned.json @@ -13,7 +13,7 @@ "Z": 321.06494 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "AcceptQuest" } ] }, diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4569_Tusk Trouble.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4569_Tusk Trouble.json index 0d01f3861..acafd9fa3 100644 --- a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4569_Tusk Trouble.json +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/4569_Tusk Trouble.json @@ -13,7 +13,7 @@ "Z": 321.06494 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "AcceptQuest" } ] }, diff --git a/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/~template.json b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/~template.json new file mode 100644 index 000000000..f7be2e843 --- /dev/null +++ b/QuestPaths/6.x - Endwalker/Tribal/Arkasodara/Dailies/~template.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://carvel.li/questionable/quest-1.0", + "Author": "liza", + "QuestSequence": [ + { + "Sequence": 0, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "AcceptQuest" + } + ] + }, + { + "Sequence": 1, + "Steps": [ + + ] + }, + { + "Sequence": 255, + "Steps": [ + { + "DataId": 1042301, + "Position": { + "X": -66.02582, + "Y": 39.994705, + "Z": 321.06494 + }, + "TerritoryId": 957, + "InteractionType": "CompleteQuest", + "Fly": true + } + ] + } + ] +} diff --git a/QuestPaths/quest-v1.json b/QuestPaths/quest-v1.json index 34fe82010..31d0f17ac 100644 --- a/QuestPaths/quest-v1.json +++ b/QuestPaths/quest-v1.json @@ -1009,12 +1009,14 @@ }, "required": [ "Sequence" - ] + ], + "additionalProperties": false } } }, "required": [ "QuestSequence", "Author" - ] + ], + "additionalProperties": false } diff --git a/Questionable/Controller/CombatController.cs b/Questionable/Controller/CombatController.cs new file mode 100644 index 000000000..5b31530a8 --- /dev/null +++ b/Questionable/Controller/CombatController.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Dalamud.Game.ClientState.Conditions; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game.Object; +using Microsoft.Extensions.Logging; +using Questionable.Controller.CombatModules; + +namespace Questionable.Controller; + +internal sealed class CombatController +{ + private readonly List _combatModules; + private readonly ITargetManager _targetManager; + private readonly IObjectTable _objectTable; + private readonly ICondition _condition; + private readonly IClientState _clientState; + private readonly ILogger _logger; + + private CurrentFight? _currentFight; + + public CombatController(IEnumerable combatModules, ITargetManager targetManager, + IObjectTable objectTable, ICondition condition, IClientState clientState, ILogger logger) + { + _combatModules = combatModules.ToList(); + _targetManager = targetManager; + _objectTable = objectTable; + _condition = condition; + _clientState = clientState; + _logger = logger; + } + + public bool IsRunning => _currentFight != null; + + public bool Start(CombatData combatData) + { + Stop(); + + var combatModule = _combatModules.FirstOrDefault(x => x.IsLoaded); + if (combatModule == null) + return false; + + if (combatModule.Start()) + { + _currentFight = new CurrentFight + { + Module = combatModule, + Data = combatData, + }; + return true; + } + else + return false; + } + + /// true if still in combat, false otherwise + public bool Update() + { + if (_currentFight == null) + return false; + + var target = _targetManager.Target; + if (target != null) + { + if (IsEnemyToKill(target)) + return true; + + var nextTarget = FindNextTarget(); + if (nextTarget != null) + { + _logger.LogInformation("Changing next target to {TargetName} ({TargetId:X8})", + nextTarget.Name.ToString(), nextTarget.GameObjectId); + _targetManager.Target = nextTarget; + _currentFight.Module.SetTarget(nextTarget); + } + else + { + _logger.LogInformation("Resetting next target"); + _targetManager.Target = null; + } + } + else + { + var nextTarget = FindNextTarget(); + if (nextTarget != null) + { + _logger.LogInformation("Setting next target to {TargetName} ({TargetId:X8})", + nextTarget.Name.ToString(), nextTarget.GameObjectId); + _targetManager.Target = nextTarget; + _currentFight.Module.SetTarget(nextTarget); + } + } + + return _condition[ConditionFlag.InCombat]; + } + + private IGameObject? FindNextTarget() + { + return _objectTable.Where(IsEnemyToKill).MinBy(x => (x.Position - _clientState.LocalPlayer!.Position).Length()); + } + + private unsafe bool IsEnemyToKill(IGameObject gameObject) + { + if (gameObject is IBattleChara battleChara) + { + if (battleChara.IsDead) + return false; + + if (!battleChara.IsTargetable) + return false; + + if (battleChara.TargetObjectId == _clientState.LocalPlayer?.GameObjectId) + return true; + + if (_currentFight != null && _currentFight.Data.KillEnemyDataIds.Contains(battleChara.DataId)) + return true; + + if (battleChara.StatusFlags.HasFlag(StatusFlags.Hostile)) + { + var gameObjectStruct = (GameObject*)gameObject.Address; + return gameObjectStruct->NamePlateIconId != 0; + } + else + return false; + } + else + return false; + } + + public void Stop() + { + if (_currentFight != null) + { + _logger.LogInformation("Stopping current fight"); + _currentFight.Module.Stop(); + } + + _currentFight = null; + } + + private sealed class CurrentFight + { + public required ICombatModule Module { get; init; } + public required CombatData Data { get; init; } + } + + public sealed class CombatData + { + public required ReadOnlyCollection KillEnemyDataIds { get; init; } + } +} diff --git a/Questionable/Controller/CombatModules/ICombatModule.cs b/Questionable/Controller/CombatModules/ICombatModule.cs new file mode 100644 index 000000000..1a8d003d0 --- /dev/null +++ b/Questionable/Controller/CombatModules/ICombatModule.cs @@ -0,0 +1,14 @@ +using Dalamud.Game.ClientState.Objects.Types; + +namespace Questionable.Controller.CombatModules; + +internal interface ICombatModule +{ + bool IsLoaded { get; } + + bool Start(); + + bool Stop(); + + void SetTarget(IGameObject nextTarget); +} diff --git a/Questionable/Controller/CombatModules/RotationSolverRebornModule.cs b/Questionable/Controller/CombatModules/RotationSolverRebornModule.cs new file mode 100644 index 000000000..23e94b360 --- /dev/null +++ b/Questionable/Controller/CombatModules/RotationSolverRebornModule.cs @@ -0,0 +1,98 @@ +using System; +using System.Numerics; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; +using Dalamud.Plugin.Ipc.Exceptions; +using Dalamud.Plugin.Services; +using Microsoft.Extensions.Logging; +using Questionable.Model; + +namespace Questionable.Controller.CombatModules; + +internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable +{ + private readonly ILogger _logger; + private readonly MovementController _movementController; + private readonly IClientState _clientState; + private readonly ICallGateSubscriber _test; + private readonly ICallGateSubscriber _changeOperationMode; + + public RotationSolverRebornModule(ILogger logger, MovementController movementController, + IClientState clientState, IDalamudPluginInterface pluginInterface) + { + _logger = logger; + _movementController = movementController; + _clientState = clientState; + _test = pluginInterface.GetIpcSubscriber("RotationSolverReborn.Test"); + _changeOperationMode = + pluginInterface.GetIpcSubscriber("RotationSolverReborn.ChangeOperatingMode"); + } + + public bool IsLoaded + { + get + { + try + { + _test.InvokeAction("Validate RSR is callable from Questionable"); + return true; + } + catch (IpcError) + { + return false; + } + } + } + + public bool Start() + { + try + { + _changeOperationMode.InvokeAction(StateCommandType.Manual); + return true; + } + catch (IpcError e) + { + _logger.LogWarning(e, "Could not start combat"); + return false; + } + } + + public bool Stop() + { + try + { + _changeOperationMode.InvokeAction(StateCommandType.Off); + return true; + } + catch (IpcError e) + { + _logger.LogWarning(e, "Could not turn off combat"); + return false; + } + } + + public void SetTarget(IGameObject gameObject) + { + var player = _clientState.LocalPlayer; + if (player == null) + return; // uh oh + + float hitboxOffset = player.HitboxRadius + gameObject.HitboxRadius; + float actualDistance = Vector3.Distance(player.Position, gameObject.Position); + float maxDistance = player.ClassJob.GameData?.Role is 3 or 4 ? 25f : 3f; + if (actualDistance - hitboxOffset > maxDistance) + _movementController.NavigateTo(EMovementType.Combat, null, [gameObject.Position], false, false, + maxDistance + hitboxOffset - 0.25f, true); + } + + public void Dispose() => Stop(); + + enum StateCommandType : byte + { + Off, + Auto, + Manual, + } +} diff --git a/Questionable/Controller/GameUiController.cs b/Questionable/Controller/GameUiController.cs index 5f11dbf5f..658be3652 100644 --- a/Questionable/Controller/GameUiController.cs +++ b/Questionable/Controller/GameUiController.cs @@ -151,10 +151,7 @@ internal sealed class GameUiController : IDisposable if (string.IsNullOrEmpty(actualPrompt)) actualPrompt = null; - List answers = new(); - for (ushort i = 0; i < addonSelectIconString->AtkUnitBase.AtkValues[5].Int; i++) - answers.Add(addonSelectIconString->AtkUnitBase.AtkValues[i * 3 + 7].ReadAtkString()); - + var answers = GetChoices(addonSelectIconString); int? answer = HandleListChoice(actualPrompt, answers, checkAllSteps); if (answer != null) { @@ -173,6 +170,14 @@ internal sealed class GameUiController : IDisposable } } + public static unsafe List GetChoices(AddonSelectIconString* addonSelectIconString) + { + List answers = new(); + for (ushort i = 0; i < addonSelectIconString->AtkUnitBase.AtkValues[5].Int; i++) + answers.Add( addonSelectIconString->AtkValues[i * 3 + 7].ReadAtkString()); + + return answers; + } private int? HandleListChoice(string? actualPrompt, List answers, bool checkAllSteps) { @@ -503,7 +508,7 @@ internal sealed class GameUiController : IDisposable /// /// Ensures characters like '-' are handled equally in both strings. /// - private static bool GameStringEquals(string? a, string? b) + public static bool GameStringEquals(string? a, string? b) { if (a == null) return b == null; diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs index de00083c5..37f139d16 100644 --- a/Questionable/Controller/QuestController.cs +++ b/Questionable/Controller/QuestController.cs @@ -17,6 +17,7 @@ internal sealed class QuestController private readonly IClientState _clientState; private readonly GameFunctions _gameFunctions; private readonly MovementController _movementController; + private readonly CombatController _combatController; private readonly ILogger _logger; private readonly QuestRegistry _questRegistry; private readonly IKeyState _keyState; @@ -38,6 +39,7 @@ internal sealed class QuestController IClientState clientState, GameFunctions gameFunctions, MovementController movementController, + CombatController combatController, ILogger logger, QuestRegistry questRegistry, IKeyState keyState, @@ -48,6 +50,7 @@ internal sealed class QuestController _clientState = clientState; _gameFunctions = gameFunctions; _movementController = movementController; + _combatController = combatController; _logger = logger; _questRegistry = questRegistry; _keyState = keyState; @@ -106,6 +109,7 @@ internal sealed class QuestController { Stop("ESC pressed"); _movementController.Stop(); + _combatController.Stop(); } } @@ -322,6 +326,7 @@ internal sealed class QuestController _taskQueue.Clear(); _yesAlreadyIpc.RestoreYesAlready(); + _combatController.Stop(); } public void Stop(string label, bool continueIfAutomatic = false) @@ -473,6 +478,7 @@ internal sealed class QuestController } _movementController.Stop(); + _combatController.Stop(); var newTasks = _taskFactories .SelectMany(x => diff --git a/Questionable/Controller/QuestRegistry.cs b/Questionable/Controller/QuestRegistry.cs index c6137ffc0..b7450c534 100644 --- a/Questionable/Controller/QuestRegistry.cs +++ b/Questionable/Controller/QuestRegistry.cs @@ -57,15 +57,34 @@ internal sealed class QuestRegistry new DirectoryInfo(Path.Combine(solutionDirectory.FullName, "QuestPaths")); if (pathProjectDirectory.Exists) { - LoadFromDirectory(new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "2.x - A Realm Reborn"))); - LoadFromDirectory(new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "5.x - Shadowbringers"))); - LoadFromDirectory(new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "6.x - Endwalker"))); - LoadFromDirectory(new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "7.x - Dawntrail"))); + try + { + LoadFromDirectory( + new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "2.x - A Realm Reborn"))); + LoadFromDirectory( + new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "5.x - Shadowbringers"))); + LoadFromDirectory( + new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "6.x - Endwalker"))); + LoadFromDirectory( + new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "7.x - Dawntrail"))); + } + catch (Exception e) + { + _quests.Clear(); + _logger.LogError(e, "Failed to load quests from project directory"); + } } } #endif - LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "Quests"))); + try + { + LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "Quests"))); + } + catch (Exception e) + { + _logger.LogError(e, "Failed to load all quests from user directory (some may have been successfully loaded)"); + } #if !RELEASE foreach (var quest in _quests.Values) @@ -83,14 +102,17 @@ internal sealed class QuestRegistry private void LoadQuestFromStream(string fileName, Stream stream) { _logger.LogTrace("Loading quest from '{FileName}'", fileName); - var questId = ExtractQuestIdFromName(fileName); + ushort? questId = ExtractQuestIdFromName(fileName); + if (questId == null) + return; + Quest quest = new Quest { - QuestId = questId, + QuestId = questId.Value, Root = JsonSerializer.Deserialize(stream)!, - Info = _questData.GetQuestInfo(questId), + Info = _questData.GetQuestInfo(questId.Value), }; - _quests[questId] = quest; + _quests[questId.Value] = quest; } private void LoadFromDirectory(DirectoryInfo directory) @@ -119,11 +141,14 @@ internal sealed class QuestRegistry LoadFromDirectory(childDirectory); } - private static ushort ExtractQuestIdFromName(string resourceName) + private static ushort? ExtractQuestIdFromName(string resourceName) { string name = resourceName.Substring(0, resourceName.Length - ".json".Length); name = name.Substring(name.LastIndexOf('.') + 1); + if (!name.Contains('_', StringComparison.Ordinal)) + return null; + string[] parts = name.Split('_', 2); return ushort.Parse(parts[0], CultureInfo.InvariantCulture); } diff --git a/Questionable/Controller/Steps/Interactions/Combat.cs b/Questionable/Controller/Steps/Interactions/Combat.cs index 5a868bc94..f4cac3478 100644 --- a/Questionable/Controller/Steps/Interactions/Combat.cs +++ b/Questionable/Controller/Steps/Interactions/Combat.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Extensions.DependencyInjection; using Questionable.Controller.Steps.Common; +using Questionable.Controller.Utils; using Questionable.Model; using Questionable.Model.V1; @@ -19,29 +21,105 @@ internal static class Combat ArgumentNullException.ThrowIfNull(step.EnemySpawnType); var unmount = serviceProvider.GetRequiredService(); - if (step.EnemySpawnType == EEnemySpawnType.AfterInteraction) + switch (step.EnemySpawnType) { - ArgumentNullException.ThrowIfNull(step.DataId); + case EEnemySpawnType.AfterInteraction: + { + ArgumentNullException.ThrowIfNull(step.DataId); - var task = serviceProvider.GetRequiredService() - .With(step.DataId.Value, true); - return [unmount, task]; - } - else if (step.EnemySpawnType == EEnemySpawnType.AfterItemUse) - { - ArgumentNullException.ThrowIfNull(step.DataId); - ArgumentNullException.ThrowIfNull(step.ItemId); + var interaction = serviceProvider.GetRequiredService() + .With(step.DataId.Value, true); + return [unmount, interaction, CreateTask(quest, sequence, step)]; + } - var task = serviceProvider.GetRequiredService() - .With(step.DataId.Value, step.ItemId.Value); - return [unmount, task]; + case EEnemySpawnType.AfterItemUse: + { + ArgumentNullException.ThrowIfNull(step.DataId); + ArgumentNullException.ThrowIfNull(step.ItemId); + + var useItem = serviceProvider.GetRequiredService() + .With(step.DataId.Value, step.ItemId.Value); + return [unmount, useItem, CreateTask(quest, sequence, step)]; + } + + case EEnemySpawnType.AutoOnEnterArea: + // automatically triggered when entering area, i.e. only unmount + return [unmount, CreateTask(quest, sequence, step)]; + + case EEnemySpawnType.OverworldEnemies: + // TODO currently not handled + return [unmount]; + + default: + throw new ArgumentOutOfRangeException(nameof(step), $"Unknown spawn type {step.EnemySpawnType}"); } - else - // automatically triggered when entering area, i.e. only unmount - return [unmount]; } - public ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) - => throw new InvalidOperationException(); + public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step) + { + bool isLastStep = sequence.Steps.Last() == step; + return serviceProvider.GetRequiredService() + .With(quest.QuestId, isLastStep, step.KillEnemyDataIds, step.CompletionQuestVariablesFlags); + } + } + + internal sealed class HandleCombat(CombatController combatController, GameFunctions gameFunctions) : ITask + { + private ushort _questId; + private bool _isLastStep; + private CombatController.CombatData _combatData = null!; + private IList _completionQuestVariableFlags = null!; + + public ITask With(ushort questId, bool isLastStep, IList killEnemyDataIds, + IList completionQuestVariablesFlags) + { + _questId = questId; + _isLastStep = isLastStep; + _combatData = new CombatController.CombatData + { + KillEnemyDataIds = killEnemyDataIds.AsReadOnly(), + }; + _completionQuestVariableFlags = completionQuestVariablesFlags; + return this; + } + + public bool Start() => combatController.Start(_combatData); + + public ETaskResult Update() + { + if (combatController.Update()) + return ETaskResult.StillRunning; + + // if our quest step has any completion flags, we need to check if they are set + if (QuestWorkUtils.HasCompletionFlags(_completionQuestVariableFlags)) + { + var questWork = gameFunctions.GetQuestEx(_questId); + if (questWork == null) + return ETaskResult.StillRunning; + + if (!QuestWorkUtils.MatchesQuestWork(_completionQuestVariableFlags, questWork.Value, false)) + return ETaskResult.StillRunning; + } + + // the last step, by definition, can only be progressed by the game recognizing we're in a new sequence, + // so this is an indefinite wait + if (_isLastStep) + return ETaskResult.StillRunning; + else + { + combatController.Stop(); + return ETaskResult.TaskComplete; + } + } + + public override string ToString() + { + if (QuestWorkUtils.HasCompletionFlags(_completionQuestVariableFlags)) + return "HandleCombat(wait: QW flags)"; + else if (_isLastStep) + return "HandleCombat(wait: next sequence)"; + else + return "HandleCombat(wait: not in combat)"; + } } } diff --git a/Questionable/Controller/Steps/Shared/SkipCondition.cs b/Questionable/Controller/Steps/Shared/SkipCondition.cs index 30feca43f..fbc6ff257 100644 --- a/Questionable/Controller/Steps/Shared/SkipCondition.cs +++ b/Questionable/Controller/Steps/Shared/SkipCondition.cs @@ -8,6 +8,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.System.Framework; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Questionable.Controller.Utils; using Questionable.Model; using Questionable.Model.V1; @@ -112,7 +113,8 @@ internal static class SkipCondition } QuestWork? questWork = gameFunctions.GetQuestEx(QuestId); - if (questWork != null && Step.MatchesQuestVariables(questWork.Value, true)) + if (questWork != null && + QuestWorkUtils.MatchesQuestWork(Step.CompletionQuestVariablesFlags, questWork.Value, true)) { logger.LogInformation("Skipping step, as quest variables match"); return true; diff --git a/Questionable/Controller/Steps/Shared/WaitAtEnd.cs b/Questionable/Controller/Steps/Shared/WaitAtEnd.cs index 061626e01..27feae232 100644 --- a/Questionable/Controller/Steps/Shared/WaitAtEnd.cs +++ b/Questionable/Controller/Steps/Shared/WaitAtEnd.cs @@ -9,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; using FFXIVClientStructs.FFXIV.Client.Game; using Microsoft.Extensions.DependencyInjection; using Questionable.Controller.Steps.Common; +using Questionable.Controller.Utils; using Questionable.Data; using Questionable.Model; using Questionable.Model.V1; @@ -17,7 +18,10 @@ namespace Questionable.Controller.Steps.Shared; internal static class WaitAtEnd { - internal sealed class Factory(IServiceProvider serviceProvider, IClientState clientState, ICondition condition, + internal sealed class Factory( + IServiceProvider serviceProvider, + IClientState clientState, + ICondition condition, TerritoryData territoryData) : ITaskFactory { @@ -106,14 +110,16 @@ internal static class WaitAtEnd case EInteractionType.AcceptQuest: return [ - serviceProvider.GetRequiredService().With(step.PickupQuestId ?? quest.QuestId), + serviceProvider.GetRequiredService() + .With(step.PickupQuestId ?? quest.QuestId), serviceProvider.GetRequiredService() ]; case EInteractionType.CompleteQuest: return [ - serviceProvider.GetRequiredService().With(step.TurnInQuestId ?? quest.QuestId), + serviceProvider.GetRequiredService() + .With(step.TurnInQuestId ?? quest.QuestId), serviceProvider.GetRequiredService() ]; @@ -167,7 +173,8 @@ internal static class WaitAtEnd public ETaskResult Update() { QuestWork? questWork = gameFunctions.GetQuestEx(Quest.QuestId); - return questWork != null && Step.MatchesQuestVariables(questWork.Value, false) + return questWork != null && + QuestWorkUtils.MatchesQuestWork(Step.CompletionQuestVariablesFlags, questWork.Value, false) ? ETaskResult.TaskComplete : ETaskResult.StillRunning; } diff --git a/Questionable/Model/V1/QuestStepExtensions.cs b/Questionable/Controller/Utils/QuestWorkUtils.cs similarity index 64% rename from Questionable/Model/V1/QuestStepExtensions.cs rename to Questionable/Controller/Utils/QuestWorkUtils.cs index dedf2ebc2..03ddced0f 100644 --- a/Questionable/Model/V1/QuestStepExtensions.cs +++ b/Questionable/Controller/Utils/QuestWorkUtils.cs @@ -1,22 +1,29 @@ using System; +using System.Collections.Generic; +using System.Linq; using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; -namespace Questionable.Model.V1; +namespace Questionable.Controller.Utils; -internal static class QuestStepExtensions +internal static class QuestWorkUtils { + public static bool HasCompletionFlags(IList completionQuestVariablesFlags) + { + return completionQuestVariablesFlags.Count == 6 && completionQuestVariablesFlags.Any(x => x != null); + } + /// /// Positive values: Must be set to this value; will wait for the step to have these set. /// Negative values: Will skip if set to this value, won't wait for this to be set. /// - public static unsafe bool MatchesQuestVariables(this QuestStep step, QuestWork questWork, bool forSkip) + public static bool MatchesQuestWork(IList completionQuestVariablesFlags, QuestWork questWork, bool forSkip) { - if (step.CompletionQuestVariablesFlags.Count != 6) + if (!HasCompletionFlags(completionQuestVariablesFlags)) return false; for (int i = 0; i < 6; ++i) { - short? check = step.CompletionQuestVariablesFlags[i]; + short? check = completionQuestVariablesFlags[i]; if (check == null) continue; diff --git a/Questionable/Data/QuestData.cs b/Questionable/Data/QuestData.cs index 1d309750c..bbaab3d0e 100644 --- a/Questionable/Data/QuestData.cs +++ b/Questionable/Data/QuestData.cs @@ -5,6 +5,9 @@ using System.Linq; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Text; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.UI; +using LLib.GameUI; +using Questionable.Controller; using Questionable.Model; using Quest = Lumina.Excel.GeneratedSheets.Quest; @@ -14,13 +17,15 @@ internal sealed class QuestData { private readonly ITargetManager _targetManager; private readonly IChatGui _chatGui; + private readonly IGameGui _gameGui; private readonly ImmutableDictionary _quests; - public QuestData(IDataManager dataManager, ITargetManager targetManager, IChatGui chatGui) + public QuestData(IDataManager dataManager, ITargetManager targetManager, IChatGui chatGui, IGameGui gameGui) { _targetManager = targetManager; _chatGui = chatGui; + _gameGui = gameGui; _quests = dataManager.GetExcelSheet()! .Where(x => x.RowId > 0) @@ -43,7 +48,7 @@ internal sealed class QuestData public bool IsIssuerOfAnyQuest(uint targetId) => _quests.Values.Any(x => x.IssuerDataId == targetId); - public void ShowQuestsIssuedByTarget() + public unsafe void ShowQuestsIssuedByTarget() { var targetId = _targetManager.Target?.DataId; if (targetId == null) @@ -53,9 +58,20 @@ internal sealed class QuestData } List quests = GetAllByIssuerDataId(targetId.Value); - _chatGui.Print($"{quests.Count} quest(s) issued by target {_targetManager.Target?.Name}:"); + + if (_gameGui.TryGetAddonByName("SelectIconString", out var addonSelectIconString)) + { + var answers = GameUiController.GetChoices(addonSelectIconString); + quests = quests.Where(x => answers.Any(y => GameUiController.GameStringEquals(x.Name, y))).ToList(); + + _chatGui.Print($"{quests.Count} quest(s) currently offered by target {_targetManager.Target?.Name}:"); + } + else + { + _chatGui.Print($"{quests.Count} quest(s) issued by target {_targetManager.Target?.Name}:"); + } foreach (QuestInfo quest in quests) - _chatGui.Print($" {quest.QuestId}_{quest.SimplifiedName}"); + _chatGui.Print($" {quest.QuestId}_{quest.SimplifiedName}"); } } diff --git a/Questionable/Model/EMovementType.cs b/Questionable/Model/EMovementType.cs index c02815f88..bab7f4826 100644 --- a/Questionable/Model/EMovementType.cs +++ b/Questionable/Model/EMovementType.cs @@ -7,4 +7,5 @@ public enum EMovementType DebugWindow, Shortcut, Landing, + Combat, } diff --git a/Questionable/QuestionablePlugin.cs b/Questionable/QuestionablePlugin.cs index a2600c920..495137661 100644 --- a/Questionable/QuestionablePlugin.cs +++ b/Questionable/QuestionablePlugin.cs @@ -9,6 +9,7 @@ using Dalamud.Plugin.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Questionable.Controller; +using Questionable.Controller.CombatModules; using Questionable.Controller.NavigationOverrides; using Questionable.Controller.Steps; using Questionable.Controller.Steps.Shared; @@ -92,7 +93,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin serviceCollection.AddTaskWithFactory(); serviceCollection.AddTaskWithFactory(); serviceCollection.AddTaskWithFactory(); - serviceCollection.AddSingleton(); + serviceCollection.AddTaskWithFactory(); serviceCollection.AddTaskWithFactory(); serviceCollection.AddTaskWithFactory(); serviceCollection.AddTaskWithFactory(); @@ -120,6 +121,9 @@ public sealed class QuestionablePlugin : IDalamudPlugin serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Questionable/Windows/QuestWindow.cs b/Questionable/Windows/QuestWindow.cs index 34b6c7756..583cbf654 100644 --- a/Questionable/Windows/QuestWindow.cs +++ b/Questionable/Windows/QuestWindow.cs @@ -12,7 +12,6 @@ using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin; using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.UI.Agent; @@ -40,6 +39,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig private readonly IFramework _framework; private readonly ITargetManager _targetManager; private readonly GameUiController _gameUiController; + private readonly CombatController _combatController; private readonly Configuration _configuration; private readonly NavmeshIpc _navmeshIpc; private readonly QuestRegistry _questRegistry; @@ -57,6 +57,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig IFramework framework, ITargetManager targetManager, GameUiController gameUiController, + CombatController combatController, Configuration configuration, NavmeshIpc navmeshIpc, QuestRegistry questRegistry, @@ -75,6 +76,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig _framework = framework; _targetManager = targetManager; _gameUiController = gameUiController; + _combatController = combatController; _configuration = configuration; _navmeshIpc = navmeshIpc; _questRegistry = questRegistry; @@ -186,8 +188,17 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig ImGui.TextUnformatted("(Not accepted)"); } - ImGui.TextUnformatted(_questController.DebugState ?? "--"); ImGui.EndDisabled(); + + if (_combatController.IsRunning) + ImGui.TextColored(ImGuiColors.DalamudOrange, "In Combat"); + else + { + ImGui.BeginDisabled(); + ImGui.TextUnformatted(_questController.DebugState ?? "--"); + ImGui.EndDisabled(); + } + ImGui.TextUnformatted(_questController.Comment ?? "--"); //var nextStep = _questController.GetNextStep(); @@ -523,7 +534,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig { _movementController.Destination = null; _chatFunctions.ExecuteCommand( - $"/vnav {(_gameFunctions.IsFlyingUnlockedInCurrentZone() ? "flyflag" : "moveflag")}"); + $"/vnav {(_condition[ConditionFlag.Mounted] && _gameFunctions.IsFlyingUnlockedInCurrentZone() ? "flyflag" : "moveflag")}"); } ImGui.EndDisabled();