Update dungeon/trial json schema to allow notes (similar to quest battles)
authorLiza Carvelli <liza@carvel.li>
Mon, 3 Mar 2025 01:52:57 +0000 (02:52 +0100)
committerLiza Carvelli <liza@carvel.li>
Mon, 3 Mar 2025 01:53:18 +0000 (02:53 +0100)
65 files changed:
QuestPathGenerator/RoslynElements/DutyOptionsExtensions.cs [new file with mode: 0644]
QuestPathGenerator/RoslynElements/QuestStepExtensions.cs
QuestPathGenerator/RoslynShortcuts.cs
QuestPaths/2.x - A Realm Reborn/MSQ-1/Shared/245_It's Probably Pirates.json
QuestPaths/2.x - A Realm Reborn/MSQ-1/Shared/343_Lord of the Inferno.json
QuestPaths/2.x - A Realm Reborn/MSQ-1/Shared/660_Into a Copper Hell.json
QuestPaths/2.x - A Realm Reborn/MSQ-1/Shared/677_Fire in the Gloom.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/A3-South Shroud, Buscarron’s Druthers/514_Into the Beast's Maw.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/A9-Haukke Manor/801_Skeletons in Her Closet.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/B2-Eastern La Noscea, Brayflox, Cheese and Wine/832_The Things We Do for Cheese.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/B4-Titan/857_Lord of Crags.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/C1-Coerthas Central Highlands, The Enterprise/952_In Pursuit of the Past.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/C3-Garuda/519_Lady of the Vortex.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/C9-Ultimate Weapon/3873_Rock the Castrum.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/C9-Ultimate Weapon/4522_The Ultimate Weapon.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/E4-2.4/75_The Path of the Righteous.json
QuestPaths/2.x - A Realm Reborn/MSQ-2/E5-2.5/366_The Rising Chorus.json
QuestPaths/3.x - Heavensward/MSQ/A3.2-The Dravanian Forelands/1617_Mourn in Passing.json
QuestPaths/3.x - Heavensward/MSQ/A3.3-The Churning Mists/1634_Into the Aery.json
QuestPaths/3.x - Heavensward/MSQ/A4-Ishgard/1640_A Knight's Calling.json
QuestPaths/3.x - Heavensward/MSQ/A6-The Dravanian Hinterlands/1660_Forbidden Knowledge.json
QuestPaths/3.x - Heavensward/MSQ/A7-Azys Lla/1669_Heavensward.json
QuestPaths/3.x - Heavensward/MSQ/C-3.2/2232_The Word of the Mother.json
QuestPaths/3.x - Heavensward/MSQ/E-3.4/2342_Shadows of the First.json
QuestPaths/3.x - Heavensward/MSQ/E-3.4/2345_The Beast That Mourned at the Heart of the Mountain.json
QuestPaths/3.x - Heavensward/MSQ/F-3.5/2354_Griffin, Griffin on the Wall.json
QuestPaths/4.x - Stormblood/MSQ/A1.3-Rhalgr's Reach 2/2469_Not without Incident.json
QuestPaths/4.x - Stormblood/MSQ/A5-Yanxia 2/2524_The Die Is Cast.json
QuestPaths/4.x - Stormblood/MSQ/A6.2-Peaks 2/2544_The Price of Freedom.json
QuestPaths/4.x - Stormblood/MSQ/B-4.1/2964_The Mad King's Trove.json
QuestPaths/4.x - Stormblood/MSQ/E-4.4/3144_Feel the Burn.json
QuestPaths/4.x - Stormblood/MSQ/F-4.5/3183_The Face of War.json
QuestPaths/5.x - Shadowbringers/MSQ/A4-Crystarium 2/3300_The Lightwardens.json
QuestPaths/5.x - Shadowbringers/MSQ/B-Il Mheg/3312_The Key to the Castle.json
QuestPaths/5.x - Shadowbringers/MSQ/C-Rak'tika/3340_The Burden of Knowledge.json
QuestPaths/5.x - Shadowbringers/MSQ/E-Kholusia 2/3643_Extinguishing the Last Light.json
QuestPaths/5.x - Shadowbringers/MSQ/H-5.2/3769_Beneath the Surface.json
QuestPaths/6.x - Endwalker/MSQ/A-Thavnair1-Labyrinthos1/4377_In the Dark of the Tower.json
QuestPaths/6.x - Endwalker/MSQ/D-Thavnair2/4409_Skies Aflame.json
QuestPaths/6.x - Endwalker/MSQ/E-Elpis/4437_Caging the Messenger.json
QuestPaths/6.x - Endwalker/MSQ/F-Labyrinthos2/4449_Her Children One and All.json
QuestPaths/6.x - Endwalker/MSQ/H-6.1/4529_Alzadaals Legacy.json
QuestPaths/6.x - Endwalker/MSQ/I-6.2/4592_In Search of Azdaja.json
QuestPaths/6.x - Endwalker/MSQ/J-6.3/4674_King of the Mountain.json
QuestPaths/6.x - Endwalker/MSQ/K-6.4/4736_Going Haam.json
QuestPaths/6.x - Endwalker/MSQ/L-6.5/4748_Down in the Dark.json
QuestPaths/7.x - Dawntrail/MSQ/A-Kozama'uka1-Urqopacha1/4879_For All Turali.json
QuestPaths/7.x - Dawntrail/MSQ/B-Kozama'uka2-Urqopacha2/4891_The High Luminary.json
QuestPaths/7.x - Dawntrail/MSQ/C-Yak T'el/4909_Road to the Golden City.json
QuestPaths/7.x - Dawntrail/MSQ/D-Shaaloani-HeritageFound1/4926_All Aboard.json
QuestPaths/7.x - Dawntrail/MSQ/E-SolutionNine-HeritageFound2/4945_The Resilient Son.json
QuestPaths/7.x - Dawntrail/MSQ/F-Living Memory/4959_Dawntrail.json
QuestPaths/7.x - Dawntrail/MSQ/G-7.1/5246_In Search of the Past.json
QuestPaths/quest-v1.json
Questionable.Model/Questing/DutyOptions.cs [new file with mode: 0644]
Questionable.Model/Questing/QuestStep.cs
Questionable/Controller/QuestRegistry.cs
Questionable/Controller/Steps/Common/SendNotification.cs
Questionable/Controller/Steps/Interactions/Duty.cs
Questionable/Controller/Steps/Shared/WaitAtEnd.cs
Questionable/Data/TerritoryData.cs
Questionable/External/AutoDutyIpc.cs
Questionable/Windows/ConfigComponents/ConfigComponent.cs
Questionable/Windows/ConfigComponents/DutyConfigComponent.cs
Questionable/Windows/ConfigComponents/SinglePlayerDutyConfigComponent.cs

diff --git a/QuestPathGenerator/RoslynElements/DutyOptionsExtensions.cs b/QuestPathGenerator/RoslynElements/DutyOptionsExtensions.cs
new file mode 100644 (file)
index 0000000..c7092a4
--- /dev/null
@@ -0,0 +1,30 @@
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Questionable.Model.Questing;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+using static Questionable.QuestPathGenerator.RoslynShortcuts;
+
+namespace Questionable.QuestPathGenerator.RoslynElements;
+
+internal static class DutyOptionsExtensions
+{
+    public static ExpressionSyntax ToExpressionSyntax(this DutyOptions dutyOptions)
+    {
+        var emptyOptions = new DutyOptions();
+        return ObjectCreationExpression(
+                IdentifierName(nameof(DutyOptions)))
+            .WithInitializer(
+                InitializerExpression(
+                    SyntaxKind.ObjectInitializerExpression,
+                    SeparatedList<ExpressionSyntax>(
+                        SyntaxNodeList(
+                            Assignment(nameof(DutyOptions.Enabled),
+                                    dutyOptions.Enabled, emptyOptions.Enabled)
+                                .AsSyntaxNodeOrToken(),
+                            Assignment(nameof(DutyOptions.ContentFinderConditionId),
+                                    dutyOptions.ContentFinderConditionId, emptyOptions.ContentFinderConditionId)
+                                .AsSyntaxNodeOrToken(),
+                            AssignmentList(nameof(DutyOptions.Notes), dutyOptions.Notes)
+                                .AsSyntaxNodeOrToken()))));
+    }
+}
index 1ff4fbc..00a36db 100644 (file)
@@ -117,11 +117,8 @@ internal static class QuestStepExtensions
                             Assignment(nameof(QuestStep.JumpDestination), step.JumpDestination,
                                     emptyStep.JumpDestination)
                                 .AsSyntaxNodeOrToken(),
-                            Assignment(nameof(QuestStep.ContentFinderConditionId),
-                                    step.ContentFinderConditionId, emptyStep.ContentFinderConditionId)
-                                .AsSyntaxNodeOrToken(),
-                            Assignment(nameof(QuestStep.AutoDutyEnabled),
-                                    step.AutoDutyEnabled, emptyStep.AutoDutyEnabled)
+                            Assignment(nameof(QuestStep.DutyOptions), step.DutyOptions,
+                                    emptyStep.DutyOptions)
                                 .AsSyntaxNodeOrToken(),
                             Assignment(nameof(QuestStep.SinglePlayerDutyOptions), step.SinglePlayerDutyOptions,
                                 emptyStep.SinglePlayerDutyOptions)
index 70cae35..e9e07b8 100644 (file)
@@ -62,6 +62,7 @@ public static class RoslynShortcuts
                 ComplexCombatData complexCombatData => complexCombatData.ToExpressionSyntax(),
                 QuestWorkValue questWorkValue => questWorkValue.ToExpressionSyntax(),
                 List<QuestWorkValue> list => list.ToExpressionSyntax(), // TODO fix in AssignmentList
+                DutyOptions dutyOptions => dutyOptions.ToExpressionSyntax(),
                 SinglePlayerDutyOptions dutyOptions => dutyOptions.ToExpressionSyntax(),
                 SkipConditions skipConditions => skipConditions.ToExpressionSyntax(),
                 SkipStepConditions skipStepConditions => skipStepConditions.ToExpressionSyntax(),
index 0d3aada..2104a8f 100644 (file)
         {
           "TerritoryId": 138,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 4,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 4,
+            "Enabled": true
+          }
         }
       ]
     },
index be80477..1edd6de 100644 (file)
         {
           "TerritoryId": 146,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 56,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 56,
+            "Enabled": true
+          }
         }
       ]
     },
index 299a07d..ae27c13 100644 (file)
         {
           "TerritoryId": 140,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 3,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 3,
+            "Enabled": true
+          }
         }
       ]
     },
index 726eae2..636b99f 100644 (file)
         {
           "TerritoryId": 148,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 2,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 2,
+            "Enabled": true
+          }
         }
       ]
     },
index 45b28b3..c9cb93f 100644 (file)
         {
           "TerritoryId": 153,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 1,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 1,
+            "Enabled": true
+          }
         }
       ]
     },
index a17b715..e8b7817 100644 (file)
         {
           "TerritoryId": 148,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 6,
-          "AutoDutyEnabled": false
+          "DutyOptions": {
+            "ContentFinderConditionId": 6,
+            "Enabled": false,
+            "Notes": [
+              "(after boss 2) Will not pick up the bloody parchment, and instead run off to open a random unrelated chest that it can't reach"
+            ]
+          }
         }
       ]
     },
index 960ac4e..69c77dc 100644 (file)
         {
           "TerritoryId": 137,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 8,
-          "AutoDutyEnabled": false
+          "DutyOptions": {
+            "ContentFinderConditionId": 8,
+            "Enabled": false,
+            "Notes": [
+              "(boss 2) Will walk out of the boss arena to try and attack optional enemies on the upper level, thus resetting the boss and breaking the path"
+            ]
+          }
         }
       ]
     },
index 4cc4c57..b3516cd 100644 (file)
         {
           "TerritoryId": 139,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 57,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 57,
+            "Enabled": true
+          }
         }
       ]
     },
index 57ef1fa..a984477 100644 (file)
         {
           "TerritoryId": 155,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 11,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 11,
+            "Enabled": false,
+            "Notes": [
+              "(boss 1) AI will not face cleaves away from healer, typically killing them",
+              "(boss 3) AI will not face cleaves away from healer, which doesn't always kill them"
+            ]
+          }
         }
       ]
     },
index d9315ec..bb48332 100644 (file)
         {
           "TerritoryId": 331,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 58,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 58,
+            "Enabled": true
+          }
         }
       ]
     },
index dba66d1..d4914c2 100644 (file)
         {
           "TerritoryId": 147,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 15,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 15,
+            "Enabled": true
+          }
         }
       ]
     },
index 8122668..aad57d7 100644 (file)
         {
           "TerritoryId": 147,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 16,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 16,
+            "Enabled": true
+          }
         }
       ]
     },
         {
           "TerritoryId": 1053,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 830,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 830,
+            "Enabled": true
+          }
         }
       ]
     },
index b6581b9..e8534c3 100644 (file)
         {
           "TerritoryId": 155,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 27,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 27,
+            "Enabled": true
+          }
         }
       ]
     },
index 24d72b2..235ab5e 100644 (file)
         {
           "TerritoryId": 156,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 32,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 32,
+            "Enabled": true
+          }
         }
       ]
     },
index a321c74..df6d17d 100644 (file)
         {
           "TerritoryId": 398,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 37,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 37,
+            "Enabled": true
+          }
         }
       ]
     },
index 02098ca..97ead07 100644 (file)
         {
           "TerritoryId": 418,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 39,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 39,
+            "Enabled": true
+          }
         }
       ]
     },
index 9165e2a..da55ca4 100644 (file)
         {
           "TerritoryId": 419,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 34,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 34,
+            "Enabled": true
+          }
         }
       ]
     },
index dab4f3d..6c94266 100644 (file)
         {
           "TerritoryId": 399,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 31,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 31,
+            "Enabled": true
+          }
         }
       ]
     },
index 7bef51c..215f184 100644 (file)
         {
           "TerritoryId": 402,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 38,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 38,
+            "Enabled": true
+          }
         }
       ]
     },
         {
           "TerritoryId": 402,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 90
+          "DutyOptions": {
+            "ContentFinderConditionId": 90,
+            "Enabled": false
+          }
         }
       ]
     },
index fe2c336..f601632 100644 (file)
         {
           "TerritoryId": 463,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 141,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 141,
+            "Enabled": true,
+            "TestedAutoDutyVersion": "0.0.0.191"
+          }
         }
       ]
     },
index 3cee72b..7821fc9 100644 (file)
         {
           "TerritoryId": 155,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 182,
-          "AutoDutyEnabled": false
+          "DutyOptions": {
+            "ContentFinderConditionId": 182,
+            "Enabled": false,
+            "Notes": [
+              "(after boss 1) the drawbridges being up will lead you to die from the spikes",
+              "(after boss 1) the lift isn't working properly"
+            ]
+          }
         }
       ]
     },
index b04f3f5..00e0988 100644 (file)
         {
           "TerritoryId": 180,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 60
+          "DutyOptions": {
+            "ContentFinderConditionId": 60,
+            "Enabled": false
+          }
         }
       ]
     },
index 42aef1f..9d1c4d8 100644 (file)
         {
           "TerritoryId": 152,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 219,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 219,
+            "Enabled": true
+          }
         }
       ]
     },
index 6477857..c5d3ef5 100644 (file)
         {
           "TerritoryId": 680,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 238,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 238,
+            "Enabled": true
+          }
         }
       ]
     },
index 702737b..eacefe6 100644 (file)
         {
           "TerritoryId": 614,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 241,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 241,
+            "Enabled": true
+          }
         }
       ]
     },
index a0b37e3..798f15a 100644 (file)
         {
           "TerritoryId": 620,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 242,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 242,
+            "Enabled": true
+          }
         }
       ]
     },
index 0856bbe..02436ae 100644 (file)
         {\r
           "TerritoryId": 621,\r
           "InteractionType": "Duty",\r
-          "ContentFinderConditionId": 279,\r
-          "AutoDutyEnabled": true\r
+          "DutyOptions": {\r
+            "ContentFinderConditionId": 279,\r
+            "Enabled": true\r
+          }\r
         }\r
       ]\r
     },\r
index fba0763..1757d80 100644 (file)
         {
           "TerritoryId": 614,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 585,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 585,
+            "Enabled": true
+          }
         }
       ]
     },
index fb9960d..0dcf298 100644 (file)
         {
           "TerritoryId": 829,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 611,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 611,
+            "Enabled": true
+          }
         }
       ]
     },
index 7f9a5a9..de440ed 100644 (file)
         {
           "TerritoryId": 813,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 676,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 676,
+            "Enabled": true
+          }
         }
       ]
     },
index d1dfe7d..ea66e5a 100644 (file)
         {
           "TerritoryId": 816,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 649,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 649,
+            "Enabled": true
+          }
         }
       ]
     },
index 39284d2..8a07ab8 100644 (file)
         {
           "TerritoryId": 817,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 651,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 651,
+            "Enabled": true
+          }
         }
       ]
     },
index 80cbd17..ef9aa4d 100644 (file)
         {
           "TerritoryId": 814,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 659,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 659,
+            "Enabled": true
+          }
         }
       ]
     },
         {
           "TerritoryId": 880,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 666
+          "DutyOptions": {
+            "ContentFinderConditionId": 666,
+            "Enabled": false
+          }
         }
       ]
     },
index 41dd831..61a471a 100644 (file)
         {
           "TerritoryId": 814,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 714,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 714,
+            "Enabled": true
+          }
         }
       ]
     },
index c433695..d07dba7 100644 (file)
         {
           "TerritoryId": 957,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 783,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 783,
+            "Enabled": true
+          }
         }
       ]
     },
index 9c0d3a4..21ad7d9 100644 (file)
         {
           "TerritoryId": 957,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 789,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 789,
+            "Enabled": true
+          }
         }
       ]
     },
index 9dd680e..89c6fc5 100644 (file)
         {
           "TerritoryId": 961,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 787,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 787,
+            "Enabled": true
+          }
         }
       ]
     },
index 7494374..fc0aac0 100644 (file)
         {
           "TerritoryId": 956,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 786,
-          "AutoDutyEnabled": false
+          "DutyOptions": {
+            "ContentFinderConditionId": 786,
+            "Enabled": false,
+            "Notes": [
+              "No VBM module"
+            ]
+          }
         }
       ]
     },
         {
           "TerritoryId": 1030,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 790,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 790,
+            "Enabled": true
+          }
         }
       ]
     },
index dc9324d..8638a86 100644 (file)
         {
           "TerritoryId": 957,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 844,
-          "AutoDutyEnabled": false
+          "DutyOptions": {
+            "ContentFinderConditionId": 844,
+            "Enabled": false,
+            "Notes": [
+              "No VBM module"
+            ]
+          }
         }
       ]
     },
index c91af59..561b190 100644 (file)
         {
           "TerritoryId": 1056,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 869,
-          "AutoDutyEnabled": false
+          "DutyOptions": {
+            "ContentFinderConditionId": 869,
+            "Enabled": false,
+            "Notes": [
+              "No VBM module"
+            ]
+          }
         }
       ]
     },
index df1bd69..14c3171 100644 (file)
         {
           "TerritoryId": 958,
           "InteractionType": "Duty",
-          "Comment": "Lapis Manalis",
-          "ContentFinderConditionId": 896,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 896,
+            "Enabled": true
+          }
         }
       ]
     },
index 8af3608..30fa509 100644 (file)
         {
           "TerritoryId": 962,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 822,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 822,
+            "Enabled": false,
+            "Notes": [
+              "Navigation issues for area transitions"
+            ]
+          }
         }
       ]
     },
index 96953ea..8b1016d 100644 (file)
         {
           "TerritoryId": 1162,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 823,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 823,
+            "Enabled": true
+          }
         }
       ]
     },
index 28a6850..986d752 100644 (file)
         {
           "TerritoryId": 1185,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 826,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 826,
+            "Enabled": true
+          }
         }
       ]
     },
index 2ef3a8e..39268a0 100644 (file)
         {
           "TerritoryId": 1187,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 824,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 824,
+            "Enabled": true
+          }
         }
       ]
     },
index 528fde3..9a1488e 100644 (file)
         {
           "TerritoryId": 1189,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 829,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 829,
+            "Enabled": true
+          }
         }
       ]
     },
index 682576c..5c83c61 100644 (file)
         {
           "TerritoryId": 1219,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 831,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 831,
+            "Enabled": true
+          }
         }
       ]
     },
index 738c878..e57ca85 100644 (file)
         {
           "TerritoryId": 1191,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 825,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 825,
+            "Enabled": true
+          }
         }
       ]
     },
         {
           "TerritoryId": 1220,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 995
+          "DutyOptions": {
+            "ContentFinderConditionId": 995,
+            "Enabled": false,
+            "Notes": [
+              "No VBM module"
+            ]
+          }
         }
       ]
     },
index cafc897..0561e14 100644 (file)
         {
           "TerritoryId": 1192,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 827,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 827,
+            "Enabled": true
+          }
         }
       ]
     },
         {
           "TerritoryId": 1221,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 984
+          "DutyOptions": {
+            "ContentFinderConditionId": 984,
+            "Enabled": false
+          }
         }
       ]
     },
index 40f6d44..00f23c4 100644 (file)
         {
           "TerritoryId": 1191,
           "InteractionType": "Duty",
-          "ContentFinderConditionId": 1008,
-          "AutoDutyEnabled": true
+          "DutyOptions": {
+            "ContentFinderConditionId": 1008,
+            "Enabled": true,
+            "Notes": [
+              "(boss 2) Requires vbm's auto-turn gaze option",
+              "(boss 3) Dashes (such as on SMN) will dash into the hole"
+            ]
+          }
         }
       ]
     },
index e1670a9..2411657 100644 (file)
           },
           "then": {
             "properties": {
-              "ContentFinderConditionId": {
-                "type": "integer",
-                "exclusiveMinimum": 0,
-                "exclusiveMaximum": 3000
-              },
-              "AutoDutyEnabled": {
-                "type": "boolean"
+              "DutyOptions": {
+                "type": "object",
+                "properties": {
+                  "ContentFinderConditionId": {
+                    "type": "integer",
+                    "exclusiveMinimum": 0,
+                    "exclusiveMaximum": 3000
+                  },
+                  "Enabled": {
+                    "type": "boolean"
+                  },
+                  "Notes": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    }
+                  },
+                  "TestedAutoDutyVersion": {
+                    "type": "string",
+                    "pattern": "^0\\.\\d+\\.\\d+\\.\\d+$"
+                  },
+                  "$": {
+                    "type": "string"
+                  }
+                },
+                "required": [
+                  "ContentFinderConditionId",
+                  "Enabled"
+                ]
               },
               "DataId": {
                 "type": "null"
               }
             },
             "required": [
-              "ContentFinderConditionId"
+              "DutyOptions"
             ]
           }
         },
diff --git a/Questionable.Model/Questing/DutyOptions.cs b/Questionable.Model/Questing/DutyOptions.cs
new file mode 100644 (file)
index 0000000..12ea14e
--- /dev/null
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace Questionable.Model.Questing;
+
+public class DutyOptions
+{
+    public bool Enabled { get; set; }
+    public uint ContentFinderConditionId { get; set; }
+    public List<string> Notes { get; set; } = [];
+}
index c14d2e7..26148ce 100644 (file)
@@ -73,8 +73,7 @@ public sealed class QuestStep
     public float? CombatDelaySecondsAtStart { get; set; }
 
     public JumpDestination? JumpDestination { get; set; }
-    public uint? ContentFinderConditionId { get; set; }
-    public bool AutoDutyEnabled { get; set; }
+    public DutyOptions? DutyOptions { get; set; }
     public SinglePlayerDutyOptions? SinglePlayerDutyOptions { get; set; }
     public byte SinglePlayerDutyIndex => SinglePlayerDutyOptions?.Index ?? 0;
     public SkipConditions? SkipConditions { get; set; }
index 942a1fc..8242273 100644 (file)
@@ -165,8 +165,8 @@ internal sealed class QuestRegistry
             foreach (var dutyStep in quest.AllSteps().Where(x =>
                          x.Step.InteractionType is EInteractionType.Duty or EInteractionType.SinglePlayerDuty))
             {
-                if (dutyStep.Step is { InteractionType: EInteractionType.Duty, ContentFinderConditionId: not null })
-                    _contentFinderConditionIds[dutyStep.Step.ContentFinderConditionId!.Value] =
+                if (dutyStep.Step is { InteractionType: EInteractionType.Duty, DutyOptions: { } dutyOptions })
+                    _contentFinderConditionIds[dutyOptions.ContentFinderConditionId] =
                         (quest.Id, dutyStep.Step);
                 else if (dutyStep.Step.InteractionType == EInteractionType.SinglePlayerDuty &&
                          _territoryData.TryGetContentFinderConditionForSoloInstance(quest.Id,
@@ -262,15 +262,15 @@ internal sealed class QuestRegistry
             .ToList();
     }
 
-    public bool TryGetDutyByContentFinderConditionId(uint cfcId, out bool autoDutyEnabledByDefault)
+    public bool TryGetDutyByContentFinderConditionId(uint cfcId, [NotNullWhen(true)] out DutyOptions? dutyOptions)
     {
         if (_contentFinderConditionIds.TryGetValue(cfcId, out var value))
         {
-            autoDutyEnabledByDefault = value.Step.AutoDutyEnabled;
-            return true;
+            dutyOptions = value.Step.DutyOptions;
+            return dutyOptions != null;
         }
 
-        autoDutyEnabledByDefault = false;
+        dutyOptions = null;
         return false;
     }
 }
index 9708993..dda0384 100644 (file)
@@ -23,9 +23,9 @@ internal static class SendNotification
             {
                 EInteractionType.Snipe when !automatonIpc.IsAutoSnipeEnabled =>
                     new Task(step.InteractionType, step.Comment),
-                EInteractionType.Duty when !autoDutyIpc.IsConfiguredToRunContent(step.ContentFinderConditionId, step.AutoDutyEnabled) =>
-                    new Task(step.InteractionType, step.ContentFinderConditionId.HasValue
-                        ? territoryData.GetContentFinderCondition(step.ContentFinderConditionId.Value)?.Name
+                EInteractionType.Duty when !autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions) =>
+                    new Task(step.InteractionType, step.DutyOptions?.ContentFinderConditionId is {} contentFinderConditionId
+                        ? territoryData.GetContentFinderCondition(contentFinderConditionId)?.Name
                         : step.Comment),
                 EInteractionType.SinglePlayerDuty when !bossModIpc.IsConfiguredToRunSoloInstance(quest.Id, step.SinglePlayerDutyOptions) =>
                     new Task(step.InteractionType, quest.Info.Name),
index f40c467..250c015 100644 (file)
@@ -23,17 +23,17 @@ internal static class Duty
             if (step.InteractionType != EInteractionType.Duty)
                 yield break;
 
-            ArgumentNullException.ThrowIfNull(step.ContentFinderConditionId);
+            ArgumentNullException.ThrowIfNull(step.DutyOptions);
 
-            if (autoDutyIpc.IsConfiguredToRunContent(step.ContentFinderConditionId, step.AutoDutyEnabled))
+            if (autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions))
             {
-                yield return new StartAutoDutyTask(step.ContentFinderConditionId.Value);
-                yield return new WaitAutoDutyTask(step.ContentFinderConditionId.Value);
+                yield return new StartAutoDutyTask(step.DutyOptions.ContentFinderConditionId);
+                yield return new WaitAutoDutyTask(step.DutyOptions.ContentFinderConditionId);
                 yield return new WaitAtEnd.WaitNextStepOrSequence();
             }
             else
             {
-                yield return new OpenDutyFinderTask(step.ContentFinderConditionId.Value);
+                yield return new OpenDutyFinderTask(step.DutyOptions.ContentFinderConditionId);
             }
         }
     }
index 86e5d47..82decad 100644 (file)
@@ -53,7 +53,7 @@ internal static class WaitAtEnd
                 case EInteractionType.Snipe:
                     return [new WaitNextStepOrSequence()];
 
-                case EInteractionType.Duty when !autoDutyIpc.IsConfiguredToRunContent(step.ContentFinderConditionId, step.AutoDutyEnabled):
+                case EInteractionType.Duty when !autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions):
                 case EInteractionType.SinglePlayerDuty when !bossModIpc.IsConfiguredToRunSoloInstance(quest.Id, step.SinglePlayerDutyOptions):
                     return [new EndAutomation()];
 
index f35f9ce..342f631 100644 (file)
@@ -19,7 +19,7 @@ internal sealed class TerritoryData
     private readonly ImmutableDictionary<ushort, uint> _dutyTerritories;
     private readonly ImmutableDictionary<uint, string> _instanceNames;
     private readonly ImmutableDictionary<uint, ContentFinderConditionData> _contentFinderConditions;
-    private readonly ImmutableDictionary<(ElementId QuestId, byte Index), uint> _questsToCfc;
+    private readonly ImmutableDictionary<(ElementId QuestId, byte Index), uint> _questBattlesToContentFinderCondition;
 
     public TerritoryData(IDataManager dataManager)
     {
@@ -52,7 +52,7 @@ internal sealed class TerritoryData
             .Select(x => new ContentFinderConditionData(x, dataManager.Language))
             .ToImmutableDictionary(x => x.ContentFinderConditionId, x => x);
 
-        _questsToCfc = dataManager.GetExcelSheet<Quest>()
+        _questBattlesToContentFinderCondition = dataManager.GetExcelSheet<Quest>()
             .Where(x => x is { RowId: > 0, IssuerLocation.RowId: > 0 })
             .SelectMany(GetQuestBattles)
             .Select(x => (x.QuestId, x.Index,
@@ -90,7 +90,7 @@ internal sealed class TerritoryData
     public bool TryGetContentFinderConditionForSoloInstance(ElementId questId, byte index,
         [NotNullWhen(true)] out ContentFinderConditionData? contentFinderConditionData)
     {
-        if (_questsToCfc.TryGetValue((questId, index), out uint cfcId))
+        if (_questBattlesToContentFinderCondition.TryGetValue((questId, index), out uint cfcId))
             return _contentFinderConditions.TryGetValue(cfcId, out contentFinderConditionData);
         else
         {
@@ -101,7 +101,7 @@ internal sealed class TerritoryData
 
     public IEnumerable<(ElementId QuestId, byte Index, ContentFinderConditionData Data)> GetAllQuestsWithQuestBattles()
     {
-        return _questsToCfc.Select(x => (x.Key.QuestId, x.Key.Index, _contentFinderConditions[x.Value]));
+        return _questBattlesToContentFinderCondition.Select(x => (x.Key.QuestId, x.Key.Index, _contentFinderConditions[x.Value]));
     }
 
     private static string FixName(string name, ClientLanguage language)
index 67ea9fb..29675cd 100644 (file)
@@ -4,6 +4,7 @@ using Dalamud.Plugin.Ipc.Exceptions;
 using Microsoft.Extensions.Logging;
 using Questionable.Controller.Steps;
 using Questionable.Data;
+using Questionable.Model.Questing;
 
 namespace Questionable.External;
 
@@ -31,22 +32,22 @@ internal sealed class AutoDutyIpc
         _stop = pluginInterface.GetIpcSubscriber<object>("AutoDuty.Stop");
     }
 
-    public bool IsConfiguredToRunContent(uint? cfcId, bool enabledByDefault)
+    public bool IsConfiguredToRunContent(DutyOptions? dutyOptions)
     {
-        if (cfcId == null)
+        if (dutyOptions == null || dutyOptions.ContentFinderConditionId == 0)
             return false;
 
         if (!_configuration.Duties.RunInstancedContentWithAutoDuty)
             return false;
 
-        if (_configuration.Duties.BlacklistedDutyCfcIds.Contains(cfcId.Value))
+        if (_configuration.Duties.BlacklistedDutyCfcIds.Contains(dutyOptions.ContentFinderConditionId))
             return false;
 
-        if (_configuration.Duties.WhitelistedDutyCfcIds.Contains(cfcId.Value) &&
-            _territoryData.TryGetContentFinderCondition(cfcId.Value, out _))
+        if (_configuration.Duties.WhitelistedDutyCfcIds.Contains(dutyOptions.ContentFinderConditionId) &&
+            _territoryData.TryGetContentFinderCondition(dutyOptions.ContentFinderConditionId, out _))
             return true;
 
-        return enabledByDefault && HasPath(cfcId.Value);
+        return dutyOptions.Enabled && HasPath(dutyOptions.ContentFinderConditionId);
     }
 
     public bool HasPath(uint cfcId)
index e82cf07..d851563 100644 (file)
@@ -1,5 +1,9 @@
+using System.Collections.Generic;
 using System.Text;
 using Dalamud.Game.Text;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Utility.Raii;
 using Dalamud.Plugin;
 using ImGuiNET;
 
@@ -61,4 +65,29 @@ internal abstract class ConfigComponent
             ++byteCount;
         return Encoding.UTF8.GetString(ptr, byteCount);
     }
+
+    protected static void DrawNotes(bool enabledByDefault, IReadOnlyList<string> notes)
+    {
+        using var color = new ImRaii.Color();
+        color.Push(ImGuiCol.TextDisabled, !enabledByDefault ? ImGuiColors.DalamudYellow : ImGuiColors.ParsedBlue);
+
+        ImGui.SameLine();
+        using (ImRaii.PushFont(UiBuilder.IconFont))
+        {
+            if (!enabledByDefault)
+                ImGui.TextDisabled(FontAwesomeIcon.ExclamationTriangle.ToIconString());
+            else
+                ImGui.TextDisabled(FontAwesomeIcon.InfoCircle.ToIconString());
+        }
+
+        if (!ImGui.IsItemHovered())
+            return;
+
+        using var _ = ImRaii.Tooltip();
+
+        ImGui.TextColored(ImGuiColors.DalamudYellow,
+            "While testing, the following issues have been found:");
+        foreach (string note in notes)
+            ImGui.BulletText(note);
+    }
 }
index a04319e..35f79cf 100644 (file)
@@ -18,6 +18,7 @@ using Questionable.Controller;
 using Questionable.Data;
 using Questionable.External;
 using Questionable.Model;
+using Questionable.Model.Questing;
 
 namespace Questionable.Windows.ConfigComponents;
 
@@ -125,12 +126,11 @@ internal sealed class DutyConfigComponent : ConfigComponent
                     {
                         foreach (var (cfcId, territoryId, name) in cfcNames)
                         {
-                            if (_questRegistry.TryGetDutyByContentFinderConditionId(cfcId,
-                                    out bool autoDutyEnabledByDefault))
+                            if (_questRegistry.TryGetDutyByContentFinderConditionId(cfcId, out DutyOptions? dutyOptions))
                             {
                                 ImGui.TableNextRow();
 
-                                string[] labels = autoDutyEnabledByDefault
+                                string[] labels = dutyOptions.Enabled
                                     ? SupportedCfcOptions
                                     : UnsupportedCfcOptions;
                                 int value = 0;
@@ -159,6 +159,8 @@ internal sealed class DutyConfigComponent : ConfigComponent
                                     if (runInstancedContentWithAutoDuty && !_autoDutyIpc.HasPath(cfcId))
                                         ImGuiComponents.HelpMarker("This duty is not supported by AutoDuty",
                                             FontAwesomeIcon.Times, ImGuiColors.DalamudRed);
+                                    else if (dutyOptions.Notes.Count > 0)
+                                        DrawNotes(dutyOptions.Enabled, dutyOptions.Notes);
                                 }
 
                                 if (ImGui.TableNextColumn())
index c51de1e..637ca7a 100644 (file)
@@ -460,32 +460,7 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
                             FontAwesomeIcon.Times, ImGuiColors.DalamudRed);
                     }
                     else if (dutyInfo.Notes.Count > 0)
-                    {
-                        using var color = new ImRaii.Color();
-                        if (!dutyInfo.EnabledByDefault)
-                            color.Push(ImGuiCol.TextDisabled, ImGuiColors.DalamudYellow);
-                        else
-                            color.Push(ImGuiCol.TextDisabled, ImGuiColors.ParsedBlue);
-
-                        ImGui.SameLine();
-                        using (ImRaii.PushFont(UiBuilder.IconFont))
-                        {
-                            if (!dutyInfo.EnabledByDefault)
-                                ImGui.TextDisabled(FontAwesomeIcon.ExclamationTriangle.ToIconString());
-                            else
-                                ImGui.TextDisabled(FontAwesomeIcon.InfoCircle.ToIconString());
-                        }
-
-                        if (ImGui.IsItemHovered())
-                        {
-                            using var _ = ImRaii.Tooltip();
-
-                            ImGui.TextColored(ImGuiColors.DalamudYellow,
-                                "While testing, the following issues have been found:");
-                            foreach (string note in dutyInfo.Notes)
-                                ImGui.BulletText(note);
-                        }
-                    }
+                        DrawNotes(dutyInfo.EnabledByDefault, dutyInfo.Notes);
                 }
 
                 if (ImGui.TableNextColumn())