Compare commits

...

14 Commits

74 changed files with 682 additions and 107 deletions

View File

@ -62,6 +62,9 @@ internal static class QuestStepExtensions
Assignment(nameof(QuestStep.IgnoreDistanceToObject), Assignment(nameof(QuestStep.IgnoreDistanceToObject),
step.IgnoreDistanceToObject, emptyStep.IgnoreDistanceToObject) step.IgnoreDistanceToObject, emptyStep.IgnoreDistanceToObject)
.AsSyntaxNodeOrToken(), .AsSyntaxNodeOrToken(),
Assignment(nameof(QuestStep.RestartNavigationIfCancelled),
step.RestartNavigationIfCancelled, emptyStep.RestartNavigationIfCancelled)
.AsSyntaxNodeOrToken(),
Assignment(nameof(QuestStep.Comment), step.Comment, emptyStep.Comment) Assignment(nameof(QuestStep.Comment), step.Comment, emptyStep.Comment)
.AsSyntaxNodeOrToken(), .AsSyntaxNodeOrToken(),
Assignment(nameof(QuestStep.Aetheryte), step.Aetheryte, emptyStep.Aetheryte) Assignment(nameof(QuestStep.Aetheryte), step.Aetheryte, emptyStep.Aetheryte)

View File

@ -36,7 +36,7 @@ public static class Utils
if (questSchemaFile != null) if (questSchemaFile != null)
{ {
SchemaRegistry.Global.Register( SchemaRegistry.Global.Register(
new Uri("https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json"), new Uri("https://git.carvel.li/plogon_enjoyer/Questionable/raw/branch/temp/QuestPaths/quest-v1.json"),
JsonSchema.FromText(questSchemaFile.GetText()!.ToString())); JsonSchema.FromText(questSchemaFile.GetText()!.ToString()));
} }

View File

@ -53,6 +53,7 @@
"Y": -0.63573146, "Y": -0.63573146,
"Z": -166.33862 "Z": -166.33862
}, },
"StopDistance": 5,
"TerritoryId": 141, "TerritoryId": 141,
"InteractionType": "Interact" "InteractionType": "Interact"
} }

View File

@ -35,6 +35,7 @@
}, },
"TerritoryId": 155, "TerritoryId": 155,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest",
"Mount": true,
"DialogueChoices": [ "DialogueChoices": [
{ {
"Type": "YesNo", "Type": "YesNo",

View File

@ -337,16 +337,6 @@
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"Fly": true "Fly": true
}, },
{
"Position": {
"X": 233.0817,
"Y": 8,
"Z": -21.83023
},
"TerritoryId": 146,
"InteractionType": "WalkTo",
"Fly": true
},
{ {
"DataId": 2000077, "DataId": 2000077,
"Position": { "Position": {
@ -360,18 +350,16 @@
"KillEnemyDataIds": [ "KillEnemyDataIds": [
9489 9489
], ],
"$": "0 0 0 0 0 0 -> 16 17 0 0 0 128" "$": "0 0 0 0 0 0 -> 16 17 0 0 0 128",
}, "CompletionQuestVariablesFlags": [
{ null,
"Position": { null,
"X": 614.4023, null,
"Y": 301.81046, null,
"Z": -101.94888 null,
}, 128
"TerritoryId": 155, ],
"InteractionType": "WalkTo", "Fly": true
"Fly": true,
"AetheryteShortcut": "Coerthas Central Highlands - Camp Dragonhead"
}, },
{ {
"DataId": 2000078, "DataId": 2000078,
@ -386,7 +374,17 @@
"KillEnemyDataIds": [ "KillEnemyDataIds": [
9490 9490
], ],
"$": "16 17 0 0 0 128 -> 0 17 0 0 0 0" "Fly": true,
"AetheryteShortcut": "Coerthas Central Highlands - Camp Dragonhead",
"$": "16 17 0 0 0 128 -> 0 17 0 0 0 0",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
]
} }
] ]
}, },

View File

@ -191,6 +191,15 @@
"AethernetShard": "[Gold Saucer] Cactpot Board", "AethernetShard": "[Gold Saucer] Cactpot Board",
"DelaySecondsAtStart": 3 "DelaySecondsAtStart": 3
}, },
{
"Position": {
"X": 111.36922,
"Y": 13.000123,
"Z": -24.209782
},
"TerritoryId": 144,
"InteractionType": "WalkTo"
},
{ {
"DataId": 1011079, "DataId": 1011079,
"Position": { "Position": {
@ -207,6 +216,15 @@
{ {
"Sequence": 6, "Sequence": 6,
"Steps": [ "Steps": [
{
"Position": {
"X": 111.36922,
"Y": 13.000123,
"Z": -24.209782
},
"TerritoryId": 144,
"InteractionType": "WalkTo"
},
{ {
"TerritoryId": 144, "TerritoryId": 144,
"InteractionType": "None", "InteractionType": "None",

View File

@ -196,6 +196,17 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"Position": {
"X": -465.66104,
"Y": 43.041187,
"Z": 380.90747
},
"TerritoryId": 400,
"InteractionType": "WalkTo",
"Mount": true,
"DisableNavmesh": true
},
{ {
"DataId": 1013420, "DataId": 1013420,
"Position": { "Position": {
@ -204,8 +215,7 @@
"Z": 347.0968 "Z": 347.0968
}, },
"TerritoryId": 400, "TerritoryId": 400,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest"
"DisableNavmesh": true
} }
] ]
} }

View File

@ -111,6 +111,7 @@
}, },
"TerritoryId": 398, "TerritoryId": 398,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"Mount": true,
"SkipConditions": { "SkipConditions": {
"StepIf": { "StepIf": {
"CompletionQuestVariablesFlags": [ "CompletionQuestVariablesFlags": [

View File

@ -20,6 +20,15 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": 67.682434,
"Y": -49.685223,
"Z": -148.21802
},
"TerritoryId": 398,
"InteractionType": "WalkTo"
},
{ {
"DataId": 4001, "DataId": 4001,
"Position": { "Position": {

View File

@ -118,6 +118,7 @@
}, },
"TerritoryId": 398, "TerritoryId": 398,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"Mount": true,
"SkipConditions": { "SkipConditions": {
"StepIf": { "StepIf": {
"CompletionQuestVariablesFlags": [ "CompletionQuestVariablesFlags": [

View File

@ -152,7 +152,8 @@
"Z": 22.9953 "Z": 22.9953
}, },
"TerritoryId": 398, "TerritoryId": 398,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest",
"AetheryteShortcut": "The Dravanian Forelands - Anyx Trine"
} }
] ]
} }

View File

@ -98,6 +98,7 @@
"Y": 205.6815, "Y": 205.6815,
"Z": 31.631958 "Z": 31.631958
}, },
"StopDistance": 5,
"TerritoryId": 478, "TerritoryId": 478,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest",
"AetheryteShortcut": "Idyllshire" "AetheryteShortcut": "Idyllshire"

View File

@ -30,6 +30,17 @@
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"TargetTerritoryId": 399 "TargetTerritoryId": 399
}, },
{
"DataId": 2006210,
"Position": {
"X": -487.48004,
"Y": 144.64026,
"Z": -285.359
},
"TerritoryId": 399,
"InteractionType": "AttuneAetherCurrent",
"AetherCurrentId": 2818088
},
{ {
"DataId": 1013651, "DataId": 1013651,
"Position": { "Position": {

View File

@ -20,6 +20,15 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": -44.83911,
"Y": -1.6023016,
"Z": -655.73804
},
"TerritoryId": 401,
"InteractionType": "WalkTo"
},
{ {
"DataId": 2005824, "DataId": 2005824,
"Position": { "Position": {

View File

@ -135,6 +135,20 @@
{ {
"Sequence": 3, "Sequence": 3,
"Steps": [ "Steps": [
{
"Position": {
"X": 423.53406,
"Y": -26.253891,
"Z": -550.60297
},
"TerritoryId": 398,
"InteractionType": "WalkTo",
"SkipConditions": {
"StepIf": {
"Flying": "Unlocked"
}
}
},
{ {
"DataId": 2005573, "DataId": 2005573,
"Position": { "Position": {
@ -177,7 +191,8 @@
"StepIf": { "StepIf": {
"Flying": "Unlocked" "Flying": "Unlocked"
} }
} },
"DisableNavmesh": true
} }
] ]
}, },

View File

@ -12,6 +12,7 @@
"Y": 0.014982708, "Y": 0.014982708,
"Z": -0.07635498 "Z": -0.07635498
}, },
"StopDistance": 7,
"TerritoryId": 395, "TerritoryId": 395,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }

View File

@ -114,8 +114,7 @@
"Z": 761.01306 "Z": 761.01306
}, },
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest"
"AetheryteShortcut": "The Sea of Clouds - Camp Cloudtop"
} }
] ]
} }

View File

@ -83,6 +83,15 @@
128 128
] ]
}, },
{
"Position": {
"X": -368.08698,
"Y": -185.05266,
"Z": 792.836
},
"TerritoryId": 401,
"InteractionType": "WalkTo"
},
{ {
"DataId": 1012360, "DataId": 1012360,
"Position": { "Position": {

View File

@ -20,6 +20,23 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1011952,
"Position": {
"X": -277.63788,
"Y": -184.59735,
"Z": 741.60376
},
"TerritoryId": 401,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "The Sea of Clouds - Camp Cloudtop",
"PickUpQuestId": 1748,
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
},
{ {
"DataId": 1001029, "DataId": 1001029,
"Position": { "Position": {

View File

@ -47,6 +47,7 @@
"Y": 16.009666, "Y": 16.009666,
"Z": -9.567444 "Z": -9.567444
}, },
"StopDistance": 7,
"TerritoryId": 419, "TerritoryId": 419,
"InteractionType": "Interact", "InteractionType": "Interact",
"TargetTerritoryId": 433 "TargetTerritoryId": 433
@ -63,6 +64,7 @@
"Y": 0.022254243, "Y": 0.022254243,
"Z": -4.409851 "Z": -4.409851
}, },
"StopDistance": 5,
"TerritoryId": 433, "TerritoryId": 433,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest",
"DialogueChoices": [ "DialogueChoices": [

View File

@ -12,6 +12,7 @@
"Y": 0.022254243, "Y": 0.022254243,
"Z": -4.409851 "Z": -4.409851
}, },
"StopDistance": 5,
"TerritoryId": 433, "TerritoryId": 433,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }
@ -27,7 +28,6 @@
"Y": 1.1443481, "Y": 1.1443481,
"Z": 13.199036 "Z": 13.199036
}, },
"StopDistance": 7,
"TerritoryId": 433, "TerritoryId": 433,
"InteractionType": "Interact", "InteractionType": "Interact",
"TargetTerritoryId": 419 "TargetTerritoryId": 419

View File

@ -87,6 +87,17 @@
{ {
"Sequence": 4, "Sequence": 4,
"Steps": [ "Steps": [
{
"DataId": 1011240,
"Position": {
"X": 493.15625,
"Y": 200.2377,
"Z": 663.01965
},
"TerritoryId": 397,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1744
},
{ {
"DataId": 2005536, "DataId": 2005536,
"Position": { "Position": {

View File

@ -20,6 +20,39 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1011910,
"Position": {
"X": -298.26813,
"Y": 126.67049,
"Z": -1.4191895
},
"TerritoryId": 397,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1759
},
{
"DataId": 1011907,
"Position": {
"X": -288.8686,
"Y": 127.06639,
"Z": 13.199036
},
"TerritoryId": 397,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1760
},
{
"DataId": 1011911,
"Position": {
"X": -279.56055,
"Y": 127.08131,
"Z": 13.595764
},
"TerritoryId": 397,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 2111
},
{ {
"Position": { "Position": {
"X": -365.48965, "X": -365.48965,

View File

@ -20,6 +20,16 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": -826.9342,
"Y": 117.95439,
"Z": -642.92413
},
"TerritoryId": 397,
"InteractionType": "WalkTo",
"Fly": true
},
{ {
"Position": { "Position": {
"X": -850.3646, "X": -850.3646,

View File

@ -35,6 +35,17 @@
{ {
"Sequence": 2, "Sequence": 2,
"Steps": [ "Steps": [
{
"DataId": 1011916,
"Position": {
"X": 470.02356,
"Y": -49.89133,
"Z": 20.370789
},
"TerritoryId": 398,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1771
},
{ {
"Position": { "Position": {
"X": 634.0371, "X": 634.0371,

View File

@ -105,6 +105,17 @@
{ {
"Sequence": 3, "Sequence": 3,
"Steps": [ "Steps": [
{
"DataId": 1011929,
"Position": {
"X": 70.81714,
"Y": -49.2083,
"Z": -141.55798
},
"TerritoryId": 398,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1790
},
{ {
"Position": { "Position": {
"X": 70.535545, "X": 70.535545,

View File

@ -90,13 +90,52 @@
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{ {
"DataId": 1011916,
"Position": { "Position": {
"X": 123.44939, "X": 470.02356,
"Y": -60.88712, "Y": -49.89133,
"Z": -118.79085 "Z": 20.370789
}, },
"TerritoryId": 398, "TerritoryId": 398,
"InteractionType": "WalkTo" "InteractionType": "AcceptQuest",
"PickUpQuestId": 1797,
"AetheryteShortcut": "The Dravanian Forelands - Tailfeather",
"SkipConditions": {
"AetheryteShortcutIf": {
"QuestsCompleted": [
1797
]
}
}
},
{
"DataId": 1011937,
"Position": {
"X": -305.56195,
"Y": 39.04307,
"Z": 22.9953
},
"TerritoryId": 398,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1802,
"AetheryteShortcut": "The Dravanian Forelands - Anyx Trine",
"SkipConditions": {
"AetheryteShortcutIf": {
"QuestsCompleted": [
1802
]
}
}
},
{
"Position": {
"X": 22.235865,
"Y": -24.754946,
"Z": -119.65716
},
"TerritoryId": 398,
"InteractionType": "WalkTo",
"AetheryteShortcut": "The Dravanian Forelands - Anyx Trine"
}, },
{ {
"DataId": 1014544, "DataId": 1014544,
@ -106,7 +145,8 @@
"Z": -142.50409 "Z": -142.50409
}, },
"TerritoryId": 398, "TerritoryId": 398,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest",
"DisableNavmesh": true
} }
] ]
} }

View File

@ -12,7 +12,7 @@
"Y": -50.325172, "Y": -50.325172,
"Z": -146.95972 "Z": -146.95972
}, },
"StopDistance": 5, "StopDistance": 7,
"TerritoryId": 398, "TerritoryId": 398,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }

View File

@ -12,7 +12,7 @@
"Y": -69.42934, "Y": -69.42934,
"Z": 693.5072 "Z": 693.5072
}, },
"StopDistance": 5, "StopDistance": 6.5,
"TerritoryId": 400, "TerritoryId": 400,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }

View File

@ -31,6 +31,17 @@
"DisableNavmesh": true, "DisableNavmesh": true,
"Mount": true "Mount": true
}, },
{
"DataId": 1012284,
"Position": {
"X": 363.24097,
"Y": -73.25598,
"Z": 678.4314
},
"TerritoryId": 400,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1819
},
{ {
"Position": { "Position": {
"X": 286.46622, "X": 286.46622,

View File

@ -155,7 +155,7 @@
"Y": -69.42934, "Y": -69.42934,
"Z": 693.5072 "Z": 693.5072
}, },
"StopDistance": 5, "StopDistance": 7,
"TerritoryId": 400, "TerritoryId": 400,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest"
} }

View File

@ -12,7 +12,7 @@
"Y": -69.42934, "Y": -69.42934,
"Z": 693.5072 "Z": 693.5072
}, },
"StopDistance": 5, "StopDistance": 7,
"TerritoryId": 400, "TerritoryId": 400,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }

View File

@ -58,6 +58,25 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 1012285,
"Position": {
"X": 247.11987,
"Y": -42.33362,
"Z": 565.3314
},
"TerritoryId": 400,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1823,
"AetheryteShortcut": "The Churning Mists - Moghome",
"SkipConditions": {
"AetheryteShortcutIf": {
"QuestsCompleted": [
1823
]
}
}
},
{ {
"DataId": 1013202, "DataId": 1013202,
"Position": { "Position": {

View File

@ -85,6 +85,7 @@
"Y": 123.72873, "Y": 123.72873,
"Z": 210.74231 "Z": 210.74231
}, },
"StopDistance": 7,
"TerritoryId": 400, "TerritoryId": 400,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest"
} }

View File

@ -12,6 +12,7 @@
"Y": 123.72873, "Y": 123.72873,
"Z": 210.74231 "Z": 210.74231
}, },
"StopDistance": 7,
"TerritoryId": 400, "TerritoryId": 400,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }

View File

@ -20,6 +20,17 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1013434,
"Position": {
"X": 517.9064,
"Y": -1.1917055,
"Z": -354.63495
},
"TerritoryId": 400,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1835
},
{ {
"DataId": 1012720, "DataId": 1012720,
"Position": { "Position": {

View File

@ -12,6 +12,7 @@
"Y": 16.979584, "Y": 16.979584,
"Z": -37.521973 "Z": -37.521973
}, },
"StopDistance": 7,
"TerritoryId": 418, "TerritoryId": 418,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }
@ -58,6 +59,7 @@
"Y": 0, "Y": 0,
"Z": -3.1281738 "Z": -3.1281738
}, },
"StopDistance": 7,
"TerritoryId": 212, "TerritoryId": 212,
"InteractionType": "Interact" "InteractionType": "Interact"
} }

View File

@ -21,6 +21,25 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 1013420,
"Position": {
"X": -511.46716,
"Y": 50,
"Z": 347.0968
},
"TerritoryId": 400,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "The Churning Mists - Zenith",
"PickUpQuestId": 1828,
"SkipConditions": {
"AetheryteShortcutIf": {
"QuestsCompleted": [
1828
]
}
}
},
{ {
"DataId": 1013172, "DataId": 1013172,
"Position": { "Position": {
@ -30,7 +49,6 @@
}, },
"TerritoryId": 400, "TerritoryId": 400,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest",
"AetheryteShortcut": "The Churning Mists - Zenith",
"Fly": true "Fly": true
} }
] ]

View File

@ -21,6 +21,18 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 1014720,
"Position": {
"X": 13.809326,
"Y": 15.96505,
"Z": -13.870483
},
"StopDistance": 7,
"TerritoryId": 419,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1874
},
{ {
"DataId": 1011223, "DataId": 1011223,
"Position": { "Position": {
@ -37,8 +49,9 @@
"Yes": true "Yes": true
} }
], ],
"AetheryteShortcut": "Ishgard",
"AethernetShortcut": [ "AethernetShortcut": [
"[Ishgard] The Last Vigil", "[Ishgard] Aetheryte Plaza",
"[Ishgard] The Forgotten Knight" "[Ishgard] The Forgotten Knight"
] ]
}, },

View File

@ -38,7 +38,7 @@
"Z": 113.54166 "Z": 113.54166
}, },
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "SinglePlayerDuty", "InteractionType": "Emote",
"Emote": "lookout", "Emote": "lookout",
"StopDistance": 0.25 "StopDistance": 0.25
} }
@ -54,10 +54,9 @@
"Z": 77.39858 "Z": 77.39858
}, },
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "Emote", "InteractionType": "SinglePlayerDuty",
"Emote": "lookout", "Emote": "lookout",
"StopDistance": 0.25, "StopDistance": 0.25
"Comment": "Solo Duty triggered by emoting?"
} }
] ]
}, },

View File

@ -20,6 +20,17 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1012069,
"Position": {
"X": -647.0283,
"Y": -51.05719,
"Z": -417.74628
},
"TerritoryId": 401,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1909
},
{ {
"DataId": 1012064, "DataId": 1012064,
"Position": { "Position": {
@ -35,6 +46,17 @@
{ {
"Sequence": 2, "Sequence": 2,
"Steps": [ "Steps": [
{
"DataId": 1012068,
"Position": {
"X": -597.0398,
"Y": -51.05185,
"Z": -387.0451
},
"TerritoryId": 401,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1910
},
{ {
"DataId": 2006231, "DataId": 2006231,
"Position": { "Position": {
@ -68,6 +90,7 @@
"Y": -14.730623, "Y": -14.730623,
"Z": -537.56006 "Z": -537.56006
}, },
"StopDistance": 7,
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "Interact", "InteractionType": "Interact",
"DialogueChoices": [ "DialogueChoices": [
@ -100,7 +123,7 @@
"Y": -14.153766, "Y": -14.153766,
"Z": -543.0228 "Z": -543.0228
}, },
"StopDistance": 5, "StopDistance": 7,
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest"
} }

View File

@ -12,7 +12,7 @@
"Y": -14.153766, "Y": -14.153766,
"Z": -543.0228 "Z": -543.0228
}, },
"StopDistance": 5, "StopDistance": 7,
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }

View File

@ -21,6 +21,15 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": 43.620056,
"Y": -7.9655867,
"Z": 103.63884
},
"TerritoryId": 132,
"InteractionType": "WalkTo"
},
{ {
"DataId": 1000691, "DataId": 1000691,
"Position": { "Position": {

View File

@ -45,7 +45,8 @@
}, },
"TerritoryId": 398, "TerritoryId": 398,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest",
"AetheryteShortcut": "The Dravanian Forelands - Tailfeather" "AetheryteShortcut": "The Dravanian Forelands - Tailfeather",
"Fly": true
} }
] ]
} }

View File

@ -27,6 +27,7 @@
"Y": 205.62819, "Y": 205.62819,
"Z": 26.901611 "Z": 26.901611
}, },
"StopDistance": 7,
"TerritoryId": 478, "TerritoryId": 478,
"InteractionType": "Interact" "InteractionType": "Interact"
} }
@ -47,7 +48,15 @@
"$": "0 0 0 0 0 0 -> 1 0 0 0 0 64", "$": "0 0 0 0 0 0 -> 1 0 0 0 0 64",
"ChatMessage": { "ChatMessage": {
"Key": "TEXT_HEAVNA607_01656_SAYTODO_000" "Key": "TEXT_HEAVNA607_01656_SAYTODO_000"
} },
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
]
}, },
{ {
"DataId": 1012413, "DataId": 1012413,
@ -60,7 +69,15 @@
"InteractionType": "Say", "InteractionType": "Say",
"ChatMessage": { "ChatMessage": {
"Key": "TEXT_HEAVNA607_01656_SAYTODO_000" "Key": "TEXT_HEAVNA607_01656_SAYTODO_000"
} },
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
]
} }
] ]
}, },

View File

@ -20,6 +20,21 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1012287,
"Position": {
"X": -28.397034,
"Y": 100.969696,
"Z": -186.4195
},
"TerritoryId": 399,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1936,
"AethernetShortcut": [
"[Idyllshire] Aetheryte Plaza",
"[Idyllshire] Epilogue Gate (Eastern Hinterlands)"
]
},
{ {
"DataId": 1012416, "DataId": 1012416,
"Position": { "Position": {

View File

@ -20,6 +20,17 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1012102,
"Position": {
"X": 72.40405,
"Y": 205.6815,
"Z": 31.631958
},
"TerritoryId": 478,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1945
},
{ {
"DataId": 1012419, "DataId": 1012419,
"Position": { "Position": {

View File

@ -21,6 +21,17 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1012133,
"Position": {
"X": -26.840637,
"Y": 206.49944,
"Z": 28.67163
},
"TerritoryId": 478,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1963
},
{ {
"Position": { "Position": {
"X": 71.86769, "X": 71.86769,
@ -29,18 +40,15 @@
}, },
"TerritoryId": 478, "TerritoryId": 478,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"TargetTerritoryId": 399 "TargetTerritoryId": 399,
}, "SkipConditions": {
{ "StepIf": {
"DataId": 2006210, "InTerritory": [
"Position": { 399
"X": -487.48004, ]
"Y": 144.64026, }
"Z": -285.359
}, },
"TerritoryId": 399, "$": "Skipped if already in the Hinterlands, since 'Taking Stock' already leaves you closer to the entrance to Matoya's cave"
"InteractionType": "AttuneAetherCurrent",
"AetherCurrentId": 2818088
}, },
{ {
"DataId": 2006214, "DataId": 2006214,

View File

@ -44,7 +44,7 @@
"Y": 38.43, "Y": 38.43,
"Z": 3.5552979 "Z": 3.5552979
}, },
"StopDistance": 7, "StopDistance": 6,
"TerritoryId": 463, "TerritoryId": 463,
"InteractionType": "Interact" "InteractionType": "Interact"
} }
@ -53,6 +53,17 @@
{ {
"Sequence": 3, "Sequence": 3,
"Steps": [ "Steps": [
{
"DataId": 1012141,
"Position": {
"X": 35.690796,
"Y": 38.43,
"Z": 12.985352
},
"TerritoryId": 463,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 1966
},
{ {
"DataId": 2005337, "DataId": 2005337,
"Position": { "Position": {
@ -114,7 +125,8 @@
}, },
"TerritoryId": 399, "TerritoryId": 399,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"DisableNavmesh": true "DisableNavmesh": true,
"Mount": true
}, },
{ {
"DataId": 2005336, "DataId": 2005336,

View File

@ -28,6 +28,7 @@
"Y": -14.52896, "Y": -14.52896,
"Z": 41.153564 "Z": 41.153564
}, },
"StopDistance": 7,
"TerritoryId": 419, "TerritoryId": 419,
"InteractionType": "Interact" "InteractionType": "Interact"
} }

View File

@ -61,7 +61,8 @@
"Z": -659.2356 "Z": -659.2356
}, },
"TerritoryId": 402, "TerritoryId": 402,
"InteractionType": "Interact" "InteractionType": "Interact",
"DisableNavmesh": true
} }
] ]
}, },

View File

@ -35,6 +35,15 @@
{ {
"Sequence": 2, "Sequence": 2,
"Steps": [ "Steps": [
{
"Position": {
"X": 642.1002,
"Y": -32.964188,
"Z": -532.8383
},
"TerritoryId": 402,
"InteractionType": "WalkTo"
},
{ {
"DataId": 2005568, "DataId": 2005568,
"Position": { "Position": {

View File

@ -49,8 +49,7 @@
}, },
"StopDistance": 0.5, "StopDistance": 0.5,
"TerritoryId": 612, "TerritoryId": 612,
"InteractionType": "Instruction", "InteractionType": "Combat",
"Comment": "Manual combat",
"EnemySpawnType": "AutoOnEnterArea", "EnemySpawnType": "AutoOnEnterArea",
"KillEnemyDataIds": [ "KillEnemyDataIds": [
7504 7504
@ -69,8 +68,7 @@
}, },
"StopDistance": 2, "StopDistance": 2,
"TerritoryId": 612, "TerritoryId": 612,
"InteractionType": "Instruction", "InteractionType": "Combat",
"Comment": "Manual combat",
"EnemySpawnType": "AutoOnEnterArea", "EnemySpawnType": "AutoOnEnterArea",
"KillEnemyDataIds": [ "KillEnemyDataIds": [
7505 7505

View File

@ -1,13 +1,16 @@
{ {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json", "$id": "https://git.carvel.li/plogon_enjoyer/Questionable/raw/branch/temp/QuestPaths/quest-v1.json",
"title": "Questionable V1", "title": "Questionable V1",
"description": "A series of quest sequences", "description": "A series of quest sequences",
"type": "object", "type": "object",
"properties": { "properties": {
"$schema": { "$schema": {
"type": "string", "type": "string",
"const": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json" "enum":[
"https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"https://git.carvel.li/plogon_enjoyer/Questionable/raw/branch/temp/QuestPaths/quest-v1.json"
]
}, },
"Author": { "Author": {
"description": "Author of the quest sequence", "description": "Author of the quest sequence",
@ -584,6 +587,7 @@
"AutoOnEnterArea", "AutoOnEnterArea",
"AfterInteraction", "AfterInteraction",
"AfterItemUse", "AfterItemUse",
"AfterAction",
"OverworldEnemies" "OverworldEnemies"
] ]
}, },
@ -859,6 +863,7 @@
"Cure II", "Cure II",
"Esuna", "Esuna",
"Physick", "Physick",
"Aspected Benefic",
"Buffet (Sanuwa)", "Buffet (Sanuwa)",
"Buffet (Griffin)", "Buffet (Griffin)",
"Fumigate", "Fumigate",

View File

@ -13,6 +13,7 @@ public sealed class ActionConverter() : EnumConverter<EAction>(Values)
{ EAction.Cure2, "Cure II" }, { EAction.Cure2, "Cure II" },
{ EAction.Esuna, "Esuna" }, { EAction.Esuna, "Esuna" },
{ EAction.Physick, "Physick" }, { EAction.Physick, "Physick" },
{ EAction.AspectedBenefic, "Aspected Benefic" },
{ EAction.BuffetSanuwa, "Buffet (Sanuwa)" }, { EAction.BuffetSanuwa, "Buffet (Sanuwa)" },
{ EAction.BuffetGriffin, "Buffet (Griffin)" }, { EAction.BuffetGriffin, "Buffet (Griffin)" },
{ EAction.Fumigate, "Fumigate" }, { EAction.Fumigate, "Fumigate" },

View File

@ -9,6 +9,7 @@ public sealed class EnemySpawnTypeConverter() : EnumConverter<EEnemySpawnType>(V
{ {
{ EEnemySpawnType.AfterInteraction, "AfterInteraction" }, { EEnemySpawnType.AfterInteraction, "AfterInteraction" },
{ EEnemySpawnType.AfterItemUse, "AfterItemUse" }, { EEnemySpawnType.AfterItemUse, "AfterItemUse" },
{ EEnemySpawnType.AfterAction, "AfterAction" },
{ EEnemySpawnType.AutoOnEnterArea, "AutoOnEnterArea" }, { EEnemySpawnType.AutoOnEnterArea, "AutoOnEnterArea" },
{ EEnemySpawnType.OverworldEnemies, "OverworldEnemies" }, { EEnemySpawnType.OverworldEnemies, "OverworldEnemies" },
}; };

View File

@ -12,9 +12,12 @@ public enum EAction
Cure2 = 135, Cure2 = 135,
Esuna = 7568, Esuna = 7568,
Physick = 190, Physick = 190,
AspectedBenefic = 3595,
BuffetSanuwa = 4931, BuffetSanuwa = 4931,
BuffetGriffin = 4583, BuffetGriffin = 4583,
Fumigate = 5872, Fumigate = 5872,
MagitekPulse = 8624,
MagitekThunder = 8625,
SiphonSnout = 18187, SiphonSnout = 18187,
Cannonfire = 20121, Cannonfire = 20121,
RedGulal = 29382, RedGulal = 29382,

View File

@ -9,6 +9,7 @@ public enum EEnemySpawnType
None = 0, None = 0,
AfterInteraction, AfterInteraction,
AfterItemUse, AfterItemUse,
AfterAction,
AutoOnEnterArea, AutoOnEnterArea,
OverworldEnemies, OverworldEnemies,
} }

View File

@ -191,6 +191,9 @@ internal sealed class CombatController : IDisposable
{ {
if (gameObject is IBattleNpc battleNpc) if (gameObject is IBattleNpc battleNpc)
{ {
if (_currentFight != null && !_currentFight.Module.CanAttack(battleNpc))
return 0;
// TODO this works as somewhat of a delay between killing enemies if certain items/flags are checked // TODO this works as somewhat of a delay between killing enemies if certain items/flags are checked
// but also delays killing the next enemy a little // but also delays killing the next enemy a little
if (_currentFight == null || _currentFight.Data.SpawnType != EEnemySpawnType.OverworldEnemies || if (_currentFight == null || _currentFight.Data.SpawnType != EEnemySpawnType.OverworldEnemies ||

View File

@ -13,4 +13,6 @@ internal interface ICombatModule
void Update(IGameObject nextTarget); void Update(IGameObject nextTarget);
void MoveToTarget(IGameObject nextTarget); void MoveToTarget(IGameObject nextTarget);
bool CanAttack(IBattleNpc target);
} }

View File

@ -0,0 +1,51 @@
using System;
using System.Numerics;
using Dalamud.Game.ClientState.Objects.Types;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Controller.CombatModules;
/// <summary>
/// Commandeered Magitek Armor; used in 'Magiteknical Failure' quest.
/// </summary>
internal sealed class Mount128Module : ICombatModule
{
public const ushort MountId = 128;
private readonly EAction[] _actions = [EAction.MagitekThunder, EAction.MagitekPulse];
private readonly MovementController _movementController;
private readonly GameFunctions _gameFunctions;
public Mount128Module(MovementController movementController, GameFunctions gameFunctions)
{
_movementController = movementController;
_gameFunctions = gameFunctions;
}
public bool IsLoaded => _gameFunctions.GetMountId() == MountId;
public bool Start() => true;
public bool Stop() => true;
public void Update(IGameObject gameObject)
{
if (_movementController.IsPathfinding || _movementController.IsPathRunning)
return;
foreach (EAction action in _actions)
{
if (_gameFunctions.UseAction(gameObject, action, checkCanUse: false))
return;
}
}
public void MoveToTarget(IGameObject gameObject)
{
}
public bool CanAttack(IBattleNpc target) => target.DataId is 7504 or 7505;
}

View File

@ -85,7 +85,7 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable
float hitboxOffset = player.HitboxRadius + gameObject.HitboxRadius; float hitboxOffset = player.HitboxRadius + gameObject.HitboxRadius;
float actualDistance = Vector3.Distance(player.Position, gameObject.Position); float actualDistance = Vector3.Distance(player.Position, gameObject.Position);
float maxDistance = player.ClassJob.GameData?.Role is 3 or 4 ? 20f : 3f; float maxDistance = player.ClassJob.GameData?.Role is 3 or 4 ? 20f : 2.9f;
if (actualDistance - hitboxOffset >= maxDistance) if (actualDistance - hitboxOffset >= maxDistance)
{ {
if (actualDistance - hitboxOffset <= 5) if (actualDistance - hitboxOffset <= 5)
@ -119,6 +119,8 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable
} }
} }
public bool CanAttack(IBattleNpc target) => true;
public void Dispose() => Stop(); public void Dispose() => Stop();
[PublicAPI] [PublicAPI]

View File

@ -3,16 +3,20 @@ using System.Linq;
using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets;
using Questionable.Functions; using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing; using Questionable.Model.Questing;
using Questionable.Windows; using Questionable.Windows;
using Questionable.Windows.QuestComponents; using Questionable.Windows.QuestComponents;
using Quest = Questionable.Model.Quest;
namespace Questionable.Controller; namespace Questionable.Controller;
internal sealed class CommandHandler : IDisposable internal sealed class CommandHandler : IDisposable
{ {
private const string MessageTag = "Questionable";
private const ushort TagColor = 576;
private readonly ICommandManager _commandManager; private readonly ICommandManager _commandManager;
private readonly IChatGui _chatGui; private readonly IChatGui _chatGui;
private readonly QuestController _questController; private readonly QuestController _questController;
@ -24,6 +28,8 @@ internal sealed class CommandHandler : IDisposable
private readonly QuestSelectionWindow _questSelectionWindow; private readonly QuestSelectionWindow _questSelectionWindow;
private readonly ITargetManager _targetManager; private readonly ITargetManager _targetManager;
private readonly QuestFunctions _questFunctions; private readonly QuestFunctions _questFunctions;
private readonly GameFunctions _gameFunctions;
private readonly IDataManager _dataManager;
public CommandHandler( public CommandHandler(
ICommandManager commandManager, ICommandManager commandManager,
@ -36,7 +42,9 @@ internal sealed class CommandHandler : IDisposable
QuestWindow questWindow, QuestWindow questWindow,
QuestSelectionWindow questSelectionWindow, QuestSelectionWindow questSelectionWindow,
ITargetManager targetManager, ITargetManager targetManager,
QuestFunctions questFunctions) QuestFunctions questFunctions,
GameFunctions gameFunctions,
IDataManager dataManager)
{ {
_commandManager = commandManager; _commandManager = commandManager;
_chatGui = chatGui; _chatGui = chatGui;
@ -49,6 +57,8 @@ internal sealed class CommandHandler : IDisposable
_questSelectionWindow = questSelectionWindow; _questSelectionWindow = questSelectionWindow;
_targetManager = targetManager; _targetManager = targetManager;
_questFunctions = questFunctions; _questFunctions = questFunctions;
_gameFunctions = gameFunctions;
_dataManager = dataManager;
_commandManager.AddHandler("/qst", new CommandInfo(ProcessCommand) _commandManager.AddHandler("/qst", new CommandInfo(ProcessCommand)
{ {
@ -108,12 +118,16 @@ internal sealed class CommandHandler : IDisposable
_questSelectionWindow.OpenForCurrentZone(); _questSelectionWindow.OpenForCurrentZone();
break; break;
case "mountid":
PrintMountId();
break;
case "": case "":
_questWindow.Toggle(); _questWindow.Toggle();
break; break;
default: default:
_chatGui.PrintError($"Unknown subcommand {parts[0]}", "Questionable"); _chatGui.PrintError($"Unknown subcommand {parts[0]}", MessageTag, TagColor);
break; break;
} }
} }
@ -122,7 +136,7 @@ internal sealed class CommandHandler : IDisposable
{ {
if (!_debugOverlay.DrawConditions()) if (!_debugOverlay.DrawConditions())
{ {
_chatGui.PrintError("[Questionable] You don't have the debug overlay enabled."); _chatGui.PrintError("You don't have the debug overlay enabled.", MessageTag, TagColor);
return; return;
} }
@ -131,15 +145,15 @@ internal sealed class CommandHandler : IDisposable
if (_questRegistry.TryGetQuest(questId, out Quest? quest)) if (_questRegistry.TryGetQuest(questId, out Quest? quest))
{ {
_debugOverlay.HighlightedQuest = quest.Id; _debugOverlay.HighlightedQuest = quest.Id;
_chatGui.Print($"[Questionable] Set highlighted quest to {questId} ({quest.Info.Name})."); _chatGui.Print($"Set highlighted quest to {questId} ({quest.Info.Name}).", MessageTag, TagColor);
} }
else else
_chatGui.PrintError($"[Questionable] Unknown quest {questId}."); _chatGui.PrintError($"Unknown quest {questId}.", MessageTag, TagColor);
} }
else else
{ {
_debugOverlay.HighlightedQuest = null; _debugOverlay.HighlightedQuest = null;
_chatGui.Print("[Questionable] Cleared highlighted quest."); _chatGui.Print("Cleared highlighted quest.", MessageTag, TagColor);
} }
} }
@ -148,21 +162,21 @@ internal sealed class CommandHandler : IDisposable
if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId? questId) && questId != null) if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId? questId) && questId != null)
{ {
if (_questFunctions.IsQuestLocked(questId)) if (_questFunctions.IsQuestLocked(questId))
_chatGui.PrintError($"[Questionable] Quest {questId} is locked."); _chatGui.PrintError($"Quest {questId} is locked.", MessageTag, TagColor);
else if (_questRegistry.TryGetQuest(questId, out Quest? quest)) else if (_questRegistry.TryGetQuest(questId, out Quest? quest))
{ {
_questController.SetNextQuest(quest); _questController.SetNextQuest(quest);
_chatGui.Print($"[Questionable] Set next quest to {questId} ({quest.Info.Name})."); _chatGui.Print($"Set next quest to {questId} ({quest.Info.Name}).", MessageTag, TagColor);
} }
else else
{ {
_chatGui.PrintError($"[Questionable] Unknown quest {questId}."); _chatGui.PrintError($"Unknown quest {questId}.", MessageTag, TagColor);
} }
} }
else else
{ {
_questController.SetNextQuest(null); _questController.SetNextQuest(null);
_chatGui.Print("[Questionable] Cleared next quest."); _chatGui.Print("Cleared next quest.", MessageTag, TagColor);
} }
} }
@ -173,18 +187,32 @@ internal sealed class CommandHandler : IDisposable
if (_questRegistry.TryGetQuest(questId, out Quest? quest)) if (_questRegistry.TryGetQuest(questId, out Quest? quest))
{ {
_questController.SimulateQuest(quest); _questController.SimulateQuest(quest);
_chatGui.Print($"[Questionable] Simulating quest {questId} ({quest.Info.Name})."); _chatGui.Print($"Simulating quest {questId} ({quest.Info.Name}).", MessageTag, TagColor);
} }
else else
_chatGui.PrintError($"[Questionable] Unknown quest {questId}."); _chatGui.PrintError($"Unknown quest {questId}.", MessageTag, TagColor);
} }
else else
{ {
_questController.SimulateQuest(null); _questController.SimulateQuest(null);
_chatGui.Print("[Questionable] Cleared simulated quest."); _chatGui.Print("Cleared simulated quest.", MessageTag, TagColor);
} }
} }
private void PrintMountId()
{
ushort? mountId = _gameFunctions.GetMountId();
if (mountId != null)
{
var row = _dataManager.GetExcelSheet<Mount>()!.GetRow(mountId.Value);
_chatGui.Print(
$"Mount ID: {mountId}, Name: {row?.Singular}, Obtainable: {(row?.Order == -1 ? "No" : "Yes")}",
MessageTag, TagColor);
}
else
_chatGui.Print("You are not mounted.", MessageTag, TagColor);
}
public void Dispose() public void Dispose()
{ {
_commandManager.RemoveHandler("/qst"); _commandManager.RemoveHandler("/qst");

View File

@ -235,6 +235,16 @@ internal sealed class InteractionUiController : IDisposable
_logger.LogInformation("Checking if current quest {Name} is on the list", currentQuest.Quest.Info.Name); _logger.LogInformation("Checking if current quest {Name} is on the list", currentQuest.Quest.Info.Name);
if (CheckQuestSelection(addonSelectIconString, currentQuest.Quest, answers)) if (CheckQuestSelection(addonSelectIconString, currentQuest.Quest, answers))
return; return;
var sequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
QuestStep? step = sequence?.FindStep(currentQuest.Step);
if (step is { InteractionType: EInteractionType.AcceptQuest, PickUpQuestId: not null } &&
_questRegistry.TryGetQuest(step.PickUpQuestId, out Quest? pickupQuest))
{
_logger.LogInformation("Checking if current picked-up {Name} is on the list", pickupQuest.Info.Name);
if (CheckQuestSelection(addonSelectIconString, pickupQuest, answers))
return;
}
} }
var nextQuest = _questController.NextQuest; var nextQuest = _questController.NextQuest;

View File

@ -20,6 +20,10 @@ internal static class NextQuest
if (step.NextQuestId == quest.Id) if (step.NextQuestId == quest.Id)
return null; return null;
// probably irrelevant, since pick up is handled elsewhere (and, in particular, checks for aetherytes and stuff)
if (questFunctions.GetPriorityQuests().Contains(step.NextQuestId))
return null;
return new SetQuest(step.NextQuestId, quest.Id, questRegistry, questController, questFunctions, loggerFactory.CreateLogger<SetQuest>()); return new SetQuest(step.NextQuestId, quest.Id, questRegistry, questController, questFunctions, loggerFactory.CreateLogger<SetQuest>());
} }
} }

View File

@ -24,13 +24,18 @@ internal static class Action
ArgumentNullException.ThrowIfNull(step.Action); ArgumentNullException.ThrowIfNull(step.Action);
var task = new UseOnObject(step.DataId, step.Action.Value, gameFunctions, var task = OnObject(step.DataId, step.Action.Value);
loggerFactory.CreateLogger<UseOnObject>());
if (step.Action.Value.RequiresMount()) if (step.Action.Value.RequiresMount())
return [task]; return [task];
else else
return [mountFactory.Unmount(), task]; return [mountFactory.Unmount(), task];
} }
public ITask OnObject(uint? dataId, EAction action)
{
return new UseOnObject(dataId, action, gameFunctions,
loggerFactory.CreateLogger<UseOnObject>());
}
} }
private sealed class UseOnObject( private sealed class UseOnObject(

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.DependencyInjection; using Questionable.Controller.CombatModules;
using Questionable.Controller.Steps.Common; using Questionable.Controller.Steps.Common;
using Questionable.Controller.Steps.Shared; using Questionable.Controller.Steps.Shared;
using Questionable.Controller.Utils; using Questionable.Controller.Utils;
@ -18,7 +18,9 @@ internal static class Combat
Interact.Factory interactFactory, Interact.Factory interactFactory,
Mount.Factory mountFactory, Mount.Factory mountFactory,
UseItem.Factory useItemFactory, UseItem.Factory useItemFactory,
QuestFunctions questFunctions) : ITaskFactory Action.Factory actionFactory,
QuestFunctions questFunctions,
GameFunctions gameFunctions) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -27,7 +29,8 @@ internal static class Combat
ArgumentNullException.ThrowIfNull(step.EnemySpawnType); ArgumentNullException.ThrowIfNull(step.EnemySpawnType);
yield return mountFactory.Unmount(); if (gameFunctions.GetMountId() != Mount128Module.MountId)
yield return mountFactory.Unmount();
if (step.CombatDelaySecondsAtStart != null) if (step.CombatDelaySecondsAtStart != null)
{ {
@ -58,6 +61,19 @@ internal static class Combat
break; break;
} }
case EEnemySpawnType.AfterAction:
{
ArgumentNullException.ThrowIfNull(step.DataId);
ArgumentNullException.ThrowIfNull(step.Action);
if (!step.Action.Value.RequiresMount())
yield return mountFactory.Unmount();
yield return actionFactory.OnObject(step.DataId.Value, step.Action.Value);
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1));
yield return CreateTask(quest, sequence, step);
break;
}
case EEnemySpawnType.AutoOnEnterArea: case EEnemySpawnType.AutoOnEnterArea:
if (step.CombatDelaySecondsAtStart == null) if (step.CombatDelaySecondsAtStart == null)
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1)); yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1));

View File

@ -14,7 +14,8 @@ internal static class Emote
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
if (step.InteractionType is EInteractionType.AcceptQuest or EInteractionType.CompleteQuest) if (step.InteractionType is EInteractionType.AcceptQuest or EInteractionType.CompleteQuest
or EInteractionType.SinglePlayerDuty)
{ {
if (step.Emote == null) if (step.Emote == null)
return []; return [];

View File

@ -68,7 +68,7 @@ internal static class MoveTo
public ITask Move(MoveParams moveParams) public ITask Move(MoveParams moveParams)
{ {
return new MoveInternal(moveParams, movementController, gameFunctions, return new MoveInternal(moveParams, movementController, gameFunctions,
loggerFactory.CreateLogger<MoveInternal>(), condition, clientState, dataManager); loggerFactory.CreateLogger<MoveInternal>(), clientState, dataManager);
} }
public ITask Land() public ITask Land()
@ -163,26 +163,22 @@ internal static class MoveTo
private readonly string _cannotExecuteAtThisTime; private readonly string _cannotExecuteAtThisTime;
private readonly MovementController _movementController; private readonly MovementController _movementController;
private readonly ILogger<MoveInternal> _logger; private readonly ILogger<MoveInternal> _logger;
private readonly ICondition _condition;
private readonly IClientState _clientState; private readonly IClientState _clientState;
private readonly Action _startAction; private readonly Action _startAction;
private readonly Vector3 _destination; private readonly Vector3 _destination;
private readonly MoveParams _moveParams; private readonly MoveParams _moveParams;
private readonly bool _isUnderwaterInitially;
private bool _canRestart; private bool _canRestart;
public MoveInternal(MoveParams moveParams, public MoveInternal(MoveParams moveParams,
MovementController movementController, MovementController movementController,
GameFunctions gameFunctions, GameFunctions gameFunctions,
ILogger<MoveInternal> logger, ILogger<MoveInternal> logger,
ICondition condition,
IClientState clientState, IClientState clientState,
IDataManager dataManager) IDataManager dataManager)
{ {
_movementController = movementController; _movementController = movementController;
_logger = logger; _logger = logger;
_condition = condition;
_clientState = clientState; _clientState = clientState;
_cannotExecuteAtThisTime = dataManager.GetString<LogMessage>(579, x => x.Text)!; _cannotExecuteAtThisTime = dataManager.GetString<LogMessage>(579, x => x.Text)!;
@ -257,8 +253,7 @@ internal static class MoveTo
public bool OnErrorToast(SeString message) public bool OnErrorToast(SeString message)
{ {
if (GameFunctions.GameStringEquals(_cannotExecuteAtThisTime, message.TextValue) && if (GameFunctions.GameStringEquals(_cannotExecuteAtThisTime, message.TextValue))
_condition[ConditionFlag.Diving])
return true; return true;
return false; return false;

View File

@ -0,0 +1,31 @@
using System;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Questionable.Controller;
namespace Questionable.External;
internal sealed class QuestionableIpc : IDisposable
{
private const string IpcIsRunning = "Questionable.IsRunning";
private const string IpcGetCurrentQuestId = "Questionable.GetCurrentQuestId";
private readonly ICallGateProvider<bool> _isRunning;
private readonly ICallGateProvider<string?> _getCurrentQuestId;
public QuestionableIpc(QuestController questController, IDalamudPluginInterface pluginInterface)
{
_isRunning = pluginInterface.GetIpcProvider<bool>(IpcIsRunning);
_isRunning.RegisterFunc(() => questController.IsRunning);
_getCurrentQuestId = pluginInterface.GetIpcProvider<string?>(IpcGetCurrentQuestId);
_getCurrentQuestId.RegisterFunc(() => questController.CurrentQuest?.Quest.Id.ToString());
}
public void Dispose()
{
_getCurrentQuestId.UnregisterFunc();
_isRunning.UnregisterFunc();
}
}

View File

@ -81,8 +81,9 @@ internal sealed unsafe class GameFunctions
if (_questFunctions.IsQuestAccepted(new QuestId(3304)) && _condition[ConditionFlag.Mounted]) if (_questFunctions.IsQuestAccepted(new QuestId(3304)) && _condition[ConditionFlag.Mounted])
{ {
BattleChara* battleChara = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0); // special quest amaro, not the normal one
if (battleChara != null && battleChara->Mount.MountId == 198) // special quest amaro, not the normal one // TODO Check if this also applies to beast tribe mounts
if (GetMountId() == 198)
return true; return true;
} }
@ -92,6 +93,15 @@ internal sealed unsafe class GameFunctions
playerState->IsAetherCurrentZoneComplete(aetherCurrentCompFlgSet); playerState->IsAetherCurrentZoneComplete(aetherCurrentCompFlgSet);
} }
public ushort? GetMountId()
{
BattleChara* battleChara = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
if (battleChara != null && battleChara->Mount.MountId != 0)
return battleChara->Mount.MountId;
else
return null;
}
public bool IsFlyingUnlockedInCurrentZone() => IsFlyingUnlocked(_clientState.TerritoryType); public bool IsFlyingUnlockedInCurrentZone() => IsFlyingUnlocked(_clientState.TerritoryType);
public bool IsAetherCurrentUnlocked(uint aetherCurrentId) public bool IsAetherCurrentUnlocked(uint aetherCurrentId)
@ -210,10 +220,10 @@ internal sealed unsafe class GameFunctions
return false; return false;
} }
public bool UseAction(IGameObject gameObject, EAction action) public bool UseAction(IGameObject gameObject, EAction action, bool checkCanUse = true)
{ {
var actionRow = _dataManager.GetExcelSheet<Action>()!.GetRow((uint)action)!; var actionRow = _dataManager.GetExcelSheet<Action>()!.GetRow((uint)action)!;
if (!ActionManager.CanUseActionOnTarget((uint)action, (GameObject*)gameObject.Address)) if (checkCanUse && !ActionManager.CanUseActionOnTarget((uint)action, (GameObject*)gameObject.Address))
{ {
_logger.LogWarning("Can not use action {Action} on target {Target}", action, gameObject); _logger.LogWarning("Can not use action {Action} on target {Target}", action, gameObject);
return false; return false;

View File

@ -254,7 +254,8 @@ internal sealed unsafe class QuestFunctions
InventoryManager* inventoryManager = InventoryManager.Instance(); InventoryManager* inventoryManager = InventoryManager.Instance();
int gil = inventoryManager->GetItemCountInContainer(1, InventoryType.Currency); int gil = inventoryManager->GetItemCountInContainer(1, InventoryType.Currency);
return GetPriorityQuestsThatCanBeAccepted() return GetPriorityQuests()
.Where(IsReadyToAcceptQuest)
.Where(x => .Where(x =>
{ {
if (!_questRegistry.TryGetQuest(x, out Quest? quest)) if (!_questRegistry.TryGetQuest(x, out Quest? quest))
@ -311,7 +312,7 @@ internal sealed unsafe class QuestFunctions
return 1000 * quest.AllSteps().Count(x => x.Step.AetheryteShortcut != null); return 1000 * quest.AllSteps().Count(x => x.Step.AetheryteShortcut != null);
} }
private List<ElementId> GetPriorityQuestsThatCanBeAccepted() public List<ElementId> GetPriorityQuests()
{ {
List<ElementId> priorityQuests = List<ElementId> priorityQuests =
[ [
@ -349,7 +350,6 @@ internal sealed unsafe class QuestFunctions
return priorityQuests return priorityQuests
.Where(_questRegistry.IsKnownQuest) .Where(_questRegistry.IsKnownQuest)
.Where(IsReadyToAcceptQuest)
.ToList(); .ToList();
} }

View File

@ -120,6 +120,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton<LifestreamIpc>(); serviceCollection.AddSingleton<LifestreamIpc>();
serviceCollection.AddSingleton<YesAlreadyIpc>(); serviceCollection.AddSingleton<YesAlreadyIpc>();
serviceCollection.AddSingleton<ArtisanIpc>(); serviceCollection.AddSingleton<ArtisanIpc>();
serviceCollection.AddSingleton<QuestionableIpc>();
} }
private static void AddTaskFactories(ServiceCollection serviceCollection) private static void AddTaskFactories(ServiceCollection serviceCollection)
@ -184,6 +185,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton<InteractionUiController>(); serviceCollection.AddSingleton<InteractionUiController>();
serviceCollection.AddSingleton<LeveUiController>(); serviceCollection.AddSingleton<LeveUiController>();
serviceCollection.AddSingleton<ICombatModule, Mount128Module>();
serviceCollection.AddSingleton<ICombatModule, RotationSolverRebornModule>(); serviceCollection.AddSingleton<ICombatModule, RotationSolverRebornModule>();
} }
@ -235,6 +237,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceProvider.GetRequiredService<CreditsController>(); serviceProvider.GetRequiredService<CreditsController>();
serviceProvider.GetRequiredService<HelpUiController>(); serviceProvider.GetRequiredService<HelpUiController>();
serviceProvider.GetRequiredService<LeveUiController>(); serviceProvider.GetRequiredService<LeveUiController>();
serviceProvider.GetRequiredService<QuestionableIpc>();
serviceProvider.GetRequiredService<DalamudInitializer>(); serviceProvider.GetRequiredService<DalamudInitializer>();
} }

View File

@ -177,7 +177,7 @@ internal sealed partial class ActiveQuestComponent
{ {
using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow);
ImGui.TextUnformatted( ImGui.TextUnformatted(
$"Next Quest: {Shorten(currentQuest.Quest.Info.Name)} / {currentQuest.Sequence} / {currentQuest.Step}"); $"Next Quest: {Shorten(nextQuest.Quest.Info.Name)} / {nextQuest.Sequence} / {nextQuest.Step}");
} }
} }
} }