Quest automation + various fixes
This commit is contained in:
parent
1356818abe
commit
410d891f7f
@ -68,7 +68,15 @@
|
||||
"Z": -464.7746
|
||||
},
|
||||
"TerritoryId": 956,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1038716,
|
||||
@ -78,7 +86,15 @@
|
||||
"Z": -458.2132
|
||||
},
|
||||
"TerritoryId": 956,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -108,8 +124,8 @@
|
||||
"Z": -519.18823
|
||||
},
|
||||
"TerritoryId": 956,
|
||||
"InteractionType": "SinglePlayerDuty",
|
||||
"Comment": "Duty - Shoot Large Green Bird"
|
||||
"InteractionType": "WaitForManualProgress",
|
||||
"Comment": "Shoot Large Green Bird"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -126,7 +126,15 @@
|
||||
"Z": -108.537415
|
||||
},
|
||||
"TerritoryId": 956,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1038707,
|
||||
@ -136,7 +144,15 @@
|
||||
"Z": -150.04199
|
||||
},
|
||||
"TerritoryId": 956,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1038708,
|
||||
@ -146,7 +162,15 @@
|
||||
"Z": -130.11371
|
||||
},
|
||||
"TerritoryId": 956,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -35,13 +35,13 @@
|
||||
"Comment": "A Frosty Reception",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "ContentTalkList",
|
||||
"Prompt": "264",
|
||||
"Answer": "267"
|
||||
"Type": "List",
|
||||
"Prompt": 264,
|
||||
"Answer": 267
|
||||
},
|
||||
{
|
||||
"Type": "ContentTalkYesNo",
|
||||
"Prompt": "268",
|
||||
"Type": "YesNo",
|
||||
"Prompt": 268,
|
||||
"Yes": true
|
||||
}
|
||||
]
|
||||
|
@ -97,7 +97,7 @@
|
||||
},
|
||||
"TerritoryId": 960,
|
||||
"InteractionType": "WaitForManualProgress",
|
||||
"Comment": "Duty - Find Errant Omicron"
|
||||
"Comment": "Find Errant Omicron"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -113,7 +113,8 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] Aetheryte Plaza",
|
||||
"[Crystarium] The Dossal Gate"
|
||||
]
|
||||
],
|
||||
"TargetTerritoryId": 844
|
||||
},
|
||||
{
|
||||
"DataId": 1032121,
|
||||
|
@ -33,7 +33,8 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] Aetheryte Plaza",
|
||||
"[Crystarium] The Dossal Gate"
|
||||
]
|
||||
],
|
||||
"TargetTerritoryId": 844
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -82,7 +83,15 @@
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "UseItem",
|
||||
"ItemId": 2002904,
|
||||
"$.1": "QuestVariables if done first: 1 32 0 0 0 64"
|
||||
"$.1": "QuestVariables if done first: 1 32 0 0 0 64",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1027909,
|
||||
@ -94,7 +103,15 @@
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "UseItem",
|
||||
"ItemId": 2002904,
|
||||
"$.1": "QuestVariables if done after [1]: 2 16 0 0 0 96"
|
||||
"$.1": "QuestVariables if done after [1]: 2 16 0 0 0 96",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1027939,
|
||||
@ -105,7 +122,15 @@
|
||||
},
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "UseItem",
|
||||
"ItemId": 2002904
|
||||
"ItemId": 2002904,
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -28,7 +28,14 @@
|
||||
"Z": 305.19568
|
||||
},
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMG104_03676_Q2_000_100",
|
||||
"Answer": "TEXT_LUCKMG104_03676_A1_000_100"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -61,7 +68,16 @@
|
||||
"StopDistance": 1,
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "Interact",
|
||||
"$.1": "QuestVariables if done first: 16 16 16 0 0 32"
|
||||
"$.1": "QuestVariables if done first: 16 16 16 0 0 32",
|
||||
"Fly": true,
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 2010810,
|
||||
@ -72,7 +88,15 @@
|
||||
},
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "Interact",
|
||||
"$.1": "QuestVariables if done after [1]: 33 16 32 0 0 96"
|
||||
"$.1": "QuestVariables if done after [1]: 33 16 32 0 0 96",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 2010809,
|
||||
@ -83,13 +107,31 @@
|
||||
},
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "Interact",
|
||||
"Comment": "Combat not necessary to progress quest"
|
||||
"Comment": "Combat not necessary to progress quest",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Sequence": 4,
|
||||
"Steps": [
|
||||
{
|
||||
"Position": {
|
||||
"X": 47.593674,
|
||||
"Y": 42.681213,
|
||||
"Z": -511.2799
|
||||
},
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "WalkTo",
|
||||
"Comment": "Should be far enough to reset combat"
|
||||
},
|
||||
{
|
||||
"DataId": 1031732,
|
||||
"Position": {
|
||||
@ -99,7 +141,14 @@
|
||||
},
|
||||
"TerritoryId": 815,
|
||||
"InteractionType": "Interact",
|
||||
"AetheryteShortcut": "Amh Araeng - Inn at Journey's Head"
|
||||
"AetheryteShortcut": "Amh Araeng - Inn at Journey's Head",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMG104_03676_Q3_000_200",
|
||||
"Answer": "TEXT_LUCKMG104_03676_A1_000_200"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -29,7 +29,15 @@
|
||||
},
|
||||
"TerritoryId": 820,
|
||||
"InteractionType": "Interact",
|
||||
"$.1": "QuestValues if done first: 16 1 16 0 0 128"
|
||||
"$.1": "QuestValues if done first: 16 1 16 0 0 128",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1027602,
|
||||
@ -44,7 +52,15 @@
|
||||
"[Eulmore] Aetheryte Plaza",
|
||||
"[Eulmore] Southeast Derelicts"
|
||||
],
|
||||
"$.1": "QuestValues if done after [1]: 32 17 16 16 0 160"
|
||||
"$.1": "QuestValues if done after [1]: 32 17 16 16 0 160",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1029990,
|
||||
@ -58,6 +74,14 @@
|
||||
"AethernetShortcut": [
|
||||
"[Eulmore] Southeast Derelicts",
|
||||
"[Eulmore] The Glory Gate"
|
||||
],
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -44,7 +44,7 @@
|
||||
"Z": -161.45575
|
||||
},
|
||||
"TerritoryId": 814,
|
||||
"InteractionType": "SinglePlayerDuty",
|
||||
"InteractionType": "WaitForManualProgress",
|
||||
"Comment": "Help Master Chai dodge enemies"
|
||||
}
|
||||
]
|
||||
|
@ -36,7 +36,8 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] Aetheryte Plaza",
|
||||
"[Crystarium] The Dossal Gate"
|
||||
]
|
||||
],
|
||||
"TargetTerritoryId": 844
|
||||
},
|
||||
{
|
||||
"DataId": 1032121,
|
||||
@ -74,7 +75,14 @@
|
||||
},
|
||||
"TerritoryId": 351,
|
||||
"InteractionType": "SinglePlayerDuty",
|
||||
"Comment": "Estinien vs. Arch Ultima"
|
||||
"Comment": "Estinien vs. Arch Ultima",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"Prompt": "TEXT_LUCKMG110_03682_Q1_100_125",
|
||||
"Yes": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -33,7 +33,8 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] Aetheryte Plaza",
|
||||
"[Crystarium] The Dossal Gate"
|
||||
]
|
||||
],
|
||||
"TargetTerritoryId": 844
|
||||
},
|
||||
{
|
||||
"DataId": 1032121,
|
||||
|
@ -28,7 +28,8 @@
|
||||
"Z": 14.22064
|
||||
},
|
||||
"TerritoryId": 844,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"TargetTerritoryId": 819
|
||||
},
|
||||
{
|
||||
"DataId": 1030370,
|
||||
@ -38,7 +39,14 @@
|
||||
"Z": 13.321045
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMH103_03763_Q1_000_500",
|
||||
"Answer": "TEXT_LUCKMH103_03763_A1_000_500"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -33,7 +33,8 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] Aetheryte Plaza",
|
||||
"[Crystarium] The Dossal Gate"
|
||||
]
|
||||
],
|
||||
"TargetTerritoryId": 844
|
||||
},
|
||||
{
|
||||
"DataId": 1031722,
|
||||
@ -58,7 +59,8 @@
|
||||
"Z": 14.206055
|
||||
},
|
||||
"TerritoryId": 844,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"TargetTerritoryId": 819
|
||||
},
|
||||
{
|
||||
"DataId": 1027248,
|
||||
@ -71,7 +73,15 @@
|
||||
"InteractionType": "Interact",
|
||||
"Comment": "Chessamile",
|
||||
"$.0": "[1]",
|
||||
"$.1": "QuestVariables if done first: 1 0 0 0 0 64"
|
||||
"$.1": "QuestVariables if done first: 1 0 0 0 0 64",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1027224,
|
||||
@ -84,7 +94,15 @@
|
||||
"InteractionType": "Interact",
|
||||
"Comment": "Bragi",
|
||||
"$.0": "[2]",
|
||||
"$.1": "QuestVariables if done after [1]: 2 0 0 0 0 192"
|
||||
"$.1": "QuestVariables if done after [1]: 2 0 0 0 0 192",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1027322,
|
||||
@ -98,7 +116,15 @@
|
||||
"InteractionType": "Interact",
|
||||
"Comment": "Glynard",
|
||||
"$.0": "[3]",
|
||||
"$.1": "QuestVariables if done after [1, 2]: 3 0 0 0 0 200"
|
||||
"$.1": "QuestVariables if done after [1, 2]: 3 0 0 0 0 200",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
8
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1027232,
|
||||
@ -115,7 +141,15 @@
|
||||
"[Crystarium] The Crystalline Mean"
|
||||
],
|
||||
"$.0": "[4]",
|
||||
"$.1": "QuestVariables if done after [1, 2, 3]: 4 0 0 0 0 216"
|
||||
"$.1": "QuestVariables if done after [1, 2, 3]: 4 0 0 0 0 216",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
16
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1027226,
|
||||
@ -130,6 +164,14 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] The Crystalline Mean",
|
||||
"[Crystarium] The Cabinet of Curiosity"
|
||||
],
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -47,7 +47,31 @@
|
||||
"TerritoryId": 817,
|
||||
"InteractionType": "SinglePlayerDuty",
|
||||
"Fly": true,
|
||||
"Comment": "Duty - A Sleep Disturbed (Opo-Opo, Wolf, Serpent)"
|
||||
"Comment": "A Sleep Disturbed (Opo-Opo, Wolf, Serpent)",
|
||||
"$": "The dialogue choices and data ids here are recycled",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"DataId": 2011009,
|
||||
"ExcelSheet": "GimmickYesNo",
|
||||
"Prompt": 138,
|
||||
"Yes": true
|
||||
},
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"DataId": 2011006,
|
||||
"ExcelSheet": "GimmickYesNo",
|
||||
"Prompt": 139,
|
||||
"Yes": true
|
||||
},
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"DataId": 2011007,
|
||||
"ExcelSheet": "GimmickYesNo",
|
||||
"Prompt": 142,
|
||||
"Yes": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -36,6 +36,19 @@
|
||||
{
|
||||
"Sequence": 2,
|
||||
"Steps": [
|
||||
{
|
||||
"Position": {
|
||||
"X": -475.38354,
|
||||
"Y": 400.55338,
|
||||
"Z": -779.4299
|
||||
},
|
||||
"TerritoryId": 818,
|
||||
"InteractionType": "WalkTo",
|
||||
"Fly": true,
|
||||
"SkipIf": [
|
||||
"FlyingLocked"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"X": -423.6145,
|
||||
|
@ -28,7 +28,7 @@
|
||||
},
|
||||
"TerritoryId": 813,
|
||||
"InteractionType": "WalkTo",
|
||||
"AetheryteShortcut": "Lakeland - Ostall Imperative",
|
||||
"AetheryteShortcut": "Lakeland - Fort Jobb",
|
||||
"Fly": true
|
||||
},
|
||||
{
|
||||
@ -95,7 +95,15 @@
|
||||
"TerritoryId": 813,
|
||||
"InteractionType": "Interact",
|
||||
"DisableNavmesh": true,
|
||||
"$.1": "QuestVariables if done first: 1 0 0 0 0 64"
|
||||
"$.1": "QuestVariables if done first: 1 0 0 0 0 64",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 2010278,
|
||||
@ -106,7 +114,15 @@
|
||||
},
|
||||
"TerritoryId": 813,
|
||||
"InteractionType": "Interact",
|
||||
"DisableNavmesh": true
|
||||
"DisableNavmesh": true,
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 2010282,
|
||||
@ -117,7 +133,16 @@
|
||||
},
|
||||
"TerritoryId": 813,
|
||||
"InteractionType": "Interact",
|
||||
"DisableNavmesh": true
|
||||
"DisableNavmesh": true,
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
],
|
||||
"Comment": "TODO Check if pathfinding works automatically now"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"$schema": "https://carvel.li/questionable/quest-1.0",
|
||||
"Author": "liza",
|
||||
"Comment": "TODO Missing quest end",
|
||||
"TerritoryBlacklist": [
|
||||
898
|
||||
],
|
||||
@ -33,6 +32,7 @@
|
||||
},
|
||||
"TerritoryId": 814,
|
||||
"InteractionType": "Interact",
|
||||
"AetheryteShortcut": "Kholusia - Wright",
|
||||
"Fly": true
|
||||
}
|
||||
]
|
||||
@ -46,6 +46,21 @@
|
||||
"ContentFinderConditionId": 714
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Sequence": 255,
|
||||
"Steps": [
|
||||
{
|
||||
"DataId": 1032549,
|
||||
"Position": {
|
||||
"X": -1.9074707,
|
||||
"Y": -200.00002,
|
||||
"Z": -425.10114
|
||||
},
|
||||
"TerritoryId": 918,
|
||||
"InteractionType": "Interact"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -18,8 +18,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"Sequence": 1,
|
||||
"Comment": "TODO verify this is the correct sequence and/or where sequence 2 went",
|
||||
"Sequence": 2,
|
||||
"Steps": [
|
||||
{
|
||||
"DataId": 1032529,
|
||||
@ -67,7 +66,8 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] The Amaro Launch",
|
||||
"[Crystarium] The Dossal Gate"
|
||||
]
|
||||
],
|
||||
"TargetTerritoryId": 844
|
||||
},
|
||||
{
|
||||
"DataId": 1032121,
|
||||
@ -92,7 +92,8 @@
|
||||
"Z": 14.206055
|
||||
},
|
||||
"TerritoryId": 844,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"TargetTerritoryId": 819
|
||||
},
|
||||
{
|
||||
"DataId": 1030610,
|
||||
@ -107,6 +108,13 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] The Dossal Gate",
|
||||
"[Crystarium] The Pendants"
|
||||
],
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"Prompt": "TEXT_LUCKMH110_03770_Q1_000_600",
|
||||
"Yes": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -48,7 +48,14 @@
|
||||
"Z": -277.7906
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMI101_03771_Q3_000_148",
|
||||
"Answer": "TEXT_LUCKMI101_03771_A3_000_149"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -98,6 +105,14 @@
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
],
|
||||
"$.1": "QuestVariables if done first: 1 16 0 0 0 64"
|
||||
},
|
||||
{
|
||||
@ -110,6 +125,14 @@
|
||||
"StopDistance": 5,
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
],
|
||||
"$.1": "QuestVariables if done after [1]: 2 32 0 0 0 192"
|
||||
},
|
||||
{
|
||||
@ -120,7 +143,15 @@
|
||||
"Z": 173.38818
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -142,15 +173,6 @@
|
||||
{
|
||||
"Sequence": 255,
|
||||
"Steps": [
|
||||
{
|
||||
"Position": {
|
||||
"X": -140.22343,
|
||||
"Y": 0.05337119,
|
||||
"Z": 34.20123
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "WalkTo"
|
||||
},
|
||||
{
|
||||
"DataId": 1027248,
|
||||
"Position": {
|
||||
@ -159,7 +181,8 @@
|
||||
"Z": -51.438232
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"AetheryteShortcut": "Crystarium"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
},
|
||||
{
|
||||
"Sequence": 2,
|
||||
"Comment": "This isn't solving for the 'best' results, but for the closest waypoints",
|
||||
"Steps": [
|
||||
{
|
||||
"DataId": 2011078,
|
||||
@ -56,7 +57,15 @@
|
||||
],
|
||||
"Fly": true,
|
||||
"$.0": "[1]",
|
||||
"$.1": "QuestVariables if done first: 0 0 0 3 0 0 during fight; 16 1 0 2 16 8 after"
|
||||
"$.1": "QuestVariables if done first: 0 0 0 3 0 0 during fight; 16 1 0 2 16 8 after",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
8
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 2011076,
|
||||
@ -69,10 +78,19 @@
|
||||
"InteractionType": "Combat",
|
||||
"EnemySpawnType": "AfterItemUse",
|
||||
"ItemId": 2003001,
|
||||
"KillEnemyDataIds": [],
|
||||
"Comment": "TODO Missing enemy ids",
|
||||
"KillEnemyDataIds": [
|
||||
12166
|
||||
],
|
||||
"Fly": true,
|
||||
"$.1": "QuestVariables if done after [1]: 34 1 0 1 48 40"
|
||||
"$.1": "QuestVariables if done after [1]: 34 1 0 1 48 40",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 2011079,
|
||||
@ -86,10 +104,11 @@
|
||||
"EnemySpawnType": "AfterItemUse",
|
||||
"ItemId": 2003001,
|
||||
"KillEnemyDataIds": [
|
||||
12168
|
||||
],
|
||||
"Comment": "TODO Missing enemy ids",
|
||||
"Fly": true,
|
||||
"$.2": "QuestVariables if done after [1, 2]: 0 64 0 0 0 0"
|
||||
"$.2": "QuestVariables if done after [1, 2]: irrelevant because it automatically progresses to the next step"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -79,7 +79,8 @@
|
||||
"AethernetShortcut": [
|
||||
"[Crystarium] Aetheryte Plaza",
|
||||
"[Crystarium] The Dossal Gate"
|
||||
]
|
||||
],
|
||||
"TargetTerritoryId": 844
|
||||
},
|
||||
{
|
||||
"DataId": 1033819,
|
||||
|
@ -42,7 +42,14 @@
|
||||
"Z": 604.27246
|
||||
},
|
||||
"TerritoryId": 814,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"Prompt": "TEXT_LUCKMI105_03775_Q2_000_052",
|
||||
"Yes": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -16,12 +16,19 @@
|
||||
"Z": 1.6021729
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMI108_03778_Q1_000_001",
|
||||
"Answer": "TEXT_LUCKMI108_03778_A1_000_002"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Sequence": 1,
|
||||
"Sequence": 2,
|
||||
"Steps": [
|
||||
{
|
||||
"TerritoryId": 931,
|
||||
|
@ -85,8 +85,13 @@
|
||||
"Sequence": 5,
|
||||
"Steps": [
|
||||
{
|
||||
"Position": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"TerritoryId": 820,
|
||||
"InteractionType": "Interact",
|
||||
"InteractionType": "WalkTo",
|
||||
"AetheryteShortcut": "Eulmore"
|
||||
}
|
||||
]
|
||||
|
@ -75,7 +75,15 @@
|
||||
"Z": 3.982544
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"AetheryteShortcut": "Crystarium",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMI111_03781_Q1_000_153",
|
||||
"Answer": "TEXT_LUCKMI111_03781_A1_000_154"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -29,7 +29,14 @@
|
||||
"Z": 7.156433
|
||||
},
|
||||
"TerritoryId": 819,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"Prompt": "TEXT_LUCKMI112_03782_Q1_000_007",
|
||||
"Yes": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1033888,
|
||||
@ -39,7 +46,14 @@
|
||||
"Z": -5.081299
|
||||
},
|
||||
"TerritoryId": 844,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"Prompt": "TEXT_LUCKMI112_03782_Q2_000_044",
|
||||
"Yes": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -64,7 +64,8 @@
|
||||
"EnemySpawnType": "AfterInteraction",
|
||||
"KillEnemyDataIds": [
|
||||
12661
|
||||
]
|
||||
],
|
||||
"Fly": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -55,7 +55,8 @@
|
||||
"Z": -6.9733887
|
||||
},
|
||||
"TerritoryId": 351,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"TargetTerritoryId": 351
|
||||
},
|
||||
{
|
||||
"DataId": 2011332,
|
||||
|
@ -14,7 +14,14 @@
|
||||
},
|
||||
"StopDistance": 5,
|
||||
"TerritoryId": 351,
|
||||
"InteractionType": "Interact"
|
||||
"InteractionType": "Interact",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMJ104_04010_Q1_000_000",
|
||||
"Answer": "TEXT_LUCKMJ104_04010_A1_000_002"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -29,7 +29,14 @@
|
||||
},
|
||||
"TerritoryId": 129,
|
||||
"InteractionType": "Interact",
|
||||
"AetheryteShortcut": "Limsa Lominsa"
|
||||
"AetheryteShortcut": "Limsa Lominsa",
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "YesNo",
|
||||
"Prompt": "TEXT_LUCKMJ108_04014_SYSTEM_100_010",
|
||||
"Yes": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1002694,
|
||||
|
@ -39,6 +39,26 @@
|
||||
{
|
||||
"Sequence": 2,
|
||||
"Steps": [
|
||||
{
|
||||
"Position": {
|
||||
"X": 46.600548,
|
||||
"Y": 77.45801,
|
||||
"Z": -366.82053
|
||||
},
|
||||
"TerritoryId": 180,
|
||||
"InteractionType": "WalkTo",
|
||||
"Fly": true
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"X": 111.927666,
|
||||
"Y": 26.050894,
|
||||
"Z": -612.8873
|
||||
},
|
||||
"TerritoryId": 180,
|
||||
"InteractionType": "WalkTo",
|
||||
"Fly": true
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"X": 82.19566,
|
||||
|
@ -79,7 +79,15 @@
|
||||
"TerritoryId": 402,
|
||||
"InteractionType": "Interact",
|
||||
"Fly": true,
|
||||
"$.1": "QuestVariables if done first: 1 16 0 0 0 64"
|
||||
"$.1": "QuestVariables if done first: 1 16 0 0 0 64",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
64
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1036359,
|
||||
@ -92,7 +100,15 @@
|
||||
"InteractionType": "Interact",
|
||||
"Mount": true,
|
||||
"Fly": true,
|
||||
"$.1": "QuestVariables if done after [1]: 2 16 0 0 0 96"
|
||||
"$.1": "QuestVariables if done after [1]: 2 16 0 0 0 96",
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"DataId": 1036357,
|
||||
@ -103,7 +119,15 @@
|
||||
},
|
||||
"TerritoryId": 402,
|
||||
"InteractionType": "Interact",
|
||||
"DisableNavmesh": true
|
||||
"DisableNavmesh": true,
|
||||
"CompletionQuestVariablesFlags": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
128
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -119,7 +143,14 @@
|
||||
},
|
||||
"TerritoryId": 402,
|
||||
"InteractionType": "Interact",
|
||||
"Fly": true
|
||||
"Fly": true,
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMK103_04060_Q1_000_100",
|
||||
"Answer": "TEXT_LUCKMK103_04060_A2_000_100"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -33,6 +33,13 @@
|
||||
"AethernetShortcut": [
|
||||
"[Ul'dah] Aetheryte Plaza",
|
||||
"[Ul'dah] Alchemists' Guild"
|
||||
],
|
||||
"DialogueChoices": [
|
||||
{
|
||||
"Type": "List",
|
||||
"Prompt": "TEXT_LUCKMK106_04063_Q1_000_100",
|
||||
"Answer": "TEXT_LUCKMK106_04063_A2_000_100"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -672,49 +672,40 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"YesNo",
|
||||
"List",
|
||||
"ContentTalkYesNo",
|
||||
"ContentTalkList"
|
||||
"List"
|
||||
]
|
||||
},
|
||||
"ExcelSheet": {
|
||||
"type": "string"
|
||||
},
|
||||
"Prompt": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Type",
|
||||
"Prompt"
|
||||
"Type"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"Type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"const": "YesNo"
|
||||
},
|
||||
{
|
||||
"const": "ContentTalkYesNo"
|
||||
}
|
||||
]
|
||||
"const": "YesNo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"Prompt": {
|
||||
"type": [
|
||||
"string",
|
||||
"integer"
|
||||
]
|
||||
},
|
||||
"Yes": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Prompt",
|
||||
"Yes"
|
||||
]
|
||||
}
|
||||
@ -723,24 +714,28 @@
|
||||
"if": {
|
||||
"properties": {
|
||||
"Type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"const": "List"
|
||||
},
|
||||
{
|
||||
"const": "ContentTalkList"
|
||||
}
|
||||
]
|
||||
"const": "List"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"Prompt": {
|
||||
"type": [
|
||||
"string",
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"Answer": {
|
||||
"type": "string"
|
||||
"type": [
|
||||
"string",
|
||||
"integer"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Prompt",
|
||||
"Answer"
|
||||
]
|
||||
}
|
||||
|
@ -6,5 +6,19 @@ namespace Questionable;
|
||||
internal sealed class Configuration : IPluginConfiguration
|
||||
{
|
||||
public int Version { get; set; } = 1;
|
||||
public WindowConfig DebugWindowConfig { get; set; } = new();
|
||||
public GeneralConfiguration General { get; } = new();
|
||||
public AdvancedConfiguration Advanced { get; } = new();
|
||||
public WindowConfig DebugWindowConfig { get; } = new();
|
||||
public WindowConfig ConfigWindowConfig { get; } = new();
|
||||
|
||||
internal sealed class GeneralConfiguration
|
||||
{
|
||||
public bool AutoAcceptNextQuest { get; set; }
|
||||
public uint MountId { get; set; } = 71;
|
||||
}
|
||||
|
||||
internal sealed class AdvancedConfiguration
|
||||
{
|
||||
public bool NeverFly { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
@ -23,16 +24,19 @@ internal sealed class GameUiController : IDisposable
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
private readonly QuestController _questController;
|
||||
private readonly IGameGui _gameGui;
|
||||
private readonly ITargetManager _targetManager;
|
||||
private readonly ILogger<GameUiController> _logger;
|
||||
|
||||
public GameUiController(IAddonLifecycle addonLifecycle, IDataManager dataManager, GameFunctions gameFunctions,
|
||||
QuestController questController, IGameGui gameGui, ILogger<GameUiController> logger)
|
||||
QuestController questController, IGameGui gameGui, ITargetManager targetManager,
|
||||
ILogger<GameUiController> logger)
|
||||
{
|
||||
_addonLifecycle = addonLifecycle;
|
||||
_dataManager = dataManager;
|
||||
_gameFunctions = gameFunctions;
|
||||
_questController = questController;
|
||||
_gameGui = gameGui;
|
||||
_targetManager = targetManager;
|
||||
_logger = logger;
|
||||
|
||||
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
|
||||
@ -181,40 +185,26 @@ internal sealed class GameUiController : IDisposable
|
||||
|
||||
foreach (var dialogueChoice in dialogueChoices)
|
||||
{
|
||||
if (dialogueChoice.Type != EDialogChoiceType.List)
|
||||
continue;
|
||||
|
||||
if (dialogueChoice.Answer == null)
|
||||
{
|
||||
_logger.LogInformation("Ignoring entry in DialogueChoices, no answer");
|
||||
_logger.LogDebug("Ignoring entry in DialogueChoices, no answer");
|
||||
continue;
|
||||
}
|
||||
|
||||
string? excelPrompt = null, excelAnswer;
|
||||
switch (dialogueChoice.Type)
|
||||
if (dialogueChoice.DataId != null && dialogueChoice.DataId != _targetManager.Target?.DataId)
|
||||
{
|
||||
case EDialogChoiceType.ContentTalkList:
|
||||
if (dialogueChoice.Prompt != null)
|
||||
{
|
||||
excelPrompt =
|
||||
_gameFunctions.GetContentTalk(uint.Parse(dialogueChoice.Prompt,
|
||||
CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
excelAnswer =
|
||||
_gameFunctions.GetContentTalk(uint.Parse(dialogueChoice.Answer, CultureInfo.InvariantCulture));
|
||||
break;
|
||||
case EDialogChoiceType.List:
|
||||
if (dialogueChoice.Prompt != null)
|
||||
{
|
||||
excelPrompt =
|
||||
_gameFunctions.GetDialogueText(quest, dialogueChoice.ExcelSheet, dialogueChoice.Prompt);
|
||||
}
|
||||
|
||||
excelAnswer =
|
||||
_gameFunctions.GetDialogueText(quest, dialogueChoice.ExcelSheet, dialogueChoice.Answer);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
_logger.LogDebug(
|
||||
"Skipping entry in DialogueChoice expecting target dataId {ExpectedDataId}, actual target is {ActualTargetId}",
|
||||
dialogueChoice.DataId, _targetManager.Target?.DataId);
|
||||
continue;
|
||||
}
|
||||
|
||||
string? excelPrompt = ResolveReference(quest, dialogueChoice.ExcelSheet, dialogueChoice.Prompt);
|
||||
string? excelAnswer = ResolveReference(quest, dialogueChoice.ExcelSheet, dialogueChoice.Answer);
|
||||
|
||||
if (actualPrompt == null && !string.IsNullOrEmpty(excelPrompt))
|
||||
{
|
||||
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}", excelPrompt);
|
||||
@ -288,30 +278,25 @@ internal sealed class GameUiController : IDisposable
|
||||
_logger.LogTrace("DefaultYesNo: Choice count: {Count}", dialogueChoices.Count);
|
||||
foreach (var dialogueChoice in dialogueChoices)
|
||||
{
|
||||
string? excelPrompt;
|
||||
if (dialogueChoice.Prompt != null)
|
||||
{
|
||||
switch (dialogueChoice.Type)
|
||||
{
|
||||
case EDialogChoiceType.ContentTalkYesNo:
|
||||
excelPrompt =
|
||||
_gameFunctions.GetContentTalk(uint.Parse(dialogueChoice.Prompt,
|
||||
CultureInfo.InvariantCulture));
|
||||
break;
|
||||
case EDialogChoiceType.YesNo:
|
||||
excelPrompt =
|
||||
_gameFunctions.GetDialogueText(quest, dialogueChoice.ExcelSheet, dialogueChoice.Prompt);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
excelPrompt = null;
|
||||
|
||||
if (excelPrompt == null || !GameStringEquals(actualPrompt, excelPrompt))
|
||||
if (dialogueChoice.Type != EDialogChoiceType.YesNo)
|
||||
continue;
|
||||
|
||||
if (dialogueChoice.DataId != null && dialogueChoice.DataId != _targetManager.Target?.DataId)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
"Skipping entry in DialogueChoice expecting target dataId {ExpectedDataId}, actual target is {ActualTargetId}",
|
||||
dialogueChoice.DataId, _targetManager.Target?.DataId);
|
||||
continue;
|
||||
}
|
||||
|
||||
string? excelPrompt = ResolveReference(quest, dialogueChoice.ExcelSheet, dialogueChoice.Prompt);
|
||||
if (excelPrompt == null || !GameStringEquals(actualPrompt, excelPrompt))
|
||||
{
|
||||
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}, actualPrompt: {ActualPrompt}",
|
||||
excelPrompt, actualPrompt);
|
||||
continue;
|
||||
}
|
||||
|
||||
addonSelectYesno->AtkUnitBase.FireCallbackInt(dialogueChoice.Yes ? 0 : 1);
|
||||
if (!checkAllSteps)
|
||||
_questController.IncreaseDialogueChoicesSelected();
|
||||
@ -343,7 +328,8 @@ internal sealed class GameUiController : IDisposable
|
||||
increaseStepCount = false;
|
||||
|
||||
if (step != null)
|
||||
_logger.LogTrace("Previous step: {CurrentTerritory}, {TargetTerritory}", step.TerritoryId, step.TargetTerritoryId);
|
||||
_logger.LogTrace("Previous step: {CurrentTerritory}, {TargetTerritory}", step.TerritoryId,
|
||||
step.TargetTerritoryId);
|
||||
}
|
||||
|
||||
if (step == null || step.TargetTerritoryId == null)
|
||||
@ -367,7 +353,7 @@ internal sealed class GameUiController : IDisposable
|
||||
_logger.LogInformation("Using warp {Id}, {Prompt}", entry.RowId, excelPrompt);
|
||||
addonSelectYesno->AtkUnitBase.FireCallbackInt(0);
|
||||
//if (increaseStepCount)
|
||||
//_questController.IncreaseStepCount();
|
||||
//_questController.IncreaseStepCount();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -403,6 +389,19 @@ internal sealed class GameUiController : IDisposable
|
||||
return a.ReplaceLineEndings().Replace('\u2013', '-') == b.ReplaceLineEndings().Replace('\u2013', '-');
|
||||
}
|
||||
|
||||
private string? ResolveReference(Quest quest, string? excelSheet, ExcelRef? excelRef)
|
||||
{
|
||||
if (excelRef == null)
|
||||
return null;
|
||||
|
||||
if (excelRef.Type == ExcelRef.EType.Key)
|
||||
return _gameFunctions.GetDialogueText(quest, excelSheet, excelRef.AsKey());
|
||||
else if (excelRef.Type == ExcelRef.EType.RowId)
|
||||
return _gameFunctions.GetDialogueTextByRowId(excelSheet, excelRef.AsRowId());
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
|
||||
|
@ -76,7 +76,7 @@ internal sealed class MovementController : IDisposable
|
||||
}
|
||||
}
|
||||
else if (!Destination.IsFlying && !_condition[ConditionFlag.Mounted] && navPoints.Count > 0 &&
|
||||
!_gameFunctions.HasStatusPreventingSprintOrMount() && Destination.CanSprint)
|
||||
!_gameFunctions.HasStatusPreventingSprintOrMount(true) && Destination.CanSprint)
|
||||
{
|
||||
float actualDistance = 0;
|
||||
foreach (Vector3 end in navPoints)
|
||||
@ -210,7 +210,7 @@ internal sealed class MovementController : IDisposable
|
||||
_logger.LogInformation("Pathfinding to {Destination}", Destination);
|
||||
|
||||
_cancellationTokenSource = new();
|
||||
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
|
||||
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(30));
|
||||
_pathfindTask =
|
||||
_navmeshIpc.Pathfind(_clientState.LocalPlayer!.Position, to, fly, _cancellationTokenSource.Token);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller.Steps;
|
||||
using Questionable.Data;
|
||||
@ -28,6 +29,7 @@ internal sealed class QuestController
|
||||
private readonly ILogger<QuestController> _logger;
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
private readonly IKeyState _keyState;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly IReadOnlyList<ITaskFactory> _taskFactories;
|
||||
|
||||
private readonly Queue<ITask> _taskQueue = new();
|
||||
@ -41,6 +43,7 @@ internal sealed class QuestController
|
||||
ILogger<QuestController> logger,
|
||||
QuestRegistry questRegistry,
|
||||
IKeyState keyState,
|
||||
Configuration configuration,
|
||||
IEnumerable<ITaskFactory> taskFactories)
|
||||
{
|
||||
_clientState = clientState;
|
||||
@ -49,6 +52,7 @@ internal sealed class QuestController
|
||||
_logger = logger;
|
||||
_questRegistry = questRegistry;
|
||||
_keyState = keyState;
|
||||
_configuration = configuration;
|
||||
_taskFactories = taskFactories.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
@ -79,6 +83,20 @@ internal sealed class QuestController
|
||||
if (CurrentQuest != null && CurrentQuest.Quest.Data.TerritoryBlacklist.Contains(_clientState.TerritoryType))
|
||||
return;
|
||||
|
||||
// not verified to work
|
||||
if (_automatic && _currentTask == null && _taskQueue.Count == 0 && CurrentQuest is { Sequence: 0, Step: 255 }
|
||||
&& DateTime.Now >= CurrentQuest.StepProgress.StartedAt.AddSeconds(15))
|
||||
{
|
||||
_logger.LogWarning("Quest accept apparently didn't work out, resetting progress");
|
||||
CurrentQuest = CurrentQuest with
|
||||
{
|
||||
Step = 0
|
||||
};
|
||||
|
||||
ExecuteNextStep(true);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateCurrentTask();
|
||||
}
|
||||
|
||||
@ -102,7 +120,13 @@ internal sealed class QuestController
|
||||
{
|
||||
_logger.LogInformation("New quest: {QuestName}", quest.Name);
|
||||
CurrentQuest = new QuestProgress(quest, currentSequence, 0);
|
||||
Stop("Different Quest");
|
||||
|
||||
bool continueAutomatically = _configuration.General.AutoAcceptNextQuest;
|
||||
|
||||
if (_clientState.LocalPlayer?.Level < quest.Level)
|
||||
continueAutomatically = false;
|
||||
|
||||
Stop("Different Quest", continueAutomatically);
|
||||
}
|
||||
else if (CurrentQuest != null)
|
||||
{
|
||||
@ -208,7 +232,7 @@ internal sealed class QuestController
|
||||
CurrentQuest = CurrentQuest with
|
||||
{
|
||||
Step = CurrentQuest.Step + 1,
|
||||
StepProgress = new()
|
||||
StepProgress = new(DateTime.Now),
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -216,7 +240,7 @@ internal sealed class QuestController
|
||||
CurrentQuest = CurrentQuest with
|
||||
{
|
||||
Step = 255,
|
||||
StepProgress = new()
|
||||
StepProgress = new(DateTime.Now),
|
||||
};
|
||||
}
|
||||
|
||||
@ -416,6 +440,8 @@ internal sealed class QuestController
|
||||
public bool HasCurrentTaskMatching<T>() =>
|
||||
_currentTask is T;
|
||||
|
||||
public bool IsRunning => _currentTask != null || _taskQueue.Count > 0;
|
||||
|
||||
public sealed record QuestProgress(
|
||||
Quest Quest,
|
||||
byte Sequence,
|
||||
@ -423,12 +449,13 @@ internal sealed class QuestController
|
||||
StepProgress StepProgress)
|
||||
{
|
||||
public QuestProgress(Quest quest, byte sequence, int step)
|
||||
: this(quest, sequence, step, new StepProgress())
|
||||
: this(quest, sequence, step, new StepProgress(DateTime.Now))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// TODO is this still required?
|
||||
public sealed record StepProgress(
|
||||
DateTime StartedAt,
|
||||
int DialogueChoicesSelected = 0);
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ internal sealed class QuestRegistry
|
||||
continue;
|
||||
|
||||
quest.Name = questData.Name.ToString();
|
||||
quest.Level = questData.ClassJobLevel0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,13 @@ internal static class AethernetShortcut
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
|
||||
if (aetheryteData.IsCityAetheryte(To))
|
||||
if (aetheryteData.IsAirshipLanding(To))
|
||||
{
|
||||
if (aetheryteData.CalculateAirshipLandingDistance(clientState.LocalPlayer?.Position ?? Vector3.Zero,
|
||||
clientState.TerritoryType, To) > 5)
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
else if (aetheryteData.IsCityAetheryte(To))
|
||||
{
|
||||
if (aetheryteData.CalculateDistance(clientState.LocalPlayer?.Position ?? Vector3.Zero,
|
||||
clientState.TerritoryType, To) > 11)
|
||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Questionable.Controller.Steps.BaseTasks;
|
||||
using Questionable.Model;
|
||||
@ -23,7 +24,7 @@ internal static class WaitAtEnd
|
||||
var task = serviceProvider.GetRequiredService<WaitForCompletionFlags>()
|
||||
.With(quest, step);
|
||||
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
||||
return [task, delay, new NextStep()];
|
||||
return [task, delay, Next(quest, sequence, step)];
|
||||
}
|
||||
|
||||
switch (step.InteractionType)
|
||||
@ -41,7 +42,7 @@ internal static class WaitAtEnd
|
||||
case EInteractionType.WalkTo:
|
||||
case EInteractionType.Jump:
|
||||
// no need to wait if we're just moving around
|
||||
return [new NextStep()];
|
||||
return [Next(quest, sequence, step)];
|
||||
|
||||
case EInteractionType.WaitForObjectAtPosition:
|
||||
ArgumentNullException.ThrowIfNull(step.DataId);
|
||||
@ -52,7 +53,7 @@ internal static class WaitAtEnd
|
||||
serviceProvider.GetRequiredService<WaitObjectAtPosition>()
|
||||
.With(step.DataId.Value, step.Position.Value),
|
||||
serviceProvider.GetRequiredService<WaitDelay>(),
|
||||
new NextStep()
|
||||
Next(quest, sequence, step)
|
||||
];
|
||||
|
||||
case EInteractionType.Interact when step.TargetTerritoryId != null:
|
||||
@ -81,17 +82,40 @@ internal static class WaitAtEnd
|
||||
[
|
||||
waitInteraction,
|
||||
serviceProvider.GetRequiredService<WaitDelay>(),
|
||||
new NextStep()
|
||||
Next(quest, sequence, step)
|
||||
];
|
||||
|
||||
case EInteractionType.Interact:
|
||||
default:
|
||||
return [serviceProvider.GetRequiredService<WaitDelay>(), new NextStep()];
|
||||
return [serviceProvider.GetRequiredService<WaitDelay>(), Next(quest, sequence, step)];
|
||||
}
|
||||
}
|
||||
|
||||
public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
=> throw new InvalidOperationException();
|
||||
|
||||
public ITask Next(Quest quest, QuestSequence sequence, QuestStep step)
|
||||
{
|
||||
bool lastStep = step == sequence.Steps.LastOrDefault();
|
||||
if (sequence.Sequence == 0 && lastStep)
|
||||
{
|
||||
return new WaitConditionTask(() =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var questManager = QuestManager.Instance();
|
||||
return questManager != null && questManager->IsQuestAccepted(quest.QuestId);
|
||||
}
|
||||
}, "Wait(questAccepted)");
|
||||
}
|
||||
else if (sequence.Sequence == 255 && lastStep)
|
||||
{
|
||||
return new WaitConditionTask(() => QuestManager.IsQuestComplete(quest.QuestId),
|
||||
"Wait(questComplete)");
|
||||
}
|
||||
else
|
||||
return new NextStep();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class WaitDelay() : AbstractDelayedTask(TimeSpan.FromSeconds(1))
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using System;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@ -8,6 +9,7 @@ internal sealed class UnmountTask(ICondition condition, ILogger<UnmountTask> log
|
||||
: ITask
|
||||
{
|
||||
private bool _unmountTriggered;
|
||||
private DateTime _unmountedAt = DateTime.MinValue;
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
@ -16,6 +18,8 @@ internal sealed class UnmountTask(ICondition condition, ILogger<UnmountTask> log
|
||||
|
||||
logger.LogInformation("Step explicitly wants no mount, trying to unmount...");
|
||||
_unmountTriggered = gameFunctions.Unmount();
|
||||
if (_unmountTriggered)
|
||||
_unmountedAt = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -24,9 +28,15 @@ internal sealed class UnmountTask(ICondition condition, ILogger<UnmountTask> log
|
||||
if (!_unmountTriggered)
|
||||
{
|
||||
_unmountTriggered = gameFunctions.Unmount();
|
||||
if (_unmountTriggered)
|
||||
_unmountedAt = DateTime.Now;
|
||||
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
|
||||
if (DateTime.Now < _unmountedAt.AddSeconds(1))
|
||||
return ETaskResult.StillRunning;
|
||||
|
||||
return condition[ConditionFlag.Mounted]
|
||||
? ETaskResult.StillRunning
|
||||
: ETaskResult.TaskComplete;
|
||||
|
@ -45,8 +45,38 @@ internal static class UseItem
|
||||
=> throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
internal abstract class UseItemBase : ITask
|
||||
{
|
||||
private bool _usedItem;
|
||||
private DateTime _continueAt;
|
||||
|
||||
internal sealed class UseOnGround(GameFunctions gameFunctions) : AbstractDelayedTask
|
||||
protected abstract bool UseItem();
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
_usedItem = UseItem();
|
||||
_continueAt = DateTime.Now.AddSeconds(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
public ETaskResult Update()
|
||||
{
|
||||
if (DateTime.Now > _continueAt)
|
||||
return ETaskResult.StillRunning;
|
||||
|
||||
if (!_usedItem)
|
||||
{
|
||||
_usedItem = UseItem();
|
||||
_continueAt = DateTime.Now.AddSeconds(2);
|
||||
return ETaskResult.StillRunning;
|
||||
}
|
||||
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal sealed class UseOnGround(GameFunctions gameFunctions) : UseItemBase
|
||||
{
|
||||
public uint DataId { get; set; }
|
||||
public uint ItemId { get; set; }
|
||||
@ -58,16 +88,12 @@ internal static class UseItem
|
||||
return this;
|
||||
}
|
||||
|
||||
protected override bool StartInternal()
|
||||
{
|
||||
gameFunctions.UseItemOnGround(DataId, ItemId);
|
||||
return true;
|
||||
}
|
||||
protected override bool UseItem() => gameFunctions.UseItemOnGround(DataId, ItemId);
|
||||
|
||||
public override string ToString() => $"UseItem({ItemId} on ground at {DataId})";
|
||||
}
|
||||
|
||||
internal sealed class UseOnObject(GameFunctions gameFunctions) : AbstractDelayedTask
|
||||
internal sealed class UseOnObject(GameFunctions gameFunctions) : UseItemBase
|
||||
{
|
||||
public uint DataId { get; set; }
|
||||
public uint ItemId { get; set; }
|
||||
@ -79,16 +105,12 @@ internal static class UseItem
|
||||
return this;
|
||||
}
|
||||
|
||||
protected override bool StartInternal()
|
||||
{
|
||||
gameFunctions.UseItem(DataId, ItemId);
|
||||
return true;
|
||||
}
|
||||
protected override bool UseItem() => gameFunctions.UseItem(DataId, ItemId);
|
||||
|
||||
public override string ToString() => $"UseItem({ItemId} on {DataId})";
|
||||
}
|
||||
|
||||
internal sealed class Use(GameFunctions gameFunctions) : AbstractDelayedTask
|
||||
internal sealed class Use(GameFunctions gameFunctions) : UseItemBase
|
||||
{
|
||||
public uint ItemId { get; set; }
|
||||
|
||||
@ -98,11 +120,7 @@ internal static class UseItem
|
||||
return this;
|
||||
}
|
||||
|
||||
protected override bool StartInternal()
|
||||
{
|
||||
gameFunctions.UseItem(ItemId);
|
||||
return true;
|
||||
}
|
||||
protected override bool UseItem() => gameFunctions.UseItem(ItemId);
|
||||
|
||||
public override string ToString() => $"UseItem({ItemId})";
|
||||
}
|
||||
|
@ -18,10 +18,12 @@ internal sealed class DalamudInitializer : IDisposable
|
||||
private readonly NavigationShortcutController _navigationShortcutController;
|
||||
private readonly WindowSystem _windowSystem;
|
||||
private readonly DebugWindow _debugWindow;
|
||||
private readonly ConfigWindow _configWindow;
|
||||
|
||||
public DalamudInitializer(DalamudPluginInterface pluginInterface, IFramework framework,
|
||||
ICommandManager commandManager, QuestController questController, MovementController movementController,
|
||||
GameUiController gameUiController, NavigationShortcutController navigationShortcutController, WindowSystem windowSystem, DebugWindow debugWindow)
|
||||
GameUiController gameUiController, NavigationShortcutController navigationShortcutController,
|
||||
WindowSystem windowSystem, DebugWindow debugWindow, ConfigWindow configWindow)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_framework = framework;
|
||||
@ -31,9 +33,14 @@ internal sealed class DalamudInitializer : IDisposable
|
||||
_navigationShortcutController = navigationShortcutController;
|
||||
_windowSystem = windowSystem;
|
||||
_debugWindow = debugWindow;
|
||||
_configWindow = configWindow;
|
||||
|
||||
_windowSystem.AddWindow(debugWindow);
|
||||
_windowSystem.AddWindow(configWindow);
|
||||
|
||||
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
||||
_pluginInterface.UiBuilder.OpenMainUi += _debugWindow.Toggle;
|
||||
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
|
||||
_framework.Update += FrameworkUpdate;
|
||||
_commandManager.AddHandler("/qst", new CommandInfo(ProcessCommand)
|
||||
{
|
||||
@ -60,7 +67,10 @@ internal sealed class DalamudInitializer : IDisposable
|
||||
|
||||
private void ProcessCommand(string command, string arguments)
|
||||
{
|
||||
_debugWindow.Toggle();
|
||||
if (arguments is "c" or "config")
|
||||
_configWindow.Toggle();
|
||||
else
|
||||
_debugWindow.Toggle();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@ -69,5 +79,7 @@ internal sealed class DalamudInitializer : IDisposable
|
||||
_framework.Update -= FrameworkUpdate;
|
||||
_pluginInterface.UiBuilder.OpenMainUi -= _debugWindow.Toggle;
|
||||
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
||||
|
||||
_windowSystem.RemoveAllWindows();
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +217,18 @@ internal sealed class AetheryteData
|
||||
}
|
||||
.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Airship landings are special as they're one-way only (except for Radz-at-Han, which is a normal aetheryte).
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<EAetheryteLocation, Vector3> AirshipLandingLocations { get; } =
|
||||
new Dictionary<EAetheryteLocation, Vector3>
|
||||
{
|
||||
{ EAetheryteLocation.LimsaAirship, new(-19.44352f, 91.99999f, -9.892939f) },
|
||||
{ EAetheryteLocation.GridaniaAirship, new(24.86354f, -19.000002f, 96f) },
|
||||
{ EAetheryteLocation.UldahAirship, new(-16.954851f, 82.999985f, -9.421141f) },
|
||||
{ EAetheryteLocation.KuganeAirship, new(-55.72525f, 79.10602f, 46.23109f) },
|
||||
}.AsReadOnly();
|
||||
|
||||
public ReadOnlyDictionary<EAetheryteLocation, string> AethernetNames { get; }
|
||||
public ReadOnlyDictionary<EAetheryteLocation, ushort> TerritoryIds { get; }
|
||||
public IReadOnlyList<ushort> TownTerritoryIds { get; set; }
|
||||
@ -232,9 +244,22 @@ internal sealed class AetheryteData
|
||||
return (fromPosition - toPosition).Length();
|
||||
}
|
||||
|
||||
public float CalculateAirshipLandingDistance(Vector3 fromPosition, ushort fromTerritoryType, EAetheryteLocation to)
|
||||
{
|
||||
if (!TerritoryIds.TryGetValue(to, out ushort toTerritoryType) || fromTerritoryType != toTerritoryType)
|
||||
return float.MaxValue;
|
||||
|
||||
if (!AirshipLandingLocations.TryGetValue(to, out Vector3 toPosition))
|
||||
return float.MaxValue;
|
||||
|
||||
return (fromPosition - toPosition).Length();
|
||||
}
|
||||
|
||||
public bool IsCityAetheryte(EAetheryteLocation aetheryte)
|
||||
{
|
||||
var territoryId = TerritoryIds[aetheryte];
|
||||
return TownTerritoryIds.Contains(territoryId);
|
||||
}
|
||||
|
||||
public bool IsAirshipLanding(EAetheryteLocation aetheryte) => AirshipLandingLocations.ContainsKey(aetheryte);
|
||||
}
|
||||
|
@ -23,13 +23,17 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using LLib.GameUI;
|
||||
using Lumina.Excel.CustomSheets;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Lumina.Excel.GeneratedSheets2;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller;
|
||||
using Questionable.Model.V1;
|
||||
using BattleChara = FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara;
|
||||
using ContentFinderCondition = Lumina.Excel.GeneratedSheets.ContentFinderCondition;
|
||||
using ContentTalk = Lumina.Excel.GeneratedSheets.ContentTalk;
|
||||
using Emote = Lumina.Excel.GeneratedSheets.Emote;
|
||||
using GameObject = Dalamud.Game.ClientState.Objects.Types.GameObject;
|
||||
using Quest = Questionable.Model.Quest;
|
||||
using TerritoryType = Lumina.Excel.GeneratedSheets.TerritoryType;
|
||||
|
||||
namespace Questionable;
|
||||
|
||||
@ -56,11 +60,12 @@ internal sealed unsafe class GameFunctions
|
||||
private readonly IClientState _clientState;
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
private readonly IGameGui _gameGui;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly ILogger<GameFunctions> _logger;
|
||||
|
||||
public GameFunctions(IDataManager dataManager, IObjectTable objectTable, ISigScanner sigScanner,
|
||||
ITargetManager targetManager, ICondition condition, IClientState clientState, QuestRegistry questRegistry,
|
||||
IGameGui gameGui, ILogger<GameFunctions> logger)
|
||||
IGameGui gameGui, Configuration configuration, ILogger<GameFunctions> logger)
|
||||
{
|
||||
_dataManager = dataManager;
|
||||
_objectTable = objectTable;
|
||||
@ -69,6 +74,7 @@ internal sealed unsafe class GameFunctions
|
||||
_clientState = clientState;
|
||||
_questRegistry = questRegistry;
|
||||
_gameGui = gameGui;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
_processChatBox =
|
||||
Marshal.GetDelegateForFunctionPointer<ProcessChatBoxDelegate>(sigScanner.ScanText(Signatures.SendChat));
|
||||
@ -364,29 +370,33 @@ internal sealed unsafe class GameFunctions
|
||||
return false;
|
||||
}
|
||||
|
||||
public void UseItem(uint itemId)
|
||||
public bool UseItem(uint itemId)
|
||||
{
|
||||
AgentInventoryContext.Instance()->UseItem(itemId);
|
||||
return AgentInventoryContext.Instance()->UseItem(itemId) == 0;
|
||||
}
|
||||
|
||||
public void UseItem(uint dataId, uint itemId)
|
||||
public bool UseItem(uint dataId, uint itemId)
|
||||
{
|
||||
GameObject? gameObject = FindObjectByDataId(dataId);
|
||||
if (gameObject != null)
|
||||
{
|
||||
_targetManager.Target = gameObject;
|
||||
AgentInventoryContext.Instance()->UseItem(itemId);
|
||||
return AgentInventoryContext.Instance()->UseItem(itemId) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void UseItemOnGround(uint dataId, uint itemId)
|
||||
public bool UseItemOnGround(uint dataId, uint itemId)
|
||||
{
|
||||
GameObject? gameObject = FindObjectByDataId(dataId);
|
||||
if (gameObject != null)
|
||||
{
|
||||
var position = (FFXIVClientStructs.FFXIV.Common.Math.Vector3)gameObject.Position;
|
||||
ActionManager.Instance()->UseActionLocation(ActionType.KeyItem, itemId, location: &position);
|
||||
return ActionManager.Instance()->UseActionLocation(ActionType.KeyItem, itemId, location: &position);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void UseEmote(uint dataId, EEmote emote)
|
||||
@ -410,8 +420,11 @@ internal sealed unsafe class GameFunctions
|
||||
return gameObject != null && (gameObject.Position - position).Length() < 0.05f;
|
||||
}
|
||||
|
||||
public bool HasStatusPreventingSprintOrMount()
|
||||
public bool HasStatusPreventingSprintOrMount(bool skipConfigCheck = false)
|
||||
{
|
||||
if (!skipConfigCheck && _configuration.Advanced.NeverFly)
|
||||
return true;
|
||||
|
||||
if (_condition[ConditionFlag.Swimming] && !IsFlyingUnlockedInCurrentZone())
|
||||
return true;
|
||||
|
||||
@ -437,13 +450,14 @@ internal sealed unsafe class GameFunctions
|
||||
return true;
|
||||
|
||||
var playerState = PlayerState.Instance();
|
||||
if (playerState != null && playerState->IsMountUnlocked(71))
|
||||
if (playerState != null && _configuration.General.MountId != 0 &&
|
||||
playerState->IsMountUnlocked(_configuration.General.MountId))
|
||||
{
|
||||
if (ActionManager.Instance()->GetActionStatus(ActionType.Mount, 71) == 0)
|
||||
if (ActionManager.Instance()->GetActionStatus(ActionType.Mount, _configuration.General.MountId) == 0)
|
||||
{
|
||||
if (ActionManager.Instance()->UseAction(ActionType.Mount, 71))
|
||||
if (ActionManager.Instance()->UseAction(ActionType.Mount, _configuration.General.MountId))
|
||||
{
|
||||
_logger.LogInformation("Using SDS Fenrir as mount");
|
||||
_logger.LogInformation("Using preferred mount");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -526,10 +540,20 @@ internal sealed unsafe class GameFunctions
|
||||
return excelSheet.FirstOrDefault(x => x.Key == key)?.Value?.ToDalamudString().ToString();
|
||||
}
|
||||
|
||||
public string? GetContentTalk(uint rowId)
|
||||
public string? GetDialogueTextByRowId(string? excelSheet, uint rowId)
|
||||
{
|
||||
var questRow = _dataManager.GetExcelSheet<ContentTalk>()!.GetRow(rowId);
|
||||
return questRow?.Text?.ToString();
|
||||
if (excelSheet == "GimmickYesNo")
|
||||
{
|
||||
var questRow = _dataManager.GetExcelSheet<GimmickYesNo>()!.GetRow(rowId);
|
||||
return questRow?.Unknown0?.ToString();
|
||||
}
|
||||
else if (excelSheet is "ContentTalk" or null)
|
||||
{
|
||||
var questRow = _dataManager.GetExcelSheet<ContentTalk>()!.GetRow(rowId);
|
||||
return questRow?.Text?.ToString();
|
||||
}
|
||||
else
|
||||
throw new ArgumentOutOfRangeException(nameof(excelSheet), $"Unsupported excel sheet {excelSheet}");
|
||||
}
|
||||
|
||||
public bool IsOccupied()
|
||||
|
@ -7,6 +7,7 @@ internal sealed class Quest
|
||||
{
|
||||
public required ushort QuestId { get; init; }
|
||||
public required string Name { get; set; }
|
||||
public ushort Level { get; set; }
|
||||
public required QuestData Data { get; init; }
|
||||
|
||||
public QuestSequence? FindSequence(byte currentSequence)
|
||||
|
@ -8,7 +8,5 @@ internal sealed class DialogueChoiceTypeConverter() : EnumConverter<EDialogChoic
|
||||
{
|
||||
{ EDialogChoiceType.YesNo, "YesNo" },
|
||||
{ EDialogChoiceType.List, "List" },
|
||||
{ EDialogChoiceType.ContentTalkYesNo, "ContentTalkYesNo" },
|
||||
{ EDialogChoiceType.ContentTalkList, "ContentTalkList" },
|
||||
};
|
||||
}
|
||||
|
30
Questionable/Model/V1/Converter/ExcelRefConverter.cs
Normal file
30
Questionable/Model/V1/Converter/ExcelRefConverter.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
internal sealed class ExcelRefConverter : JsonConverter<ExcelRef>
|
||||
{
|
||||
public override ExcelRef? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
return new ExcelRef(reader.GetString()!);
|
||||
else if (reader.TokenType == JsonTokenType.Number)
|
||||
return new ExcelRef(reader.GetUInt32());
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ExcelRef? value, JsonSerializerOptions options)
|
||||
{
|
||||
if (value == null)
|
||||
writer.WriteNullValue();
|
||||
else if (value.Type == ExcelRef.EType.Key)
|
||||
writer.WriteStringValue(value.AsKey());
|
||||
else if (value.Type == ExcelRef.EType.RowId)
|
||||
writer.WriteNumberValue(value.AsRowId());
|
||||
else
|
||||
throw new JsonException();
|
||||
}
|
||||
}
|
@ -10,7 +10,17 @@ internal sealed class DialogueChoice
|
||||
[JsonConverter(typeof(DialogueChoiceTypeConverter))]
|
||||
public EDialogChoiceType Type { get; set; }
|
||||
public string? ExcelSheet { get; set; }
|
||||
public string? Prompt { get; set; }
|
||||
|
||||
[JsonConverter(typeof(ExcelRefConverter))]
|
||||
public ExcelRef? Prompt { get; set; }
|
||||
|
||||
public bool Yes { get; set; } = true;
|
||||
public string? Answer { get; set; }
|
||||
|
||||
[JsonConverter(typeof(ExcelRefConverter))]
|
||||
public ExcelRef? Answer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If set, only applies when focusing the given target id.
|
||||
/// </summary>
|
||||
public uint? DataId { get; set; }
|
||||
}
|
||||
|
@ -5,6 +5,4 @@ internal enum EDialogChoiceType
|
||||
None,
|
||||
YesNo,
|
||||
List,
|
||||
ContentTalkYesNo,
|
||||
ContentTalkList,
|
||||
}
|
||||
|
48
Questionable/Model/V1/ExcelRef.cs
Normal file
48
Questionable/Model/V1/ExcelRef.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
public class ExcelRef
|
||||
{
|
||||
private readonly string? _stringValue;
|
||||
private readonly uint? _rowIdValue;
|
||||
|
||||
public ExcelRef(string value)
|
||||
{
|
||||
_stringValue = value;
|
||||
_rowIdValue = null;
|
||||
Type = EType.Key;
|
||||
}
|
||||
|
||||
public ExcelRef(uint value)
|
||||
{
|
||||
_stringValue = null;
|
||||
_rowIdValue = value;
|
||||
Type = EType.RowId;
|
||||
}
|
||||
|
||||
public EType Type { get; }
|
||||
|
||||
public string AsKey()
|
||||
{
|
||||
if (Type != EType.Key)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
return _stringValue!;
|
||||
}
|
||||
|
||||
public uint AsRowId()
|
||||
{
|
||||
if (Type != EType.RowId)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
return _rowIdValue!.Value;
|
||||
}
|
||||
|
||||
public enum EType
|
||||
{
|
||||
None,
|
||||
Key,
|
||||
RowId,
|
||||
}
|
||||
}
|
@ -106,6 +106,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
||||
serviceCollection.AddSingleton<NavigationShortcutController>();
|
||||
|
||||
serviceCollection.AddSingleton<DebugWindow>();
|
||||
serviceCollection.AddSingleton<ConfigWindow>();
|
||||
serviceCollection.AddSingleton<DalamudInitializer>();
|
||||
|
||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
88
Questionable/Windows/ConfigWindow.cs
Normal file
88
Questionable/Windows/ConfigWindow.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using ImGuiNET;
|
||||
using LLib.ImGui;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace Questionable.Windows;
|
||||
|
||||
internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly Configuration _configuration;
|
||||
|
||||
private readonly uint[] _mountIds;
|
||||
private readonly string[] _mountNames;
|
||||
|
||||
[SuppressMessage("Performance", "CA1861", Justification = "One time initialization")]
|
||||
public ConfigWindow(DalamudPluginInterface pluginInterface, Configuration configuration, IDataManager dataManager)
|
||||
: base("Config - Questionable###QuestionableConfig", ImGuiWindowFlags.AlwaysAutoResize)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_configuration = configuration;
|
||||
|
||||
var mounts = dataManager.GetExcelSheet<Mount>()!
|
||||
.Where(x => x is { RowId: > 0, Icon: > 0 })
|
||||
.Select(x => (MountId: x.RowId, Name: x.Singular.ToString()))
|
||||
.Where(x => !string.IsNullOrEmpty(x.Name))
|
||||
.OrderBy(x => x.Name)
|
||||
.ToList();
|
||||
_mountIds = new uint[] { 0 }.Concat(mounts.Select(x => x.MountId)).ToArray();
|
||||
_mountNames = new[] { "Mount Roulette" }.Concat(mounts.Select(x => x.Name)).ToArray();
|
||||
}
|
||||
|
||||
public WindowConfig WindowConfig => _configuration.ConfigWindowConfig;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (ImGui.BeginTabBar("QuestionableConfigTabs"))
|
||||
{
|
||||
if (ImGui.BeginTabItem("General"))
|
||||
{
|
||||
int selectedMount = Array.FindIndex(_mountIds, x => x == _configuration.General.MountId);
|
||||
if (selectedMount == -1)
|
||||
{
|
||||
selectedMount = 0;
|
||||
_configuration.General.MountId = _mountIds[selectedMount];
|
||||
Save();
|
||||
}
|
||||
|
||||
if (ImGui.Combo("Preferred Mount", ref selectedMount, _mountNames, _mountNames.Length))
|
||||
{
|
||||
_configuration.General.MountId = _mountIds[selectedMount];
|
||||
Save();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Advanced"))
|
||||
{
|
||||
ImGui.TextColored(ImGuiColors.DalamudRed,
|
||||
"Enabling any option here may cause unexpected behavior. Use at your own risk.");
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
bool neverFly = _configuration.Advanced.NeverFly;
|
||||
if (ImGui.Checkbox("Disable flying (even if unlocked for the zone)", ref neverFly))
|
||||
{
|
||||
_configuration.Advanced.NeverFly = neverFly;
|
||||
Save();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
ImGui.EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
private void Save() => _pluginInterface.SavePluginConfig(_configuration);
|
||||
|
||||
public void SaveWindowConfig() => Save();
|
||||
}
|
@ -23,10 +23,9 @@ using Questionable.Model.V1;
|
||||
|
||||
namespace Questionable.Windows;
|
||||
|
||||
internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposable
|
||||
internal sealed class DebugWindow : LWindow, IPersistableWindowConfig
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly WindowSystem _windowSystem;
|
||||
private readonly MovementController _movementController;
|
||||
private readonly QuestController _questController;
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
@ -37,14 +36,19 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
private readonly Configuration _configuration;
|
||||
private readonly ILogger<DebugWindow> _logger;
|
||||
|
||||
public DebugWindow(DalamudPluginInterface pluginInterface, WindowSystem windowSystem,
|
||||
MovementController movementController, QuestController questController, GameFunctions gameFunctions,
|
||||
IClientState clientState, IFramework framework, ITargetManager targetManager, GameUiController gameUiController,
|
||||
Configuration configuration, ILogger<DebugWindow> logger)
|
||||
public DebugWindow(DalamudPluginInterface pluginInterface,
|
||||
MovementController movementController,
|
||||
QuestController questController,
|
||||
GameFunctions gameFunctions,
|
||||
IClientState clientState,
|
||||
IFramework framework,
|
||||
ITargetManager targetManager,
|
||||
GameUiController gameUiController,
|
||||
Configuration configuration,
|
||||
ILogger<DebugWindow> logger)
|
||||
: base("Questionable", ImGuiWindowFlags.AlwaysAutoResize)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_windowSystem = windowSystem;
|
||||
_movementController = movementController;
|
||||
_questController = questController;
|
||||
_gameFunctions = gameFunctions;
|
||||
@ -61,8 +65,6 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
MinimumSize = new Vector2(200, 30),
|
||||
MaximumSize = default
|
||||
};
|
||||
|
||||
_windowSystem.AddWindow(this);
|
||||
}
|
||||
|
||||
public WindowConfig WindowConfig => _configuration.DebugWindowConfig;
|
||||
@ -127,6 +129,7 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
ImGui.Text(_questController.ToStatString());
|
||||
//ImGui.EndDisabled();
|
||||
|
||||
ImGui.BeginDisabled(_questController.IsRunning);
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Play))
|
||||
{
|
||||
_questController.ExecuteNextStep(true);
|
||||
@ -139,6 +142,7 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
_questController.ExecuteNextStep(false);
|
||||
}
|
||||
|
||||
ImGui.EndDisabled();
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop))
|
||||
@ -151,7 +155,8 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
.FindSequence(currentQuest.Sequence)
|
||||
?.FindStep(currentQuest.Step);
|
||||
bool colored = currentStep != null && currentStep.InteractionType == EInteractionType.Instruction
|
||||
&& _questController.HasCurrentTaskMatching<WaitAtEnd.WaitNextStepOrSequence>();
|
||||
&& _questController
|
||||
.HasCurrentTaskMatching<WaitAtEnd.WaitNextStepOrSequence>();
|
||||
|
||||
if (colored)
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
|
||||
@ -161,8 +166,16 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
_questController.Stop("Manual");
|
||||
_questController.IncreaseStepCount();
|
||||
}
|
||||
|
||||
if (colored)
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
|
||||
if (ImGui.Checkbox("Automatically accept next quest", ref autoAcceptNextQuest))
|
||||
{
|
||||
_configuration.General.AutoAcceptNextQuest = autoAcceptNextQuest;
|
||||
_pluginInterface.SavePluginConfig(_configuration);
|
||||
}
|
||||
}
|
||||
else
|
||||
ImGui.Text("No active quest");
|
||||
@ -261,7 +274,8 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui.Button($"Copy"))
|
||||
ImGui.Button($"Copy");
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||
{
|
||||
ImGui.SetClipboardText($$"""
|
||||
"Position": {
|
||||
@ -273,6 +287,12 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
"InteractionType": ""
|
||||
""");
|
||||
}
|
||||
else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
{
|
||||
Vector3 position = _clientState.LocalPlayer!.Position;
|
||||
ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
|
||||
$"new({position.X}f, {position.Y}f, {position.Z}f)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,9 +337,4 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposab
|
||||
ImGui.EndDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_windowSystem.RemoveWindow(this);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user