Compare commits

..

12 Commits

71 changed files with 701 additions and 154 deletions

View File

@ -1,5 +1,5 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>2.15</Version> <Version>2.16</Version>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -42,14 +42,18 @@
"X": 419.309, "X": 419.309,
"Y": -10.25133, "Y": -10.25133,
"Z": -784.8259 "Z": -784.8259
} },
"MinimumAngle": -25,
"MaximumAngle": 85
}, },
{ {
"Position": { "Position": {
"X": 410.6989, "X": 410.6989,
"Y": -10.9656, "Y": -10.9656,
"Z": -790.8315 "Z": -790.8315
} },
"MinimumAngle": -20,
"MaximumAngle": 95
} }
] ]
} }

View File

@ -52,8 +52,8 @@
"Y": -47.86026, "Y": -47.86026,
"Z": -394.9654 "Z": -394.9654
}, },
"MinimumAngle": -120, "MinimumAngle": -145,
"MaximumAngle": 120 "MaximumAngle": -95
} }
] ]
} }

View File

@ -128,6 +128,10 @@ internal static class SkipConditionsExtensions
skipAetheryteCondition.InSameTerritory, emptyAetheryte.InSameTerritory), skipAetheryteCondition.InSameTerritory, emptyAetheryte.InSameTerritory),
AssignmentList(nameof(SkipAetheryteCondition.InTerritory), AssignmentList(nameof(SkipAetheryteCondition.InTerritory),
skipAetheryteCondition.InTerritory), skipAetheryteCondition.InTerritory),
AssignmentList(nameof(SkipAetheryteCondition.QuestsAccepted),
skipAetheryteCondition.QuestsAccepted),
AssignmentList(nameof(skipAetheryteCondition.QuestsCompleted),
skipAetheryteCondition.QuestsCompleted),
Assignment(nameof(SkipAetheryteCondition.AetheryteLocked), Assignment(nameof(SkipAetheryteCondition.AetheryteLocked),
skipAetheryteCondition.AetheryteLocked, emptyAetheryte.AetheryteLocked) skipAetheryteCondition.AetheryteLocked, emptyAetheryte.AetheryteLocked)
.AsSyntaxNodeOrToken(), .AsSyntaxNodeOrToken(),

View File

@ -13,7 +13,7 @@
"Z": 118.88306 "Z": 118.88306
}, },
"TerritoryId": 131, "TerritoryId": 131,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Ul'dah", "AetheryteShortcut": "Ul'dah",
"AethernetShortcut": [ "AethernetShortcut": [
"[Ul'dah] Aetheryte Plaza", "[Ul'dah] Aetheryte Plaza",

View File

@ -13,7 +13,7 @@
"Z": 190.41736 "Z": 190.41736
}, },
"TerritoryId": 128, "TerritoryId": 128,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Limsa Lominsa", "AetheryteShortcut": "Limsa Lominsa",
"AethernetShortcut": [ "AethernetShortcut": [
"[Limsa Lominsa] Aetheryte Plaza", "[Limsa Lominsa] Aetheryte Plaza",

View File

@ -35,8 +35,7 @@
"Prompt": "TEXT_CLSARM011_00273_Q1_000_1", "Prompt": "TEXT_CLSARM011_00273_Q1_000_1",
"Yes": true "Yes": true
} }
], ]
"NextQuestId": 273
} }
] ]
} }

View File

@ -13,7 +13,7 @@
"Z": 59.952637 "Z": 59.952637
}, },
"TerritoryId": 130, "TerritoryId": 130,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Ul'dah", "AetheryteShortcut": "Ul'dah",
"AethernetShortcut": [ "AethernetShortcut": [
"[Ul'dah] Aetheryte Plaza", "[Ul'dah] Aetheryte Plaza",

View File

@ -122,7 +122,7 @@
"TerritoryId": 153, "TerritoryId": 153,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest",
"Fly": true, "Fly": true,
"NextQuestId": 1085 "NextQuestId": 1086
} }
] ]
} }

View File

@ -30,7 +30,7 @@
"DialogueChoices": [ "DialogueChoices": [
{ {
"Type": "YesNo", "Type": "YesNo",
"Prompt": "TEXT_CLSBSM001_00185_Q1_000_1", "Prompt": "TEXT_CLSBSM011_00291_Q1_000_1",
"Yes": true "Yes": true
} }
] ]

View File

@ -35,8 +35,7 @@
"Prompt": "TEXT_CLSHRV001_00003_Q1_000_1", "Prompt": "TEXT_CLSHRV001_00003_Q1_000_1",
"Yes": true "Yes": true
} }
], ]
"NextQuestId": 3
} }
] ]
} }

View File

@ -13,7 +13,7 @@
"Z": -164.0498 "Z": -164.0498
}, },
"TerritoryId": 128, "TerritoryId": 128,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Limsa Lominsa", "AetheryteShortcut": "Limsa Lominsa",
"AethernetShortcut": [ "AethernetShortcut": [
"[Limsa Lominsa] Aetheryte Plaza", "[Limsa Lominsa] Aetheryte Plaza",

View File

@ -35,8 +35,7 @@
"Prompt": "TEXT_CLSCUL011_00271_Q1_000_1", "Prompt": "TEXT_CLSCUL011_00271_Q1_000_1",
"Yes": true "Yes": true
} }
], ]
"NextQuestId": 271
} }
] ]
} }

View File

@ -24,7 +24,7 @@
"Yes": true "Yes": true
} }
], ],
"InteractionType": "Interact" "InteractionType": "AcceptQuest"
} }
] ]
} }

View File

@ -13,7 +13,7 @@
"Z": 150.04187 "Z": 150.04187
}, },
"TerritoryId": 129, "TerritoryId": 129,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Limsa Lominsa", "AetheryteShortcut": "Limsa Lominsa",
"AethernetShortcut": [ "AethernetShortcut": [
"[Limsa Lominsa] Aetheryte Plaza", "[Limsa Lominsa] Aetheryte Plaza",

View File

@ -38,22 +38,6 @@
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }
] ]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1000857,
"Position": {
"X": -165.27051,
"Y": 5.2500057,
"Z": 164.29382
},
"TerritoryId": 129,
"InteractionType": "CompleteQuest",
"NextQuestId": 1108
}
]
} }
] ]
} }

View File

@ -13,7 +13,7 @@
"Z": 97.24573 "Z": 97.24573
}, },
"TerritoryId": 131, "TerritoryId": 131,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Ul'dah", "AetheryteShortcut": "Ul'dah",
"AethernetShortcut": [ "AethernetShortcut": [
"[Ul'dah] Aetheryte Plaza", "[Ul'dah] Aetheryte Plaza",

View File

@ -35,8 +35,7 @@
"Prompt": "TEXT_CLSGLD011_00608_A1_000_1", "Prompt": "TEXT_CLSGLD011_00608_A1_000_1",
"Yes": true "Yes": true
} }
], ]
"NextQuestId": 608
} }
] ]
} }

View File

@ -35,8 +35,7 @@
"Prompt": "TEXT_CLSTAN000_00105_Q1_000_1", "Prompt": "TEXT_CLSTAN000_00105_Q1_000_1",
"Yes": true "Yes": true
} }
], ]
"NextQuestId": 105
} }
] ]
} }

View File

@ -13,7 +13,7 @@
"Z": -147.41742 "Z": -147.41742
}, },
"TerritoryId": 133, "TerritoryId": 133,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Gridania", "AetheryteShortcut": "Gridania",
"AethernetShortcut": [ "AethernetShortcut": [
"[Gridania] Aetheryte Plaza", "[Gridania] Aetheryte Plaza",

View File

@ -13,7 +13,7 @@
"Z": -51.163513 "Z": -51.163513
}, },
"TerritoryId": 130, "TerritoryId": 130,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Ul'dah", "AetheryteShortcut": "Ul'dah",
"SkipConditions": { "SkipConditions": {
"AetheryteShortcutIf": { "AetheryteShortcutIf": {

View File

@ -1,18 +1,19 @@
{ {
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json", "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "Cacahuetes", "Author": "Cacahuetes",
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 255, "Sequence": 0,
"Steps": [ "Steps": [
{ "DataId": 1000895, {
"Position": { "DataId": 1000895,
"X": -335.74432, "Position": {
"Y": 12.899764, "X": -335.74432,
"Z": 1.3884888 "Y": 12.899764,
}, "Z": 1.3884888
"TerritoryId": 129, },
"InteractionType": "AcceptQuest", "TerritoryId": 129,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Limsa Lominsa", "AetheryteShortcut": "Limsa Lominsa",
"AethernetShortcut": [ "AethernetShortcut": [
"[Limsa Lominsa] Aetheryte Plaza", "[Limsa Lominsa] Aetheryte Plaza",
@ -35,8 +36,7 @@
], ],
"NextQuestId": 452 "NextQuestId": 452
} }
] ]
} }
] ]
} }

View File

@ -13,7 +13,7 @@
"Z": -245.80762 "Z": -245.80762
}, },
"TerritoryId": 128, "TerritoryId": 128,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Limsa Lominsa", "AetheryteShortcut": "Limsa Lominsa",
"AethernetShortcut": [ "AethernetShortcut": [
"[Limsa Lominsa] Aetheryte Plaza", "[Limsa Lominsa] Aetheryte Plaza",

View File

@ -28,7 +28,7 @@
"Z": -255.8786 "Z": -255.8786
}, },
"TerritoryId": 128, "TerritoryId": 128,
"InteractionType": "AcceptQuest", "InteractionType": "CompleteQuest",
"DialogueChoices": [ "DialogueChoices": [
{ {
"Type": "YesNo", "Type": "YesNo",

View File

@ -13,7 +13,7 @@
"Z": 98.039185 "Z": 98.039185
}, },
"TerritoryId": 131, "TerritoryId": 131,
"InteractionType": "Interact", "InteractionType": "AcceptQuest",
"AetheryteShortcut": "Ul'dah", "AetheryteShortcut": "Ul'dah",
"AethernetShortcut": [ "AethernetShortcut": [
"[Ul'dah] Aetheryte Plaza", "[Ul'dah] Aetheryte Plaza",

View File

@ -35,8 +35,7 @@
"Prompt": "TEXT_CLSWVR011_00534_SYSTEM_Q0", "Prompt": "TEXT_CLSWVR011_00534_SYSTEM_Q0",
"Yes": true "Yes": true
} }
], ]
"NextQuestId": 534
} }
] ]
} }

View File

@ -20,6 +20,15 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": -392.35907,
"Y": -14.000012,
"Z": 635.3141
},
"TerritoryId": 1191,
"InteractionType": "WalkTo"
},
{ {
"Position": { "Position": {
"X": -415.87146, "X": -415.87146,

View File

@ -55,6 +55,24 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"Position": {
"X": 184.68884,
"Y": 99.24859,
"Z": -171.97504
},
"TerritoryId": 1191,
"InteractionType": "WalkTo"
},
{
"Position": {
"X": 148.14713,
"Y": 100.07344,
"Z": -135.20776
},
"TerritoryId": 1191,
"InteractionType": "WalkTo"
},
{ {
"DataId": 2013966, "DataId": 2013966,
"Position": { "Position": {

View File

@ -102,7 +102,8 @@
"Z": 342.85498 "Z": 342.85498
}, },
"TerritoryId": 1188, "TerritoryId": 1188,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest",
"Mount": true
} }
] ]
} }

View File

@ -20,6 +20,15 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": 323.5058,
"Y": -16.330368,
"Z": -254.18399
},
"TerritoryId": 1190,
"InteractionType": "WalkTo"
},
{ {
"DataId": 1050656, "DataId": 1050656,
"Position": { "Position": {

View File

@ -28,7 +28,8 @@
"Z": 427.2068 "Z": 427.2068
}, },
"TerritoryId": 1190, "TerritoryId": 1190,
"InteractionType": "Interact" "InteractionType": "Interact",
"Mount": true
} }
] ]
}, },
@ -50,6 +51,27 @@
{ {
"Sequence": 3, "Sequence": 3,
"Steps": [ "Steps": [
{
"Position": {
"X": 369.3906,
"Y": 5.9371996,
"Z": 417.16574
},
"TerritoryId": 1190,
"InteractionType": "WalkTo",
"$": "Shaaloani Hhusatahwi Saloon Stairs (top)"
},
{
"Position": {
"X": 359.11545,
"Y": 1.9823306,
"Z": 419.5714
},
"TerritoryId": 1190,
"InteractionType": "WalkTo",
"$": "Shaaloani Hhusatahwi Saloon Stairs (bottom)",
"DisableNavmesh": true
},
{ {
"DataId": 1051286, "DataId": 1051286,
"Position": { "Position": {

View File

@ -174,6 +174,17 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 2013948,
"Position": {
"X": 610.7728,
"Y": 9.597839,
"Z": 233.05103
},
"TerritoryId": 1189,
"InteractionType": "AttuneAetherCurrent",
"AetherCurrentId": 2818440
},
{ {
"DataId": 1051073, "DataId": 1051073,
"Position": { "Position": {

View File

@ -151,13 +151,21 @@
"Steps": [ "Steps": [
{ {
"Position": { "Position": {
"X": -510.96463, "X": -510.37933,
"Y": -0.47684515, "Y": -0.39999998,
"Z": -305.96155 "Z": -277.78192
}, },
"TerritoryId": 1188, "TerritoryId": 1188,
"InteractionType": "WalkTo", "InteractionType": "WalkTo"
"Comment": "Waypoint after swimming through the river" },
{
"Position": {
"X": -521.0261,
"Y": -0.40000004,
"Z": -329.19336
},
"TerritoryId": 1188,
"InteractionType": "WalkTo"
}, },
{ {
"DataId": 2013936, "DataId": 2013936,

View File

@ -12,7 +12,7 @@
"Y": 9.773315, "Y": 9.773315,
"Z": -533.135 "Z": -533.135
}, },
"StopDistance": 5, "StopDistance": 7,
"TerritoryId": 1188, "TerritoryId": 1188,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }
@ -135,6 +135,28 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 1048782,
"Position": {
"X": -68.52832,
"Y": 6.536739,
"Z": -486.53394
},
"TerritoryId": 1188,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5064
},
{
"DataId": 1051237,
"Position": {
"X": -251.20929,
"Y": 5.5572896,
"Z": -554.89435
},
"TerritoryId": 1188,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5081
},
{ {
"DataId": 1046537, "DataId": 1046537,
"Position": { "Position": {

View File

@ -21,6 +21,11 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"TerritoryId": 1187,
"InteractionType": "AttuneAetheryte",
"Aetheryte": "Urqopacha - Wachunpelo"
},
{ {
"DataId": 1046557, "DataId": 1046557,
"Position": { "Position": {
@ -39,11 +44,6 @@
128 128
] ]
}, },
{
"TerritoryId": 1187,
"InteractionType": "AttuneAetheryte",
"Aetheryte": "Urqopacha - Wachunpelo"
},
{ {
"DataId": 1046559, "DataId": 1046559,
"Position": { "Position": {

View File

@ -52,6 +52,30 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 1050684,
"Position": {
"X": 391.37854,
"Y": -156.07434,
"Z": -388.50995
},
"TerritoryId": 1187,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5039,
"Comment": "Traveler to the Rescue"
},
{
"DataId": 1051195,
"Position": {
"X": 383.29138,
"Y": -154.50243,
"Z": -420.49292
},
"TerritoryId": 1187,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5051,
"Comment": "Crisis of Corruption"
},
{ {
"DataId": 1046537, "DataId": 1046537,
"Position": { "Position": {

View File

@ -100,7 +100,7 @@
"Y": -19.725424, "Y": -19.725424,
"Z": 203.72314 "Z": 203.72314
}, },
"StopDistance": 5, "StopDistance": 7,
"TerritoryId": 1185, "TerritoryId": 1185,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest"
} }

View File

@ -15,6 +15,7 @@
"Y": -19.725424, "Y": -19.725424,
"Z": 203.72314 "Z": 203.72314
}, },
"StopDistance": 7,
"TerritoryId": 1185, "TerritoryId": 1185,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }

View File

@ -63,6 +63,28 @@
{ {
"Sequence": 3, "Sequence": 3,
"Steps": [ "Steps": [
{
"DataId": 1051263,
"Position": {
"X": -521.0193,
"Y": 121.63345,
"Z": 319.38647
},
"TerritoryId": 1188,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5085
},
{
"DataId": 1048855,
"Position": {
"X": -491.63043,
"Y": 121.63846,
"Z": 271.50366
},
"TerritoryId": 1188,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5074
},
{ {
"DataId": 1046876, "DataId": 1046876,
"Position": { "Position": {

View File

@ -12,6 +12,7 @@
"Y": 119.49004, "Y": 119.49004,
"Z": 190.72253 "Z": 190.72253
}, },
"StopDistance": 5,
"TerritoryId": 1188, "TerritoryId": 1188,
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }
@ -29,7 +30,8 @@
}, },
"TerritoryId": 1188, "TerritoryId": 1188,
"InteractionType": "Interact", "InteractionType": "Interact",
"AetheryteShortcut": "Kozama'uka - Earthenshire" "AetheryteShortcut": "Kozama'uka - Earthenshire",
"Fly": true
} }
] ]
}, },
@ -44,7 +46,8 @@
}, },
"TerritoryId": 1188, "TerritoryId": 1188,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"TargetTerritoryId": 1187 "TargetTerritoryId": 1187,
"Fly": true
}, },
{ {
"DataId": 1046814, "DataId": 1046814,

View File

@ -21,6 +21,30 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1051207,
"Position": {
"X": 425.07043,
"Y": 118.935005,
"Z": 606.13403
},
"StopDistance": 5,
"TerritoryId": 1187,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5055
},
{
"DataId": 1048730,
"Position": {
"X": 399.0692,
"Y": 122.53533,
"Z": 542.9922
},
"StopDistance": 5,
"TerritoryId": 1187,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5047
},
{ {
"DataId": 1046926, "DataId": 1046926,
"Position": { "Position": {

View File

@ -21,6 +21,36 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1050872,
"Position": {
"X": -408.71295,
"Y": 20.420113,
"Z": -398.8861
},
"TerritoryId": 1189,
"InteractionType": "AcceptQuest",
"DialogueChoices": [
{
"Type": "List",
"ExcelSheet": "quest/051/KinGzd201_05110",
"Prompt": "TEXT_KINGZD201_05110_Q1_000_000",
"Answer": "TEXT_KINGZD201_05110_A1_000_001"
}
],
"PickUpQuestId": 5110
},
{
"DataId": 1051073,
"Position": {
"X": 41.09253,
"Y": 8.205902,
"Z": -629.8467
},
"TerritoryId": 1189,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5094
},
{ {
"DataId": 1047577, "DataId": 1047577,
"Position": { "Position": {
@ -36,28 +66,6 @@
{ {
"Sequence": 2, "Sequence": 2,
"Steps": [ "Steps": [
{
"DataId": 2013948,
"Position": {
"X": 610.7728,
"Y": 9.597839,
"Z": 233.05103
},
"TerritoryId": 1189,
"InteractionType": "AttuneAetherCurrent",
"AetherCurrentId": 2818440
},
{
"Position": {
"X": 614.25964,
"Y": -29.554798,
"Z": 202.75368
},
"TerritoryId": 1189,
"InteractionType": "WalkTo",
"DisableNavmesh": true,
"Mount": false
},
{ {
"DataId": 1048230, "DataId": 1048230,
"Position": { "Position": {
@ -66,8 +74,7 @@
"Z": 214.83167 "Z": 214.83167
}, },
"TerritoryId": 1189, "TerritoryId": 1189,
"InteractionType": "Interact", "InteractionType": "Interact"
"DisableNavmesh": true
} }
] ]
}, },

View File

@ -24,6 +24,28 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1051052,
"Position": {
"X": 585.90063,
"Y": -142.49187,
"Z": 510.21594
},
"TerritoryId": 1189,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5103
},
{
"DataId": 1050884,
"Position": {
"X": 531.0597,
"Y": -142.49185,
"Z": 492.0271
},
"TerritoryId": 1189,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5114
},
{ {
"DataId": 1047701, "DataId": 1047701,
"Position": { "Position": {

View File

@ -52,6 +52,62 @@
{ {
"Sequence": 3, "Sequence": 3,
"Steps": [ "Steps": [
{
"DataId": 1050655,
"Position": {
"X": 289.02112,
"Y": -15.566031,
"Z": -478.78235
},
"TerritoryId": 1190,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5138
},
{
"Position": {
"X": 386.84927,
"Y": -0.84392637,
"Z": 435.0579
},
"TerritoryId": 1190,
"InteractionType": "WalkTo",
"AetheryteShortcut": "Shaaloani - Hhusatahwi",
"SkipConditions": {
"StepIf": {
"QuestsCompleted": [
5140
]
},
"AetheryteShortcutIf": {
"QuestsCompleted": [
5140
]
}
}
},
{
"DataId": 1051283,
"Position": {
"X": 374.19702,
"Y": -0.24794838,
"Z": 437.2472
},
"StopDistance": 6,
"TerritoryId": 1190,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5140
},
{
"DataId": 1049324,
"Position": {
"X": -108.659546,
"Y": 4.055336,
"Z": 319.5697
},
"TerritoryId": 1190,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5130
},
{ {
"DataId": 1047001, "DataId": 1047001,
"Position": { "Position": {
@ -61,6 +117,7 @@
}, },
"TerritoryId": 1190, "TerritoryId": 1190,
"InteractionType": "Interact", "InteractionType": "Interact",
"AetheryteShortcut": "Shaaloani - Sheshenewezi Springs",
"DialogueChoices": [ "DialogueChoices": [
{ {
"Type": "YesNo", "Type": "YesNo",

View File

@ -161,6 +161,17 @@
{ {
"Sequence": 7, "Sequence": 7,
"Steps": [ "Steps": [
{
"DataId": 1051316,
"Position": {
"X": -380.30066,
"Y": 18.718708,
"Z": -114.24432
},
"TerritoryId": 1190,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5144
},
{ {
"DataId": 1047082, "DataId": 1047082,
"Position": { "Position": {

View File

@ -69,6 +69,7 @@
"Y": 0.003171, "Y": 0.003171,
"Z": 1.296936 "Z": 1.296936
}, },
"StopDistance": 5,
"TerritoryId": 1171, "TerritoryId": 1171,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest"
} }

View File

@ -21,6 +21,34 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1049443,
"Position": {
"X": -177.56934,
"Y": 29.999998,
"Z": -601.15967
},
"TerritoryId": 1191,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5153,
"AetheryteShortcut": "Heritage Found - The Outskirts",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
},
{
"DataId": 1050805,
"Position": {
"X": -153.09375,
"Y": 34.946026,
"Z": -581.0178
},
"TerritoryId": 1191,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5160
},
{ {
"DataId": 1048053, "DataId": 1048053,
"Position": { "Position": {
@ -29,8 +57,7 @@
"Z": -828.58014 "Z": -828.58014
}, },
"TerritoryId": 1191, "TerritoryId": 1191,
"InteractionType": "Interact", "InteractionType": "Interact"
"AetheryteShortcut": "Heritage Found - The Outskirts"
} }
] ]
}, },

View File

@ -37,6 +37,17 @@
{ {
"Sequence": 2, "Sequence": 2,
"Steps": [ "Steps": [
{
"DataId": 1049505,
"Position": {
"X": -209.85736,
"Y": 7.49638,
"Z": 595.9104
},
"TerritoryId": 1191,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5159
},
{ {
"DataId": 1048099, "DataId": 1048099,
"Position": { "Position": {
@ -53,6 +64,17 @@
{ {
"Sequence": 3, "Sequence": 3,
"Steps": [ "Steps": [
{
"DataId": 1049501,
"Position": {
"X": -592.7062,
"Y": -2.4803436,
"Z": -489.28055
},
"TerritoryId": 1191,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5156
},
{ {
"DataId": 1048116, "DataId": 1048116,
"Position": { "Position": {

View File

@ -32,7 +32,8 @@
"Z": -565.48413 "Z": -565.48413
}, },
"TerritoryId": 1191, "TerritoryId": 1191,
"InteractionType": "Interact" "InteractionType": "Interact",
"Fly": true
} }
] ]
}, },

View File

@ -21,6 +21,17 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1050617,
"Position": {
"X": -631.4031,
"Y": 2.9305653E-07,
"Z": 497.12354
},
"TerritoryId": 1192,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5174
},
{ {
"DataId": 1047884, "DataId": 1047884,
"Position": { "Position": {

View File

@ -21,6 +21,41 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1048243,
"Position": {
"X": 57.87744,
"Y": 53.200012,
"Z": 772.03015
},
"TerritoryId": 1192,
"InteractionType": "Interact",
"AetheryteShortcut": "Living Memory - Leynode Mnemo",
"TargetTerritoryId": 1192,
"SkipConditions": {
"StepIf": {
"QuestsCompleted": [
5176
]
},
"AetheryteShortcutIf": {
"QuestsCompleted": [
5176
]
}
}
},
{
"DataId": 1050621,
"Position": {
"X": 477.1344,
"Y": -0.034497976,
"Z": 711.6654
},
"TerritoryId": 1192,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5176
},
{ {
"DataId": 1047917, "DataId": 1047917,
"Position": { "Position": {

View File

@ -21,6 +21,44 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1050625,
"Position": {
"X": 628.6869,
"Y": 24.99949,
"Z": -283.3753
},
"TerritoryId": 1192,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5178,
"AetheryteShortcut": "Living Memory - Leynode Pyro",
"SkipConditions": {
"AetheryteShortcutIf": {
"QuestsCompleted": [
5178
]
}
}
},
{
"DataId": 1050632,
"Position": {
"X": 538.9944,
"Y": 25.001822,
"Z": -194.3847
},
"TerritoryId": 1192,
"InteractionType": "AcceptQuest",
"PickUpQuestId": 5179,
"AetheryteShortcut": "Living Memory - Leynode Pyro",
"SkipConditions": {
"AetheryteShortcutIf": {
"QuestsCompleted": [
5179
]
}
}
},
{ {
"DataId": 1047971, "DataId": 1047971,
"Position": { "Position": {

View File

@ -33,8 +33,12 @@
}, },
"TerritoryId": 1192, "TerritoryId": 1192,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"AetheryteShortcut": "Living Memory - Leynode Mnemo", "$": "Leynode Mnemo to Meso Terminal waypoint",
"$": "Leynode Mnemo to Meso Terminal waypoint" "SkipConditions": {
"StepIf": {
"Flying": "Unlocked"
}
}
}, },
{ {
"DataId": 1048014, "DataId": 1048014,
@ -44,7 +48,8 @@
"Z": 363.05774 "Z": 363.05774
}, },
"TerritoryId": 1192, "TerritoryId": 1192,
"InteractionType": "Interact" "InteractionType": "Interact",
"Fly": true
} }
] ]
}, },

View File

@ -234,13 +234,13 @@
"QuestsAccepted": { "QuestsAccepted": {
"type": "array", "type": "array",
"items": { "items": {
"type": "number" "type": ["number", "string"]
} }
}, },
"QuestsCompleted": { "QuestsCompleted": {
"type": "array", "type": "array",
"items": { "items": {
"type": "number" "type": ["number", "string"]
} }
}, },
"AetheryteLocked": { "AetheryteLocked": {
@ -290,6 +290,18 @@
"type": "integer" "type": "integer"
} }
}, },
"QuestsAccepted": {
"type": "array",
"items": {
"type": ["number", "string"]
}
},
"QuestsCompleted": {
"type": "array",
"items": {
"type": ["number", "string"]
}
},
"AetheryteLocked": { "AetheryteLocked": {
"$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/Aetheryte" "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/Aetheryte"
}, },

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Questionable.Model.Questing.Converter;
public sealed class ElementIdListConverter : JsonConverter<List<ElementId>>
{
public override List<ElementId> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
reader.Read();
List<ElementId> values = [];
while (reader.TokenType != JsonTokenType.EndArray)
{
if (reader.TokenType == JsonTokenType.Number)
values.Add(new QuestId(reader.GetUInt16()));
else
values.Add(ElementId.FromString(reader.GetString() ?? throw new JsonException()));
reader.Read();
}
return values;
}
public override void Write(Utf8JsonWriter writer, List<ElementId> value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}

View File

@ -1,5 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization;
using Questionable.Model.Common; using Questionable.Model.Common;
using Questionable.Model.Questing.Converter;
namespace Questionable.Model.Questing; namespace Questionable.Model.Questing;
@ -8,6 +10,13 @@ public sealed class SkipAetheryteCondition
public bool Never { get; set; } public bool Never { get; set; }
public bool InSameTerritory { get; set; } public bool InSameTerritory { get; set; }
public List<ushort> InTerritory { get; set; } = new(); public List<ushort> InTerritory { get; set; } = new();
[JsonConverter(typeof(ElementIdListConverter))]
public List<ElementId> QuestsAccepted { get; set; } = new();
[JsonConverter(typeof(ElementIdListConverter))]
public List<ElementId> QuestsCompleted { get; set; } = new();
public EAetheryteLocation? AetheryteLocked { get; set; } public EAetheryteLocation? AetheryteLocked { get; set; }
public EAetheryteLocation? AetheryteUnlocked { get; set; } public EAetheryteLocation? AetheryteUnlocked { get; set; }
public bool RequiredQuestVariablesNotMet { get; set; } public bool RequiredQuestVariablesNotMet { get; set; }

View File

@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json.Serialization;
using Questionable.Model.Common; using Questionable.Model.Common;
using Questionable.Model.Questing.Converter;
namespace Questionable.Model.Questing; namespace Questionable.Model.Questing;
@ -14,8 +16,13 @@ public sealed class SkipStepConditions
public List<ushort> InTerritory { get; set; } = new(); public List<ushort> InTerritory { get; set; } = new();
public List<ushort> NotInTerritory { get; set; } = new(); public List<ushort> NotInTerritory { get; set; } = new();
public SkipItemConditions? Item { get; set; } public SkipItemConditions? Item { get; set; }
[JsonConverter(typeof(ElementIdListConverter))]
public List<ElementId> QuestsAccepted { get; set; } = new(); public List<ElementId> QuestsAccepted { get; set; } = new();
[JsonConverter(typeof(ElementIdListConverter))]
public List<ElementId> QuestsCompleted { get; set; } = new(); public List<ElementId> QuestsCompleted { get; set; } = new();
public EAetheryteLocation? AetheryteLocked { get; set; } public EAetheryteLocation? AetheryteLocked { get; set; }
public EAetheryteLocation? AetheryteUnlocked { get; set; } public EAetheryteLocation? AetheryteUnlocked { get; set; }
public NearPositionCondition? NearPosition { get; set; } public NearPositionCondition? NearPosition { get; set; }

View File

@ -42,6 +42,7 @@ internal sealed class InteractionUiController : IDisposable
private readonly GatheringPointRegistry _gatheringPointRegistry; private readonly GatheringPointRegistry _gatheringPointRegistry;
private readonly QuestRegistry _questRegistry; private readonly QuestRegistry _questRegistry;
private readonly QuestData _questData; private readonly QuestData _questData;
private readonly TerritoryData _territoryData;
private readonly IGameGui _gameGui; private readonly IGameGui _gameGui;
private readonly ITargetManager _targetManager; private readonly ITargetManager _targetManager;
private readonly IClientState _clientState; private readonly IClientState _clientState;
@ -61,6 +62,7 @@ internal sealed class InteractionUiController : IDisposable
GatheringPointRegistry gatheringPointRegistry, GatheringPointRegistry gatheringPointRegistry,
QuestRegistry questRegistry, QuestRegistry questRegistry,
QuestData questData, QuestData questData,
TerritoryData territoryData,
IGameGui gameGui, IGameGui gameGui,
ITargetManager targetManager, ITargetManager targetManager,
IPluginLog pluginLog, IPluginLog pluginLog,
@ -77,6 +79,7 @@ internal sealed class InteractionUiController : IDisposable
_gatheringPointRegistry = gatheringPointRegistry; _gatheringPointRegistry = gatheringPointRegistry;
_questRegistry = questRegistry; _questRegistry = questRegistry;
_questData = questData; _questData = questData;
_territoryData = territoryData;
_gameGui = gameGui; _gameGui = gameGui;
_targetManager = targetManager; _targetManager = targetManager;
_clientState = clientState; _clientState = clientState;
@ -101,7 +104,9 @@ internal sealed class InteractionUiController : IDisposable
} }
} }
private bool ShouldHandleUiInteractions => _isInitialCheck || _questController.IsRunning; private bool ShouldHandleUiInteractions => _isInitialCheck ||
_questController.IsRunning ||
_territoryData.IsQuestBattleInstance(_clientState.TerritoryType);
internal unsafe void HandleCurrentDialogueChoices() internal unsafe void HandleCurrentDialogueChoices()
{ {

View File

@ -735,7 +735,9 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
return false; return false;
QuestStep? currentStep = currentSequence?.FindStep(currentQuest.Step); QuestStep? currentStep = currentSequence?.FindStep(currentQuest.Step);
return currentStep?.AetheryteShortcut != null; return currentStep?.AetheryteShortcut != null &&
(currentStep.SkipConditions?.AetheryteShortcutIf?.QuestsCompleted.Count ?? 0) == 0 &&
(currentStep.SkipConditions?.AetheryteShortcutIf?.QuestsAccepted.Count ?? 0) == 0;
} }
public bool TryPickPriorityQuest() public bool TryPickPriorityQuest()

View File

@ -41,6 +41,7 @@ internal static class Combat
ArgumentNullException.ThrowIfNull(step.DataId); ArgumentNullException.ThrowIfNull(step.DataId);
yield return interactFactory.Interact(step.DataId.Value, quest, EInteractionType.None, true); yield return interactFactory.Interact(step.DataId.Value, quest, EInteractionType.None, true);
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1));
yield return CreateTask(quest, sequence, step); yield return CreateTask(quest, sequence, step);
break; break;
} }
@ -52,11 +53,15 @@ internal static class Combat
yield return useItemFactory.OnObject(quest.Id, step.DataId.Value, step.ItemId.Value, yield return useItemFactory.OnObject(quest.Id, step.DataId.Value, step.ItemId.Value,
step.CompletionQuestVariablesFlags, true); step.CompletionQuestVariablesFlags, true);
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1));
yield return CreateTask(quest, sequence, step); yield return CreateTask(quest, sequence, step);
break; break;
} }
case EEnemySpawnType.AutoOnEnterArea: case EEnemySpawnType.AutoOnEnterArea:
if (step.CombatDelaySecondsAtStart == null)
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(1));
// automatically triggered when entering area, i.e. only unmount // automatically triggered when entering area, i.e. only unmount
yield return CreateTask(quest, sequence, step); yield return CreateTask(quest, sequence, step);
break; break;

View File

@ -174,6 +174,14 @@ internal static class Interact
logger.LogInformation("Interaction was most likely triggered"); logger.LogInformation("Interaction was most likely triggered");
_interactionState = EInteractionState.InteractionConfirmed; _interactionState = EInteractionState.InteractionConfirmed;
} }
else if (dataId is >= 1047901 and <= 1047905 &&
condition[ConditionFlag.Disguised] &&
flag == ConditionFlag.Mounting71 && // why the fuck is this the flag that's used, instead of OccupiedIn[Quest]Event
value)
{
logger.LogInformation("(A Knight of Alexandria) Interaction was most likely triggered");
_interactionState = EInteractionState.InteractionConfirmed;
}
} }
private enum EInteractionState private enum EInteractionState

View File

@ -24,6 +24,7 @@ internal static class AethernetShortcut
MovementController movementController, MovementController movementController,
AetheryteFunctions aetheryteFunctions, AetheryteFunctions aetheryteFunctions,
GameFunctions gameFunctions, GameFunctions gameFunctions,
QuestFunctions questFunctions,
IClientState clientState, IClientState clientState,
AetheryteData aetheryteData, AetheryteData aetheryteData,
TerritoryData territoryData, TerritoryData territoryData,
@ -46,8 +47,8 @@ internal static class AethernetShortcut
public ITask Use(EAetheryteLocation from, EAetheryteLocation to, SkipAetheryteCondition? skipConditions = null) public ITask Use(EAetheryteLocation from, EAetheryteLocation to, SkipAetheryteCondition? skipConditions = null)
{ {
return new UseAethernetShortcut(from, to, skipConditions ?? new(), return new UseAethernetShortcut(from, to, skipConditions ?? new(),
loggerFactory.CreateLogger<UseAethernetShortcut>(), aetheryteFunctions, gameFunctions, clientState, loggerFactory.CreateLogger<UseAethernetShortcut>(), aetheryteFunctions, gameFunctions, questFunctions,
aetheryteData, territoryData, lifestreamIpc, movementController, condition); clientState, aetheryteData, territoryData, lifestreamIpc, movementController, condition);
} }
} }
@ -58,6 +59,7 @@ internal static class AethernetShortcut
ILogger<UseAethernetShortcut> logger, ILogger<UseAethernetShortcut> logger,
AetheryteFunctions aetheryteFunctions, AetheryteFunctions aetheryteFunctions,
GameFunctions gameFunctions, GameFunctions gameFunctions,
QuestFunctions questFunctions,
IClientState clientState, IClientState clientState,
AetheryteData aetheryteData, AetheryteData aetheryteData,
TerritoryData territoryData, TerritoryData territoryData,
@ -90,6 +92,20 @@ internal static class AethernetShortcut
return false; return false;
} }
if (skipConditions.QuestsCompleted.Count > 0 &&
skipConditions.QuestsCompleted.All(questFunctions.IsQuestComplete))
{
logger.LogInformation("Skipping aethernet shortcut, all prequisite quests are complete");
return true;
}
if (skipConditions.QuestsAccepted.Count > 0 &&
skipConditions.QuestsAccepted.All(questFunctions.IsQuestAccepted))
{
logger.LogInformation("Skipping aethernet shortcut, all prequisite quests are accepted");
return true;
}
if (skipConditions.AetheryteLocked != null && if (skipConditions.AetheryteLocked != null &&
!aetheryteFunctions.IsAetheryteUnlocked(skipConditions.AetheryteLocked.Value)) !aetheryteFunctions.IsAetheryteUnlocked(skipConditions.AetheryteLocked.Value))
{ {
@ -126,10 +142,10 @@ internal static class AethernetShortcut
logger.LogInformation("Moving to S9 aetheryte"); logger.LogInformation("Moving to S9 aetheryte");
List<Vector3> nearbyPoints = List<Vector3> nearbyPoints =
[ [
new(7.225532f, 8.467899f, -7.1670876f), new(0, 8.442986f, 9),
new(7.177844f, 8.467899f, 7.2216787f), new(9, 8.442986f, 0),
new(-7.0762224f, 8.467898f, 7.1924725f), new(-9, 8.442986f, 0),
new(-7.1289554f, 8.467898f, -7.0594683f) new(0, 8.442986f, -9),
]; ];
Vector3 closestPoint = nearbyPoints.MinBy(x => (playerPosition - x).Length()); Vector3 closestPoint = nearbyPoints.MinBy(x => (playerPosition - x).Length());

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
@ -93,6 +94,20 @@ internal static class AetheryteShortcut
return true; return true;
} }
if (skipConditions.QuestsCompleted.Count > 0 &&
skipConditions.QuestsCompleted.All(questFunctions.IsQuestComplete))
{
logger.LogInformation("Skipping aetheryte, all prequisite quests are complete");
return true;
}
if (skipConditions.QuestsAccepted.Count > 0 &&
skipConditions.QuestsAccepted.All(questFunctions.IsQuestAccepted))
{
logger.LogInformation("Skipping aetheryte, all prequisite quests are accepted");
return true;
}
if (skipConditions.AetheryteLocked != null && if (skipConditions.AetheryteLocked != null &&
!aetheryteFunctions.IsAetheryteUnlocked(skipConditions.AetheryteLocked.Value)) !aetheryteFunctions.IsAetheryteUnlocked(skipConditions.AetheryteLocked.Value))
{ {

View File

@ -8,7 +8,8 @@ using LLib.GameData;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Questionable.Model; using Questionable.Model;
using Questionable.Model.Questing; using Questionable.Model.Questing;
using Quest = Lumina.Excel.GeneratedSheets.Quest; using Leve = Lumina.Excel.GeneratedSheets2.Leve;
using Quest = Lumina.Excel.GeneratedSheets2.Quest;
namespace Questionable.Data; namespace Questionable.Data;
@ -62,7 +63,7 @@ internal sealed class QuestData
// workaround because the game doesn't require completion of the CT questline through normal means // workaround because the game doesn't require completion of the CT questline through normal means
QuestInfo aTimeToEveryPurpose = (QuestInfo)_quests[new QuestId(425)]; QuestInfo aTimeToEveryPurpose = (QuestInfo)_quests[new QuestId(425)];
aTimeToEveryPurpose.AddPreviousQuest(new QuestId(495)); aTimeToEveryPurpose.AddPreviousQuest(new QuestInfo.PreviousQuestInfo(new QuestId(495)));
} }
public IQuestInfo GetQuestInfo(ElementId elementId) public IQuestInfo GetQuestInfo(ElementId elementId)

View File

@ -12,7 +12,7 @@ internal sealed class TerritoryData
{ {
private readonly ImmutableDictionary<uint, string> _territoryNames; private readonly ImmutableDictionary<uint, string> _territoryNames;
private readonly ImmutableHashSet<ushort> _territoriesWithMount; private readonly ImmutableHashSet<ushort> _territoriesWithMount;
private readonly ImmutableHashSet<ushort> _dutyTerritories; private readonly ImmutableDictionary<ushort, uint> _dutyTerritories;
private readonly ImmutableDictionary<ushort, string> _instanceNames; private readonly ImmutableDictionary<ushort, string> _instanceNames;
public TerritoryData(IDataManager dataManager) public TerritoryData(IDataManager dataManager)
@ -35,8 +35,7 @@ internal sealed class TerritoryData
_dutyTerritories = dataManager.GetExcelSheet<TerritoryType>()! _dutyTerritories = dataManager.GetExcelSheet<TerritoryType>()!
.Where(x => x.RowId > 0 && x.ContentFinderCondition.Row != 0) .Where(x => x.RowId > 0 && x.ContentFinderCondition.Row != 0)
.Select(x => (ushort)x.RowId) .ToImmutableDictionary(x => (ushort)x.RowId, x => x.ContentFinderCondition.Value!.ContentType.Row);
.ToImmutableHashSet();
_instanceNames = dataManager.GetExcelSheet<ContentFinderCondition>()! _instanceNames = dataManager.GetExcelSheet<ContentFinderCondition>()!
.Where(x => x.RowId > 0 && x.Content != 0 && x.ContentLinkType == 1 && x.ContentType.Row != 6) .Where(x => x.RowId > 0 && x.Content != 0 && x.ContentLinkType == 1 && x.ContentType.Row != 6)
@ -56,7 +55,10 @@ internal sealed class TerritoryData
public bool CanUseMount(ushort territoryId) => _territoriesWithMount.Contains(territoryId); public bool CanUseMount(ushort territoryId) => _territoriesWithMount.Contains(territoryId);
public bool IsDutyInstance(ushort territoryId) => _dutyTerritories.Contains(territoryId); public bool IsDutyInstance(ushort territoryId) => _dutyTerritories.ContainsKey(territoryId);
public bool IsQuestBattleInstance(ushort territoryId) =>
_dutyTerritories.TryGetValue(territoryId, out uint contentType) && contentType == 7;
public string? GetInstanceName(ushort instanceId) => _instanceNames.GetValueOrDefault(instanceId); public string? GetInstanceName(ushort instanceId) => _instanceNames.GetValueOrDefault(instanceId);
} }

View File

@ -431,7 +431,8 @@ internal sealed unsafe class GameFunctions
if (!AgentSatisfactionSupply.Instance()->IsAgentActive()) if (!AgentSatisfactionSupply.Instance()->IsAgentActive())
return false; return false;
var flags = _condition.AsReadOnlySet(); var flags = _condition.AsReadOnlySet().ToHashSet();
flags.Remove(ConditionFlag.InDutyQueue); // irrelevant
return flags.Count == 2 && return flags.Count == 2 &&
flags.Contains(ConditionFlag.NormalConditions) && flags.Contains(ConditionFlag.NormalConditions) &&
flags.Contains(ConditionFlag.OccupiedInQuestEvent); flags.Contains(ConditionFlag.OccupiedInQuestEvent);

View File

@ -494,7 +494,8 @@ internal sealed unsafe class QuestFunctions
if (questInfo.PreviousQuests.Count == 0) if (questInfo.PreviousQuests.Count == 0)
return true; return true;
var completedQuests = questInfo.PreviousQuests.Count(x => IsQuestComplete(x) || x.Equals(extraCompletedQuest)); var completedQuests = questInfo.PreviousQuests.Count(x =>
HasEnoughProgressOnPreviousQuest(x) || x.QuestId.Equals(extraCompletedQuest));
if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.All && if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.All &&
questInfo.PreviousQuests.Count == completedQuests) questInfo.PreviousQuests.Count == completedQuests)
return true; return true;
@ -504,6 +505,20 @@ internal sealed unsafe class QuestFunctions
return false; return false;
} }
private bool HasEnoughProgressOnPreviousQuest(QuestInfo.PreviousQuestInfo previousQuestInfo)
{
if (IsQuestComplete(previousQuestInfo.QuestId))
return true;
if (previousQuestInfo.Sequence != 0 && IsQuestAccepted(previousQuestInfo.QuestId))
{
var progress = GetQuestProgressInfo(previousQuestInfo.QuestId);
return progress != null && progress.Sequence >= previousQuestInfo.Sequence;
}
return false;
}
private static bool HasCompletedPreviousInstances(QuestInfo questInfo) private static bool HasCompletedPreviousInstances(QuestInfo questInfo)
{ {
if (questInfo.PreviousInstanceContent.Count == 0) if (questInfo.PreviousInstanceContent.Count == 0)

View File

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using LLib.GameData; using LLib.GameData;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets2;
using Questionable.Model.Questing; using Questionable.Model.Questing;
namespace Questionable.Model; namespace Questionable.Model;
@ -14,7 +14,7 @@ internal sealed class LeveInfo : IQuestInfo
Level = leve.ClassJobLevel; Level = leve.ClassJobLevel;
JournalGenre = leve.JournalGenre.Row; JournalGenre = leve.JournalGenre.Row;
SortKey = QuestId.Value; SortKey = QuestId.Value;
IssuerDataId = leve.LevelLevemete.Value!.Object; IssuerDataId = leve.LevelLevemete.Value!.Object.Row;
ClassJobs = QuestInfoUtils.AsList(leve.ClassJobCategory.Value!); ClassJobs = QuestInfoUtils.AsList(leve.ClassJobCategory.Value!);
Expansion = (EExpansionVersion)leve.LevelLevemete.Value.Territory.Value!.ExVersion.Row; Expansion = (EExpansionVersion)leve.LevelLevemete.Value.Territory.Value!.ExVersion.Row;
} }

View File

@ -5,7 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using JetBrains.Annotations; using JetBrains.Annotations;
using LLib.GameData; using LLib.GameData;
using Questionable.Model.Questing; using Questionable.Model.Questing;
using ExcelQuest = Lumina.Excel.GeneratedSheets.Quest; using ExcelQuest = Lumina.Excel.GeneratedSheets2.Quest;
namespace Questionable.Model; namespace Questionable.Model;
@ -31,13 +31,18 @@ internal sealed class QuestInfo : IQuestInfo
}; };
Name = $"{quest.Name}{suffix}"; Name = $"{quest.Name}{suffix}";
Level = quest.ClassJobLevel0; Level = quest.ClassJobLevel[0];
IssuerDataId = quest.IssuerStart; IssuerDataId = quest.IssuerStart.Row;
IsRepeatable = quest.IsRepeatable; IsRepeatable = quest.IsRepeatable;
PreviousQuests = quest.PreviousQuest PreviousQuests =
.Select(x => new QuestId((ushort)(x.Row & 0xFFFF))) new List<PreviousQuestInfo>
.Where(x => x.Value != 0) {
.ToImmutableList(); new(new QuestId((ushort)(quest.PreviousQuest[0].Row & 0xFFFF)), quest.Unknown7),
new(new QuestId((ushort)(quest.PreviousQuest[1].Row & 0xFFFF))),
new(new QuestId((ushort)(quest.PreviousQuest[2].Row & 0xFFFF)))
}
.Where(x => x.QuestId.Value != 0)
.ToImmutableList();
PreviousQuestJoin = (QuestJoin)quest.PreviousQuestJoin; PreviousQuestJoin = (QuestJoin)quest.PreviousQuestJoin;
QuestLocks = quest.QuestLock QuestLocks = quest.QuestLock
.Select(x => new QuestId((ushort)(x.Row & 0xFFFFF))) .Select(x => new QuestId((ushort)(x.Row & 0xFFFFF)))
@ -47,7 +52,7 @@ internal sealed class QuestInfo : IQuestInfo
JournalGenre = quest.JournalGenre?.Row; JournalGenre = quest.JournalGenre?.Row;
SortKey = quest.SortKey; SortKey = quest.SortKey;
IsMainScenarioQuest = quest.JournalGenre?.Value?.JournalCategory?.Value?.JournalSection?.Row is 0 or 1; IsMainScenarioQuest = quest.JournalGenre?.Value?.JournalCategory?.Value?.JournalSection?.Row is 0 or 1;
CompletesInstantly = quest.ToDoCompleteSeq[0] == 0; CompletesInstantly = quest.TodoParams[0].ToDoCompleteSeq == 0;
PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList(); PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList();
PreviousInstanceContentJoin = (QuestJoin)quest.InstanceContentJoin; PreviousInstanceContentJoin = (QuestJoin)quest.InstanceContentJoin;
GrandCompany = (GrandCompany)quest.GrandCompany.Row; GrandCompany = (GrandCompany)quest.GrandCompany.Row;
@ -64,7 +69,7 @@ internal sealed class QuestInfo : IQuestInfo
public ushort Level { get; } public ushort Level { get; }
public uint IssuerDataId { get; } public uint IssuerDataId { get; }
public bool IsRepeatable { get; } public bool IsRepeatable { get; }
public ImmutableList<QuestId> PreviousQuests { get; set; } public ImmutableList<PreviousQuestInfo> PreviousQuests { get; set; }
public QuestJoin PreviousQuestJoin { get; } public QuestJoin PreviousQuestJoin { get; }
public ImmutableList<QuestId> QuestLocks { get; } public ImmutableList<QuestId> QuestLocks { get; }
public QuestJoin QuestLockJoin { get; } public QuestJoin QuestLockJoin { get; }
@ -89,8 +94,10 @@ internal sealed class QuestInfo : IQuestInfo
AtLeastOne = 2, AtLeastOne = 2,
} }
public void AddPreviousQuest(QuestId questId) public void AddPreviousQuest(PreviousQuestInfo questId)
{ {
PreviousQuests = [..PreviousQuests, questId]; PreviousQuests = [..PreviousQuests, questId];
} }
public sealed record PreviousQuestInfo(QuestId QuestId, byte Sequence = 0);
} }

View File

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using LLib.GameData; using LLib.GameData;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets2;
namespace Questionable.Model; namespace Questionable.Model;
@ -57,8 +57,8 @@ internal static class QuestInfoUtils
{ EClassJob.Dancer, classJobCategory.DNC }, { EClassJob.Dancer, classJobCategory.DNC },
{ EClassJob.Reaper, classJobCategory.RPR }, { EClassJob.Reaper, classJobCategory.RPR },
{ EClassJob.Sage, classJobCategory.SGE }, { EClassJob.Sage, classJobCategory.SGE },
{ EClassJob.Viper, classJobCategory.VPR }, { EClassJob.Viper, classJobCategory.Unknown1 },
{ EClassJob.Pictomancer, classJobCategory.PCT } { EClassJob.Pictomancer, classJobCategory.Unknown2 }
} }
.Where(y => y.Value) .Where(y => y.Value)
.Select(y => y.Key) .Select(y => y.Key)

View File

@ -19,19 +19,22 @@ internal sealed class QuestTooltipComponent
private readonly TerritoryData _territoryData; private readonly TerritoryData _territoryData;
private readonly QuestFunctions _questFunctions; private readonly QuestFunctions _questFunctions;
private readonly UiUtils _uiUtils; private readonly UiUtils _uiUtils;
private readonly Configuration _configuration;
public QuestTooltipComponent( public QuestTooltipComponent(
QuestRegistry questRegistry, QuestRegistry questRegistry,
QuestData questData, QuestData questData,
TerritoryData territoryData, TerritoryData territoryData,
QuestFunctions questFunctions, QuestFunctions questFunctions,
UiUtils uiUtils) UiUtils uiUtils,
Configuration configuration)
{ {
_questRegistry = questRegistry; _questRegistry = questRegistry;
_questData = questData; _questData = questData;
_territoryData = territoryData; _territoryData = territoryData;
_questFunctions = questFunctions; _questFunctions = questFunctions;
_uiUtils = uiUtils; _uiUtils = uiUtils;
_configuration = configuration;
} }
public void Draw(IQuestInfo quest) public void Draw(IQuestInfo quest)
@ -105,13 +108,13 @@ internal sealed class QuestTooltipComponent
foreach (var q in quest.PreviousQuests) foreach (var q in quest.PreviousQuests)
{ {
if (_questData.TryGetQuestInfo(q, out var qInfo)) if (_questData.TryGetQuestInfo(q.QuestId, out var qInfo))
{ {
var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q); var (iconColor, icon, _) = _uiUtils.GetQuestStyle(q.QuestId);
if (!_questRegistry.IsKnownQuest(qInfo.QuestId)) if (!_questRegistry.IsKnownQuest(qInfo.QuestId))
iconColor = ImGuiColors.DalamudGrey; iconColor = ImGuiColors.DalamudGrey;
_uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon); _uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo, _questFunctions.IsQuestComplete(q.QuestId) ? byte.MinValue : q.Sequence), iconColor, icon);
if (qInfo is QuestInfo qstInfo && (counter <= 2 || icon != FontAwesomeIcon.Check)) if (qInfo is QuestInfo qstInfo && (counter <= 2 || icon != FontAwesomeIcon.Check))
DrawQuestUnlocks(qstInfo, counter + 1); DrawQuestUnlocks(qstInfo, counter + 1);
@ -188,11 +191,17 @@ internal sealed class QuestTooltipComponent
ImGui.Unindent(); ImGui.Unindent();
} }
private static string FormatQuestUnlockName(IQuestInfo questInfo) private string FormatQuestUnlockName(IQuestInfo questInfo, byte sequence = 0)
{ {
string name = questInfo.Name;
if (_configuration.Advanced.AdditionalStatusInformation && sequence != 0)
name += $" {SeIconChar.ItemLevel.ToIconString()}";
if (questInfo.IsMainScenarioQuest) if (questInfo.IsMainScenarioQuest)
return $"{questInfo.Name} ({questInfo.QuestId}, MSQ)"; name += $" ({questInfo.QuestId}, MSQ)";
else else
return $"{questInfo.Name} ({questInfo.QuestId})"; name += $" ({questInfo.QuestId})";
return name;
} }
} }