Compare commits

...

10 Commits

110 changed files with 5903 additions and 1545 deletions

View File

@ -1,5 +1,5 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>2.11</Version> <Version>2.13</Version>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -238,8 +238,10 @@ public sealed class RendererPlugin : IDalamudPlugin
maximumAngle = x.MaximumAngle.GetValueOrDefault(); maximumAngle = x.MaximumAngle.GetValueOrDefault();
} }
#if false
var a = GatheringMath.CalculateLandingLocation(x, 0, 0); var a = GatheringMath.CalculateLandingLocation(x, 0, 0);
var b = GatheringMath.CalculateLandingLocation(x, 1, 1); var b = GatheringMath.CalculateLandingLocation(x, 1, 1);
#endif
return new List<Element> return new List<Element>
{ {
new Element(isCone new Element(isCone

View File

@ -0,0 +1,78 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
"Author": "liza",
"Steps": [
{
"DataId": 2013074,
"Position": {
"X": 304.3412,
"Y": 483.48206,
"Z": 143.11438
},
"TerritoryId": 960,
"InteractionType": "Interact",
"TargetTerritoryId": 1073,
"SkipConditions": {
"StepIf": {
"InTerritory": [
1073
]
}
}
},
{
"TerritoryId": 1073,
"InteractionType": "None"
}
],
"Groups": [
{
"Nodes": [
{
"DataId": 33840,
"Locations": [
{
"Position": {
"X": 10.28351,
"Y": 486.144,
"Z": -136.9586
}
}
]
}
]
},
{
"Nodes": [
{
"DataId": 33841,
"Locations": [
{
"Position": {
"X": 19.46428,
"Y": 485.9226,
"Z": -136.738
}
}
]
}
]
},
{
"Nodes": [
{
"DataId": 33643,
"Locations": [
{
"Position": {
"X": 14.21117,
"Y": 486.0551,
"Z": -143.435
}
}
]
}
]
}
]
}

View File

@ -0,0 +1,78 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
"Author": "liza",
"Steps": [
{
"DataId": 2013074,
"Position": {
"X": 304.3412,
"Y": 483.48206,
"Z": 143.11438
},
"TerritoryId": 960,
"InteractionType": "Interact",
"TargetTerritoryId": 1073,
"SkipConditions": {
"StepIf": {
"InTerritory": [
1073
]
}
}
},
{
"TerritoryId": 1073,
"InteractionType": "None"
}
],
"Groups": [
{
"Nodes": [
{
"DataId": 34350,
"Locations": [
{
"Position": {
"X": 18.9518,
"Y": 485.9131,
"Z": -133.3762
}
}
]
}
]
},
{
"Nodes": [
{
"DataId": 34351,
"Locations": [
{
"Position": {
"X": 15.71416,
"Y": 486.0302,
"Z": -136.2497
}
}
]
}
]
},
{
"Nodes": [
{
"DataId": 34349,
"Locations": [
{
"Position": {
"X": 9.524881,
"Y": 486.2234,
"Z": -142.2316
}
}
]
}
]
}
]
}

View File

@ -114,7 +114,7 @@
}, },
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "Action", "InteractionType": "Action",
"Action": "Buffet", "Action": "Buffet (Sanuwa)",
"Fly": true, "Fly": true,
"CompletionQuestVariablesFlags": [ "CompletionQuestVariablesFlags": [
null, null,
@ -134,7 +134,7 @@
}, },
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "Action", "InteractionType": "Action",
"Action": "Buffet", "Action": "Buffet (Sanuwa)",
"Fly": true, "Fly": true,
"CompletionQuestVariablesFlags": [ "CompletionQuestVariablesFlags": [
null, null,
@ -154,7 +154,7 @@
}, },
"TerritoryId": 401, "TerritoryId": 401,
"InteractionType": "Action", "InteractionType": "Action",
"Action": "Buffet", "Action": "Buffet (Sanuwa)",
"Fly": true, "Fly": true,
"CompletionQuestVariablesFlags": [ "CompletionQuestVariablesFlags": [
null, null,

View File

@ -1,7 +1,7 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true, "$": "TODO Check if this is a sp1ecial mount that can always fly",
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -22,7 +22,61 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1024775,
"Position": {
"X": -35.599304,
"Y": 55.9782,
"Z": 214.58752
},
"TerritoryId": 612,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1025170,
"Position": {
"X": 253.10132,
"Y": 45.434376,
"Z": 416.98328
},
"TerritoryId": 612,
"InteractionType": "Action",
"Action": "Buffet (Griffin)",
"Fly": true,
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
]
},
{
"DataId": 1025171,
"Position": {
"X": 349.8435,
"Y": 51.839485,
"Z": 229.66345
},
"TerritoryId": 612,
"InteractionType": "Action",
"Action": "Buffet (Griffin)",
"Fly": true,
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
]
}
] ]
}, },
{ {
@ -48,7 +102,7 @@
"StopDistance": 7, "StopDistance": 7,
"TerritoryId": 612, "TerritoryId": 612,
"InteractionType": "CompleteQuest", "InteractionType": "CompleteQuest",
"Fly": true "Mount": false
} }
] ]
} }

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -22,7 +21,20 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": -50.436653,
"Y": 41.158955,
"Z": -264.4358
},
"TerritoryId": 612,
"InteractionType": "Instruction",
"Comment": "Use item on enemy, then kill it",
"EnemySpawnType": "AutoOnEnterArea",
"KillEnemyDataIds": [
7030
]
}
] ]
}, },
{ {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -22,7 +21,126 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 2009344,
"Position": {
"X": 101.304565,
"Y": 41.031494,
"Z": -180.89569
},
"TerritoryId": 612,
"InteractionType": "WaitForManualProgress",
"Comment": "Kill enemy, then use item",
"ItemId": 2002439,
"EnemySpawnType": "AfterItemUse",
"KillEnemyDataIds": [
8591
],
"Fly": true,
"RequiredQuestVariables": [
null,
null,
[
{
"Low": 2
},
{
"Low": 3
}
],
null,
null,
null
],
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
]
},
{
"DataId": 2009345,
"Position": {
"X": 136.40039,
"Y": 50.369995,
"Z": -397.69592
},
"TerritoryId": 612,
"InteractionType": "WaitForManualProgress",
"Comment": "Kill enemy, then use item",
"ItemId": 2002439,
"EnemySpawnType": "AfterItemUse",
"KillEnemyDataIds": [
8591
],
"Fly": true,
"RequiredQuestVariables": [
null,
null,
[
{
"Low": 1
},
{
"Low": 3
}
],
null,
null,
null
],
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
]
},
{
"DataId": 2009346,
"Position": {
"X": 12.588623,
"Y": 41.672363,
"Z": -294.02612
},
"TerritoryId": 612,
"InteractionType": "WaitForManualProgress",
"Comment": "Kill enemy, then use item",
"ItemId": 2002439,
"EnemySpawnType": "AfterItemUse",
"KillEnemyDataIds": [
8591
],
"Fly": true,
"RequiredQuestVariables": [
null,
null,
[
{
"Low": 1
},
{
"Low": 2
}
],
null,
null,
null
],
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
]
}
] ]
}, },
{ {

View File

@ -0,0 +1,6 @@
```
0 0 xx 0 0 0 | 2009344 2009345 2009346
33 x x | unconfirmed
34 x x
35 x x
```

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,

View File

@ -0,0 +1,70 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1019468,
"Position": {
"X": 170.58057,
"Y": 13.02367,
"Z": -91.96619
},
"TerritoryId": 635,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Rhalgr's Reach",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024136,
"Position": {
"X": -29.34314,
"Y": -0.1424195,
"Z": -124.52893
},
"TerritoryId": 635,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"Position": {
"X": -63.63005,
"Y": -0.0012056828,
"Z": -101.09714
},
"TerritoryId": 635,
"InteractionType": "WalkTo"
},
{
"DataId": 2009050,
"Position": {
"X": 49.66809,
"Y": -0.015319824,
"Z": 58.182617
},
"TerritoryId": 635,
"InteractionType": "CompleteQuest",
"AethernetShortcut": [
"[Rhalgr's Reach] Western Rhalgr's Reach",
"[Rhalgr's Reach] Aetheryte Plaza"
]
}
]
}
]
}

View File

@ -0,0 +1,99 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024137,
"Position": {
"X": 47.77588,
"Y": 0,
"Z": 58.64038
},
"TerritoryId": 635,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Rhalgr's Reach",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024130,
"Position": {
"X": 614.61804,
"Y": 80.000015,
"Z": 624.567
},
"TerritoryId": 621,
"InteractionType": "Interact",
"AetheryteShortcut": "Lochs - Ala Mhigan Quarter",
"$": "QW: 0 0 0 0 0 0 -> QW: 1 0 0 0 0 128"
},
{
"DataId": 1024131,
"Position": {
"X": 585.3512,
"Y": 70,
"Z": 564.0801
},
"TerritoryId": 621,
"InteractionType": "Interact",
"$": "QW: 1 0 0 0 0 128 -> QW: 2 0 0 0 0 192",
"Fly": true
},
{
"DataId": 1024132,
"Position": {
"X": 683.71094,
"Y": 70,
"Z": 540.5508
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024133,
"Position": {
"X": 745.20483,
"Y": 69.99999,
"Z": 489.76868
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024103,
"Position": {
"X": 532.494,
"Y": 70,
"Z": 576.1653
},
"TerritoryId": 621,
"InteractionType": "CompleteQuest",
"Fly": true
}
]
}
]
}

View File

@ -0,0 +1,108 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024104,
"Position": {
"X": 530.81555,
"Y": 70,
"Z": 576.0128
},
"TerritoryId": 621,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Lochs - Ala Mhigan Quarter",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024134,
"Position": {
"X": 207.84314,
"Y": -0.3,
"Z": 338.55188
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"Position": {
"X": 196.67705,
"Y": -0.6,
"Z": 318.12054
},
"TerritoryId": 621,
"InteractionType": "Dive",
"StopDistance": 0.25
},
{
"DataId": 2009052,
"Position": {
"X": 144.97595,
"Y": -270.98505,
"Z": -12.283508
},
"TerritoryId": 621,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 2009054,
"Position": {
"X": 258.19788,
"Y": -274.06738,
"Z": -12.588745
},
"TerritoryId": 621,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 4,
"Steps": [
{
"TerritoryId": 621,
"InteractionType": "Duty",
"ContentFinderConditionId": 279
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024104,
"Position": {
"X": 530.81555,
"Y": 70,
"Z": 576.0128
},
"TerritoryId": 621,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,73 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024103,
"Position": {
"X": 532.494,
"Y": 70,
"Z": 576.1653
},
"TerritoryId": 621,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Lochs - Ala Mhigan Quarter",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024105,
"Position": {
"X": 739.22327,
"Y": 70,
"Z": 537.6515
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024107,
"Position": {
"X": 788.3878,
"Y": 69.999916,
"Z": 637.3845
},
"TerritoryId": 621,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024112,
"Position": {
"X": 796.933,
"Y": 69.9999,
"Z": 634.76
},
"TerritoryId": 621,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,159 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024125,
"Position": {
"X": 775.6007,
"Y": 69.99993,
"Z": 639.4292
},
"TerritoryId": 621,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Lochs - Ala Mhigan Quarter",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024113,
"Position": {
"X": 754.26855,
"Y": 70,
"Z": 424.70435
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024114,
"Position": {
"X": 5.2338257,
"Y": 0.054396715,
"Z": -3.3417358
},
"TerritoryId": 738,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 3,
"Steps": [
{
"Position": {
"X": -2.9216702,
"Y": -2.0000002,
"Z": -34.863174
},
"TerritoryId": 738,
"InteractionType": "Jump",
"JumpDestination": {
"Position": {
"X": -2.9769177,
"Y": -1.3092512,
"Z": -36.29651
}
},
"StopDistance": 0.25
},
{
"Position": {
"X": -2.9769177,
"Y": -1.3092512,
"Z": -36.29651
},
"TerritoryId": 738,
"InteractionType": "Jump",
"JumpDestination": {
"Position": {
"X": -2.8188858,
"Y": 0.16235979,
"Z": -38.626305
}
},
"StopDistance": 0.25
},
{
"DataId": 2009058,
"Position": {
"X": -2.8188858,
"Y": 0.16235979,
"Z": -38.626305
},
"TerritoryId": 738,
"InteractionType": "Interact",
"StopDistance": 4,
"$": "QW: 0 0 0 0 0 0 -> QW: 16 16 0 0 0 128"
},
{
"Position": {
"X": -0.01923807,
"Y": -2.0000002,
"Z": -35.31207
},
"TerritoryId": 738,
"InteractionType": "WalkTo",
"DisableNavmesh": true
},
{
"DataId": 1024118,
"Position": {
"X": 24.612793,
"Y": -0.3670061,
"Z": 5.0201416
},
"TerritoryId": 738,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1024114,
"Position": {
"X": 5.2338257,
"Y": 0.054396715,
"Z": -3.3417358
},
"TerritoryId": 738,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024119,
"Position": {
"X": 758.6632,
"Y": 70,
"Z": 446.31104
},
"TerritoryId": 621,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,145 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024120,
"Position": {
"X": 757.68665,
"Y": 70,
"Z": 444.3275
},
"TerritoryId": 621,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Lochs - Ala Mhigan Quarter",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1001821,
"Position": {
"X": -24.124573,
"Y": 38.000004,
"Z": 85.31323
},
"TerritoryId": 131,
"InteractionType": "Interact",
"AetheryteShortcut": "Ul'dah",
"AethernetShortcut": [
"[Ul'dah] Aetheryte Plaza",
"[Ul'dah] The Chamber of Rule"
]
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024122,
"Position": {
"X": 57.968994,
"Y": 4,
"Z": -121.53815
},
"TerritoryId": 130,
"InteractionType": "Interact",
"AethernetShortcut": [
"[Ul'dah] The Chamber of Rule",
"[Ul'dah] Adventurers' Guild"
]
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1024123,
"Position": {
"X": -132.9519,
"Y": 4.590753,
"Z": 238.7273
},
"TerritoryId": 141,
"InteractionType": "Interact",
"Fly": true,
"AethernetShortcut": [
"[Ul'dah] Adventurers' Guild",
"[Ul'dah] Gate of Nald (Central Thanalan)"
],
"DialogueChoices": [
{
"Type": "List",
"Prompt": "TEXT_STMBDB106_02967_Q1_000_000",
"Answer": "TEXT_STMBDB106_02967_A1_000_002"
}
]
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1024124,
"Position": {
"X": 254.35254,
"Y": -6.26633,
"Z": -152.36133
},
"TerritoryId": 141,
"InteractionType": "Interact",
"Fly": true,
"AetheryteShortcut": "Central Thanalan - Black Brush Station"
}
]
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1024102,
"Position": {
"X": -105.7298,
"Y": 6.9839897,
"Z": -4.135254
},
"TerritoryId": 131,
"InteractionType": "Interact",
"AetheryteShortcut": "Ul'dah",
"AethernetShortcut": [
"[Ul'dah] Aetheryte Plaza",
"[Ul'dah] Gladiators' Guild"
]
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024102,
"Position": {
"X": -105.7298,
"Y": 6.9839897,
"Z": -4.135254
},
"TerritoryId": 131,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,116 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024102,
"Position": {
"X": -105.7298,
"Y": 6.9839897,
"Z": -4.135254
},
"TerritoryId": 131,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024040,
"Position": {
"X": -201.06812,
"Y": 18,
"Z": 78.62964
},
"TerritoryId": 130,
"InteractionType": "Interact",
"AethernetShortcut": [
"[Ul'dah] Gladiators' Guild",
"[Ul'dah] Thaumaturges' Guild"
]
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024041,
"Position": {
"X": -22.781738,
"Y": 83.19999,
"Z": -7.5532227
},
"TerritoryId": 130,
"InteractionType": "Interact",
"AethernetShortcut": [
"[Ul'dah] Thaumaturges' Guild",
"[Ul'dah] Airship Landing"
]
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1004433,
"Position": {
"X": -23.605713,
"Y": 83.19999,
"Z": -2.3041382
},
"TerritoryId": 130,
"InteractionType": "Interact",
"TargetTerritoryId": 144
},
{
"DataId": 1024042,
"Position": {
"X": -51.682373,
"Y": 0.04428002,
"Z": 53.94055
},
"TerritoryId": 144,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"Position": {
"X": 12.5078335,
"Y": 4.1552944,
"Z": 45.718685
},
"TerritoryId": 144,
"InteractionType": "Jump",
"JumpDestination": {
"Position": {
"X": 12.60962,
"Y": 4.2181597,
"Z": 49.651253
}
}
},
{
"DataId": 1024043,
"Position": {
"X": -12.77179,
"Y": 20.999727,
"Z": 47.837036
},
"TerritoryId": 144,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,104 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024043,
"Position": {
"X": -12.77179,
"Y": 20.999727,
"Z": 47.837036
},
"TerritoryId": 144,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Gold Saucer",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1019070,
"Position": {
"X": 151.20166,
"Y": 14.7757225,
"Z": 95.78088
},
"TerritoryId": 628,
"InteractionType": "Interact",
"TargetTerritoryId": 639,
"AetheryteShortcut": "Kugane",
"AethernetShortcut": [
"[Kugane] Aetheryte Plaza",
"[Kugane] The Ruby Bazaar"
]
},
{
"DataId": 1020622,
"Position": {
"X": 0.045776367,
"Y": 0,
"Z": -2.3041382
},
"TerritoryId": 639,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024044,
"Position": {
"X": -110.36853,
"Y": 4,
"Z": -114.00018
},
"TerritoryId": 130,
"InteractionType": "Interact",
"AetheryteShortcut": "Ul'dah"
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1024045,
"Position": {
"X": -2.822998,
"Y": -2.0000012,
"Z": -17.166443
},
"TerritoryId": 212,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024045,
"Position": {
"X": -2.822998,
"Y": -2.0000012,
"Z": -17.166443
},
"TerritoryId": 212,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,132 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024045,
"Position": {
"X": -2.822998,
"Y": -2.0000012,
"Z": -17.166443
},
"TerritoryId": 212,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024046,
"Position": {
"X": 592.70605,
"Y": 80.00001,
"Z": 618.6465
},
"TerritoryId": 621,
"InteractionType": "Interact",
"AetheryteShortcut": "Lochs - Ala Mhigan Quarter"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1023036,
"Position": {
"X": -285.23688,
"Y": 11.183244,
"Z": 188.83032
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1024052,
"Position": {
"X": -238.54431,
"Y": 0.9244179,
"Z": 196.55139
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Comment": "Aim at jaw"
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1024340,
"Position": {
"X": -208.20941,
"Y": -0.3,
"Z": 207.6294
},
"TerritoryId": 621,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1024053,
"Position": {
"X": 8.590759,
"Y": 5.3881354,
"Z": 282.7954
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true,
"Comment": "Aim at nearest eye"
}
]
},
{
"Sequence": 6,
"Steps": [
{
"DataId": 1024341,
"Position": {
"X": -3.5248413,
"Y": 3.1742485,
"Z": 249.34766
},
"TerritoryId": 621,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1023036,
"Position": {
"X": -285.23688,
"Y": 11.183244,
"Z": 188.83032
},
"TerritoryId": 621,
"InteractionType": "CompleteQuest",
"Fly": true
}
]
}
]
}

View File

@ -0,0 +1,69 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024051,
"Position": {
"X": -285.29797,
"Y": 11.183233,
"Z": 187.02979
},
"TerritoryId": 621,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 2009047,
"Position": {
"X": 526.8176,
"Y": 69.962524,
"Z": 577.3861
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true,
"AetheryteShortcut": "Lochs - Ala Mhigan Quarter"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024055,
"Position": {
"X": 525.0781,
"Y": 70,
"Z": 574.6699
},
"TerritoryId": 621,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024057,
"Position": {
"X": 340.23022,
"Y": 74.00002,
"Z": 70.66455
},
"TerritoryId": 621,
"InteractionType": "CompleteQuest",
"Fly": true
}
]
}
]
}

View File

@ -0,0 +1,143 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024063,
"Position": {
"X": 339.62,
"Y": 74.00002,
"Z": 75.48633
},
"TerritoryId": 621,
"InteractionType": "AcceptQuest",
"DialogueChoices": [
{
"Type": "List",
"Prompt": "TEXT_STMBDB111_02972_Q1_000_000",
"Answer": "TEXT_STMBDB111_02972_A1_000_001"
}
]
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 2009048,
"Position": {
"X": 593.56055,
"Y": 79.972534,
"Z": 621.9425
},
"TerritoryId": 621,
"InteractionType": "Interact",
"AetheryteShortcut": "Lochs - Ala Mhigan Quarter"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024065,
"Position": {
"X": 749.9961,
"Y": 70.139626,
"Z": 522.88086
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1024066,
"Position": {
"X": 245.07507,
"Y": 122,
"Z": -349.0807
},
"TerritoryId": 737,
"InteractionType": "SinglePlayerDuty"
}
]
},
{
"Sequence": 4
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1024206,
"Position": {
"X": 251.69751,
"Y": 122,
"Z": -345.5406
},
"TerritoryId": 737,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 6,
"Steps": [
{
"DataId": 1024068,
"Position": {
"X": 741.1764,
"Y": 70,
"Z": 525.5055
},
"TerritoryId": 621,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 7,
"Steps": [
{
"DataId": 1024068,
"Position": {
"X": 741.1764,
"Y": 70,
"Z": 525.5055
},
"TerritoryId": 621,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1019468,
"Position": {
"X": 170.58057,
"Y": 13.02367,
"Z": -91.96619
},
"TerritoryId": 635,
"InteractionType": "CompleteQuest",
"AetheryteShortcut": "Rhalgr's Reach",
"AethernetShortcut": [
"[Rhalgr's Reach] Aetheryte Plaza",
"[Rhalgr's Reach] Northeastern Rhalgr's Reach"
]
}
]
}
]
}

View File

@ -0,0 +1,67 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1019468,
"Position": {
"X": 170.58057,
"Y": 13.02367,
"Z": -91.96619
},
"TerritoryId": 635,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Rhalgr's Reach",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1020463,
"Position": {
"X": 151.20166,
"Y": 14.7757225,
"Z": 95.750244
},
"TerritoryId": 628,
"InteractionType": "Interact",
"TargetTerritoryId": 639,
"AetheryteShortcut": "Kugane",
"AethernetShortcut": [
"[Kugane] Aetheryte Plaza",
"[Kugane] The Ruby Bazaar"
]
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024726,
"Position": {
"X": -11.367981,
"Y": 10.503965,
"Z": -212.75659
},
"TerritoryId": 628,
"InteractionType": "CompleteQuest",
"AethernetShortcut": [
"[Kugane] The Ruby Bazaar",
"[Kugane] Rakuza District"
]
}
]
}
]
}

View File

@ -0,0 +1,65 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024727,
"Position": {
"X": -12.375122,
"Y": 10.503965,
"Z": -213.76367
},
"TerritoryId": 628,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Kugane",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024731,
"Position": {
"X": 89.92139,
"Y": 3.9999497,
"Z": 52.262085
},
"TerritoryId": 628,
"InteractionType": "Interact",
"AethernetShortcut": [
"[Kugane] Rakuza District",
"[Kugane] Kogane Dori Markets"
]
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1019061,
"Position": {
"X": -0.045776367,
"Y": -7.9738514E-11,
"Z": -80.857605
},
"TerritoryId": 628,
"InteractionType": "CompleteQuest",
"AethernetShortcut": [
"[Kugane] Kogane Dori Markets",
"[Kugane] Aetheryte Plaza"
]
}
]
}
]
}

View File

@ -0,0 +1,106 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024738,
"Position": {
"X": 1.3884888,
"Y": -7.717861E-11,
"Z": -78.14148
},
"TerritoryId": 628,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Kugane",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024743,
"Position": {
"X": -123.1861,
"Y": -6.9999976,
"Z": -58.854065
},
"TerritoryId": 628,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024822,
"Position": {
"X": 885.0995,
"Y": 1.1792068,
"Z": 861.38696
},
"TerritoryId": 613,
"InteractionType": "SinglePlayerDuty"
}
]
},
{
"Sequence": 3
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1024826,
"Position": {
"X": 458.94556,
"Y": 30.580631,
"Z": 748.74475
},
"TerritoryId": 613,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1024830,
"Position": {
"X": 577.26404,
"Y": 0.93538165,
"Z": 852.7809
},
"TerritoryId": 613,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024836,
"Position": {
"X": -788.2658,
"Y": 11.709066,
"Z": -283.0091
},
"TerritoryId": 613,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,145 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024837,
"Position": {
"X": -791.04297,
"Y": 12.349811,
"Z": -283.8026
},
"TerritoryId": 613,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"Position": {
"X": -831.06116,
"Y": 20.168653,
"Z": -266.25217
},
"TerritoryId": 613,
"InteractionType": "WalkTo",
"TargetTerritoryId": 614,
"Mount": true
},
{
"DataId": 1024842,
"Position": {
"X": 789.6085,
"Y": 99.21144,
"Z": -288.5329
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1019285,
"Position": {
"X": 195.63586,
"Y": 5.16971,
"Z": -437.2473
},
"TerritoryId": 614,
"InteractionType": "Interact",
"$": "QW: 0 0 0 0 0 0 -> QW: 1 0 0 0 0 128"
},
{
"DataId": 1019286,
"Position": {
"X": 233.60034,
"Y": 5.2518563,
"Z": -425.3758
},
"TerritoryId": 614,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1024846,
"Position": {
"X": 309.46814,
"Y": 17.755785,
"Z": -445.97546
},
"TerritoryId": 614,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1024847,
"Position": {
"X": 59.067627,
"Y": 37.21815,
"Z": -529.1676
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1024849,
"Position": {
"X": 57.328125,
"Y": 36.90612,
"Z": -524.71204
},
"TerritoryId": 614,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"Position": {
"X": 228.99776,
"Y": 5.218606,
"Z": -407.7175
},
"TerritoryId": 614,
"InteractionType": "WalkTo",
"Fly": true
},
{
"DataId": 1020524,
"Position": {
"X": 173.20508,
"Y": 5.1910434,
"Z": -433.24945
},
"TerritoryId": 614,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,213 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1020524,
"Position": {
"X": 173.20508,
"Y": 5.1910434,
"Z": -433.24945
},
"TerritoryId": 614,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Yanxia - House of the Fierce",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024955,
"Position": {
"X": 350.14868,
"Y": 26.448109,
"Z": 609.1859
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true,
"AetheryteShortcut": "Yanxia - Namai"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024957,
"Position": {
"X": 365.9265,
"Y": 1.7862457,
"Z": 738.9486
},
"TerritoryId": 614,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1024966,
"Position": {
"X": -472.3125,
"Y": 1.2300053,
"Z": 537.77356
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1024794,
"Position": {
"X": -493.98032,
"Y": 1.3011811,
"Z": 542.90063
},
"TerritoryId": 614,
"InteractionType": "Interact",
"TargetTerritoryId": 759
},
{
"DataId": 162,
"Position": {
"X": 96.269165,
"Y": -3.4332886,
"Z": 81.01013
},
"TerritoryId": 759,
"InteractionType": "Interact",
"Comment": "Aethernet Attunement: [Doman Enclave] Ferry Docks"
},
{
"DataId": 130,
"Position": {
"X": -61.57019,
"Y": 0.77819824,
"Z": 90.684326
},
"TerritoryId": 759,
"InteractionType": "Interact",
"Comment": "Aethernet Attunement: [Doman Enclave] The Southern Enclave"
},
{
"DataId": 129,
"Position": {
"X": 8.987488,
"Y": 0.8086548,
"Z": -105.85187
},
"TerritoryId": 759,
"InteractionType": "Interact",
"Comment": "Aethernet Attunement: [Doman Enclave] The Northern Enclave"
},
{
"TerritoryId": 759,
"InteractionType": "AttuneAetheryte",
"Aetheryte": "Doman Enclave"
},
{
"DataId": 1024970,
"Position": {
"X": 40.238037,
"Y": 0,
"Z": 5.874695
},
"TerritoryId": 759,
"InteractionType": "Interact",
"DialogueChoices": [
{
"Type": "List",
"Prompt": "TEXT_STMBDC105_03026_Q1_000_000",
"Answer": "TEXT_STMBDC105_03026_A1_000_001"
}
]
}
]
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1024974,
"Position": {
"X": -10.330383,
"Y": 0.19997318,
"Z": 12.893799
},
"TerritoryId": 759,
"InteractionType": "Interact",
"DialogueChoices": [
{
"Type": "YesNo",
"Prompt": "TEXT_STMBDC105_03026_EVENTAREA_WARP_000_000",
"Yes": true
}
],
"TargetTerritoryId": 744
}
]
},
{
"Sequence": 6
},
{
"Sequence": 7,
"Steps": [
{
"DataId": 1025049,
"Position": {
"X": -0.015319824,
"Y": 0.19999999,
"Z": -4.837158
},
"TerritoryId": 744,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 2009289,
"Position": {
"X": 0.02468622,
"Y": 0.9079783,
"Z": 18.30971
},
"TerritoryId": 744,
"InteractionType": "Interact",
"TargetTerritoryId": 759
},
{
"DataId": 1024971,
"Position": {
"X": 6.0272217,
"Y": 0,
"Z": 18.631226
},
"TerritoryId": 759,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,91 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024971,
"Position": {
"X": 6.0272217,
"Y": 0,
"Z": 18.631226
},
"TerritoryId": 759,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Doman Enclave",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024980,
"Position": {
"X": -276.26465,
"Y": 53.240116,
"Z": -368.8869
},
"TerritoryId": 614,
"InteractionType": "Interact",
"AetheryteShortcut": "Yanxia - House of the Fierce",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024983,
"Position": {
"X": -260.60883,
"Y": 53.217503,
"Z": -645.594
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1024986,
"Position": {
"X": -348.9281,
"Y": 1.230035,
"Z": -344.13672
},
"TerritoryId": 614,
"InteractionType": "SinglePlayerDuty",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024989,
"Position": {
"X": 464.10315,
"Y": 17.720512,
"Z": 301.59448
},
"TerritoryId": 614,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,70 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024989,
"Position": {
"X": 464.10315,
"Y": 17.720512,
"Z": 301.59448
},
"TerritoryId": 614,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1025026,
"Position": {
"X": 3.7078857,
"Y": 0,
"Z": 17.471558
},
"TerritoryId": 759,
"InteractionType": "Interact",
"AetheryteShortcut": "Doman Enclave"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024974,
"Position": {
"X": -10.330383,
"Y": 0.19997318,
"Z": 12.893799
},
"TerritoryId": 759,
"InteractionType": "Interact",
"DialogueChoices": [
{
"Type": "YesNo",
"Prompt": "TEXT_STMBDC107_03028_EVENTAREA_WARP_000_056",
"Yes": true
}
],
"TargetTerritoryId": 744
},
{
"DataId": 1024999,
"Position": {
"X": 0.19836426,
"Y": 0.021091364,
"Z": -3.0975952
},
"TerritoryId": 744,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,96 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1025008,
"Position": {
"X": -0.19836426,
"Y": 0.021091364,
"Z": -2.7619019
},
"TerritoryId": 744,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 2009289,
"Position": {
"X": 0.02468622,
"Y": 0.9079783,
"Z": 18.30971
},
"TerritoryId": 744,
"InteractionType": "Interact",
"TargetTerritoryId": 759
},
{
"DataId": 1025012,
"Position": {
"X": 1.5715942,
"Y": 0,
"Z": 18.631226
},
"TerritoryId": 759,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1025019,
"Position": {
"X": 366.53687,
"Y": 1.286227,
"Z": 746.2118
},
"TerritoryId": 614,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1019070,
"Position": {
"X": 151.20166,
"Y": 14.7757225,
"Z": 95.78088
},
"TerritoryId": 628,
"InteractionType": "Interact",
"TargetTerritoryId": 639,
"AetheryteShortcut": "Kugane",
"AethernetShortcut": [
"[Kugane] Aetheryte Plaza",
"[Kugane] The Ruby Bazaar"
]
},
{
"DataId": 1020622,
"Position": {
"X": 0.045776367,
"Y": 0,
"Z": -2.3041382
},
"TerritoryId": 639,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,82 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1025023,
"Position": {
"X": -1.7853394,
"Y": 0.024148807,
"Z": -0.015319824
},
"TerritoryId": 639,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1024974,
"Position": {
"X": -10.330383,
"Y": 0.19997318,
"Z": 12.893799
},
"TerritoryId": 759,
"InteractionType": "Interact",
"AetheryteShortcut": "Doman Enclave",
"DialogueChoices": [
{
"Type": "YesNo",
"Prompt": "TEXT_STMBDD101_03070_EVENTAREA_WARP_100_027",
"Yes": true
}
],
"TargetTerritoryId": 744
},
{
"DataId": 1025552,
"Position": {
"X": 0.045776367,
"Y": 0.021091362,
"Z": -2.9145508
},
"TerritoryId": 744,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 2009289,
"Position": {
"X": 0.02468622,
"Y": 0.9079783,
"Z": 18.30971
},
"TerritoryId": 744,
"InteractionType": "Interact",
"TargetTerritoryId": 759
},
{
"DataId": 1025555,
"Position": {
"X": 148.05823,
"Y": -4.178815,
"Z": 60.135742
},
"TerritoryId": 759,
"InteractionType": "CompleteQuest",
"Comment": "AethernetShortcut: [Doman Enclave] Aetheryte Plaza -> [Doman Enclave] Ferry Docks"
}
]
}
]
}

View File

@ -0,0 +1,118 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1025557,
"Position": {
"X": 146.37976,
"Y": -4.178755,
"Z": 60.135742
},
"TerritoryId": 759,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Doman Enclave",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1019297,
"Position": {
"X": -275.9289,
"Y": 17.31996,
"Z": 512.9625
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true,
"$": "QW: 0 0 0 0 0 0 -> QW: 1 0 0 0 0 128"
},
{
"DataId": 1019303,
"Position": {
"X": -308.94946,
"Y": 17.73554,
"Z": 512.47424
},
"TerritoryId": 614,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 2009474,
"Position": {
"X": 67.185425,
"Y": 17.440979,
"Z": 349.3247
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 2009527,
"Position": {
"X": 457.26697,
"Y": 31.265625,
"Z": 234.79053
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 2009528,
"Position": {
"X": 545.3727,
"Y": 84.70276,
"Z": 126.32947
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 2009475,
"Position": {
"X": 546.9595,
"Y": 72.129395,
"Z": 39.78015
},
"TerritoryId": 614,
"InteractionType": "CompleteQuest",
"Fly": true
}
]
}
]
}

View File

@ -0,0 +1,73 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1025095,
"Position": {
"X": 466.23938,
"Y": 70.27501,
"Z": -58.701477
},
"TerritoryId": 614,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Yanxia - Namai",
"SkipConditions": {
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1025104,
"Position": {
"X": 408.71277,
"Y": 14.6418705,
"Z": 622.8275
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1024999,
"Position": {
"X": 0.19836426,
"Y": 0.021091364,
"Z": -3.0975952
},
"TerritoryId": 744,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024999,
"Position": {
"X": 0.19836426,
"Y": 0.021091364,
"Z": -3.0975952
},
"TerritoryId": 744,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,136 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024999,
"Position": {
"X": 0.19836426,
"Y": 0.021091364,
"Z": -3.0975952
},
"TerritoryId": 744,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1021505,
"Position": {
"X": 79.42322,
"Y": 33.00897,
"Z": -669.9474
},
"TerritoryId": 613,
"InteractionType": "Interact",
"AetheryteShortcut": "Ruby Sea - Onokoro",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"Position": {
"X": 116.05634,
"Y": 3.799895,
"Z": -868.98865
},
"TerritoryId": 613,
"InteractionType": "WalkTo",
"Fly": true
},
{
"DataId": 2009420,
"Position": {
"X": 106.30957,
"Y": 0.47296143,
"Z": -874.38776
},
"TerritoryId": 613,
"InteractionType": "Interact",
"Fly": true,
"DisableNavmesh": true
},
{
"Position": {
"X": 63.579952,
"Y": -1.1090306,
"Z": -869.52625
},
"TerritoryId": 613,
"InteractionType": "WalkTo",
"Fly": true
},
{
"DataId": 2009469,
"Position": {
"X": 78.20239,
"Y": 0.47296143,
"Z": -870.66455
},
"TerritoryId": 613,
"InteractionType": "Interact",
"Mount": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1025106,
"Position": {
"X": 97.00159,
"Y": 5.1987257,
"Z": -877.0428
},
"TerritoryId": 613,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024974,
"Position": {
"X": -10.330383,
"Y": 0.19997318,
"Z": 12.893799
},
"TerritoryId": 759,
"InteractionType": "Interact",
"AetheryteShortcut": "Doman Enclave",
"DialogueChoices": [
{
"Type": "YesNo",
"Prompt": "TEXT_STMBDD104_03073_EVENTAREA_WARP_100_004",
"Yes": true
}
],
"TargetTerritoryId": 744
},
{
"DataId": 1024999,
"Position": {
"X": 0.19836426,
"Y": 0.021091364,
"Z": -3.0975952
},
"TerritoryId": 744,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,120 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1024999,
"Position": {
"X": 0.19836426,
"Y": 0.021091364,
"Z": -3.0975952
},
"TerritoryId": 744,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1025523,
"Position": {
"X": 239.12415,
"Y": 0.9334852,
"Z": 754.1161
},
"TerritoryId": 614,
"InteractionType": "Interact",
"Fly": true,
"AetheryteShortcut": "Yanxia - Namai"
}
]
},
{
"Sequence": 2
},
{
"Sequence": 3,
"Steps": [
{
"TerritoryId": 786,
"InteractionType": "Duty",
"ContentFinderConditionId": 537
}
]
},
{
"Sequence": 4
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1025528,
"Position": {
"X": 4.3182373,
"Y": -8.000055,
"Z": 44.327393
},
"TerritoryId": 786,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 6
},
{
"Sequence": 7,
"Steps": [
{
"DataId": 1025538,
"Position": {
"X": 4.7455444,
"Y": 0,
"Z": 16.067688
},
"TerritoryId": 759,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1024974,
"Position": {
"X": -10.330383,
"Y": 0.19997318,
"Z": 12.893799
},
"TerritoryId": 759,
"InteractionType": "Interact",
"DialogueChoices": [
{
"Type": "YesNo",
"Prompt": "TEXT_STMBDD105_03074_EVENTAREA_WARP_100_004",
"Yes": true
}
],
"TargetTerritoryId": 744
},
{
"DataId": 1025597,
"Position": {
"X": 0.19836426,
"Y": 0.021091362,
"Z": -3.0060425
},
"TerritoryId": 744,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,73 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1025009,
"Position": {
"X": 1.3274536,
"Y": 0.021091362,
"Z": 0.59503174
},
"TerritoryId": 744,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1019468,
"Position": {
"X": 170.58057,
"Y": 13.02367,
"Z": -91.96619
},
"TerritoryId": 635,
"InteractionType": "Interact",
"AetheryteShortcut": "Rhalgr's Reach",
"AethernetShortcut": [
"[Rhalgr's Reach] Aetheryte Plaza",
"[Rhalgr's Reach] Northeastern Rhalgr's Reach"
]
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1025546,
"Position": {
"X": 70.87805,
"Y": 26.199663,
"Z": -609.43005
},
"TerritoryId": 621,
"InteractionType": "Interact",
"Fly": true,
"AetheryteShortcut": "Lochs - Porta Praetoria"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1025549,
"Position": {
"X": 1.4800415,
"Y": -1.1041565E-05,
"Z": -11.734253
},
"TerritoryId": 351,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,51 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "JerryWester",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1025549,
"Position": {
"X": 1.4800415,
"Y": -1.1041565E-05,
"Z": -11.734253
},
"TerritoryId": 351,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 2009467,
"Position": {
"X": -5.189492,
"Y": 0.4746897,
"Z": 0.3689831
},
"TerritoryId": 351,
"InteractionType": "SinglePlayerDuty"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1025549,
"Position": {
"X": 1.4800415,
"Y": -1.1041565E-05,
"Z": -11.734253
},
"TerritoryId": 351,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,8 +29,39 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1033750,
"Position": {
"X": -591.3329,
"Y": 72.00111,
"Z": -451.01093
},
"StopDistance": 5,
"TerritoryId": 813,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"TerritoryId": 813,
"InteractionType": "Craft",
"ItemId": 31167,
"ItemCount": 1
},
{
"DataId": 1033750,
"Position": {
"X": -591.3329,
"Y": 72.00111,
"Z": -451.01093
},
"StopDistance": 5,
"TerritoryId": 813,
"InteractionType": "Interact"
}
] ]
}, },
{ {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,8 +29,154 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 2011169,
"Position": {
"X": -95.903015,
"Y": 1.6326294,
"Z": 41.244995
},
"TerritoryId": 813,
"InteractionType": "Interact",
"Fly": true,
"RequiredQuestVariables": [
null,
null,
null,
[
{
"High": 1
},
{
"High": 4
},
{
"High": 5
}
],
null,
null
],
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
]
},
{
"DataId": 2011170,
"Position": {
"X": -84.916504,
"Y": 2.39563,
"Z": 36.850464
},
"TerritoryId": 813,
"InteractionType": "Interact",
"Fly": true,
"RequiredQuestVariables": [
null,
null,
null,
[
{
"High": 1
},
{
"High": 2
},
{
"High": 6
}
],
null,
null
],
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
]
},
{
"DataId": 2011171,
"Position": {
"X": -68.55884,
"Y": 2.5177002,
"Z": 38.04065
},
"TerritoryId": 813,
"InteractionType": "Interact",
"Fly": true,
"RequiredQuestVariables": [
null,
null,
null,
[
{
"High": 3
},
{
"High": 5
},
{
"High": 6
}
],
null,
null
],
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
]
},
{
"DataId": 2011172,
"Position": {
"X": -70.2984,
"Y": 1.449585,
"Z": 62.210938
},
"TerritoryId": 813,
"InteractionType": "Interact",
"Fly": true,
"RequiredQuestVariables": [
null,
null,
null,
[
{
"High": 2
},
{
"High": 3
},
{
"High": 4
}
],
null,
null
],
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
16
]
}
] ]
}, },
{ {
@ -45,6 +190,7 @@
}, },
"TerritoryId": 813, "TerritoryId": 813,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"AetheryteShortcut": "Lakeland - Ostall Imperative",
"Fly": true "Fly": true
}, },
{ {

View File

@ -0,0 +1,9 @@
```
0 0 0 xx 0 0 | 2011169 2011170 2011171 2011172
16(1) x x
32(2) x x
48(3) x x
64(4) x x
80(5) x x
96(6) x x
```

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,8 +29,39 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1033767,
"Position": {
"X": -608.6061,
"Y": 65.60222,
"Z": -431.81506
},
"TerritoryId": 813,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"TerritoryId": 813,
"InteractionType": "Craft",
"ItemId": 31169,
"ItemCount": 3
},
{
"DataId": 1033694,
"Position": {
"X": 593.19434,
"Y": 5.8981767,
"Z": 704.86
},
"TerritoryId": 813,
"InteractionType": "Interact",
"AetheryteShortcut": "Lakeland - Fort Jobb",
"Fly": true
}
] ]
}, },
{ {
@ -45,6 +75,7 @@
}, },
"TerritoryId": 813, "TerritoryId": 813,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"AetheryteShortcut": "Lakeland - Ostall Imperative",
"Fly": true "Fly": true
}, },
{ {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,8 +29,26 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": -27.174656,
"Y": 1.063369,
"Z": 661.43774
},
"TerritoryId": 813,
"InteractionType": "WalkTo",
"Fly": true
},
{
"DataId": 1034072,
"Position": {
"X": -25.92511,
"Y": 1.3904266,
"Z": 662.8976
},
"TerritoryId": 813,
"InteractionType": "Interact"
}
] ]
}, },
{ {
@ -45,6 +62,7 @@
}, },
"TerritoryId": 813, "TerritoryId": 813,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"AetheryteShortcut": "Lakeland - Ostall Imperative",
"Fly": true "Fly": true
}, },
{ {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,8 +29,38 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1033767,
"Position": {
"X": -608.6061,
"Y": 65.60222,
"Z": -431.81506
},
"TerritoryId": 813,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"TerritoryId": 813,
"InteractionType": "Craft",
"ItemId": 31181,
"ItemCount": 1
},
{
"DataId": 1034073,
"Position": {
"X": -504.0513,
"Y": 3.623286,
"Z": 25.802979
},
"TerritoryId": 813,
"InteractionType": "Interact",
"Fly": true
}
] ]
}, },
{ {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,13 +29,38 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"Position": {
"X": -134.17696,
"Y": 14.623874,
"Z": -124.68204
},
"TerritoryId": 813,
"InteractionType": "WalkTo",
"Fly": true
},
{
"DataId": 1034075,
"Position": {
"X": -137.71265,
"Y": 14.623856,
"Z": -124.89508
},
"StopDistance": 5,
"TerritoryId": 813,
"InteractionType": "Interact"
}
] ]
}, },
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"TerritoryId": 813,
"InteractionType": "Craft",
"ItemId": 31183,
"ItemCount": 3
},
{ {
"Position": { "Position": {
"X": -615.73865, "X": -615.73865,

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -18,6 +17,25 @@
} }
] ]
}, },
{
"Sequence": 1,
"Steps": [
{
"DataId": 1032413,
"Position": {
"X": 144.36548,
"Y": -19.982845,
"Z": -459.8001
},
"TerritoryId": 817,
"InteractionType": "Say",
"ChatMessage": {
"Key": "TEXT_BANQIQ114_03819_SAYTODO_000_000"
},
"Fly": true
}
]
},
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
@ -30,12 +48,7 @@
"TerritoryId": 817, "TerritoryId": 817,
"InteractionType": "WalkTo", "InteractionType": "WalkTo",
"AetheryteShortcut": "Rak'tika - Fanow", "AetheryteShortcut": "Rak'tika - Fanow",
"Fly": true, "Fly": true
"RequiredGatheredItems": [
]
}, },
{ {
"DataId": 1032643, "DataId": 1032643,

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -32,7 +31,16 @@
"AetheryteShortcut": "Rak'tika - Fanow", "AetheryteShortcut": "Rak'tika - Fanow",
"Fly": true, "Fly": true,
"RequiredGatheredItems": [ "RequiredGatheredItems": [
{
"QuestAcceptedAsClass": "Miner",
"ItemId": 29528,
"ItemCount": 3
},
{
"QuestAcceptedAsClass": "Botanist",
"ItemId": 29554,
"ItemCount": 3
}
] ]

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -32,9 +31,16 @@
"AetheryteShortcut": "Rak'tika - Fanow", "AetheryteShortcut": "Rak'tika - Fanow",
"Fly": true, "Fly": true,
"RequiredGatheredItems": [ "RequiredGatheredItems": [
{
"QuestAcceptedAsClass": "Miner",
"ItemId": 29529,
"ItemCount": 3
},
{
"QuestAcceptedAsClass": "Botanist",
"ItemId": 29555,
"ItemCount": 3
}
] ]
}, },
{ {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -17,6 +16,89 @@
"InteractionType": "AcceptQuest" "InteractionType": "AcceptQuest"
} }
] ]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1027264,
"Position": {
"X": 60.593506,
"Y": 36.247692,
"Z": -171.80133
},
"TerritoryId": 819,
"InteractionType": "Interact",
"TargetTerritoryId": 815,
"AethernetShortcut": [
"[Crystarium] The Pendants",
"[Crystarium] The Amaro Launch"
]
},
{
"DataId": 1030623,
"Position": {
"X": 665.6748,
"Y": -50.355476,
"Z": -641.5351
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 2010165,
"Position": {
"X": 483.48206,
"Y": -50.46167,
"Z": -631.52515
},
"TerritoryId": 815,
"InteractionType": "Combat",
"EnemySpawnType": "AfterInteraction",
"KillEnemyDataIds": [
10836
],
"Fly": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1030624,
"Position": {
"X": 500.54163,
"Y": -50.72601,
"Z": -647.5471
},
"TerritoryId": 815,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "CompleteQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"NextQuestId": 3624
}
]
} }
] ]
} }

View File

@ -0,0 +1,110 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"MaximumDistance": 100
}
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 2010166,
"Position": {
"X": 599.115,
"Y": -33.61566,
"Z": -304.49384
},
"TerritoryId": 815,
"InteractionType": "Combat",
"EnemySpawnType": "AfterInteraction",
"KillEnemyDataIds": [
10837
],
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 2010214,
"Position": {
"X": 488.85327,
"Y": -26.16925,
"Z": -12.008911
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "CompleteQuest",
"Fly": true,
"DialogueChoices": [
{
"Type": "List",
"Prompt": "TEXT_LUCKBA411_03624_Q1_000_100",
"Answer": "TEXT_LUCKBA411_03624_A1_000_100"
}
],
"NextQuestId": 3625
}
]
}
]
}

View File

@ -0,0 +1,169 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"MaximumDistance": 100
}
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1030626,
"Position": {
"X": 331.62415,
"Y": 1.4685826,
"Z": -244.95312
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1030627,
"Position": {
"X": 330.06775,
"Y": 1.4685818,
"Z": -164.0498
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1030628,
"Position": {
"X": 407.6753,
"Y": -28.443933,
"Z": 260.8224
},
"TerritoryId": 815,
"InteractionType": "Interact",
"AetheryteShortcut": "Amh Araeng - Inn at Journey's Head",
"Fly": true
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1030630,
"Position": {
"X": 368.39856,
"Y": -28.620693,
"Z": 353.78027
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1030632,
"Position": {
"X": 550.225,
"Y": -45.678886,
"Z": 282.82593
},
"StopDistance": 0.5,
"TerritoryId": 815,
"InteractionType": "Combat",
"EnemySpawnType": "AutoOnEnterArea",
"KillEnemyDataIds": [
10838
],
"Fly": true
}
]
},
{
"Sequence": 6,
"Steps": [
{
"DataId": 1030632,
"Position": {
"X": 550.225,
"Y": -45.678886,
"Z": 282.82593
},
"TerritoryId": 815,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 7,
"Steps": [
{
"DataId": 1030631,
"Position": {
"X": 367.72717,
"Y": -28.562725,
"Z": 356.19116
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "CompleteQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"Fly": true,
"NextQuestId": 3626
}
]
}
]
}

View File

@ -0,0 +1,69 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"MaximumDistance": 100
}
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1030636,
"Position": {
"X": 342.8855,
"Y": -35.843185,
"Z": 76.58496
},
"TerritoryId": 815,
"InteractionType": "SinglePlayerDuty",
"AetheryteShortcut": "Amh Araeng - Inn at Journey's Head",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "CompleteQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"Fly": true,
"NextQuestId": 3627
}
]
}
]
}

View File

@ -0,0 +1,144 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"MaximumDistance": 100
}
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1030638,
"Position": {
"X": 543.8773,
"Y": -6.503428,
"Z": 811.7036
},
"TerritoryId": 813,
"InteractionType": "Interact",
"AetheryteShortcut": "Crystarium",
"AethernetShortcut": [
"[Crystarium] Aetheryte Plaza",
"[Crystarium] Tessellation (Lakeland)"
],
"Fly": true,
"SkipConditions": {
"AetheryteShortcutIf": {
"InTerritory": [
813
]
}
}
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 2010215,
"Position": {
"X": 523.9795,
"Y": -2.0599976,
"Z": 795.1018
},
"TerritoryId": 813,
"InteractionType": "Combat",
"EnemySpawnType": "AfterInteraction",
"KillEnemyDataIds": [
10839
]
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1030639,
"Position": {
"X": 542.8092,
"Y": -6.489782,
"Z": 811.79517
},
"TerritoryId": 813,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1031033,
"Position": {
"X": 540.6727,
"Y": -6.4392195,
"Z": 801.87683
},
"TerritoryId": 813,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1031033,
"Position": {
"X": 540.6727,
"Y": -6.4392195,
"Z": 801.87683
},
"TerritoryId": 813,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "CompleteQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"Fly": true,
"NextQuestId": 3628
}
]
}
]
}

View File

@ -0,0 +1,137 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "AcceptQuest",
"AetheryteShortcut": "Amh Araeng - Mord Souq",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"MaximumDistance": 100
}
}
}
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1030697,
"Position": {
"X": 288.74634,
"Y": 7.1558266,
"Z": -219.2569
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1030641,
"Position": {
"X": -255.8786,
"Y": 23.447075,
"Z": -288.4718
},
"TerritoryId": 815,
"InteractionType": "SinglePlayerDuty",
"AetheryteShortcut": "Amh Araeng - Twine",
"Fly": true
}
]
},
{
"Sequence": 4
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1030641,
"Position": {
"X": -255.8786,
"Y": 23.447075,
"Z": -288.4718
},
"TerritoryId": 815,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 6,
"Steps": [
{
"DataId": 1030625,
"Position": {
"X": 218.73804,
"Y": 7.1558266,
"Z": -249.1341
},
"TerritoryId": 815,
"InteractionType": "Interact",
"AetheryteShortcut": "Amh Araeng - Mord Souq"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1030643,
"Position": {
"X": -0.16790771,
"Y": 3.9998174,
"Z": 201.9226
},
"TerritoryId": 819,
"InteractionType": "CompleteQuest",
"AetheryteShortcut": "Crystarium",
"AethernetShortcut": [
"[Crystarium] Aetheryte Plaza",
"[Crystarium] The Pendants"
]
}
]
}
]
}

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,6 +29,52 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1045113,
"Position": {
"X": -315.66345,
"Y": -144,
"Z": -492.0272
},
"TerritoryId": 959,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1044585,
"Position": {
"X": -496.14713,
"Y": -154.93994,
"Z": -484.88593
},
"TerritoryId": 959,
"InteractionType": "Say",
"Fly": true,
"ChatMessage": {
"Key": "TEXT_BANLOP114_04700_SAYTODO_000_050"
}
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1045113,
"Position": {
"X": -315.66345,
"Y": -144,
"Z": -492.0272
},
"TerritoryId": 959,
"InteractionType": "Interact",
"Fly": true
}
] ]
}, },
{ {
@ -42,7 +87,8 @@
"Z": -273.68756 "Z": -273.68756
}, },
"TerritoryId": 959, "TerritoryId": 959,
"InteractionType": "WalkTo" "InteractionType": "WalkTo",
"Fly": true
}, },
{ {
"DataId": 1044403, "DataId": 1044403,

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,11 +29,27 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1044412,
"Position": {
"X": -181.56714,
"Y": -49.19972,
"Z": -304.76843
},
"TerritoryId": 959,
"InteractionType": "Interact"
}
] ]
}, },
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"TerritoryId": 959,
"InteractionType": "Craft",
"ItemId": 38873,
"ItemCount": 3
},
{ {
"Position": { "Position": {
"X": -201.42024, "X": -201.42024,

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -30,6 +29,39 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1044586,
"Position": {
"X": -524.19324,
"Y": -158.8955,
"Z": -521.17194
},
"StopDistance": 0.5,
"TerritoryId": 959,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"TerritoryId": 959,
"InteractionType": "Craft",
"ItemId": 38875,
"ItemCount": 1
},
{
"DataId": 1044586,
"Position": {
"X": -524.19324,
"Y": -158.8955,
"Z": -521.17194
},
"TerritoryId": 959,
"InteractionType": "Interact"
}
] ]
}, },
{ {
@ -42,7 +74,9 @@
"Z": -273.68756 "Z": -273.68756
}, },
"TerritoryId": 959, "TerritoryId": 959,
"InteractionType": "WalkTo" "InteractionType": "WalkTo",
"AetheryteShortcut": "Mare Lamentorum - Bestways Burrow",
"Fly": true
}, },
{ {
"DataId": 1044403, "DataId": 1044403,

View File

@ -2,7 +2,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -31,6 +30,25 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1044613,
"Position": {
"X": 493.55298,
"Y": -163.52985,
"Z": -711.54346
},
"TerritoryId": 959,
"InteractionType": "Interact",
"AetheryteShortcut": "Mare Lamentorum - Bestways Burrow",
"Fly": true,
"DialogueChoices": [
{
"Type": "List",
"Prompt": "TEXT_BANLOP125_04711_Q1_000_000",
"Answer": "TEXT_BANLOP125_04711_A3_000_003"
}
]
}
] ]
}, },
{ {
@ -43,7 +61,9 @@
"Z": -273.68756 "Z": -273.68756
}, },
"TerritoryId": 959, "TerritoryId": 959,
"InteractionType": "WalkTo" "InteractionType": "WalkTo",
"AetheryteShortcut": "Mare Lamentorum - Bestways Burrow",
"Fly": true
}, },
{ {
"DataId": 1044403, "DataId": 1044403,

View File

@ -2,7 +2,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -31,6 +30,38 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1044615,
"Position": {
"X": -84.7334,
"Y": -49.589592,
"Z": -351.73578
},
"TerritoryId": 959,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 2,
"Steps": [
{
"TerritoryId": 959,
"InteractionType": "Craft",
"ItemId": 38887,
"ItemCount": 3
},
{
"DataId": 1044615,
"Position": {
"X": -84.7334,
"Y": -49.589592,
"Z": -351.73578
},
"TerritoryId": 959,
"InteractionType": "Interact"
}
] ]
}, },
{ {
@ -43,7 +74,8 @@
"Z": -273.68756 "Z": -273.68756
}, },
"TerritoryId": 959, "TerritoryId": 959,
"InteractionType": "WalkTo" "InteractionType": "WalkTo",
"Fly": true
}, },
{ {
"DataId": 1044403, "DataId": 1044403,

View File

@ -2,7 +2,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -31,11 +30,29 @@
{ {
"Sequence": 1, "Sequence": 1,
"Steps": [ "Steps": [
{
"DataId": 1044616,
"Position": {
"X": -630.67065,
"Y": -140.39087,
"Z": -789.79175
},
"StopDistance": 0.5,
"TerritoryId": 959,
"InteractionType": "Interact",
"Fly": true
}
] ]
}, },
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"TerritoryId": 959,
"InteractionType": "Craft",
"ItemId": 38889,
"ItemCount": 1
},
{ {
"Position": { "Position": {
"X": -201.42024, "X": -201.42024,
@ -43,7 +60,9 @@
"Z": -273.68756 "Z": -273.68756
}, },
"TerritoryId": 959, "TerritoryId": 959,
"InteractionType": "WalkTo" "InteractionType": "WalkTo",
"AetheryteShortcut": "Mare Lamentorum - Bestways Burrow",
"Fly": true
}, },
{ {
"DataId": 1044403, "DataId": 1044403,

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -18,9 +17,73 @@
} }
] ]
}, },
{
"Sequence": 1,
"Steps": [
{
"DataId": 2013074,
"Position": {
"X": 304.3412,
"Y": 483.48206,
"Z": 143.11438
},
"TerritoryId": 960,
"InteractionType": "Interact",
"TargetTerritoryId": 1073
},
{
"DataId": 1044059,
"Position": {
"X": -15.304871,
"Y": 494.9991,
"Z": -68.16211
},
"TerritoryId": 1073,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1044059,
"Position": {
"X": -15.304871,
"Y": 494.9991,
"Z": -68.16211
},
"TerritoryId": 1073,
"InteractionType": "Interact",
"RequiredGatheredItems": [
{
"QuestAcceptedAsClass": "Miner",
"ItemId": 38289,
"ItemCount": 3
},
{
"QuestAcceptedAsClass": "Botanist",
"ItemId": 38313,
"ItemCount": 3
}
]
}
]
},
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 1043421,
"Position": {
"X": 2.4261475,
"Y": 499.87805,
"Z": 46.036377
},
"TerritoryId": 1073,
"InteractionType": "Interact",
"TargetTerritoryId": 960
},
{ {
"DataId": 1043417, "DataId": 1043417,
"Position": { "Position": {

View File

@ -1,7 +1,7 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true, "$": "TODO Check if this is a special mount that can always fly",
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -18,9 +18,82 @@
} }
] ]
}, },
{
"Sequence": 1,
"Steps": [
{
"DataId": 1044060,
"Position": {
"X": 57.75537,
"Y": 269.00012,
"Z": -674.49457
},
"TerritoryId": 960,
"InteractionType": "Interact",
"AetheryteShortcut": "Ultima Thule - Abode of the Ea"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1044063,
"Position": {
"X": -193.53015,
"Y": 269.23087,
"Z": -315.63293
},
"TerritoryId": 960,
"InteractionType": "Action",
"Action": "Electric Flux",
"Fly": true,
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
]
},
{
"DataId": 1044062,
"Position": {
"X": -213.42798,
"Y": 268.01166,
"Z": -299.5194
},
"TerritoryId": 960,
"InteractionType": "Action",
"Action": "Electric Flux",
"Fly": true,
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
]
}
]
},
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 2013072,
"Position": {
"X": 456.65674,
"Y": 438.04077,
"Z": 310.2312
},
"TerritoryId": 960,
"InteractionType": "Interact",
"TargetTerritoryId": 960,
"AetheryteShortcut": "Ultima Thule - Base Omicron"
},
{ {
"DataId": 1043417, "DataId": 1043417,
"Position": { "Position": {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -18,9 +17,50 @@
} }
] ]
}, },
{
"Sequence": 1,
"Steps": [
{
"DataId": 1044064,
"Position": {
"X": 86.503296,
"Y": 269.08234,
"Z": -515.0683
},
"TerritoryId": 960,
"InteractionType": "Interact",
"AetheryteShortcut": "Ultima Thule - Abode of the Ea",
"Fly": true,
"RequiredGatheredItems": [
{
"QuestAcceptedAsClass": "Miner",
"ItemId": 38290,
"ItemCount": 3
},
{
"QuestAcceptedAsClass": "Botanist",
"ItemId": 38314,
"ItemCount": 3
}
]
}
]
},
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 2013072,
"Position": {
"X": 456.65674,
"Y": 438.04077,
"Z": 310.2312
},
"TerritoryId": 960,
"InteractionType": "Interact",
"TargetTerritoryId": 960,
"AetheryteShortcut": "Ultima Thule - Base Omicron"
},
{ {
"DataId": 1043417, "DataId": 1043417,
"Position": { "Position": {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -18,9 +17,50 @@
} }
] ]
}, },
{
"Sequence": 1,
"Steps": [
{
"DataId": 1044076,
"Position": {
"X": -499.96188,
"Y": 77.00467,
"Z": 241.07727
},
"StopDistance": 9,
"TerritoryId": 960,
"InteractionType": "Interact",
"AetheryteShortcut": "Ultima Thule - Reah Tahra",
"RequiredGatheredItems": [
{
"QuestAcceptedAsClass": "Miner",
"ItemId": 38297,
"ItemCount": 3
},
{
"QuestAcceptedAsClass": "Botanist",
"ItemId": 38321,
"ItemCount": 3
}
]
}
]
},
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 2013072,
"Position": {
"X": 456.65674,
"Y": 438.04077,
"Z": 310.2312
},
"TerritoryId": 960,
"InteractionType": "Interact",
"TargetTerritoryId": 960,
"AetheryteShortcut": "Ultima Thule - Base Omicron"
},
{ {
"DataId": 1043417, "DataId": 1043417,
"Position": { "Position": {

View File

@ -1,7 +1,6 @@
{ {
"$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": "liza", "Author": "liza",
"Disabled": true,
"QuestSequence": [ "QuestSequence": [
{ {
"Sequence": 0, "Sequence": 0,
@ -18,9 +17,61 @@
} }
] ]
}, },
{
"Sequence": 1,
"Steps": [
{
"DataId": 1044078,
"Position": {
"X": 97.154175,
"Y": 269.09738,
"Z": -498.3139
},
"TerritoryId": 960,
"InteractionType": "Interact",
"AetheryteShortcut": "Ultima Thule - Abode of the Ea",
"Fly": true,
"DialogueChoices": [
{
"Type": "List",
"Prompt": "TEXT_BANOMI127_04633_Q1_000_000",
"Answer": "TEXT_BANOMI127_04633_A1_000_002"
}
]
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1044079,
"Position": {
"X": 99.412476,
"Y": 269.09763,
"Z": -497.1237
},
"StopDistance": 5,
"TerritoryId": 960,
"InteractionType": "Interact"
}
]
},
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"DataId": 2013072,
"Position": {
"X": 456.65674,
"Y": 438.04077,
"Z": 310.2312
},
"TerritoryId": 960,
"InteractionType": "Interact",
"TargetTerritoryId": 960,
"AetheryteShortcut": "Ultima Thule - Base Omicron"
},
{ {
"DataId": 1043417, "DataId": 1043417,
"Position": { "Position": {

View File

@ -835,12 +835,14 @@
"Cure", "Cure",
"Esuna", "Esuna",
"Physick", "Physick",
"Buffet", "Buffet (Sanuwa)",
"Buffet (Griffin)",
"Fumigate", "Fumigate",
"Siphon Snout", "Siphon Snout",
"Red Gulal", "Red Gulal",
"Yellow Gulal", "Yellow Gulal",
"Blue Gulal" "Blue Gulal",
"Electric Flux"
] ]
} }
}, },

View File

@ -10,11 +10,13 @@ public sealed class ActionConverter() : EnumConverter<EAction>(Values)
{ EAction.Cure, "Cure" }, { EAction.Cure, "Cure" },
{ EAction.Esuna, "Esuna" }, { EAction.Esuna, "Esuna" },
{ EAction.Physick, "Physick" }, { EAction.Physick, "Physick" },
{ EAction.Buffet, "Buffet" }, { EAction.BuffetSanuwa, "Buffet (Sanuwa)" },
{ EAction.BuffetGriffin, "Buffet (Griffin)" },
{ EAction.Fumigate, "Fumigate" }, { EAction.Fumigate, "Fumigate" },
{ EAction.SiphonSnout, "Siphon Snout" }, { EAction.SiphonSnout, "Siphon Snout" },
{ EAction.RedGulal, "Red Gulal" }, { EAction.RedGulal, "Red Gulal" },
{ EAction.YellowGulal, "Yellow Gulal" }, { EAction.YellowGulal, "Yellow Gulal" },
{ EAction.BlueGulal, "Blue Gulal" }, { EAction.BlueGulal, "Blue Gulal" },
{ EAction.ElectrixFlux, "Electric Flux" },
}; };
} }

View File

@ -9,12 +9,14 @@ public enum EAction
Cure = 120, Cure = 120,
Esuna = 7568, Esuna = 7568,
Physick = 190, Physick = 190,
Buffet = 4931, BuffetSanuwa = 4931,
BuffetGriffin = 4583,
Fumigate = 5872, Fumigate = 5872,
SiphonSnout = 18187, SiphonSnout = 18187,
RedGulal = 29382, RedGulal = 29382,
YellowGulal = 29383, YellowGulal = 29383,
BlueGulal = 29384, BlueGulal = 29384,
ElectrixFlux = 29718,
CollectMiner = 240, CollectMiner = 240,
ScourMiner = 22182, ScourMiner = 22182,
@ -39,11 +41,13 @@ public static class EActionExtensions
public static bool RequiresMount(this EAction action) public static bool RequiresMount(this EAction action)
{ {
return action return action
is EAction.Buffet is EAction.BuffetSanuwa
or EAction.BuffetGriffin
or EAction.Fumigate or EAction.Fumigate
or EAction.SiphonSnout or EAction.SiphonSnout
or EAction.RedGulal or EAction.RedGulal
or EAction.YellowGulal or EAction.YellowGulal
or EAction.BlueGulal; or EAction.BlueGulal
or EAction.ElectrixFlux;
} }
} }

View File

@ -63,7 +63,6 @@ internal sealed class InteractionUiController : IDisposable
QuestData questData, QuestData questData,
IGameGui gameGui, IGameGui gameGui,
ITargetManager targetManager, ITargetManager targetManager,
IFramework framework,
IPluginLog pluginLog, IPluginLog pluginLog,
IClientState clientState, IClientState clientState,
ILogger<InteractionUiController> logger) ILogger<InteractionUiController> logger)
@ -92,6 +91,14 @@ internal sealed class InteractionUiController : IDisposable
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "TelepotTown", TeleportTownPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "TelepotTown", TeleportTownPostSetup);
unsafe
{
if (_gameGui.TryGetAddonByName("RhythmAction", out AtkUnitBase* addon))
{
addon->Close(true);
}
}
} }
private bool ShouldHandleUiInteractions => _isInitialCheck || _questController.IsRunning; private bool ShouldHandleUiInteractions => _isInitialCheck || _questController.IsRunning;
@ -778,7 +785,7 @@ internal sealed class InteractionUiController : IDisposable
{ {
if (ShouldHandleUiInteractions && if (ShouldHandleUiInteractions &&
_questController.HasCurrentTaskMatching(out AethernetShortcut.UseAethernetShortcut? aethernetShortcut) && _questController.HasCurrentTaskMatching(out AethernetShortcut.UseAethernetShortcut? aethernetShortcut) &&
EAetheryteLocationExtensions.IsFirmamentAetheryte(aethernetShortcut.From)) aethernetShortcut.From.IsFirmamentAetheryte())
{ {
// this might be better via atkvalues; but this works for now // this might be better via atkvalues; but this works for now
uint toIndex = aethernetShortcut.To switch uint toIndex = aethernetShortcut.To switch

View File

@ -76,7 +76,7 @@ internal sealed class LeveUiController : IDisposable
} }
} }
private unsafe void GuildLevePostSetup(AddonEvent type, AddonArgs args) private void GuildLevePostSetup(AddonEvent type, AddonArgs args)
{ {
var target = _targetManager.Target; var target = _targetManager.Target;
if (target == null) if (target == null)
@ -85,8 +85,8 @@ internal sealed class LeveUiController : IDisposable
if (_questController is { IsRunning: true, NextQuest: { Quest.Id: LeveId } nextQuest } && if (_questController is { IsRunning: true, NextQuest: { Quest.Id: LeveId } nextQuest } &&
_questFunctions.IsReadyToAcceptQuest(nextQuest.Quest.Id)) _questFunctions.IsReadyToAcceptQuest(nextQuest.Quest.Id))
{ {
var addon = (AddonGuildLeve*)args.Addon;
/* /*
var addon = (AddonGuildLeve*)args.Addon;
var atkValues = addon->AtkValues; var atkValues = addon->AtkValues;
var availableLeves = _questData.GetAllByIssuerDataId(target.DataId); var availableLeves = _questData.GetAllByIssuerDataId(target.DataId);

View File

@ -25,43 +25,59 @@ using Questionable.Functions;
using Questionable.GatheringPaths; using Questionable.GatheringPaths;
using Questionable.Model.Gathering; using Questionable.Model.Gathering;
using Questionable.Model.Questing; using Questionable.Model.Questing;
using Mount = Questionable.Controller.Steps.Common.Mount;
namespace Questionable.Controller; namespace Questionable.Controller;
internal sealed unsafe class GatheringController : MiniTaskController<GatheringController> internal sealed unsafe class GatheringController : MiniTaskController<GatheringController>
{ {
private readonly MovementController _movementController; private readonly MovementController _movementController;
private readonly MoveTo.Factory _moveFactory;
private readonly Mount.Factory _mountFactory;
private readonly Interact.Factory _interactFactory;
private readonly GatheringPointRegistry _gatheringPointRegistry; private readonly GatheringPointRegistry _gatheringPointRegistry;
private readonly GameFunctions _gameFunctions; private readonly GameFunctions _gameFunctions;
private readonly NavmeshIpc _navmeshIpc; private readonly NavmeshIpc _navmeshIpc;
private readonly IObjectTable _objectTable; private readonly IObjectTable _objectTable;
private readonly IServiceProvider _serviceProvider;
private readonly ICondition _condition; private readonly ICondition _condition;
private readonly ILoggerFactory _loggerFactory;
private readonly IGameGui _gameGui;
private readonly IClientState _clientState;
private readonly Regex _revisitRegex; private readonly Regex _revisitRegex;
private CurrentRequest? _currentRequest; private CurrentRequest? _currentRequest;
public GatheringController( public GatheringController(
MovementController movementController, MovementController movementController,
MoveTo.Factory moveFactory,
Mount.Factory mountFactory,
Interact.Factory interactFactory,
GatheringPointRegistry gatheringPointRegistry, GatheringPointRegistry gatheringPointRegistry,
GameFunctions gameFunctions, GameFunctions gameFunctions,
NavmeshIpc navmeshIpc, NavmeshIpc navmeshIpc,
IObjectTable objectTable, IObjectTable objectTable,
IChatGui chatGui, IChatGui chatGui,
ILogger<GatheringController> logger, ILogger<GatheringController> logger,
IServiceProvider serviceProvider,
ICondition condition, ICondition condition,
IDataManager dataManager, IDataManager dataManager,
ILoggerFactory loggerFactory,
IGameGui gameGui,
IClientState clientState,
IPluginLog pluginLog) IPluginLog pluginLog)
: base(chatGui, logger) : base(chatGui, logger)
{ {
_movementController = movementController; _movementController = movementController;
_moveFactory = moveFactory;
_mountFactory = mountFactory;
_interactFactory = interactFactory;
_gatheringPointRegistry = gatheringPointRegistry; _gatheringPointRegistry = gatheringPointRegistry;
_gameFunctions = gameFunctions; _gameFunctions = gameFunctions;
_navmeshIpc = navmeshIpc; _navmeshIpc = navmeshIpc;
_objectTable = objectTable; _objectTable = objectTable;
_serviceProvider = serviceProvider;
_condition = condition; _condition = condition;
_loggerFactory = loggerFactory;
_gameGui = gameGui;
_clientState = clientState;
_revisitRegex = dataManager.GetRegex<LogMessage>(5574, x => x.Text, pluginLog) _revisitRegex = dataManager.GetRegex<LogMessage>(5574, x => x.Text, pluginLog)
?? throw new InvalidDataException("No regex found for revisit message"); ?? throw new InvalidDataException("No regex found for revisit message");
@ -152,8 +168,7 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
return; return;
ushort territoryId = _currentRequest.Root.Steps.Last().TerritoryId; ushort territoryId = _currentRequest.Root.Steps.Last().TerritoryId;
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<MountTask>() _taskQueue.Enqueue(_mountFactory.Mount(territoryId, Mount.EMountIf.Always));
.With(territoryId, MountTask.EMountIf.Always));
bool fly = currentNode.Fly.GetValueOrDefault(_currentRequest.Root.FlyBetweenNodes.GetValueOrDefault(true)) && bool fly = currentNode.Fly.GetValueOrDefault(_currentRequest.Root.FlyBetweenNodes.GetValueOrDefault(true)) &&
_gameFunctions.IsFlyingUnlocked(territoryId); _gameFunctions.IsFlyingUnlocked(territoryId);
@ -170,24 +185,28 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
if (pointOnFloor != null) if (pointOnFloor != null)
pointOnFloor = pointOnFloor.Value with { Y = pointOnFloor.Value.Y + (fly ? 3f : 0f) }; pointOnFloor = pointOnFloor.Value with { Y = pointOnFloor.Value.Y + (fly ? 3f : 0f) };
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<Move.MoveInternal>() _taskQueue.Enqueue(_moveFactory.Move(new MoveTo.MoveParams(territoryId, pointOnFloor ?? averagePosition,
.With(territoryId, pointOnFloor ?? averagePosition, 50f, fly: fly, 50f,
ignoreDistanceToObject: true)); Fly: fly, IgnoreDistanceToObject: true)));
} }
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<MoveToLandingLocation>() _taskQueue.Enqueue(new MoveToLandingLocation(territoryId, fly, currentNode, _moveFactory, _gameFunctions,
.With(territoryId, fly, currentNode)); _objectTable, _loggerFactory.CreateLogger<MoveToLandingLocation>()));
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<Interact.DoInteract>() _taskQueue.Enqueue(_interactFactory.Interact(currentNode.DataId, null, EInteractionType.InternalGather, true));
.With(currentNode.DataId, null, EInteractionType.InternalGather, true));
QueueGatherNode(currentNode);
}
private void QueueGatherNode(GatheringNode currentNode)
{
foreach (bool revisitRequired in new[] { false, true }) foreach (bool revisitRequired in new[] { false, true })
{ {
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGather>() _taskQueue.Enqueue(new DoGather(_currentRequest!.Data, currentNode, revisitRequired, this, _gameFunctions,
.With(_currentRequest.Data, currentNode, revisitRequired)); _gameGui, _clientState, _condition, _loggerFactory.CreateLogger<DoGather>()));
if (_currentRequest.Data.Collectability > 0) if (_currentRequest.Data.Collectability > 0)
{ {
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGatherCollectable>() _taskQueue.Enqueue(new DoGatherCollectable(_currentRequest.Data, currentNode, revisitRequired, this,
.With(_currentRequest.Data, currentNode, revisitRequired)); _gameFunctions, _clientState, _gameGui, _loggerFactory.CreateLogger<DoGatherCollectable>()));
} }
} }
} }

View File

@ -151,8 +151,9 @@ internal sealed class MovementController : IDisposable
if (IsPathRunning && Destination != null) if (IsPathRunning && Destination != null)
{ {
if (_gameFunctions.IsLoadingScreenVisible()) if (_gameFunctions.IsLoadingScreenVisible(false))
{ {
_logger.LogInformation("Stopping movement, loading screen visible");
Stop(); Stop();
return; return;
} }

View File

@ -0,0 +1,183 @@
using System;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Common.Math;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Functions;
namespace Questionable.Controller.Steps.Common;
internal static class Mount
{
internal sealed class Factory(
GameFunctions gameFunctions,
ICondition condition,
TerritoryData territoryData,
IClientState clientState,
ILoggerFactory loggerFactory)
{
public ITask Mount(ushort territoryId, EMountIf mountIf, Vector3? position = null)
{
if (mountIf == EMountIf.AwayFromPosition)
ArgumentNullException.ThrowIfNull(position);
return new MountTask(territoryId, mountIf, position, gameFunctions, condition, territoryData, clientState,
loggerFactory.CreateLogger<MountTask>());
}
public ITask Unmount()
{
return new UnmountTask(condition, loggerFactory.CreateLogger<UnmountTask>(), gameFunctions);
}
}
private sealed class MountTask(
ushort territoryId,
EMountIf mountIf,
Vector3? position,
GameFunctions gameFunctions,
ICondition condition,
TerritoryData territoryData,
IClientState clientState,
ILogger<MountTask> logger) : ITask
{
private bool _mountTriggered;
private DateTime _retryAt = DateTime.MinValue;
public bool Start()
{
if (condition[ConditionFlag.Mounted])
return false;
if (!territoryData.CanUseMount(territoryId))
{
logger.LogInformation("Can't use mount in current territory {Id}", territoryId);
return false;
}
if (gameFunctions.HasStatusPreventingMount())
{
logger.LogInformation("Can't mount due to status preventing sprint or mount");
return false;
}
if (mountIf == EMountIf.AwayFromPosition)
{
Vector3 playerPosition = clientState.LocalPlayer?.Position ?? Vector3.Zero;
float distance = System.Numerics.Vector3.Distance(playerPosition, position.GetValueOrDefault());
if (territoryId == clientState.TerritoryType && distance < 30f && !Conditions.IsDiving)
{
logger.LogInformation("Not using mount, as we're close to the target");
return false;
}
logger.LogInformation(
"Want to use mount if away from destination ({Distance} yalms), trying (in territory {Id})...",
distance, territoryId);
}
else
logger.LogInformation("Want to use mount, trying (in territory {Id})...", territoryId);
if (!condition[ConditionFlag.InCombat])
{
_retryAt = DateTime.Now.AddSeconds(0.5);
return true;
}
return false;
}
public ETaskResult Update()
{
if (_mountTriggered && !condition[ConditionFlag.Mounted] && DateTime.Now > _retryAt)
{
logger.LogInformation("Not mounted, retrying...");
_mountTriggered = false;
_retryAt = DateTime.MaxValue;
}
if (!_mountTriggered)
{
if (gameFunctions.HasStatusPreventingMount())
{
logger.LogInformation("Can't mount due to status preventing sprint or mount");
return ETaskResult.TaskComplete;
}
_mountTriggered = gameFunctions.Mount();
_retryAt = DateTime.Now.AddSeconds(5);
return ETaskResult.StillRunning;
}
return condition[ConditionFlag.Mounted]
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
}
public override string ToString() => "Mount";
}
private sealed class UnmountTask(ICondition condition, ILogger<UnmountTask> logger, GameFunctions gameFunctions)
: ITask
{
private bool _unmountTriggered;
private DateTime _continueAt = DateTime.MinValue;
public bool Start()
{
if (!condition[ConditionFlag.Mounted])
return false;
logger.LogInformation("Step explicitly wants no mount, trying to unmount...");
if (condition[ConditionFlag.InFlight])
{
gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1);
return true;
}
_unmountTriggered = gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1);
return true;
}
public ETaskResult Update()
{
if (_continueAt >= DateTime.Now)
return ETaskResult.StillRunning;
if (!_unmountTriggered)
{
// if still flying, we still need to land
if (condition[ConditionFlag.InFlight])
gameFunctions.Unmount();
else
_unmountTriggered = gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1);
return ETaskResult.StillRunning;
}
if (condition[ConditionFlag.Mounted] && condition[ConditionFlag.InCombat])
{
_unmountTriggered = gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1);
return ETaskResult.StillRunning;
}
return condition[ConditionFlag.Mounted]
? ETaskResult.StillRunning
: ETaskResult.TaskComplete;
}
public override string ToString() => "Unmount";
}
public enum EMountIf
{
Always,
AwayFromPosition,
}
}

View File

@ -1,114 +0,0 @@
using System;
using System.Numerics;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Functions;
namespace Questionable.Controller.Steps.Common;
internal sealed class MountTask(
GameFunctions gameFunctions,
ICondition condition,
TerritoryData territoryData,
IClientState clientState,
ILogger<MountTask> logger) : ITask
{
private ushort _territoryId;
private EMountIf _mountIf;
private Vector3? _position;
private bool _mountTriggered;
private DateTime _retryAt = DateTime.MinValue;
public ITask With(ushort territoryId, EMountIf mountIf, Vector3? position = null)
{
_territoryId = territoryId;
_mountIf = mountIf;
_position = position;
if (_mountIf == EMountIf.AwayFromPosition)
ArgumentNullException.ThrowIfNull(position);
return this;
}
public bool Start()
{
if (condition[ConditionFlag.Mounted])
return false;
if (!territoryData.CanUseMount(_territoryId))
{
logger.LogInformation("Can't use mount in current territory {Id}", _territoryId);
return false;
}
if (gameFunctions.HasStatusPreventingMount())
{
logger.LogInformation("Can't mount due to status preventing sprint or mount");
return false;
}
if (_mountIf == EMountIf.AwayFromPosition)
{
Vector3 playerPosition = clientState.LocalPlayer?.Position ?? Vector3.Zero;
float distance = (playerPosition - _position.GetValueOrDefault()).Length();
if (_territoryId == clientState.TerritoryType && distance < 30f && !Conditions.IsDiving)
{
logger.LogInformation("Not using mount, as we're close to the target");
return false;
}
logger.LogInformation(
"Want to use mount if away from destination ({Distance} yalms), trying (in territory {Id})...",
distance, _territoryId);
}
else
logger.LogInformation("Want to use mount, trying (in territory {Id})...", _territoryId);
if (!condition[ConditionFlag.InCombat])
{
_retryAt = DateTime.Now.AddSeconds(0.5);
return true;
}
return false;
}
public ETaskResult Update()
{
if (_mountTriggered && !condition[ConditionFlag.Mounted] && DateTime.Now > _retryAt)
{
logger.LogInformation("Not mounted, retrying...");
_mountTriggered = false;
_retryAt = DateTime.MaxValue;
}
if (!_mountTriggered)
{
if (gameFunctions.HasStatusPreventingMount())
{
logger.LogInformation("Can't mount due to status preventing sprint or mount");
return ETaskResult.TaskComplete;
}
_mountTriggered = gameFunctions.Mount();
_retryAt = DateTime.Now.AddSeconds(5);
return ETaskResult.StillRunning;
}
return condition[ConditionFlag.Mounted]
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
}
public override string ToString() => "Mount";
public enum EMountIf
{
Always,
AwayFromPosition,
}
}

View File

@ -1,6 +1,4 @@
using System; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Functions; using Questionable.Functions;
using Questionable.Model; using Questionable.Model;
using Questionable.Model.Questing; using Questionable.Model.Questing;
@ -9,7 +7,7 @@ namespace Questionable.Controller.Steps.Common;
internal static class NextQuest internal static class NextQuest
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(QuestRegistry questRegistry, QuestController questController, QuestFunctions questFunctions, ILoggerFactory loggerFactory) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -22,37 +20,26 @@ internal static class NextQuest
if (step.NextQuestId == quest.Id) if (step.NextQuestId == quest.Id)
return null; return null;
return serviceProvider.GetRequiredService<SetQuest>() return new SetQuest(step.NextQuestId, quest.Id, questRegistry, questController, questFunctions, loggerFactory.CreateLogger<SetQuest>());
.With(step.NextQuestId, quest.Id);
} }
} }
internal sealed class SetQuest(QuestRegistry questRegistry, QuestController questController, QuestFunctions questFunctions, ILogger<SetQuest> logger) : ITask private sealed class SetQuest(ElementId nextQuestId, ElementId currentQuestId, QuestRegistry questRegistry, QuestController questController, QuestFunctions questFunctions, ILogger<SetQuest> logger) : ITask
{ {
public ElementId NextQuestId { get; set; } = null!;
public ElementId CurrentQuestId { get; set; } = null!;
public ITask With(ElementId nextQuestId, ElementId currentQuestId)
{
NextQuestId = nextQuestId;
CurrentQuestId = currentQuestId;
return this;
}
public bool Start() public bool Start()
{ {
if (questFunctions.IsQuestLocked(NextQuestId, CurrentQuestId)) if (questFunctions.IsQuestLocked(nextQuestId, currentQuestId))
{ {
logger.LogInformation("Can't set next quest to {QuestId}, quest is locked", NextQuestId); logger.LogInformation("Can't set next quest to {QuestId}, quest is locked", nextQuestId);
} }
else if (questRegistry.TryGetQuest(NextQuestId, out Quest? quest)) else if (questRegistry.TryGetQuest(nextQuestId, out Quest? quest))
{ {
logger.LogInformation("Setting next quest to {QuestId}: '{QuestName}'", NextQuestId, quest.Info.Name); logger.LogInformation("Setting next quest to {QuestId}: '{QuestName}'", nextQuestId, quest.Info.Name);
questController.SetNextQuest(quest); questController.SetNextQuest(quest);
} }
else else
{ {
logger.LogInformation("Next quest with id {QuestId} not found", NextQuestId); logger.LogInformation("Next quest with id {QuestId} not found", nextQuestId);
questController.SetNextQuest(null); questController.SetNextQuest(null);
} }
@ -61,6 +48,6 @@ internal static class NextQuest
public ETaskResult Update() => ETaskResult.TaskComplete; public ETaskResult Update() => ETaskResult.TaskComplete;
public override string ToString() => $"SetNextQuest({NextQuestId})"; public override string ToString() => $"SetNextQuest({nextQuestId})";
} }
} }

View File

@ -1,63 +0,0 @@
using System;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Functions;
namespace Questionable.Controller.Steps.Common;
internal sealed class UnmountTask(ICondition condition, ILogger<UnmountTask> logger, GameFunctions gameFunctions)
: ITask
{
private bool _unmountTriggered;
private DateTime _continueAt = DateTime.MinValue;
public bool Start()
{
if (!condition[ConditionFlag.Mounted])
return false;
logger.LogInformation("Step explicitly wants no mount, trying to unmount...");
if (condition[ConditionFlag.InFlight])
{
gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1);
return true;
}
_unmountTriggered = gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1);
return true;
}
public ETaskResult Update()
{
if (_continueAt >= DateTime.Now)
return ETaskResult.StillRunning;
if (!_unmountTriggered)
{
// if still flying, we still need to land
if (condition[ConditionFlag.InFlight])
gameFunctions.Unmount();
else
_unmountTriggered = gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1);
return ETaskResult.StillRunning;
}
if (condition[ConditionFlag.Mounted] && condition[ConditionFlag.InCombat])
{
_unmountTriggered = gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1);
return ETaskResult.StillRunning;
}
return condition[ConditionFlag.Mounted]
? ETaskResult.StillRunning
: ETaskResult.TaskComplete;
}
public override string ToString() => "Unmount";
}

View File

@ -1,8 +1,7 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
using Dalamud.Memory;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
@ -17,6 +16,9 @@ using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Gathering; namespace Questionable.Controller.Steps.Gathering;
internal sealed class DoGather( internal sealed class DoGather(
GatheringController.GatheringRequest currentRequest,
GatheringNode currentNode,
bool revisitRequired,
GatheringController gatheringController, GatheringController gatheringController,
GameFunctions gameFunctions, GameFunctions gameFunctions,
IGameGui gameGui, IGameGui gameGui,
@ -26,34 +28,22 @@ internal sealed class DoGather(
{ {
private const uint StatusGatheringRateUp = 218; private const uint StatusGatheringRateUp = 218;
private GatheringController.GatheringRequest _currentRequest = null!;
private GatheringNode _currentNode = null!;
private bool _revisitRequired;
private bool _revisitTriggered; private bool _revisitTriggered;
private bool _wasGathering; private bool _wasGathering;
private SlotInfo? _slotToGather; private SlotInfo? _slotToGather;
private Queue<EAction>? _actionQueue; private Queue<EAction>? _actionQueue;
public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode,
bool revisitRequired)
{
_currentRequest = currentRequest;
_currentNode = currentNode;
_revisitRequired = revisitRequired;
return this;
}
public bool Start() => true; public bool Start() => true;
public unsafe ETaskResult Update() public unsafe ETaskResult Update()
{ {
if (_revisitRequired && !_revisitTriggered) if (revisitRequired && !_revisitTriggered)
{ {
logger.LogInformation("No revisit"); logger.LogInformation("No revisit");
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
if (gatheringController.HasNodeDisappeared(_currentNode)) if (gatheringController.HasNodeDisappeared(currentNode))
{ {
logger.LogInformation("Node disappeared"); logger.LogInformation("Node disappeared");
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
@ -78,9 +68,9 @@ internal sealed class DoGather(
else else
{ {
var slots = ReadSlots(addonGathering); var slots = ReadSlots(addonGathering);
if (_currentRequest.Collectability > 0) if (currentRequest.Collectability > 0)
{ {
var slot = slots.Single(x => x.ItemId == _currentRequest.ItemId); var slot = slots.Single(x => x.ItemId == currentRequest.ItemId);
addonGathering->FireCallbackInt(slot.Index); addonGathering->FireCallbackInt(slot.Index);
} }
else else
@ -103,7 +93,7 @@ internal sealed class DoGather(
_actionQueue = GetNextActions(nodeCondition, slots); _actionQueue = GetNextActions(nodeCondition, slots);
if (_actionQueue.Count == 0) if (_actionQueue.Count == 0)
{ {
var slot = _slotToGather ?? slots.Single(x => x.ItemId == _currentRequest.ItemId); var slot = _slotToGather ?? slots.Single(x => x.ItemId == currentRequest.ItemId);
addonGathering->FireCallbackInt(slot.Index); addonGathering->FireCallbackInt(slot.Index);
} }
} }
@ -149,17 +139,18 @@ internal sealed class DoGather(
return slots; return slots;
} }
[SuppressMessage("ReSharper", "UnusedParameter.Local")]
private Queue<EAction> GetNextActions(NodeCondition nodeCondition, List<SlotInfo> slots) private Queue<EAction> GetNextActions(NodeCondition nodeCondition, List<SlotInfo> slots)
{ {
uint gp = clientState.LocalPlayer!.CurrentGp; //uint gp = clientState.LocalPlayer!.CurrentGp;
Queue<EAction> actions = new(); Queue<EAction> actions = new();
if (!gameFunctions.HasStatus(StatusGatheringRateUp)) if (!gameFunctions.HasStatus(StatusGatheringRateUp))
{ {
// do we have an alternative item? only happens for 'evaluation' leve quests // do we have an alternative item? only happens for 'evaluation' leve quests
if (_currentRequest.AlternativeItemId != 0) if (currentRequest.AlternativeItemId != 0)
{ {
var alternativeSlot = slots.Single(x => x.ItemId == _currentRequest.AlternativeItemId); var alternativeSlot = slots.Single(x => x.ItemId == currentRequest.AlternativeItemId);
if (alternativeSlot.GatheringChance == 100) if (alternativeSlot.GatheringChance == 100)
{ {
@ -195,7 +186,7 @@ internal sealed class DoGather(
} }
} }
var slot = slots.Single(x => x.ItemId == _currentRequest.ItemId); var slot = slots.Single(x => x.ItemId == currentRequest.ItemId);
if (slot.GatheringChance > 0 && slot.GatheringChance < 100) if (slot.GatheringChance > 0 && slot.GatheringChance < 100)
{ {
if (slot.GatheringChance >= 95 && if (slot.GatheringChance >= 95 &&
@ -243,10 +234,12 @@ internal sealed class DoGather(
_revisitTriggered = true; _revisitTriggered = true;
} }
public override string ToString() => $"DoGather{(_revisitRequired ? " if revist" : "")}"; public override string ToString() => $"DoGather{(revisitRequired ? " if revist" : "")}";
[SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
private sealed record SlotInfo(int Index, uint ItemId, int GatheringChance, int BoonChance, int Quantity); private sealed record SlotInfo(int Index, uint ItemId, int GatheringChance, int BoonChance, int Quantity);
[SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
private sealed record NodeCondition( private sealed record NodeCondition(
uint CurrentIntegrity, uint CurrentIntegrity,
uint MaxIntegrity); uint MaxIntegrity);

View File

@ -14,40 +14,31 @@ using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Gathering; namespace Questionable.Controller.Steps.Gathering;
internal sealed class DoGatherCollectable( internal sealed class DoGatherCollectable(
GatheringController.GatheringRequest currentRequest,
GatheringNode currentNode,
bool revisitRequired,
GatheringController gatheringController, GatheringController gatheringController,
GameFunctions gameFunctions, GameFunctions gameFunctions,
IClientState clientState, IClientState clientState,
IGameGui gameGui, IGameGui gameGui,
ILogger<DoGatherCollectable> logger) : ITask, IRevisitAware ILogger<DoGatherCollectable> logger) : ITask, IRevisitAware
{ {
private GatheringController.GatheringRequest _currentRequest = null!;
private GatheringNode _currentNode = null!;
private bool _revisitRequired;
private bool _revisitTriggered; private bool _revisitTriggered;
private Queue<EAction>? _actionQueue; private Queue<EAction>? _actionQueue;
private bool? _expectedScrutiny; private bool? _expectedScrutiny;
public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode,
bool revisitRequired)
{
_currentRequest = currentRequest;
_currentNode = currentNode;
_revisitRequired = revisitRequired;
return this;
}
public bool Start() => true; public bool Start() => true;
public unsafe ETaskResult Update() public unsafe ETaskResult Update()
{ {
if (_revisitRequired && !_revisitTriggered) if (revisitRequired && !_revisitTriggered)
{ {
logger.LogInformation("No revisit"); logger.LogInformation("No revisit");
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
if (gatheringController.HasNodeDisappeared(_currentNode)) if (gatheringController.HasNodeDisappeared(currentNode))
{ {
logger.LogInformation("Node disappeared"); logger.LogInformation("Node disappeared");
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
@ -103,7 +94,7 @@ internal sealed class DoGatherCollectable(
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
if (nodeCondition.CollectabilityToGoal(_currentRequest.Collectability) > 0) if (nodeCondition.CollectabilityToGoal(currentRequest.Collectability) > 0)
{ {
_actionQueue = GetNextActions(nodeCondition); _actionQueue = GetNextActions(nodeCondition);
if (_actionQueue != null) if (_actionQueue != null)
@ -147,7 +138,7 @@ internal sealed class DoGatherCollectable(
Queue<EAction> actions = new(); Queue<EAction> actions = new();
uint neededCollectability = nodeCondition.CollectabilityToGoal(_currentRequest.Collectability); uint neededCollectability = nodeCondition.CollectabilityToGoal(currentRequest.Collectability);
if (neededCollectability <= nodeCondition.CollectabilityFromMeticulous) if (neededCollectability <= nodeCondition.CollectabilityFromMeticulous)
{ {
logger.LogTrace("Can get all needed {NeededCollectability} from {Collectability}~ meticulous", logger.LogTrace("Can get all needed {NeededCollectability} from {Collectability}~ meticulous",
@ -203,7 +194,7 @@ internal sealed class DoGatherCollectable(
} }
public override string ToString() => public override string ToString() =>
$"DoGatherCollectable({SeIconChar.Collectible.ToIconString()}/{_expectedScrutiny} {_currentRequest.Collectability}){(_revisitRequired ? " if revist" : "")}"; $"DoGatherCollectable({SeIconChar.Collectible.ToIconString()}/{_expectedScrutiny} {currentRequest.Collectability}){(revisitRequired ? " if revist" : "")}";
[SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")] [SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
private sealed record NodeCondition( private sealed record NodeCondition(

View File

@ -1,5 +1,4 @@
using System; using System.Globalization;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
@ -14,49 +13,40 @@ using Questionable.Model.Gathering;
namespace Questionable.Controller.Steps.Gathering; namespace Questionable.Controller.Steps.Gathering;
internal sealed class MoveToLandingLocation( internal sealed class MoveToLandingLocation(
IServiceProvider serviceProvider, ushort territoryId,
bool flyBetweenNodes,
GatheringNode gatheringNode,
MoveTo.Factory moveFactory,
GameFunctions gameFunctions, GameFunctions gameFunctions,
IObjectTable objectTable, IObjectTable objectTable,
ILogger<MoveToLandingLocation> logger) : ITask ILogger<MoveToLandingLocation> logger) : ITask
{ {
private ushort _territoryId;
private bool _flyBetweenNodes;
private GatheringNode _gatheringNode = null!;
private ITask _moveTask = null!; private ITask _moveTask = null!;
public ITask With(ushort territoryId, bool flyBetweenNodes, GatheringNode gatheringNode)
{
_territoryId = territoryId;
_flyBetweenNodes = flyBetweenNodes;
_gatheringNode = gatheringNode;
return this;
}
public bool Start() public bool Start()
{ {
var location = _gatheringNode.Locations.First(); var location = gatheringNode.Locations.First();
if (_gatheringNode.Locations.Count > 1) if (gatheringNode.Locations.Count > 1)
{ {
var gameObject = objectTable.SingleOrDefault(x => var gameObject = objectTable.SingleOrDefault(x =>
x.ObjectKind == ObjectKind.GatheringPoint && x.DataId == _gatheringNode.DataId && x.IsTargetable); x.ObjectKind == ObjectKind.GatheringPoint && x.DataId == gatheringNode.DataId && x.IsTargetable);
if (gameObject == null) if (gameObject == null)
return false; return false;
location = _gatheringNode.Locations.Single(x => Vector3.Distance(x.Position, gameObject.Position) < 0.1f); location = gatheringNode.Locations.Single(x => Vector3.Distance(x.Position, gameObject.Position) < 0.1f);
} }
var (target, degrees, range) = GatheringMath.CalculateLandingLocation(location); var (target, degrees, range) = GatheringMath.CalculateLandingLocation(location);
logger.LogInformation("Preliminary landing location: {Location}, with degrees = {Degrees}, range = {Range}", logger.LogInformation("Preliminary landing location: {Location}, with degrees = {Degrees}, range = {Range}",
target.ToString("G", CultureInfo.InvariantCulture), degrees, range); target.ToString("G", CultureInfo.InvariantCulture), degrees, range);
bool fly = _flyBetweenNodes && gameFunctions.IsFlyingUnlocked(_territoryId); bool fly = flyBetweenNodes && gameFunctions.IsFlyingUnlocked(territoryId);
_moveTask = serviceProvider.GetRequiredService<Move.MoveInternal>() _moveTask = moveFactory.Move(new MoveTo.MoveParams(territoryId, target, 0.25f, DataId: gatheringNode.DataId,
.With(_territoryId, target, 0.25f, dataId: _gatheringNode.DataId, fly: fly, Fly: fly, IgnoreDistanceToObject: true));
ignoreDistanceToObject: true);
return _moveTask.Start(); return _moveTask.Start();
} }
public ETaskResult Update() => _moveTask.Update(); public ETaskResult Update() => _moveTask.Update();
public override string ToString() => $"Land/{_moveTask}/{_flyBetweenNodes}"; public override string ToString() => $"Land/{_moveTask}/{flyBetweenNodes}";
} }

View File

@ -13,18 +13,18 @@ namespace Questionable.Controller.Steps.Gathering;
internal static class TurnInDelivery internal static class TurnInDelivery
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(ILoggerFactory loggerFactory) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
if (quest.Id is not SatisfactionSupplyNpcId || sequence.Sequence != 1) if (quest.Id is not SatisfactionSupplyNpcId || sequence.Sequence != 1)
return null; return null;
return serviceProvider.GetRequiredService<SatisfactionSupplyTurnIn>(); return new SatisfactionSupplyTurnIn(loggerFactory.CreateLogger<SatisfactionSupplyTurnIn>());
} }
} }
internal sealed class SatisfactionSupplyTurnIn(ILogger<SatisfactionSupplyTurnIn> logger) : ITask private sealed class SatisfactionSupplyTurnIn(ILogger<SatisfactionSupplyTurnIn> logger) : ITask
{ {
private ushort? _remainingAllowances; private ushort? _remainingAllowances;

View File

@ -14,7 +14,8 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Action internal static class Action
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory internal sealed class Factory(GameFunctions gameFunctions, Mount.Factory mountFactory, ILoggerFactory loggerFactory)
: ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -23,54 +24,45 @@ internal static class Action
ArgumentNullException.ThrowIfNull(step.Action); ArgumentNullException.ThrowIfNull(step.Action);
var task = serviceProvider.GetRequiredService<UseOnObject>() var task = new UseOnObject(step.DataId, step.Action.Value, gameFunctions,
.With(step.DataId, step.Action.Value); loggerFactory.CreateLogger<UseOnObject>());
if (step.Action.Value.RequiresMount()) if (step.Action.Value.RequiresMount())
return [task]; return [task];
else else
{ return [mountFactory.Unmount(), task];
var unmount = serviceProvider.GetRequiredService<UnmountTask>();
return [unmount, task];
}
} }
} }
internal sealed class UseOnObject(GameFunctions gameFunctions, ILogger<UseOnObject> logger) : ITask private sealed class UseOnObject(
uint? dataId,
EAction action,
GameFunctions gameFunctions,
ILogger<UseOnObject> logger) : ITask
{ {
private bool _usedAction; private bool _usedAction;
private DateTime _continueAt = DateTime.MinValue; private DateTime _continueAt = DateTime.MinValue;
public uint? DataId { get; set; }
public EAction Action { get; set; }
public ITask With(uint? dataId, EAction action)
{
DataId = dataId;
Action = action;
return this;
}
public bool Start() public bool Start()
{ {
if (DataId != null) if (dataId != null)
{ {
IGameObject? gameObject = gameFunctions.FindObjectByDataId(DataId.Value); IGameObject? gameObject = gameFunctions.FindObjectByDataId(dataId.Value);
if (gameObject == null) if (gameObject == null)
{ {
logger.LogWarning("No game object with dataId {DataId}", DataId); logger.LogWarning("No game object with dataId {DataId}", dataId);
return false; return false;
} }
if (gameObject.IsTargetable) if (gameObject.IsTargetable)
{ {
_usedAction = gameFunctions.UseAction(gameObject, Action); _usedAction = gameFunctions.UseAction(gameObject, action);
_continueAt = DateTime.Now.AddSeconds(0.5); _continueAt = DateTime.Now.AddSeconds(0.5);
return true; return true;
} }
} }
else else
{ {
_usedAction = gameFunctions.UseAction(Action); _usedAction = gameFunctions.UseAction(action);
_continueAt = DateTime.Now.AddSeconds(0.5); _continueAt = DateTime.Now.AddSeconds(0.5);
return true; return true;
} }
@ -85,18 +77,18 @@ internal static class Action
if (!_usedAction) if (!_usedAction)
{ {
if (DataId != null) if (dataId != null)
{ {
IGameObject? gameObject = gameFunctions.FindObjectByDataId(DataId.Value); IGameObject? gameObject = gameFunctions.FindObjectByDataId(dataId.Value);
if (gameObject == null || !gameObject.IsTargetable) if (gameObject == null || !gameObject.IsTargetable)
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
_usedAction = gameFunctions.UseAction(gameObject, Action); _usedAction = gameFunctions.UseAction(gameObject, action);
_continueAt = DateTime.Now.AddSeconds(0.5); _continueAt = DateTime.Now.AddSeconds(0.5);
} }
else else
{ {
_usedAction = gameFunctions.UseAction(Action); _usedAction = gameFunctions.UseAction(action);
_continueAt = DateTime.Now.AddSeconds(0.5); _continueAt = DateTime.Now.AddSeconds(0.5);
} }
@ -106,6 +98,6 @@ internal static class Action
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
public override string ToString() => $"Action({Action})"; public override string ToString() => $"Action({action})";
} }
} }

View File

@ -11,7 +11,11 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class AetherCurrent internal static class AetherCurrent
{ {
internal sealed class Factory(IServiceProvider serviceProvider, AetherCurrentData aetherCurrentData, IChatGui chatGui) : SimpleTaskFactory internal sealed class Factory(
GameFunctions gameFunctions,
AetherCurrentData aetherCurrentData,
IChatGui chatGui,
ILoggerFactory loggerFactory) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -23,47 +27,37 @@ internal static class AetherCurrent
if (!aetherCurrentData.IsValidAetherCurrent(step.TerritoryId, step.AetherCurrentId.Value)) if (!aetherCurrentData.IsValidAetherCurrent(step.TerritoryId, step.AetherCurrentId.Value))
{ {
chatGui.PrintError($"[Questionable] Aether current with id {step.AetherCurrentId} is referencing an invalid aether current, will skip attunement"); chatGui.PrintError(
$"[Questionable] Aether current with id {step.AetherCurrentId} is referencing an invalid aether current, will skip attunement");
return null; return null;
} }
return serviceProvider.GetRequiredService<DoAttune>() return new DoAttune(step.DataId.Value, step.AetherCurrentId.Value, gameFunctions, loggerFactory.CreateLogger<DoAttune>());
.With(step.DataId.Value, step.AetherCurrentId.Value);
} }
} }
internal sealed class DoAttune(GameFunctions gameFunctions, ILogger<DoAttune> logger) : ITask private sealed class DoAttune(uint dataId, uint aetherCurrentId, GameFunctions gameFunctions, ILogger<DoAttune> logger) : ITask
{ {
public uint DataId { get; set; }
public uint AetherCurrentId { get; set; }
public ITask With(uint dataId, uint aetherCurrentId)
{
DataId = dataId;
AetherCurrentId = aetherCurrentId;
return this;
}
public bool Start() public bool Start()
{ {
if (!gameFunctions.IsAetherCurrentUnlocked(AetherCurrentId)) if (!gameFunctions.IsAetherCurrentUnlocked(aetherCurrentId))
{ {
logger.LogInformation("Attuning to aether current {AetherCurrentId} / {DataId}", AetherCurrentId, logger.LogInformation("Attuning to aether current {AetherCurrentId} / {DataId}", aetherCurrentId,
DataId); dataId);
gameFunctions.InteractWith(DataId); gameFunctions.InteractWith(dataId);
return true; return true;
} }
logger.LogInformation("Already attuned to aether current {AetherCurrentId} / {DataId}", AetherCurrentId, logger.LogInformation("Already attuned to aether current {AetherCurrentId} / {DataId}", aetherCurrentId,
DataId); dataId);
return false; return false;
} }
public ETaskResult Update() => public ETaskResult Update() =>
gameFunctions.IsAetherCurrentUnlocked(AetherCurrentId) gameFunctions.IsAetherCurrentUnlocked(aetherCurrentId)
? ETaskResult.TaskComplete ? ETaskResult.TaskComplete
: ETaskResult.StillRunning; : ETaskResult.StillRunning;
public override string ToString() => $"AttuneAetherCurrent({AetherCurrentId})"; public override string ToString() => $"AttuneAetherCurrent({aetherCurrentId})";
} }
} }

View File

@ -11,7 +11,10 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class AethernetShard internal static class AethernetShard
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(
AetheryteFunctions aetheryteFunctions,
GameFunctions gameFunctions,
ILoggerFactory loggerFactory) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -20,42 +23,35 @@ internal static class AethernetShard
ArgumentNullException.ThrowIfNull(step.AethernetShard); ArgumentNullException.ThrowIfNull(step.AethernetShard);
return serviceProvider.GetRequiredService<DoAttune>() return new DoAttune(step.AethernetShard.Value, aetheryteFunctions, gameFunctions,
.With(step.AethernetShard.Value); loggerFactory.CreateLogger<DoAttune>());
} }
} }
internal sealed class DoAttune( private sealed class DoAttune(
EAetheryteLocation aetheryteLocation,
AetheryteFunctions aetheryteFunctions, AetheryteFunctions aetheryteFunctions,
GameFunctions gameFunctions, GameFunctions gameFunctions,
ILogger<DoAttune> logger) : ITask ILogger<DoAttune> logger) : ITask
{ {
public EAetheryteLocation AetheryteLocation { get; set; }
public ITask With(EAetheryteLocation aetheryteLocation)
{
AetheryteLocation = aetheryteLocation;
return this;
}
public bool Start() public bool Start()
{ {
if (!aetheryteFunctions.IsAetheryteUnlocked(AetheryteLocation)) if (!aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
{ {
logger.LogInformation("Attuning to aethernet shard {AethernetShard}", AetheryteLocation); logger.LogInformation("Attuning to aethernet shard {AethernetShard}", aetheryteLocation);
gameFunctions.InteractWith((uint)AetheryteLocation, ObjectKind.Aetheryte); gameFunctions.InteractWith((uint)aetheryteLocation, ObjectKind.Aetheryte);
return true; return true;
} }
logger.LogInformation("Already attuned to aethernet shard {AethernetShard}", AetheryteLocation); logger.LogInformation("Already attuned to aethernet shard {AethernetShard}", aetheryteLocation);
return false; return false;
} }
public ETaskResult Update() => public ETaskResult Update() =>
aetheryteFunctions.IsAetheryteUnlocked(AetheryteLocation) aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation)
? ETaskResult.TaskComplete ? ETaskResult.TaskComplete
: ETaskResult.StillRunning; : ETaskResult.StillRunning;
public override string ToString() => $"AttuneAethernetShard({AetheryteLocation})"; public override string ToString() => $"AttuneAethernetShard({aetheryteLocation})";
} }
} }

View File

@ -10,7 +10,10 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Aetheryte internal static class Aetheryte
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(
AetheryteFunctions aetheryteFunctions,
GameFunctions gameFunctions,
ILoggerFactory loggerFactory) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -19,42 +22,35 @@ internal static class Aetheryte
ArgumentNullException.ThrowIfNull(step.Aetheryte); ArgumentNullException.ThrowIfNull(step.Aetheryte);
return serviceProvider.GetRequiredService<DoAttune>() return new DoAttune(step.Aetheryte.Value, aetheryteFunctions, gameFunctions,
.With(step.Aetheryte.Value); loggerFactory.CreateLogger<DoAttune>());
} }
} }
internal sealed class DoAttune( private sealed class DoAttune(
EAetheryteLocation aetheryteLocation,
AetheryteFunctions aetheryteFunctions, AetheryteFunctions aetheryteFunctions,
GameFunctions gameFunctions, GameFunctions gameFunctions,
ILogger<DoAttune> logger) : ITask ILogger<DoAttune> logger) : ITask
{ {
public EAetheryteLocation AetheryteLocation { get; set; }
public ITask With(EAetheryteLocation aetheryteLocation)
{
AetheryteLocation = aetheryteLocation;
return this;
}
public bool Start() public bool Start()
{ {
if (!aetheryteFunctions.IsAetheryteUnlocked(AetheryteLocation)) if (!aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
{ {
logger.LogInformation("Attuning to aetheryte {Aetheryte}", AetheryteLocation); logger.LogInformation("Attuning to aetheryte {Aetheryte}", aetheryteLocation);
gameFunctions.InteractWith((uint)AetheryteLocation); gameFunctions.InteractWith((uint)aetheryteLocation);
return true; return true;
} }
logger.LogInformation("Already attuned to aetheryte {Aetheryte}", AetheryteLocation); logger.LogInformation("Already attuned to aetheryte {Aetheryte}", aetheryteLocation);
return false; return false;
} }
public ETaskResult Update() => public ETaskResult Update() =>
aetheryteFunctions.IsAetheryteUnlocked(AetheryteLocation) aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation)
? ETaskResult.TaskComplete ? ETaskResult.TaskComplete
: ETaskResult.StillRunning; : ETaskResult.StillRunning;
public override string ToString() => $"AttuneAetheryte({AetheryteLocation})"; public override string ToString() => $"AttuneAetheryte({aetheryteLocation})";
} }
} }

View File

@ -13,7 +13,12 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Combat internal static class Combat
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory internal sealed class Factory(
CombatController combatController,
Interact.Factory interactFactory,
Mount.Factory mountFactory,
UseItem.Factory useItemFactory,
QuestFunctions questFunctions) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -22,12 +27,11 @@ internal static class Combat
ArgumentNullException.ThrowIfNull(step.EnemySpawnType); ArgumentNullException.ThrowIfNull(step.EnemySpawnType);
yield return serviceProvider.GetRequiredService<UnmountTask>(); yield return mountFactory.Unmount();
if (step.CombatDelaySecondsAtStart != null) if (step.CombatDelaySecondsAtStart != null)
{ {
yield return serviceProvider.GetRequiredService<WaitAtStart.WaitDelay>() yield return new WaitAtStart.WaitDelay(TimeSpan.FromSeconds(step.CombatDelaySecondsAtStart.Value));
.With(TimeSpan.FromSeconds(step.CombatDelaySecondsAtStart.Value));
} }
switch (step.EnemySpawnType) switch (step.EnemySpawnType)
@ -36,8 +40,7 @@ internal static class Combat
{ {
ArgumentNullException.ThrowIfNull(step.DataId); ArgumentNullException.ThrowIfNull(step.DataId);
yield return serviceProvider.GetRequiredService<Interact.DoInteract>() yield return interactFactory.Interact(step.DataId.Value, quest, EInteractionType.None, true);
.With(step.DataId.Value, quest, EInteractionType.None, true);
yield return CreateTask(quest, sequence, step); yield return CreateTask(quest, sequence, step);
break; break;
} }
@ -47,9 +50,8 @@ internal static class Combat
ArgumentNullException.ThrowIfNull(step.DataId); ArgumentNullException.ThrowIfNull(step.DataId);
ArgumentNullException.ThrowIfNull(step.ItemId); ArgumentNullException.ThrowIfNull(step.ItemId);
yield return serviceProvider.GetRequiredService<UseItem.UseOnObject>() yield return useItemFactory.OnObject(quest.Id, step.DataId.Value, step.ItemId.Value,
.With(quest.Id, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags, step.CompletionQuestVariablesFlags, true);
true);
yield return CreateTask(quest, sequence, step); yield return CreateTask(quest, sequence, step);
break; break;
} }
@ -73,34 +75,32 @@ internal static class Combat
ArgumentNullException.ThrowIfNull(step.EnemySpawnType); ArgumentNullException.ThrowIfNull(step.EnemySpawnType);
bool isLastStep = sequence.Steps.Last() == step; bool isLastStep = sequence.Steps.Last() == step;
return serviceProvider.GetRequiredService<HandleCombat>() return CreateTask(quest.Id, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds,
.With(quest.Id, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds, step.CompletionQuestVariablesFlags, step.ComplexCombatData);
step.CompletionQuestVariablesFlags, step.ComplexCombatData);
} }
}
internal sealed class HandleCombat(CombatController combatController, QuestFunctions questFunctions) : ITask private HandleCombat CreateTask(ElementId elementId, bool isLastStep, EEnemySpawnType enemySpawnType,
{ IList<uint> killEnemyDataIds, IList<QuestWorkValue?> completionQuestVariablesFlags,
private bool _isLastStep; IList<ComplexCombatData> complexCombatData)
private CombatController.CombatData _combatData = null!;
private IList<QuestWorkValue?> _completionQuestVariableFlags = null!;
public ITask With(ElementId elementId, bool isLastStep, EEnemySpawnType enemySpawnType, IList<uint> killEnemyDataIds,
IList<QuestWorkValue?> completionQuestVariablesFlags, IList<ComplexCombatData> complexCombatData)
{ {
_isLastStep = isLastStep; return new HandleCombat(isLastStep, new CombatController.CombatData
_combatData = new CombatController.CombatData
{ {
ElementId = elementId, ElementId = elementId,
SpawnType = enemySpawnType, SpawnType = enemySpawnType,
KillEnemyDataIds = killEnemyDataIds.ToList(), KillEnemyDataIds = killEnemyDataIds.ToList(),
ComplexCombatDatas = complexCombatData.ToList(), ComplexCombatDatas = complexCombatData.ToList(),
}; }, completionQuestVariablesFlags, combatController, questFunctions);
_completionQuestVariableFlags = completionQuestVariablesFlags;
return this;
} }
}
public bool Start() => combatController.Start(_combatData); private sealed class HandleCombat(
bool isLastStep,
CombatController.CombatData combatData,
IList<QuestWorkValue?> completionQuestVariableFlags,
CombatController combatController,
QuestFunctions questFunctions) : ITask
{
public bool Start() => combatController.Start(combatData);
public ETaskResult Update() public ETaskResult Update()
{ {
@ -108,13 +108,14 @@ internal static class Combat
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
// if our quest step has any completion flags, we need to check if they are set // if our quest step has any completion flags, we need to check if they are set
if (QuestWorkUtils.HasCompletionFlags(_completionQuestVariableFlags) && _combatData.ElementId is QuestId questId) if (QuestWorkUtils.HasCompletionFlags(completionQuestVariableFlags) &&
combatData.ElementId is QuestId questId)
{ {
var questWork = questFunctions.GetQuestProgressInfo(questId); var questWork = questFunctions.GetQuestProgressInfo(questId);
if (questWork == null) if (questWork == null)
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
if (QuestWorkUtils.MatchesQuestWork(_completionQuestVariableFlags, questWork)) if (QuestWorkUtils.MatchesQuestWork(completionQuestVariableFlags, questWork))
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
else else
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
@ -122,7 +123,7 @@ internal static class Combat
// the last step, by definition, can only be progressed by the game recognizing we're in a new sequence, // the last step, by definition, can only be progressed by the game recognizing we're in a new sequence,
// so this is an indefinite wait // so this is an indefinite wait
if (_isLastStep) if (isLastStep)
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
else else
{ {
@ -133,9 +134,9 @@ internal static class Combat
public override string ToString() public override string ToString()
{ {
if (QuestWorkUtils.HasCompletionFlags(_completionQuestVariableFlags)) if (QuestWorkUtils.HasCompletionFlags(completionQuestVariableFlags))
return "HandleCombat(wait: QW flags)"; return "HandleCombat(wait: QW flags)";
else if (_isLastStep) else if (isLastStep)
return "HandleCombat(wait: next sequence)"; return "HandleCombat(wait: next sequence)";
else else
return "HandleCombat(wait: not in combat)"; return "HandleCombat(wait: not in combat)";

View File

@ -18,18 +18,23 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Dive internal static class Dive
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(ICondition condition, ILoggerFactory loggerFactory) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
if (step.InteractionType != EInteractionType.Dive) if (step.InteractionType != EInteractionType.Dive)
return null; return null;
return serviceProvider.GetRequiredService<DoDive>(); return Dive();
}
public ITask Dive()
{
return new DoDive(condition, loggerFactory.CreateLogger<DoDive>());
} }
} }
internal sealed class DoDive(ICondition condition, ILogger<DoDive> logger) private sealed class DoDive(ICondition condition, ILogger<DoDive> logger)
: AbstractDelayedTask(TimeSpan.FromSeconds(5)) : AbstractDelayedTask(TimeSpan.FromSeconds(5))
{ {
private readonly Queue<(uint Type, nint Key)> _keysToPress = []; private readonly Queue<(uint Type, nint Key)> _keysToPress = [];

View File

@ -10,7 +10,7 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Duty internal static class Duty
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(GameFunctions gameFunctions, ICondition condition) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -18,33 +18,26 @@ internal static class Duty
return null; return null;
ArgumentNullException.ThrowIfNull(step.ContentFinderConditionId); ArgumentNullException.ThrowIfNull(step.ContentFinderConditionId);
return new OpenDutyFinder(step.ContentFinderConditionId.Value, gameFunctions, condition);
return serviceProvider.GetRequiredService<OpenDutyFinder>()
.With(step.ContentFinderConditionId.Value);
} }
} }
internal sealed class OpenDutyFinder(GameFunctions gameFunctions, ICondition condition) : ITask private sealed class OpenDutyFinder(
uint contentFinderConditionId,
GameFunctions gameFunctions,
ICondition condition) : ITask
{ {
public uint ContentFinderConditionId { get; set; }
public ITask With(uint contentFinderConditionId)
{
ContentFinderConditionId = contentFinderConditionId;
return this;
}
public bool Start() public bool Start()
{ {
if (condition[ConditionFlag.InDutyQueue]) if (condition[ConditionFlag.InDutyQueue])
return false; return false;
gameFunctions.OpenDutyFinder(ContentFinderConditionId); gameFunctions.OpenDutyFinder(contentFinderConditionId);
return true; return true;
} }
public ETaskResult Update() => ETaskResult.TaskComplete; public ETaskResult Update() => ETaskResult.TaskComplete;
public override string ToString() => $"OpenDutyFinder({ContentFinderConditionId})"; public override string ToString() => $"OpenDutyFinder({contentFinderConditionId})";
} }
} }

View File

@ -10,7 +10,7 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Emote internal static class Emote
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory internal sealed class Factory(ChatFunctions chatFunctions, Mount.Factory mountFactory) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -24,57 +24,39 @@ internal static class Emote
ArgumentNullException.ThrowIfNull(step.Emote); ArgumentNullException.ThrowIfNull(step.Emote);
var unmount = serviceProvider.GetRequiredService<UnmountTask>(); var unmount = mountFactory.Unmount();
if (step.DataId != null) if (step.DataId != null)
{ {
var task = serviceProvider.GetRequiredService<UseOnObject>().With(step.Emote.Value, step.DataId.Value); var task = new UseOnObject(step.Emote.Value, step.DataId.Value, chatFunctions);
return [unmount, task]; return [unmount, task];
} }
else else
{ {
var task = serviceProvider.GetRequiredService<Use>().With(step.Emote.Value); var task = new UseOnSelf(step.Emote.Value, chatFunctions);
return [unmount, task]; return [unmount, task];
} }
} }
} }
internal sealed class UseOnObject(ChatFunctions chatFunctions) : AbstractDelayedTask private sealed class UseOnObject(EEmote emote, uint dataId, ChatFunctions chatFunctions) : AbstractDelayedTask
{ {
public EEmote Emote { get; set; }
public uint DataId { get; set; }
public ITask With(EEmote emote, uint dataId)
{
Emote = emote;
DataId = dataId;
return this;
}
protected override bool StartInternal() protected override bool StartInternal()
{ {
chatFunctions.UseEmote(DataId, Emote); chatFunctions.UseEmote(dataId, emote);
return true; return true;
} }
public override string ToString() => $"Emote({Emote} on {DataId})"; public override string ToString() => $"Emote({emote} on {dataId})";
} }
internal sealed class Use(ChatFunctions chatFunctions) : AbstractDelayedTask private sealed class UseOnSelf(EEmote emote, ChatFunctions chatFunctions) : AbstractDelayedTask
{ {
public EEmote Emote { get; set; }
public ITask With(EEmote emote)
{
Emote = emote;
return this;
}
protected override bool StartInternal() protected override bool StartInternal()
{ {
chatFunctions.UseEmote(Emote); chatFunctions.UseEmote(emote);
return true; return true;
} }
public override string ToString() => $"Emote({Emote})"; public override string ToString() => $"Emote({emote})";
} }
} }

View File

@ -16,7 +16,7 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class EquipItem internal static class EquipItem
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(IDataManager dataManager, ILoggerFactory loggerFactory) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -24,14 +24,39 @@ internal static class EquipItem
return null; return null;
ArgumentNullException.ThrowIfNull(step.ItemId); ArgumentNullException.ThrowIfNull(step.ItemId);
return serviceProvider.GetRequiredService<DoEquip>() return Equip(step.ItemId.Value);
.With(step.ItemId.Value); }
private DoEquip Equip(uint itemId)
{
var item = dataManager.GetExcelSheet<Item>()!.GetRow(itemId) ??
throw new ArgumentOutOfRangeException(nameof(itemId));
var targetSlots = GetEquipSlot(item) ?? throw new InvalidOperationException("Not a piece of equipment");
return new DoEquip(itemId, item, targetSlots, dataManager, loggerFactory.CreateLogger<DoEquip>());
}
private static List<ushort>? GetEquipSlot(Item item)
{
return item.EquipSlotCategory.Row switch
{
>= 1 and <= 11 => [(ushort)(item.EquipSlotCategory.Row - 1)],
12 => [11, 12], // rings
13 => [0],
17 => [13], // soul crystal
_ => null
};
} }
} }
internal sealed class DoEquip(IDataManager dataManager, ILogger<DoEquip> logger) : ITask, IToastAware private sealed class DoEquip(
uint itemId,
Item item,
List<ushort> targetSlots,
IDataManager dataManager,
ILogger<DoEquip> logger) : ITask, IToastAware
{ {
private const int MaxAttempts = 3; private const int MaxAttempts = 3;
private static readonly IReadOnlyList<InventoryType> SourceInventoryTypes = private static readonly IReadOnlyList<InventoryType> SourceInventoryTypes =
[ [
InventoryType.ArmoryMainHand, InventoryType.ArmoryMainHand,
@ -55,22 +80,9 @@ internal static class EquipItem
InventoryType.Inventory4, InventoryType.Inventory4,
]; ];
private uint _itemId;
private Item _item = null!;
private List<ushort> _targetSlots = [];
private int _attempts; private int _attempts;
private DateTime _continueAt = DateTime.MaxValue; private DateTime _continueAt = DateTime.MaxValue;
public ITask With(uint itemId)
{
_itemId = itemId;
_item = dataManager.GetExcelSheet<Item>()!.GetRow(itemId) ??
throw new ArgumentOutOfRangeException(nameof(itemId));
_targetSlots = GetEquipSlot(_item) ?? throw new InvalidOperationException("Not a piece of equipment");
return this;
}
public bool Start() public bool Start()
{ {
Equip(); Equip();
@ -87,10 +99,10 @@ internal static class EquipItem
if (inventoryManager == null) if (inventoryManager == null)
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
foreach (ushort x in _targetSlots) foreach (ushort x in targetSlots)
{ {
var itemSlot = inventoryManager->GetInventorySlot(InventoryType.EquippedItems, x); var itemSlot = inventoryManager->GetInventorySlot(InventoryType.EquippedItems, x);
if (itemSlot != null && itemSlot->ItemId == _itemId) if (itemSlot != null && itemSlot->ItemId == itemId)
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
@ -113,12 +125,12 @@ internal static class EquipItem
if (equippedContainer == null) if (equippedContainer == null)
return; return;
foreach (ushort slot in _targetSlots) foreach (ushort slot in targetSlots)
{ {
var itemSlot = equippedContainer->GetInventorySlot(slot); var itemSlot = equippedContainer->GetInventorySlot(slot);
if (itemSlot != null && itemSlot->ItemId == _itemId) if (itemSlot != null && itemSlot->ItemId == itemId)
{ {
logger.LogInformation("Already equipped {Item}, skipping step", _item.Name?.ToString()); logger.LogInformation("Already equipped {Item}, skipping step", item.Name?.ToString());
return; return;
} }
} }
@ -129,24 +141,24 @@ internal static class EquipItem
if (sourceContainer == null) if (sourceContainer == null)
continue; continue;
if (inventoryManager->GetItemCountInContainer(_itemId, sourceInventoryType, true) == 0 && if (inventoryManager->GetItemCountInContainer(itemId, sourceInventoryType, true) == 0 &&
inventoryManager->GetItemCountInContainer(_itemId, sourceInventoryType) == 0) inventoryManager->GetItemCountInContainer(itemId, sourceInventoryType) == 0)
continue; continue;
for (ushort sourceSlot = 0; sourceSlot < sourceContainer->Size; sourceSlot++) for (ushort sourceSlot = 0; sourceSlot < sourceContainer->Size; sourceSlot++)
{ {
var sourceItem = sourceContainer->GetInventorySlot(sourceSlot); var sourceItem = sourceContainer->GetInventorySlot(sourceSlot);
if (sourceItem == null || sourceItem->ItemId != _itemId) if (sourceItem == null || sourceItem->ItemId != itemId)
continue; continue;
// Move the item to the first available slot // Move the item to the first available slot
ushort targetSlot = _targetSlots ushort targetSlot = targetSlots
.Where(x => .Where(x =>
{ {
var itemSlot = inventoryManager->GetInventorySlot(InventoryType.EquippedItems, x); var itemSlot = inventoryManager->GetInventorySlot(InventoryType.EquippedItems, x);
return itemSlot == null || itemSlot->ItemId == 0; return itemSlot == null || itemSlot->ItemId == 0;
}) })
.Concat(_targetSlots).First(); .Concat(targetSlots).First();
logger.LogInformation( logger.LogInformation(
"Equipping item from {SourceInventory}, {SourceSlot} to {TargetInventory}, {TargetSlot}", "Equipping item from {SourceInventory}, {SourceSlot} to {TargetInventory}, {TargetSlot}",
@ -160,19 +172,7 @@ internal static class EquipItem
} }
} }
private static List<ushort>? GetEquipSlot(Item item) public override string ToString() => $"Equip({item.Name})";
{
return item.EquipSlotCategory.Row switch
{
>= 1 and <= 11 => [(ushort)(item.EquipSlotCategory.Row - 1)],
12 => [11, 12], // rings
13 => [0],
17 => [13], // soul crystal
_ => null
};
}
public override string ToString() => $"Equip({_item.Name})";
public bool OnErrorToast(SeString message) public bool OnErrorToast(SeString message)
{ {

View File

@ -10,18 +10,23 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class EquipRecommended internal static class EquipRecommended
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(IClientState clientState, IChatGui chatGui) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
if (step.InteractionType != EInteractionType.EquipRecommended) if (step.InteractionType != EInteractionType.EquipRecommended)
return null; return null;
return serviceProvider.GetRequiredService<DoEquipRecommended>(); return DoEquip();
}
public ITask DoEquip()
{
return new DoEquipRecommended(clientState, chatGui);
} }
} }
internal sealed class BeforeDutyOrInstance(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class BeforeDutyOrInstance(IClientState clientState, IChatGui chatGui) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -30,11 +35,11 @@ internal static class EquipRecommended
step.InteractionType != EInteractionType.Combat) step.InteractionType != EInteractionType.Combat)
return null; return null;
return serviceProvider.GetRequiredService<DoEquipRecommended>(); return new DoEquipRecommended(clientState, chatGui);
} }
} }
internal sealed unsafe class DoEquipRecommended(IClientState clientState, IChatGui chatGui) : ITask private sealed unsafe class DoEquipRecommended(IClientState clientState, IChatGui chatGui) : ITask
{ {
private bool _equipped; private bool _equipped;

View File

@ -15,7 +15,7 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Interact internal static class Interact
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory internal sealed class Factory(GameFunctions gameFunctions, ICondition condition, ILoggerFactory loggerFactory) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -36,41 +36,46 @@ internal static class Interact
// if we're fast enough, it is possible to get the smalltalk prompt // if we're fast enough, it is possible to get the smalltalk prompt
if (sequence.Sequence == 0 && sequence.Steps.IndexOf(step) == 0) if (sequence.Sequence == 0 && sequence.Steps.IndexOf(step) == 0)
yield return serviceProvider.GetRequiredService<WaitAtEnd.WaitDelay>(); yield return new WaitAtEnd.WaitDelay();
yield return serviceProvider.GetRequiredService<DoInteract>() yield return Interact(step.DataId.Value, quest, step.InteractionType,
.With(step.DataId.Value, quest, step.InteractionType,
step.TargetTerritoryId != null || quest.Id is SatisfactionSupplyNpcId); step.TargetTerritoryId != null || quest.Id is SatisfactionSupplyNpcId);
} }
internal ITask Interact(uint dataId, Quest? quest, EInteractionType interactionType, bool skipMarkerCheck = false)
{
return new DoInteract(dataId, quest, interactionType, skipMarkerCheck, gameFunctions, condition,
loggerFactory.CreateLogger<DoInteract>());
}
} }
internal sealed class DoInteract(GameFunctions gameFunctions, ICondition condition, ILogger<DoInteract> logger) internal sealed class DoInteract(
uint dataId,
Quest? quest,
EInteractionType interactionType,
bool skipMarkerCheck,
GameFunctions gameFunctions,
ICondition condition,
ILogger<DoInteract> logger)
: ITask, IConditionChangeAware : ITask, IConditionChangeAware
{ {
private bool _needsUnmount; private bool _needsUnmount;
private EInteractionState _interactionState = EInteractionState.None; private EInteractionState _interactionState = EInteractionState.None;
private DateTime _continueAt = DateTime.MinValue; private DateTime _continueAt = DateTime.MinValue;
private uint DataId { get; set; } public Quest? Quest => quest;
public Quest? Quest { get; private set; } public EInteractionType InteractionType
public EInteractionType InteractionType { get; set; }
private bool SkipMarkerCheck { get; set; }
public DoInteract With(uint dataId, Quest? quest, EInteractionType interactionType, bool skipMarkerCheck)
{ {
DataId = dataId; get => interactionType;
Quest = quest; set => interactionType = value;
InteractionType = interactionType;
SkipMarkerCheck = skipMarkerCheck;
return this;
} }
public bool Start() public bool Start()
{ {
IGameObject? gameObject = gameFunctions.FindObjectByDataId(DataId); IGameObject? gameObject = gameFunctions.FindObjectByDataId(dataId);
if (gameObject == null) if (gameObject == null)
{ {
logger.LogWarning("No game object with dataId {DataId}", DataId); logger.LogWarning("No game object with dataId {DataId}", dataId);
return false; return false;
} }
@ -78,7 +83,7 @@ internal static class Interact
if (!gameObject.IsTargetable && condition[ConditionFlag.Mounted] && if (!gameObject.IsTargetable && condition[ConditionFlag.Mounted] &&
gameObject.ObjectKind != ObjectKind.GatheringPoint) gameObject.ObjectKind != ObjectKind.GatheringPoint)
{ {
logger.LogInformation("Preparing interaction for {DataId} by unmounting", DataId); logger.LogInformation("Preparing interaction for {DataId} by unmounting", dataId);
_needsUnmount = true; _needsUnmount = true;
gameFunctions.Unmount(); gameFunctions.Unmount();
_continueAt = DateTime.Now.AddSeconds(1); _continueAt = DateTime.Now.AddSeconds(1);
@ -117,10 +122,10 @@ internal static class Interact
if (_interactionState == EInteractionState.InteractionConfirmed) if (_interactionState == EInteractionState.InteractionConfirmed)
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
if (InteractionType == EInteractionType.InternalGather && condition[ConditionFlag.Gathering]) if (interactionType == EInteractionType.InternalGather && condition[ConditionFlag.Gathering])
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
IGameObject? gameObject = gameFunctions.FindObjectByDataId(DataId); IGameObject? gameObject = gameFunctions.FindObjectByDataId(dataId);
if (gameObject == null || !gameObject.IsTargetable || !HasAnyMarker(gameObject)) if (gameObject == null || !gameObject.IsTargetable || !HasAnyMarker(gameObject))
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
@ -133,20 +138,21 @@ internal static class Interact
private unsafe bool HasAnyMarker(IGameObject gameObject) private unsafe bool HasAnyMarker(IGameObject gameObject)
{ {
if (SkipMarkerCheck || gameObject.ObjectKind != ObjectKind.EventNpc) if (skipMarkerCheck || gameObject.ObjectKind != ObjectKind.EventNpc)
return true; return true;
var gameObjectStruct = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject.Address; var gameObjectStruct = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject.Address;
return gameObjectStruct->NamePlateIconId != 0; return gameObjectStruct->NamePlateIconId != 0;
} }
public override string ToString() => $"Interact({DataId})"; public override string ToString() => $"Interact({dataId})";
public void OnConditionChange(ConditionFlag flag, bool value) public void OnConditionChange(ConditionFlag flag, bool value)
{ {
logger.LogDebug("Condition change: {Flag} = {Value}", flag, value); logger.LogDebug("Condition change: {Flag} = {Value}", flag, value);
if (_interactionState == EInteractionState.InteractionTriggered && if (_interactionState == EInteractionState.InteractionTriggered &&
flag == ConditionFlag.OccupiedInQuestEvent && value) flag is ConditionFlag.OccupiedInQuestEvent or ConditionFlag.OccupiedInEvent &&
value)
{ {
logger.LogInformation("Interaction was most likely triggered"); logger.LogInformation("Interaction was most likely triggered");
_interactionState = EInteractionState.InteractionConfirmed; _interactionState = EInteractionState.InteractionConfirmed;

View File

@ -11,7 +11,11 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Jump internal static class Jump
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : SimpleTaskFactory internal sealed class Factory(
MovementController movementController,
IClientState clientState,
IFramework framework,
ILoggerFactory loggerFactory) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -21,43 +25,39 @@ internal static class Jump
ArgumentNullException.ThrowIfNull(step.JumpDestination); ArgumentNullException.ThrowIfNull(step.JumpDestination);
if (step.JumpDestination.Type == EJumpType.SingleJump) if (step.JumpDestination.Type == EJumpType.SingleJump)
{ return SingleJump(step.DataId, step.JumpDestination, step.Comment);
return serviceProvider.GetRequiredService<SingleJump>()
.With(step.DataId, step.JumpDestination, step.Comment);
}
else else
{ return RepeatedJumps(step.DataId, step.JumpDestination, step.Comment);
return serviceProvider.GetRequiredService<RepeatedJumps>() }
.With(step.DataId, step.JumpDestination, step.Comment);
} public ITask SingleJump(uint? dataId, JumpDestination jumpDestination, string? comment)
{
return new DoSingleJump(dataId, jumpDestination, comment, movementController, clientState, framework);
}
public ITask RepeatedJumps(uint? dataId, JumpDestination jumpDestination, string? comment)
{
return new DoRepeatedJumps(dataId, jumpDestination, comment, movementController, clientState, framework,
loggerFactory.CreateLogger<DoRepeatedJumps>());
} }
} }
internal class SingleJump( private class DoSingleJump(
uint? dataId,
JumpDestination jumpDestination,
string? comment,
MovementController movementController, MovementController movementController,
IClientState clientState, IClientState clientState,
IFramework framework) : ITask IFramework framework) : ITask
{ {
public uint? DataId { get; set; }
public JumpDestination JumpDestination { get; set; } = null!;
public string? Comment { get; set; }
public ITask With(uint? dataId, JumpDestination jumpDestination, string? comment)
{
DataId = dataId;
JumpDestination = jumpDestination;
Comment = comment ?? string.Empty;
return this;
}
public virtual bool Start() public virtual bool Start()
{ {
float stopDistance = JumpDestination.CalculateStopDistance(); float stopDistance = jumpDestination.CalculateStopDistance();
if ((clientState.LocalPlayer!.Position - JumpDestination.Position).Length() <= stopDistance) if ((clientState.LocalPlayer!.Position - jumpDestination.Position).Length() <= stopDistance)
return false; return false;
movementController.NavigateTo(EMovementType.Quest, DataId, [JumpDestination.Position], false, false, movementController.NavigateTo(EMovementType.Quest, dataId, [jumpDestination.Position], false, false,
JumpDestination.StopDistance ?? stopDistance); jumpDestination.StopDistance ?? stopDistance);
framework.RunOnTick(() => framework.RunOnTick(() =>
{ {
unsafe unsafe
@ -65,7 +65,7 @@ internal static class Jump
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2); ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2);
} }
}, },
TimeSpan.FromSeconds(JumpDestination.DelaySeconds ?? 0.5f)); TimeSpan.FromSeconds(jumpDestination.DelaySeconds ?? 0.5f));
return true; return true;
} }
@ -81,22 +81,28 @@ internal static class Jump
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
public override string ToString() => $"Jump({Comment})"; public override string ToString() => $"Jump({comment})";
} }
internal sealed class RepeatedJumps( private sealed class DoRepeatedJumps(
uint? dataId,
JumpDestination jumpDestination,
string? comment,
MovementController movementController, MovementController movementController,
IClientState clientState, IClientState clientState,
IFramework framework, IFramework framework,
ILogger<RepeatedJumps> logger) : SingleJump(movementController, clientState, framework) ILogger<DoRepeatedJumps> logger)
: DoSingleJump(dataId, jumpDestination, comment, movementController, clientState, framework)
{ {
private readonly JumpDestination _jumpDestination = jumpDestination;
private readonly string? _comment = comment;
private readonly IClientState _clientState = clientState; private readonly IClientState _clientState = clientState;
private DateTime _continueAt = DateTime.MinValue; private DateTime _continueAt = DateTime.MinValue;
private int _attempts; private int _attempts;
public override bool Start() public override bool Start()
{ {
_continueAt = DateTime.Now + TimeSpan.FromSeconds(2 * (JumpDestination.DelaySeconds ?? 0.5f)); _continueAt = DateTime.Now + TimeSpan.FromSeconds(2 * (_jumpDestination.DelaySeconds ?? 0.5f));
return base.Start(); return base.Start();
} }
@ -105,13 +111,13 @@ internal static class Jump
if (DateTime.Now < _continueAt) if (DateTime.Now < _continueAt)
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
float stopDistance = JumpDestination.CalculateStopDistance(); float stopDistance = _jumpDestination.CalculateStopDistance();
if ((_clientState.LocalPlayer!.Position - JumpDestination.Position).Length() <= stopDistance || if ((_clientState.LocalPlayer!.Position - _jumpDestination.Position).Length() <= stopDistance ||
_clientState.LocalPlayer.Position.Y >= JumpDestination.Position.Y - 0.5f) _clientState.LocalPlayer.Position.Y >= _jumpDestination.Position.Y - 0.5f)
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
logger.LogTrace("Y-Heights for jumps: player={A}, target={B}", _clientState.LocalPlayer.Position.Y, logger.LogTrace("Y-Heights for jumps: player={A}, target={B}", _clientState.LocalPlayer.Position.Y,
JumpDestination.Position.Y - 0.5f); _jumpDestination.Position.Y - 0.5f);
unsafe unsafe
{ {
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2); ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2);
@ -121,10 +127,10 @@ internal static class Jump
if (_attempts >= 50) if (_attempts >= 50)
throw new TaskException("Tried to jump too many times, didn't reach the target"); throw new TaskException("Tried to jump too many times, didn't reach the target");
_continueAt = DateTime.Now + TimeSpan.FromSeconds(JumpDestination.DelaySeconds ?? 0.5f); _continueAt = DateTime.Now + TimeSpan.FromSeconds(_jumpDestination.DelaySeconds ?? 0.5f);
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
public override string ToString() => $"RepeatedJump({Comment})"; public override string ToString() => $"RepeatedJump({_comment})";
} }
} }

View File

@ -10,7 +10,10 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Say internal static class Say
{ {
internal sealed class Factory(IServiceProvider serviceProvider, ExcelFunctions excelFunctions) : ITaskFactory internal sealed class Factory(
ChatFunctions chatFunctions,
Mount.Factory mountFactory,
ExcelFunctions excelFunctions) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -21,31 +24,24 @@ internal static class Say
ArgumentNullException.ThrowIfNull(step.ChatMessage); ArgumentNullException.ThrowIfNull(step.ChatMessage);
string? excelString = string? excelString =
excelFunctions.GetDialogueText(quest, step.ChatMessage.ExcelSheet, step.ChatMessage.Key, false).GetString(); excelFunctions.GetDialogueText(quest, step.ChatMessage.ExcelSheet, step.ChatMessage.Key, false)
.GetString();
ArgumentNullException.ThrowIfNull(excelString); ArgumentNullException.ThrowIfNull(excelString);
var unmount = serviceProvider.GetRequiredService<UnmountTask>(); var unmount = mountFactory.Unmount();
var task = serviceProvider.GetRequiredService<UseChat>().With(excelString); var task = new UseChat(excelString, chatFunctions);
return [unmount, task]; return [unmount, task];
} }
} }
internal sealed class UseChat(ChatFunctions chatFunctions) : AbstractDelayedTask private sealed class UseChat(string chatMessage, ChatFunctions chatFunctions) : AbstractDelayedTask
{ {
public string ChatMessage { get; set; } = null!;
public ITask With(string chatMessage)
{
ChatMessage = chatMessage;
return this;
}
protected override bool StartInternal() protected override bool StartInternal()
{ {
chatFunctions.ExecuteCommand($"/say {ChatMessage}"); chatFunctions.ExecuteCommand($"/say {chatMessage}");
return true; return true;
} }
public override string ToString() => $"Say({ChatMessage})"; public override string ToString() => $"Say({chatMessage})";
} }
} }

View File

@ -26,9 +26,17 @@ internal static class UseItem
public const int VesperBayAetheryteTicket = 30362; public const int VesperBayAetheryteTicket = 30362;
internal sealed class Factory( internal sealed class Factory(
IServiceProvider serviceProvider, Mount.Factory mountFactory,
MoveTo.Factory moveFactory,
Interact.Factory interactFactory,
AetheryteShortcut.Factory aetheryteShortcutFactory,
AethernetShortcut.Factory aethernetShortcutFactory,
GameFunctions gameFunctions,
QuestFunctions questFunctions,
ICondition condition,
IClientState clientState, IClientState clientState,
TerritoryData territoryData, TerritoryData territoryData,
ILoggerFactory loggerFactory,
ILogger<Factory> logger) ILogger<Factory> logger)
: ITaskFactory : ITaskFactory
{ {
@ -48,8 +56,7 @@ internal static class UseItem
return CreateVesperBayFallbackTask(); return CreateVesperBayFallbackTask();
} }
var task = serviceProvider.GetRequiredService<Use>() var task = OnSelf(quest.Id, step.ItemId.Value, step.CompletionQuestVariablesFlags);
.With(quest.Id, step.ItemId.Value, step.CompletionQuestVariablesFlags);
int currentStepIndex = sequence.Steps.IndexOf(step); int currentStepIndex = sequence.Steps.IndexOf(step);
QuestStep? nextStep = sequence.Steps.Skip(currentStepIndex + 1).FirstOrDefault(); QuestStep? nextStep = sequence.Steps.Skip(currentStepIndex + 1).FirstOrDefault();
@ -59,47 +66,69 @@ internal static class UseItem
task, task,
new WaitConditionTask(() => clientState.TerritoryType == 140, new WaitConditionTask(() => clientState.TerritoryType == 140,
$"Wait(territory: {territoryData.GetNameAndId(140)})"), $"Wait(territory: {territoryData.GetNameAndId(140)})"),
serviceProvider.GetRequiredService<MountTask>() mountFactory.Mount(140,
.With(140, nextPosition != null ? Mount.EMountIf.AwayFromPosition : Mount.EMountIf.Always,
nextPosition != null ? MountTask.EMountIf.AwayFromPosition : MountTask.EMountIf.Always, nextPosition),
nextPosition), moveFactory.Move(new MoveTo.MoveParams(140, new(-408.92343f, 23.167036f, -351.16223f), 0.25f,
serviceProvider.GetRequiredService<Move.MoveInternal>() DataId: null, DisableNavMesh: true, Sprint: false, Fly: false))
.With(140, new(-408.92343f, 23.167036f, -351.16223f), 0.25f, dataId: null, disableNavMesh: true,
sprint: false, fly: false)
]; ];
} }
var unmount = serviceProvider.GetRequiredService<UnmountTask>(); var unmount = mountFactory.Unmount();
if (step.GroundTarget == true) if (step.GroundTarget == true)
{ {
ITask task; ITask task;
if (step.DataId != null) if (step.DataId != null)
task = serviceProvider.GetRequiredService<UseOnGround>() task = OnGroundTarget(quest.Id, step.DataId.Value, step.ItemId.Value,
.With(quest.Id, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags); step.CompletionQuestVariablesFlags);
else else
{ {
ArgumentNullException.ThrowIfNull(step.Position); ArgumentNullException.ThrowIfNull(step.Position);
task = serviceProvider.GetRequiredService<UseOnPosition>() task = OnPosition(quest.Id, step.Position.Value, step.ItemId.Value,
.With(quest.Id, step.Position.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
step.CompletionQuestVariablesFlags);
} }
return [unmount, task]; return [unmount, task];
} }
else if (step.DataId != null) else if (step.DataId != null)
{ {
var task = serviceProvider.GetRequiredService<UseOnObject>() var task = OnObject(quest.Id, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
.With(quest.Id, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
return [unmount, task]; return [unmount, task];
} }
else else
{ {
var task = serviceProvider.GetRequiredService<Use>() var task = OnSelf(quest.Id, step.ItemId.Value, step.CompletionQuestVariablesFlags);
.With(quest.Id, step.ItemId.Value, step.CompletionQuestVariablesFlags);
return [unmount, task]; return [unmount, task];
} }
} }
public ITask OnGroundTarget(ElementId questId, uint dataId, uint itemId,
List<QuestWorkValue?> completionQuestVariablesFlags)
{
return new UseOnGround(questId, dataId, itemId, completionQuestVariablesFlags, gameFunctions,
questFunctions, condition, loggerFactory.CreateLogger<UseOnGround>());
}
public ITask OnPosition(ElementId questId, Vector3 position, uint itemId,
List<QuestWorkValue?> completionQuestVariablesFlags)
{
return new UseOnPosition(questId, position, itemId, completionQuestVariablesFlags, gameFunctions,
questFunctions, condition, loggerFactory.CreateLogger<UseOnPosition>());
}
public ITask OnObject(ElementId questId, uint dataId, uint itemId,
List<QuestWorkValue?> completionQuestVariablesFlags, bool startingCombat = false)
{
return new UseOnObject(questId, dataId, itemId, completionQuestVariablesFlags, startingCombat,
questFunctions, gameFunctions, condition, loggerFactory.CreateLogger<UseOnObject>());
}
public ITask OnSelf(ElementId questId, uint itemId, List<QuestWorkValue?> completionQuestVariablesFlags)
{
return new Use(questId, itemId, completionQuestVariablesFlags, gameFunctions, questFunctions, condition,
loggerFactory.CreateLogger<Use>());
}
private IEnumerable<ITask> CreateVesperBayFallbackTask() private IEnumerable<ITask> CreateVesperBayFallbackTask()
{ {
logger.LogWarning("No vesper bay aetheryte tickets in inventory, navigating via ferry in Limsa instead"); logger.LogWarning("No vesper bay aetheryte tickets in inventory, navigating via ferry in Limsa instead");
@ -107,28 +136,32 @@ internal static class UseItem
uint npcId = 1003540; uint npcId = 1003540;
ushort territoryId = 129; ushort territoryId = 129;
Vector3 destination = new(-360.9217f, 8f, 38.92566f); Vector3 destination = new(-360.9217f, 8f, 38.92566f);
yield return serviceProvider.GetRequiredService<AetheryteShortcut.UseAetheryteShortcut>() yield return aetheryteShortcutFactory.Use(null, null, EAetheryteLocation.Limsa, territoryId);
.With(null, null, EAetheryteLocation.Limsa, territoryId); yield return aethernetShortcutFactory.Use(EAetheryteLocation.Limsa, EAetheryteLocation.LimsaArcanist);
yield return serviceProvider.GetRequiredService<AethernetShortcut.UseAethernetShortcut>() yield return new WaitAtEnd.WaitDelay();
.With(EAetheryteLocation.Limsa, EAetheryteLocation.LimsaArcanist); yield return
yield return serviceProvider.GetRequiredService<WaitAtEnd.WaitDelay>(); moveFactory.Move(new MoveTo.MoveParams(territoryId, destination, DataId: npcId, Sprint: false));
yield return serviceProvider.GetRequiredService<Move.MoveInternal>() yield return interactFactory.Interact(npcId, null, EInteractionType.None, true);
.With(territoryId, destination, dataId: npcId, sprint: false);
yield return serviceProvider.GetRequiredService<Interact.DoInteract>()
.With(npcId, null, EInteractionType.None, true);
} }
} }
internal abstract class UseItemBase(QuestFunctions questFunctions, ICondition condition, ILogger logger) : ITask private abstract class UseItemBase(
ElementId? questId,
uint itemId,
IList<QuestWorkValue?> completionQuestVariablesFlags,
bool startingCombat,
QuestFunctions questFunctions,
ICondition condition,
ILogger logger) : ITask
{ {
private bool _usedItem; private bool _usedItem;
private DateTime _continueAt; private DateTime _continueAt;
private int _itemCount; private int _itemCount;
public ElementId? QuestId { get; set; } public ElementId? QuestId => questId;
public uint ItemId { get; set; } public uint ItemId => itemId;
public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>(); public IList<QuestWorkValue?> CompletionQuestVariablesFlags => completionQuestVariablesFlags;
public bool StartingCombat { get; set; } public bool StartingCombat => startingCombat;
protected abstract bool UseItem(); protected abstract bool UseItem();
@ -149,9 +182,9 @@ internal static class UseItem
public unsafe ETaskResult Update() public unsafe ETaskResult Update()
{ {
if (QuestId is QuestId questId && QuestWorkUtils.HasCompletionFlags(CompletionQuestVariablesFlags)) if (QuestId is QuestId realQuestId && QuestWorkUtils.HasCompletionFlags(CompletionQuestVariablesFlags))
{ {
QuestProgressInfo? questWork = questFunctions.GetQuestProgressInfo(questId); QuestProgressInfo? questWork = questFunctions.GetQuestProgressInfo(realQuestId);
if (questWork != null && if (questWork != null &&
QuestWorkUtils.MatchesQuestWork(CompletionQuestVariablesFlags, questWork)) QuestWorkUtils.MatchesQuestWork(CompletionQuestVariablesFlags, questWork))
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
@ -203,96 +236,66 @@ internal static class UseItem
} }
internal sealed class UseOnGround( private sealed class UseOnGround(
ElementId? questId,
uint dataId,
uint itemId,
IList<QuestWorkValue?> completionQuestVariablesFlags,
GameFunctions gameFunctions, GameFunctions gameFunctions,
QuestFunctions questFunctions, QuestFunctions questFunctions,
ICondition condition, ICondition condition,
ILogger<UseOnGround> logger) ILogger<UseOnGround> logger)
: UseItemBase(questFunctions, condition, logger) : UseItemBase(questId, itemId, completionQuestVariablesFlags, false, questFunctions, condition, logger)
{ {
public uint DataId { get; set; } protected override bool UseItem() => gameFunctions.UseItemOnGround(dataId, ItemId);
public ITask With(ElementId? questId, uint dataId, uint itemId, public override string ToString() => $"UseItem({ItemId} on ground at {dataId})";
IList<QuestWorkValue?> completionQuestVariablesFlags)
{
QuestId = questId;
DataId = dataId;
ItemId = itemId;
CompletionQuestVariablesFlags = completionQuestVariablesFlags;
return this;
}
protected override bool UseItem() => gameFunctions.UseItemOnGround(DataId, ItemId);
public override string ToString() => $"UseItem({ItemId} on ground at {DataId})";
} }
internal sealed class UseOnPosition( private sealed class UseOnPosition(
ElementId? questId,
Vector3 position,
uint itemId,
IList<QuestWorkValue?> completionQuestVariablesFlags,
GameFunctions gameFunctions, GameFunctions gameFunctions,
QuestFunctions questFunctions, QuestFunctions questFunctions,
ICondition condition, ICondition condition,
ILogger<UseOnPosition> logger) ILogger<UseOnPosition> logger)
: UseItemBase(questFunctions, condition, logger) : UseItemBase(questId, itemId, completionQuestVariablesFlags, false, questFunctions, condition, logger)
{ {
public Vector3 Position { get; set; } protected override bool UseItem() => gameFunctions.UseItemOnPosition(position, ItemId);
public ITask With(ElementId? questId, Vector3 position, uint itemId,
IList<QuestWorkValue?> completionQuestVariablesFlags)
{
QuestId = questId;
Position = position;
ItemId = itemId;
CompletionQuestVariablesFlags = completionQuestVariablesFlags;
return this;
}
protected override bool UseItem() => gameFunctions.UseItemOnPosition(Position, ItemId);
public override string ToString() => public override string ToString() =>
$"UseItem({ItemId} on ground at {Position.ToString("G", CultureInfo.InvariantCulture)})"; $"UseItem({ItemId} on ground at {position.ToString("G", CultureInfo.InvariantCulture)})";
} }
internal sealed class UseOnObject( private sealed class UseOnObject(
ElementId? questId,
uint dataId,
uint itemId,
IList<QuestWorkValue?> completionQuestVariablesFlags,
bool startingCombat,
QuestFunctions questFunctions, QuestFunctions questFunctions,
GameFunctions gameFunctions, GameFunctions gameFunctions,
ICondition condition, ICondition condition,
ILogger<UseOnObject> logger) ILogger<UseOnObject> logger)
: UseItemBase(questFunctions, condition, logger) : UseItemBase(questId, itemId, completionQuestVariablesFlags, startingCombat, questFunctions, condition, logger)
{ {
public uint DataId { get; set; } protected override bool UseItem() => gameFunctions.UseItem(dataId, ItemId);
public ITask With(ElementId? questId, uint dataId, uint itemId, public override string ToString() => $"UseItem({ItemId} on {dataId})";
IList<QuestWorkValue?> completionQuestVariablesFlags,
bool startingCombat = false)
{
QuestId = questId;
DataId = dataId;
ItemId = itemId;
StartingCombat = startingCombat;
CompletionQuestVariablesFlags = completionQuestVariablesFlags;
return this;
}
protected override bool UseItem() => gameFunctions.UseItem(DataId, ItemId);
public override string ToString() => $"UseItem({ItemId} on {DataId})";
} }
internal sealed class Use( private sealed class Use(
ElementId? questId,
uint itemId,
IList<QuestWorkValue?> completionQuestVariablesFlags,
GameFunctions gameFunctions, GameFunctions gameFunctions,
QuestFunctions questFunctions, QuestFunctions questFunctions,
ICondition condition, ICondition condition,
ILogger<Use> logger) ILogger<Use> logger)
: UseItemBase(questFunctions, condition, logger) : UseItemBase(questId, itemId, completionQuestVariablesFlags, false, questFunctions, condition, logger)
{ {
public ITask With(ElementId? questId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
{
QuestId = questId;
ItemId = itemId;
CompletionQuestVariablesFlags = completionQuestVariablesFlags;
return this;
}
protected override bool UseItem() => gameFunctions.UseItem(ItemId); protected override bool UseItem() => gameFunctions.UseItem(ItemId);
public override string ToString() => $"UseItem({ItemId})"; public override string ToString() => $"UseItem({ItemId})";

View File

@ -9,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI; using LLib.GameUI;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Questionable.Controller.Steps.Common; using Questionable.Controller.Steps.Common;
using Questionable.Functions;
using Questionable.Model; using Questionable.Model;
using Questionable.Model.Questing; using Questionable.Model.Questing;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
@ -17,31 +18,23 @@ namespace Questionable.Controller.Steps.Leves;
internal static class InitiateLeve internal static class InitiateLeve
{ {
internal sealed class Factory(IServiceProvider serviceProvider, ICondition condition) : ITaskFactory internal sealed class Factory(IGameGui gameGui, ICondition condition) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
if (step.InteractionType != EInteractionType.InitiateLeve) if (step.InteractionType != EInteractionType.InitiateLeve)
yield break; yield break;
yield return serviceProvider.GetRequiredService<SkipInitiateIfActive>().With(quest.Id); yield return new SkipInitiateIfActive(quest.Id);
yield return serviceProvider.GetRequiredService<OpenJournal>().With(quest.Id); yield return new OpenJournal(quest.Id);
yield return serviceProvider.GetRequiredService<Initiate>().With(quest.Id); yield return new Initiate(quest.Id, gameGui);
yield return serviceProvider.GetRequiredService<SelectDifficulty>(); yield return new SelectDifficulty(gameGui);
yield return new WaitConditionTask(() => condition[ConditionFlag.BoundByDuty], "Wait(BoundByDuty)"); yield return new WaitConditionTask(() => condition[ConditionFlag.BoundByDuty], "Wait(BoundByDuty)");
} }
} }
internal sealed unsafe class SkipInitiateIfActive : ITask internal sealed unsafe class SkipInitiateIfActive(ElementId elementId) : ITask
{ {
private ElementId _elementId = null!;
public ITask With(ElementId elementId)
{
_elementId = elementId;
return this;
}
public bool Start() => true; public bool Start() => true;
public ETaskResult Update() public ETaskResult Update()
@ -50,31 +43,23 @@ internal static class InitiateLeve
if (director != null && if (director != null &&
director->EventHandlerInfo != null && director->EventHandlerInfo != null &&
director->EventHandlerInfo->EventId.ContentId == EventHandlerType.GatheringLeveDirector && director->EventHandlerInfo->EventId.ContentId == EventHandlerType.GatheringLeveDirector &&
director->ContentId == _elementId.Value) director->ContentId == elementId.Value)
return ETaskResult.SkipRemainingTasksForStep; return ETaskResult.SkipRemainingTasksForStep;
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
public override string ToString() => $"CheckIfAlreadyActive({_elementId})"; public override string ToString() => $"CheckIfAlreadyActive({elementId})";
} }
internal sealed unsafe class OpenJournal : ITask internal sealed unsafe class OpenJournal(ElementId elementId) : ITask
{ {
private ElementId _elementId = null!; private readonly uint _questType = elementId is LeveId ? 2u : 1u;
private uint _questType;
private DateTime _openedAt = DateTime.MinValue; private DateTime _openedAt = DateTime.MinValue;
public ITask With(ElementId elementId)
{
_elementId = elementId;
_questType = _elementId is LeveId ? 2u : 1u;
return this;
}
public bool Start() public bool Start()
{ {
AgentQuestJournal.Instance()->OpenForQuest(_elementId.Value, _questType); AgentQuestJournal.Instance()->OpenForQuest(elementId.Value, _questType);
_openedAt = DateTime.Now; _openedAt = DateTime.Now;
return true; return true;
} }
@ -83,32 +68,24 @@ internal static class InitiateLeve
{ {
AgentQuestJournal* agentQuestJournal = AgentQuestJournal.Instance(); AgentQuestJournal* agentQuestJournal = AgentQuestJournal.Instance();
if (agentQuestJournal->IsAgentActive() && if (agentQuestJournal->IsAgentActive() &&
agentQuestJournal->SelectedQuestId == _elementId.Value && agentQuestJournal->SelectedQuestId == elementId.Value &&
agentQuestJournal->SelectedQuestType == _questType) agentQuestJournal->SelectedQuestType == _questType)
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
if (DateTime.Now > _openedAt.AddSeconds(3)) if (DateTime.Now > _openedAt.AddSeconds(3))
{ {
AgentQuestJournal.Instance()->OpenForQuest(_elementId.Value, _questType); AgentQuestJournal.Instance()->OpenForQuest(elementId.Value, _questType);
_openedAt = DateTime.Now; _openedAt = DateTime.Now;
} }
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
public override string ToString() => $"OpenJournal({_elementId})"; public override string ToString() => $"OpenJournal({elementId})";
} }
internal sealed unsafe class Initiate(IGameGui gameGui) : ITask internal sealed unsafe class Initiate(ElementId elementId, IGameGui gameGui) : ITask
{ {
private ElementId _elementId = null!;
public ITask With(ElementId elementId)
{
_elementId = elementId;
return this;
}
public bool Start() => true; public bool Start() => true;
public ETaskResult Update() public ETaskResult Update()
@ -118,7 +95,7 @@ internal static class InitiateLeve
var pickQuest = stackalloc AtkValue[] var pickQuest = stackalloc AtkValue[]
{ {
new() { Type = ValueType.Int, Int = 4 }, new() { Type = ValueType.Int, Int = 4 },
new() { Type = ValueType.UInt, Int = _elementId.Value } new() { Type = ValueType.UInt, Int = elementId.Value }
}; };
addonJournalDetail->FireCallback(2, pickQuest); addonJournalDetail->FireCallback(2, pickQuest);
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
@ -127,7 +104,7 @@ internal static class InitiateLeve
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
public override string ToString() => $"InitiateLeve({_elementId})"; public override string ToString() => $"InitiateLeve({elementId})";
} }
internal sealed unsafe class SelectDifficulty(IGameGui gameGui) : ITask internal sealed unsafe class SelectDifficulty(IGameGui gameGui) : ITask

View File

@ -20,7 +20,16 @@ namespace Questionable.Controller.Steps.Shared;
internal static class AethernetShortcut internal static class AethernetShortcut
{ {
internal sealed class Factory(IServiceProvider serviceProvider, MovementController movementController) internal sealed class Factory(
MovementController movementController,
AetheryteFunctions aetheryteFunctions,
GameFunctions gameFunctions,
IClientState clientState,
AetheryteData aetheryteData,
TerritoryData territoryData,
LifestreamIpc lifestreamIpc,
ICondition condition,
ILoggerFactory loggerFactory)
: ITaskFactory : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
@ -30,12 +39,22 @@ internal static class AethernetShortcut
yield return new WaitConditionTask(() => movementController.IsNavmeshReady, yield return new WaitConditionTask(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)"); "Wait(navmesh ready)");
yield return serviceProvider.GetRequiredService<UseAethernetShortcut>() yield return Use(step.AethernetShortcut.From, step.AethernetShortcut.To,
.With(step.AethernetShortcut.From, step.AethernetShortcut.To, step.SkipConditions?.AethernetShortcutIf); step.SkipConditions?.AethernetShortcutIf);
}
public ITask Use(EAetheryteLocation from, EAetheryteLocation to, SkipAetheryteCondition? skipConditions = null)
{
return new UseAethernetShortcut(from, to, skipConditions ?? new(),
loggerFactory.CreateLogger<UseAethernetShortcut>(), aetheryteFunctions, gameFunctions, clientState,
aetheryteData, territoryData, lifestreamIpc, movementController, condition);
} }
} }
internal sealed class UseAethernetShortcut( internal sealed class UseAethernetShortcut(
EAetheryteLocation from,
EAetheryteLocation to,
SkipAetheryteCondition skipConditions,
ILogger<UseAethernetShortcut> logger, ILogger<UseAethernetShortcut> logger,
AetheryteFunctions aetheryteFunctions, AetheryteFunctions aetheryteFunctions,
GameFunctions gameFunctions, GameFunctions gameFunctions,
@ -51,68 +70,58 @@ internal static class AethernetShortcut
private bool _triedMounting; private bool _triedMounting;
private DateTime _continueAt = DateTime.MinValue; private DateTime _continueAt = DateTime.MinValue;
public EAetheryteLocation From { get; set; } public EAetheryteLocation From => from;
public EAetheryteLocation To { get; set; } public EAetheryteLocation To => to;
public SkipAetheryteCondition SkipConditions { get; set; } = null!;
public ITask With(EAetheryteLocation from, EAetheryteLocation to,
SkipAetheryteCondition? skipConditions = null)
{
From = from;
To = to;
SkipConditions = skipConditions ?? new();
return this;
}
public bool Start() public bool Start()
{ {
if (!SkipConditions.Never) if (!skipConditions.Never)
{ {
if (SkipConditions.InSameTerritory && clientState.TerritoryType == aetheryteData.TerritoryIds[To]) if (skipConditions.InSameTerritory && clientState.TerritoryType == aetheryteData.TerritoryIds[to])
{ {
logger.LogInformation("Skipping aethernet shortcut because the target is in the same territory"); logger.LogInformation("Skipping aethernet shortcut because the target is in the same territory");
return false; return false;
} }
if (SkipConditions.InTerritory.Contains(clientState.TerritoryType)) if (skipConditions.InTerritory.Contains(clientState.TerritoryType))
{ {
logger.LogInformation( logger.LogInformation(
"Skipping aethernet shortcut because the target is in the specified territory"); "Skipping aethernet shortcut because the target is in the specified territory");
return false; return false;
} }
if (SkipConditions.AetheryteLocked != null && if (skipConditions.AetheryteLocked != null &&
!aetheryteFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteLocked.Value)) !aetheryteFunctions.IsAetheryteUnlocked(skipConditions.AetheryteLocked.Value))
{ {
logger.LogInformation("Skipping aethernet shortcut because the target aetheryte is locked"); logger.LogInformation("Skipping aethernet shortcut because the target aetheryte is locked");
return false; return false;
} }
if (SkipConditions.AetheryteUnlocked != null && if (skipConditions.AetheryteUnlocked != null &&
aetheryteFunctions.IsAetheryteUnlocked(SkipConditions.AetheryteUnlocked.Value)) aetheryteFunctions.IsAetheryteUnlocked(skipConditions.AetheryteUnlocked.Value))
{ {
logger.LogInformation("Skipping aethernet shortcut because the target aetheryte is unlocked"); logger.LogInformation("Skipping aethernet shortcut because the target aetheryte is unlocked");
return false; return false;
} }
} }
if (aetheryteFunctions.IsAetheryteUnlocked(From) && if (aetheryteFunctions.IsAetheryteUnlocked(from) &&
aetheryteFunctions.IsAetheryteUnlocked(To)) aetheryteFunctions.IsAetheryteUnlocked(to))
{ {
ushort territoryType = clientState.TerritoryType; ushort territoryType = clientState.TerritoryType;
Vector3 playerPosition = clientState.LocalPlayer!.Position; Vector3 playerPosition = clientState.LocalPlayer!.Position;
// closer to the source // closer to the source
if (aetheryteData.CalculateDistance(playerPosition, territoryType, From) < if (aetheryteData.CalculateDistance(playerPosition, territoryType, from) <
aetheryteData.CalculateDistance(playerPosition, territoryType, To)) aetheryteData.CalculateDistance(playerPosition, territoryType, to))
{ {
if (aetheryteData.CalculateDistance(playerPosition, territoryType, From) < if (aetheryteData.CalculateDistance(playerPosition, territoryType, from) <
(From.IsFirmamentAetheryte() ? 11f : 4f)) (from.IsFirmamentAetheryte() ? 11f : 4f))
{ {
DoTeleport(); DoTeleport();
return true; return true;
} }
else if (From == EAetheryteLocation.SolutionNine) else if (from == EAetheryteLocation.SolutionNine)
{ {
logger.LogInformation("Moving to S9 aetheryte"); logger.LogInformation("Moving to S9 aetheryte");
List<Vector3> nearbyPoints = List<Vector3> nearbyPoints =
@ -125,14 +134,14 @@ internal static class AethernetShortcut
Vector3 closestPoint = nearbyPoints.MinBy(x => (playerPosition - x).Length()); Vector3 closestPoint = nearbyPoints.MinBy(x => (playerPosition - x).Length());
_moving = true; _moving = true;
movementController.NavigateTo(EMovementType.Quest, (uint)From, closestPoint, false, true, movementController.NavigateTo(EMovementType.Quest, (uint)from, closestPoint, false, true,
0.25f); 0.25f);
return true; return true;
} }
else else
{ {
if (territoryData.CanUseMount(territoryType) && if (territoryData.CanUseMount(territoryType) &&
aetheryteData.CalculateDistance(playerPosition, territoryType, From) > 30 && aetheryteData.CalculateDistance(playerPosition, territoryType, from) > 30 &&
!gameFunctions.HasStatusPreventingMount()) !gameFunctions.HasStatusPreventingMount())
{ {
_triedMounting = gameFunctions.Mount(); _triedMounting = gameFunctions.Mount();
@ -151,7 +160,7 @@ internal static class AethernetShortcut
else else
logger.LogWarning( logger.LogWarning(
"Aethernet shortcut not unlocked (from: {FromAetheryte}, to: {ToAetheryte}), walking manually", "Aethernet shortcut not unlocked (from: {FromAetheryte}, to: {ToAetheryte}), walking manually",
From, To); from, to);
return false; return false;
} }
@ -160,26 +169,26 @@ internal static class AethernetShortcut
{ {
logger.LogInformation("Moving to aethernet shortcut"); logger.LogInformation("Moving to aethernet shortcut");
_moving = true; _moving = true;
movementController.NavigateTo(EMovementType.Quest, (uint)From, aetheryteData.Locations[From], movementController.NavigateTo(EMovementType.Quest, (uint)from, aetheryteData.Locations[from],
false, true, false, true,
From.IsFirmamentAetheryte() from.IsFirmamentAetheryte()
? 4.4f ? 4.4f
: AetheryteConverter.IsLargeAetheryte(From) : AetheryteConverter.IsLargeAetheryte(from)
? 10.9f ? 10.9f
: 6.9f); : 6.9f);
} }
private void DoTeleport() private void DoTeleport()
{ {
if (From.IsFirmamentAetheryte()) if (from.IsFirmamentAetheryte())
{ {
logger.LogInformation("Using manual teleport interaction"); logger.LogInformation("Using manual teleport interaction");
_teleported = gameFunctions.InteractWith((uint)From, ObjectKind.EventObj); _teleported = gameFunctions.InteractWith((uint)from, ObjectKind.EventObj);
} }
else else
{ {
logger.LogInformation("Using lifestream to teleport to {Destination}", To); logger.LogInformation("Using lifestream to teleport to {Destination}", to);
lifestreamIpc.Teleport(To); lifestreamIpc.Teleport(to);
_teleported = true; _teleported = true;
} }
} }
@ -219,22 +228,22 @@ internal static class AethernetShortcut
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
if (aetheryteData.IsAirshipLanding(To)) if (aetheryteData.IsAirshipLanding(to))
{ {
if (aetheryteData.CalculateAirshipLandingDistance(clientState.LocalPlayer?.Position ?? Vector3.Zero, if (aetheryteData.CalculateAirshipLandingDistance(clientState.LocalPlayer?.Position ?? Vector3.Zero,
clientState.TerritoryType, To) > 5) clientState.TerritoryType, to) > 5)
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
else if (aetheryteData.IsCityAetheryte(To)) else if (aetheryteData.IsCityAetheryte(to))
{ {
if (aetheryteData.CalculateDistance(clientState.LocalPlayer?.Position ?? Vector3.Zero, if (aetheryteData.CalculateDistance(clientState.LocalPlayer?.Position ?? Vector3.Zero,
clientState.TerritoryType, To) > 20) clientState.TerritoryType, to) > 20)
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
else else
{ {
// some overworld location (e.g. 'Tesselation (Lakeland)' would end up here // some overworld location (e.g. 'Tesselation (Lakeland)' would end up here
if (clientState.TerritoryType != aetheryteData.TerritoryIds[To]) if (clientState.TerritoryType != aetheryteData.TerritoryIds[to])
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
@ -242,6 +251,6 @@ internal static class AethernetShortcut
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
} }
public override string ToString() => $"UseAethernet({From} -> {To})"; public override string ToString() => $"UseAethernet({from} -> {to})";
} }
} }

View File

@ -18,23 +18,38 @@ namespace Questionable.Controller.Steps.Shared;
internal static class AetheryteShortcut internal static class AetheryteShortcut
{ {
internal sealed class Factory( internal sealed class Factory(
IServiceProvider serviceProvider, AetheryteData aetheryteData,
AetheryteData aetheryteData) : ITaskFactory AetheryteFunctions aetheryteFunctions,
QuestFunctions questFunctions,
IClientState clientState,
IChatGui chatGui,
ILoggerFactory loggerFactory) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
if (step.AetheryteShortcut == null) if (step.AetheryteShortcut == null)
yield break; yield break;
yield return serviceProvider.GetRequiredService<UseAetheryteShortcut>() yield return Use(step, quest.Id, step.AetheryteShortcut.Value,
.With(step, quest.Id, step.AetheryteShortcut.Value, aetheryteData.TerritoryIds[step.AetheryteShortcut.Value]);
aetheryteData.TerritoryIds[step.AetheryteShortcut.Value]); yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(0.5));
yield return serviceProvider.GetRequiredService<WaitAtEnd.WaitDelay>() }
.With(TimeSpan.FromSeconds(0.5));
public ITask Use(QuestStep? step, ElementId? elementId, EAetheryteLocation targetAetheryte,
ushort expectedTerritoryId)
{
return new UseAetheryteShortcut(step, elementId, targetAetheryte, expectedTerritoryId,
loggerFactory.CreateLogger<UseAetheryteShortcut>(), aetheryteFunctions, questFunctions, clientState,
chatGui, aetheryteData);
} }
} }
internal sealed class UseAetheryteShortcut( /// <param name="expectedTerritoryId">If using an aethernet shortcut after, the aetheryte's territory-id and the step's territory-id can differ, we always use the aetheryte's territory-id.</param>
private sealed class UseAetheryteShortcut(
QuestStep? step,
ElementId? elementId,
EAetheryteLocation targetAetheryte,
ushort expectedTerritoryId,
ILogger<UseAetheryteShortcut> logger, ILogger<UseAetheryteShortcut> logger,
AetheryteFunctions aetheryteFunctions, AetheryteFunctions aetheryteFunctions,
QuestFunctions questFunctions, QuestFunctions questFunctions,
@ -45,26 +60,6 @@ internal static class AetheryteShortcut
private bool _teleported; private bool _teleported;
private DateTime _continueAt; private DateTime _continueAt;
public QuestStep? Step { get; set; }
public ElementId? ElementId { get; set; }
public EAetheryteLocation TargetAetheryte { get; set; }
/// <summary>
/// If using an aethernet shortcut after, the aetheryte's territory-id and the step's territory-id can differ,
/// we always use the aetheryte's territory-id.
/// </summary>
public ushort ExpectedTerritoryId { get; set; }
public ITask With(QuestStep? step, ElementId? elementId, EAetheryteLocation targetAetheryte,
ushort expectedTerritoryId)
{
Step = step;
ElementId = elementId;
TargetAetheryte = targetAetheryte;
ExpectedTerritoryId = expectedTerritoryId;
return this;
}
public bool Start() => !ShouldSkipTeleport(); public bool Start() => !ShouldSkipTeleport();
public ETaskResult Update() public ETaskResult Update()
@ -78,7 +73,7 @@ internal static class AetheryteShortcut
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
if (clientState.TerritoryType == ExpectedTerritoryId) if (clientState.TerritoryType == expectedTerritoryId)
return ETaskResult.TaskComplete; return ETaskResult.TaskComplete;
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
@ -87,9 +82,9 @@ internal static class AetheryteShortcut
private bool ShouldSkipTeleport() private bool ShouldSkipTeleport()
{ {
ushort territoryType = clientState.TerritoryType; ushort territoryType = clientState.TerritoryType;
if (Step != null) if (step != null)
{ {
var skipConditions = Step.SkipConditions?.AetheryteShortcutIf ?? new(); var skipConditions = step.SkipConditions?.AetheryteShortcutIf ?? new();
if (skipConditions is { Never: false }) if (skipConditions is { Never: false })
{ {
if (skipConditions.InTerritory.Contains(territoryType)) if (skipConditions.InTerritory.Contains(territoryType))
@ -112,12 +107,12 @@ internal static class AetheryteShortcut
return true; return true;
} }
if (ElementId != null) if (elementId != null)
{ {
QuestProgressInfo? questWork = questFunctions.GetQuestProgressInfo(ElementId); QuestProgressInfo? questWork = questFunctions.GetQuestProgressInfo(elementId);
if (skipConditions.RequiredQuestVariablesNotMet && if (skipConditions.RequiredQuestVariablesNotMet &&
questWork != null && questWork != null &&
!QuestWorkUtils.MatchesRequiredQuestWorkConfig(Step.RequiredQuestVariables, questWork, !QuestWorkUtils.MatchesRequiredQuestWorkConfig(step.RequiredQuestVariables, questWork,
logger)) logger))
{ {
logger.LogInformation("Skipping aetheryte teleport, as required variables do not match"); logger.LogInformation("Skipping aetheryte teleport, as required variables do not match");
@ -126,10 +121,11 @@ internal static class AetheryteShortcut
} }
if (skipConditions.NearPosition is { } nearPosition &&
if (skipConditions.NearPosition is { } nearPosition && clientState.TerritoryType == Step.TerritoryId) clientState.TerritoryType == step.TerritoryId)
{ {
if (Vector3.Distance(nearPosition.Position, clientState.LocalPlayer!.Position) <= nearPosition.MaximumDistance) if (Vector3.Distance(nearPosition.Position, clientState.LocalPlayer!.Position) <=
nearPosition.MaximumDistance)
{ {
logger.LogInformation("Skipping aetheryte shortcut, as we're near the position"); logger.LogInformation("Skipping aetheryte shortcut, as we're near the position");
return true; return true;
@ -137,7 +133,7 @@ internal static class AetheryteShortcut
} }
} }
if (ExpectedTerritoryId == territoryType) if (expectedTerritoryId == territoryType)
{ {
if (!skipConditions.Never) if (!skipConditions.Never)
{ {
@ -148,17 +144,17 @@ internal static class AetheryteShortcut
} }
Vector3 pos = clientState.LocalPlayer!.Position; Vector3 pos = clientState.LocalPlayer!.Position;
if (Step.Position != null && if (step.Position != null &&
(pos - Step.Position.Value).Length() < Step.CalculateActualStopDistance()) (pos - step.Position.Value).Length() < step.CalculateActualStopDistance())
{ {
logger.LogInformation("Skipping aetheryte teleport, we're near the target"); logger.LogInformation("Skipping aetheryte teleport, we're near the target");
return true; return true;
} }
if (aetheryteData.CalculateDistance(pos, territoryType, TargetAetheryte) < 20 || if (aetheryteData.CalculateDistance(pos, territoryType, targetAetheryte) < 20 ||
(Step.AethernetShortcut != null && (step.AethernetShortcut != null &&
(aetheryteData.CalculateDistance(pos, territoryType, Step.AethernetShortcut.From) < 20 || (aetheryteData.CalculateDistance(pos, territoryType, step.AethernetShortcut.From) < 20 ||
aetheryteData.CalculateDistance(pos, territoryType, Step.AethernetShortcut.To) < 20))) aetheryteData.CalculateDistance(pos, territoryType, step.AethernetShortcut.To) < 20)))
{ {
logger.LogInformation("Skipping aetheryte teleport"); logger.LogInformation("Skipping aetheryte teleport");
return true; return true;
@ -172,7 +168,7 @@ internal static class AetheryteShortcut
private bool DoTeleport() private bool DoTeleport()
{ {
if (!aetheryteFunctions.CanTeleport(TargetAetheryte)) if (!aetheryteFunctions.CanTeleport(targetAetheryte))
{ {
if (!aetheryteFunctions.IsTeleportUnlocked()) if (!aetheryteFunctions.IsTeleportUnlocked())
throw new TaskException("Teleport is not unlocked, attune to any aetheryte first."); throw new TaskException("Teleport is not unlocked, attune to any aetheryte first.");
@ -184,12 +180,12 @@ internal static class AetheryteShortcut
_continueAt = DateTime.Now.AddSeconds(8); _continueAt = DateTime.Now.AddSeconds(8);
if (!aetheryteFunctions.IsAetheryteUnlocked(TargetAetheryte)) if (!aetheryteFunctions.IsAetheryteUnlocked(targetAetheryte))
{ {
chatGui.PrintError($"[Questionable] Aetheryte {TargetAetheryte} is not unlocked."); chatGui.PrintError($"[Questionable] Aetheryte {targetAetheryte} is not unlocked.");
throw new TaskException("Aetheryte is not unlocked"); throw new TaskException("Aetheryte is not unlocked");
} }
else if (aetheryteFunctions.TeleportAetheryte(TargetAetheryte)) else if (aetheryteFunctions.TeleportAetheryte(targetAetheryte))
{ {
logger.LogInformation("Travelling via aetheryte..."); logger.LogInformation("Travelling via aetheryte...");
return true; return true;
@ -201,6 +197,6 @@ internal static class AetheryteShortcut
} }
} }
public override string ToString() => $"UseAetheryte({TargetAetheryte})"; public override string ToString() => $"UseAetheryte({targetAetheryte})";
} }
} }

View File

@ -7,18 +7,22 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameData; using LLib.GameData;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.External; using Questionable.External;
using Questionable.Model.Questing; using Questionable.Model.Questing;
using Mount = Questionable.Controller.Steps.Common.Mount;
using Quest = Questionable.Model.Quest; using Quest = Questionable.Model.Quest;
namespace Questionable.Controller.Steps.Shared; namespace Questionable.Controller.Steps.Shared;
internal static class Craft internal static class Craft
{ {
internal sealed class Factory(IServiceProvider serviceProvider) : ITaskFactory internal sealed class Factory(
IDataManager dataManager,
IClientState clientState,
ArtisanIpc artisanIpc,
Mount.Factory mountFactory,
ILoggerFactory loggerFactory) : ITaskFactory
{ {
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -29,40 +33,34 @@ internal static class Craft
ArgumentNullException.ThrowIfNull(step.ItemCount); ArgumentNullException.ThrowIfNull(step.ItemCount);
return return
[ [
serviceProvider.GetRequiredService<UnmountTask>(), mountFactory.Unmount(),
serviceProvider.GetRequiredService<DoCraft>() Craft(step.ItemId.Value, step.ItemCount.Value)
.With(step.ItemId.Value, step.ItemCount.Value)
]; ];
} }
public ITask Craft(uint itemId, int itemCount) =>
new DoCraft(itemId, itemCount, dataManager, clientState, artisanIpc, loggerFactory.CreateLogger<DoCraft>());
} }
internal sealed class DoCraft( private sealed class DoCraft(
uint itemId,
int itemCount,
IDataManager dataManager, IDataManager dataManager,
IClientState clientState, IClientState clientState,
ArtisanIpc artisanIpc, ArtisanIpc artisanIpc,
ILogger<DoCraft> logger) : ITask ILogger<DoCraft> logger) : ITask
{ {
private uint _itemId;
private int _itemCount;
public ITask With(uint itemId, int itemCount)
{
_itemId = itemId;
_itemCount = itemCount;
return this;
}
public bool Start() public bool Start()
{ {
if (HasRequestedItems()) if (HasRequestedItems())
{ {
logger.LogInformation("Already own {ItemCount}x {ItemId}", _itemCount, _itemId); logger.LogInformation("Already own {ItemCount}x {ItemId}", itemCount, itemId);
return false; return false;
} }
RecipeLookup? recipeLookup = dataManager.GetExcelSheet<RecipeLookup>()!.GetRow(_itemId); RecipeLookup? recipeLookup = dataManager.GetExcelSheet<RecipeLookup>()!.GetRow(itemId);
if (recipeLookup == null) if (recipeLookup == null)
throw new TaskException($"Item {_itemId} is not craftable"); throw new TaskException($"Item {itemId} is not craftable");
uint recipeId = (EClassJob)clientState.LocalPlayer!.ClassJob.Id switch uint recipeId = (EClassJob)clientState.LocalPlayer!.ClassJob.Id switch
{ {
@ -94,12 +92,12 @@ internal static class Craft
} }
if (recipeId == 0) if (recipeId == 0)
throw new TaskException($"Unable to determine recipe for item {_itemId}"); throw new TaskException($"Unable to determine recipe for item {itemId}");
int remainingItemCount = _itemCount - GetOwnedItemCount(); int remainingItemCount = itemCount - GetOwnedItemCount();
logger.LogInformation( logger.LogInformation(
"Starting craft for item {ItemId} with recipe {RecipeId} for {RemainingItemCount} items", "Starting craft for item {ItemId} with recipe {RecipeId} for {RemainingItemCount} items",
_itemId, recipeId, remainingItemCount); itemId, recipeId, remainingItemCount);
if (!artisanIpc.CraftItem((ushort)recipeId, remainingItemCount)) if (!artisanIpc.CraftItem((ushort)recipeId, remainingItemCount))
throw new TaskException($"Failed to start Artisan craft for recipe {recipeId}"); throw new TaskException($"Failed to start Artisan craft for recipe {recipeId}");
@ -130,15 +128,15 @@ internal static class Craft
return ETaskResult.StillRunning; return ETaskResult.StillRunning;
} }
private bool HasRequestedItems() => GetOwnedItemCount() >= _itemCount; private bool HasRequestedItems() => GetOwnedItemCount() >= itemCount;
private unsafe int GetOwnedItemCount() private unsafe int GetOwnedItemCount()
{ {
InventoryManager* inventoryManager = InventoryManager.Instance(); InventoryManager* inventoryManager = InventoryManager.Instance();
return inventoryManager->GetInventoryItemCount(_itemId, isHq: false, checkEquipped: false) return inventoryManager->GetInventoryItemCount(itemId, isHq: false, checkEquipped: false)
+ inventoryManager->GetInventoryItemCount(_itemId, isHq: true, checkEquipped: false); + inventoryManager->GetInventoryItemCount(itemId, isHq: true, checkEquipped: false);
} }
public override string ToString() => $"Craft {_itemCount}x {_itemId} (with Artisan)"; public override string ToString() => $"Craft {itemCount}x {itemId} (with Artisan)";
} }
} }

View File

@ -10,7 +10,6 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common; using Questionable.Controller.Steps.Common;
using Questionable.Data; using Questionable.Data;
using Questionable.Functions;
using Questionable.Model; using Questionable.Model;
using Questionable.Model.Gathering; using Questionable.Model.Gathering;
using Questionable.Model.Questing; using Questionable.Model.Questing;
@ -22,6 +21,7 @@ internal static class GatheringRequiredItems
internal sealed class Factory( internal sealed class Factory(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
MovementController movementController, MovementController movementController,
GatheringController gatheringController,
GatheringPointRegistry gatheringPointRegistry, GatheringPointRegistry gatheringPointRegistry,
IClientState clientState, IClientState clientState,
GatheringData gatheringData, GatheringData gatheringData,
@ -50,8 +50,7 @@ internal static class GatheringRequiredItems
if (classJob != currentClassJob) if (classJob != currentClassJob)
{ {
yield return serviceProvider.GetRequiredService<SwitchClassJob>() yield return new SwitchClassJob(classJob, clientState);
.With(classJob);
} }
if (HasRequiredItems(requiredGatheredItems)) if (HasRequiredItems(requiredGatheredItems))
@ -69,7 +68,7 @@ internal static class GatheringRequiredItems
foreach (var task in serviceProvider.GetRequiredService<TaskCreator>() foreach (var task in serviceProvider.GetRequiredService<TaskCreator>()
.CreateTasks(quest, gatheringSequence, gatheringStep)) .CreateTasks(quest, gatheringSequence, gatheringStep))
if (task is WaitAtEnd.NextStep) if (task is WaitAtEnd.NextStep)
yield return serviceProvider.GetRequiredService<SkipMarker>(); yield return CreateSkipMarkerTask();
else else
yield return task; yield return task;
} }
@ -82,8 +81,7 @@ internal static class GatheringRequiredItems
yield return new WaitConditionTask(() => movementController.IsNavmeshReady, yield return new WaitConditionTask(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)"); "Wait(navmesh ready)");
yield return serviceProvider.GetRequiredService<StartGathering>() yield return CreateStartGatheringTask(gatheringPointId, requiredGatheredItems);
.With(gatheringPointId, requiredGatheredItems);
} }
} }
@ -107,25 +105,28 @@ internal static class GatheringRequiredItems
minCollectability: (short)requiredGatheredItems.Collectability) >= minCollectability: (short)requiredGatheredItems.Collectability) >=
requiredGatheredItems.ItemCount; requiredGatheredItems.ItemCount;
} }
}
internal sealed class StartGathering(GatheringController gatheringController) : ITask private StartGathering CreateStartGatheringTask(GatheringPointId gatheringPointId, GatheredItem gatheredItem)
{
private GatheringPointId _gatheringPointId = null!;
private GatheredItem _gatheredItem = null!;
public ITask With(GatheringPointId gatheringPointId, GatheredItem gatheredItem)
{ {
_gatheringPointId = gatheringPointId; return new StartGathering(gatheringPointId, gatheredItem, gatheringController);
_gatheredItem = gatheredItem;
return this;
} }
private static SkipMarker CreateSkipMarkerTask()
{
return new SkipMarker();
}
}
private sealed class StartGathering(
GatheringPointId gatheringPointId,
GatheredItem gatheredItem,
GatheringController gatheringController) : ITask
{
public bool Start() public bool Start()
{ {
return gatheringController.Start(new GatheringController.GatheringRequest(_gatheringPointId, return gatheringController.Start(new GatheringController.GatheringRequest(gatheringPointId,
_gatheredItem.ItemId, _gatheredItem.AlternativeItemId, _gatheredItem.ItemCount, gatheredItem.ItemId, gatheredItem.AlternativeItemId, gatheredItem.ItemCount,
_gatheredItem.Collectability)); gatheredItem.Collectability));
} }
public ETaskResult Update() public ETaskResult Update()
@ -138,11 +139,11 @@ internal static class GatheringRequiredItems
public override string ToString() public override string ToString()
{ {
if (_gatheredItem.Collectability == 0) if (gatheredItem.Collectability == 0)
return $"Gather({_gatheredItem.ItemCount}x {_gatheredItem.ItemId})"; return $"Gather({gatheredItem.ItemCount}x {gatheredItem.ItemId})";
else else
return return
$"Gather({_gatheredItem.ItemCount}x {_gatheredItem.ItemId} {SeIconChar.Collectible.ToIconString()} {_gatheredItem.Collectability})"; $"Gather({gatheredItem.ItemCount}x {gatheredItem.ItemId} {SeIconChar.Collectible.ToIconString()} {gatheredItem.Collectability})";
} }
} }

View File

@ -1,330 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Numerics;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using LLib;
using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Action = System.Action;
using Quest = Questionable.Model.Quest;
namespace Questionable.Controller.Steps.Shared;
internal static class Move
{
internal sealed class Factory(IServiceProvider serviceProvider, AetheryteData aetheryteData) : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
if (step.Position != null)
{
var builder = serviceProvider.GetRequiredService<MoveBuilder>();
builder.Step = step;
builder.Destination = step.Position.Value;
return builder.Build();
}
else if (step is { DataId: not null, StopDistance: not null })
{
var task = serviceProvider.GetRequiredService<ExpectToBeNearDataId>();
task.DataId = step.DataId.Value;
task.StopDistance = step.StopDistance.Value;
return [task];
}
else if (step is { InteractionType: EInteractionType.AttuneAetheryte, Aetheryte: not null })
{
var builder = serviceProvider.GetRequiredService<MoveBuilder>();
builder.Step = step;
builder.Destination = aetheryteData.Locations[step.Aetheryte.Value];
return builder.Build();
}
else if (step is { InteractionType: EInteractionType.AttuneAethernetShard, AethernetShard: not null })
{
var builder = serviceProvider.GetRequiredService<MoveBuilder>();
builder.Step = step;
builder.Destination = aetheryteData.Locations[step.AethernetShard.Value];
return builder.Build();
}
return [];
}
}
internal sealed class MoveBuilder(
IServiceProvider serviceProvider,
ILogger<MoveBuilder> logger,
GameFunctions gameFunctions,
IClientState clientState,
MovementController movementController,
TerritoryData territoryData,
AetheryteData aetheryteData)
{
public ElementId QuestId { get; set; } = null!;
public QuestStep Step { get; set; } = null!;
public Vector3 Destination { get; set; }
public IEnumerable<ITask> Build()
{
if (Step.InteractionType == EInteractionType.Jump && Step.JumpDestination != null &&
(clientState.LocalPlayer!.Position - Step.JumpDestination.Position).Length() <=
(Step.JumpDestination.StopDistance ?? 1f))
{
logger.LogInformation("We're at the jump destination, skipping movement");
yield break;
}
yield return new WaitConditionTask(() => clientState.TerritoryType == Step.TerritoryId,
$"Wait(territory: {territoryData.GetNameAndId(Step.TerritoryId)})");
if (!Step.DisableNavmesh)
yield return new WaitConditionTask(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)");
float stopDistance = Step.CalculateActualStopDistance();
Vector3? position = clientState.LocalPlayer?.Position;
float actualDistance = position == null ? float.MaxValue : Vector3.Distance(position.Value, Destination);
// if we teleport to a different zone, assume we always need to move; this is primarily relevant for cases
// where you're e.g. in Lakeland, and the step navigates via Crystarium → Tesselation back into the same
// zone.
//
// Side effects of this check being broken include:
// - mounting when near the target npc (if you spawn close enough for the next step)
// - trying to fly when near the target npc (if close enough where no movement is required)
if (Step.AetheryteShortcut != null &&
aetheryteData.TerritoryIds[Step.AetheryteShortcut.Value] != Step.TerritoryId)
{
logger.LogDebug("Aetheryte: Changing distance to max, previous distance: {Distance}", actualDistance);
actualDistance = float.MaxValue;
}
if (QuestId is SatisfactionSupplyNpcId)
{
logger.LogDebug("SatisfactionSupply: Changing distance to max, previous distance: {Distance}",
actualDistance);
actualDistance = float.MaxValue;
}
if (Step.Mount == true)
yield return serviceProvider.GetRequiredService<MountTask>()
.With(Step.TerritoryId, MountTask.EMountIf.Always);
else if (Step.Mount == false)
yield return serviceProvider.GetRequiredService<UnmountTask>();
if (!Step.DisableNavmesh)
{
if (Step.Mount == null)
{
MountTask.EMountIf mountIf =
actualDistance > stopDistance && Step.Fly == true &&
gameFunctions.IsFlyingUnlocked(Step.TerritoryId)
? MountTask.EMountIf.Always
: MountTask.EMountIf.AwayFromPosition;
yield return serviceProvider.GetRequiredService<MountTask>()
.With(Step.TerritoryId, mountIf, Destination);
}
if (actualDistance > stopDistance)
{
yield return serviceProvider.GetRequiredService<MoveInternal>()
.With(Step, Destination);
}
else
logger.LogInformation("Skipping move task, distance: {ActualDistance} < {StopDistance}",
actualDistance, stopDistance);
}
else
{
// navmesh won't move close enough
if (actualDistance > stopDistance)
{
yield return serviceProvider.GetRequiredService<MoveInternal>()
.With(Step, Destination);
}
else
logger.LogInformation("Skipping move task, distance: {ActualDistance} < {StopDistance}",
actualDistance, stopDistance);
}
if (Step.Fly == true && Step.Land == true)
yield return serviceProvider.GetRequiredService<Land>();
}
}
internal sealed class MoveInternal(
MovementController movementController,
GameFunctions gameFunctions,
ILogger<MoveInternal> logger,
ICondition condition,
IDataManager dataManager) : ITask, IToastAware
{
private string _cannotExecuteAtThisTime = dataManager.GetString<LogMessage>(579, x => x.Text)!;
public Action StartAction { get; set; } = null!;
public Vector3 Destination { get; set; }
public ITask With(QuestStep step, Vector3 destination)
{
return With(
territoryId: step.TerritoryId,
destination: destination,
stopDistance: step.CalculateActualStopDistance(),
dataId: step.DataId,
disableNavMesh: step.DisableNavmesh,
sprint: step.Sprint != false,
fly: step.Fly == true,
land: step.Land == true,
ignoreDistanceToObject: step.IgnoreDistanceToObject == true);
}
public ITask With(ushort territoryId, Vector3 destination, float? stopDistance = null, uint? dataId = null,
bool disableNavMesh = false, bool sprint = true, bool fly = false, bool land = false,
bool ignoreDistanceToObject = false)
{
Destination = destination;
if (!gameFunctions.IsFlyingUnlocked(territoryId))
{
fly = false;
land = false;
}
if (!disableNavMesh)
{
StartAction = () =>
movementController.NavigateTo(EMovementType.Quest, dataId, Destination,
fly: fly,
sprint: sprint,
stopDistance: stopDistance,
ignoreDistanceToObject: ignoreDistanceToObject,
land: land);
}
else
{
StartAction = () =>
movementController.NavigateTo(EMovementType.Quest, dataId, [Destination],
fly: fly,
sprint: sprint,
stopDistance: stopDistance,
ignoreDistanceToObject: ignoreDistanceToObject,
land: land);
}
return this;
}
public bool Start()
{
logger.LogInformation("Moving to {Destination}", Destination.ToString("G", CultureInfo.InvariantCulture));
StartAction();
return true;
}
public ETaskResult Update()
{
if (movementController.IsPathfinding || movementController.IsPathRunning)
return ETaskResult.StillRunning;
DateTime movementStartedAt = movementController.MovementStartedAt;
if (movementStartedAt == DateTime.MaxValue || movementStartedAt.AddSeconds(2) >= DateTime.Now)
return ETaskResult.StillRunning;
return ETaskResult.TaskComplete;
}
public override string ToString() => $"MoveTo({Destination.ToString("G", CultureInfo.InvariantCulture)})";
public bool OnErrorToast(SeString message)
{
if (GameFunctions.GameStringEquals(_cannotExecuteAtThisTime, message.TextValue) &&
condition[ConditionFlag.Diving])
return true;
return false;
}
}
internal sealed class ExpectToBeNearDataId(GameFunctions gameFunctions, IClientState clientState) : ITask
{
public uint DataId { get; set; }
public float StopDistance { get; set; }
public bool Start() => true;
public ETaskResult Update()
{
IGameObject? gameObject = gameFunctions.FindObjectByDataId(DataId);
if (gameObject == null ||
(gameObject.Position - clientState.LocalPlayer!.Position).Length() > StopDistance)
{
throw new TaskException("Object not found or too far away, no position so we can't move");
}
return ETaskResult.TaskComplete;
}
}
internal sealed class Land(IClientState clientState, ICondition condition, ILogger<Land> logger) : ITask
{
private bool _landing;
private DateTime _continueAt;
public bool Start()
{
if (!condition[ConditionFlag.InFlight])
{
logger.LogInformation("Not flying, not attempting to land");
return false;
}
_landing = AttemptLanding();
_continueAt = DateTime.Now.AddSeconds(0.25);
return true;
}
public ETaskResult Update()
{
if (DateTime.Now < _continueAt)
return ETaskResult.StillRunning;
if (condition[ConditionFlag.InFlight])
{
if (!_landing)
{
_landing = AttemptLanding();
_continueAt = DateTime.Now.AddSeconds(0.25);
}
return ETaskResult.StillRunning;
}
return ETaskResult.TaskComplete;
}
private unsafe bool AttemptLanding()
{
var character = (Character*)(clientState.LocalPlayer?.Address ?? 0);
if (character != null)
{
if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 23) == 0)
{
logger.LogInformation("Attempting to land");
return ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23);
}
}
return false;
}
}
}

View File

@ -0,0 +1,340 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Numerics;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using LLib;
using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Action = System.Action;
using Mount = Questionable.Controller.Steps.Common.Mount;
using Quest = Questionable.Model.Quest;
namespace Questionable.Controller.Steps.Shared;
internal static class MoveTo
{
internal sealed class Factory(
MovementController movementController,
GameFunctions gameFunctions,
ICondition condition,
IDataManager dataManager,
IClientState clientState,
AetheryteData aetheryteData,
TerritoryData territoryData,
ILoggerFactory loggerFactory,
Mount.Factory mountFactory,
ILogger<Factory> logger) : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
if (step.Position != null)
{
return CreateMountTasks(quest.Id, step, step.Position.Value);
}
else if (step is { DataId: not null, StopDistance: not null })
{
return [ExpectToBeNearDataId(step.DataId.Value, step.StopDistance.Value)];
}
else if (step is { InteractionType: EInteractionType.AttuneAetheryte, Aetheryte: not null })
{
return CreateMountTasks(quest.Id, step, aetheryteData.Locations[step.Aetheryte.Value]);
}
else if (step is { InteractionType: EInteractionType.AttuneAethernetShard, AethernetShard: not null })
{
return CreateMountTasks(quest.Id, step, aetheryteData.Locations[step.AethernetShard.Value]);
}
return [];
}
public ITask Move(QuestStep step, Vector3 destination)
{
return Move(new MoveParams(step, destination));
}
public ITask Move(MoveParams moveParams)
{
return new MoveInternal(moveParams, movementController, gameFunctions,
loggerFactory.CreateLogger<MoveInternal>(), condition, dataManager);
}
public ITask Land()
{
return new LandTask(clientState, condition, loggerFactory.CreateLogger<LandTask>());
}
public ITask ExpectToBeNearDataId(uint dataId, float stopDistance)
{
return new WaitForNearDataId(dataId, stopDistance, gameFunctions, clientState);
}
public IEnumerable<ITask> CreateMountTasks(ElementId questId, QuestStep step, Vector3 destination)
{
if (step.InteractionType == EInteractionType.Jump && step.JumpDestination != null &&
(clientState.LocalPlayer!.Position - step.JumpDestination.Position).Length() <=
(step.JumpDestination.StopDistance ?? 1f))
{
logger.LogInformation("We're at the jump destination, skipping movement");
yield break;
}
yield return new WaitConditionTask(() => clientState.TerritoryType == step.TerritoryId,
$"Wait(territory: {territoryData.GetNameAndId(step.TerritoryId)})");
if (!step.DisableNavmesh)
yield return new WaitConditionTask(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)");
float stopDistance = step.CalculateActualStopDistance();
Vector3? position = clientState.LocalPlayer?.Position;
float actualDistance = position == null ? float.MaxValue : Vector3.Distance(position.Value, destination);
// if we teleport to a different zone, assume we always need to move; this is primarily relevant for cases
// where you're e.g. in Lakeland, and the step navigates via Crystarium → Tesselation back into the same
// zone.
//
// Side effects of this check being broken include:
// - mounting when near the target npc (if you spawn close enough for the next step)
// - trying to fly when near the target npc (if close enough where no movement is required)
if (step.AetheryteShortcut != null &&
aetheryteData.TerritoryIds[step.AetheryteShortcut.Value] != step.TerritoryId)
{
logger.LogDebug("Aetheryte: Changing distance to max, previous distance: {Distance}", actualDistance);
actualDistance = float.MaxValue;
}
if (step.Mount == true)
yield return mountFactory.Mount(step.TerritoryId, Mount.EMountIf.Always);
else if (step.Mount == false)
yield return mountFactory.Unmount();
if (!step.DisableNavmesh)
{
if (step.Mount == null)
{
Mount.EMountIf mountIf =
actualDistance > stopDistance && step.Fly == true &&
gameFunctions.IsFlyingUnlocked(step.TerritoryId)
? Mount.EMountIf.Always
: Mount.EMountIf.AwayFromPosition;
yield return mountFactory.Mount(step.TerritoryId, mountIf, destination);
}
if (actualDistance > stopDistance)
{
yield return Move(step, destination);
}
else
logger.LogInformation("Skipping move task, distance: {ActualDistance} < {StopDistance}",
actualDistance, stopDistance);
}
else
{
// navmesh won't move close enough
if (actualDistance > stopDistance)
{
yield return Move(step, destination);
}
else
logger.LogInformation("Skipping move task, distance: {ActualDistance} < {StopDistance}",
actualDistance, stopDistance);
}
if (step.Fly == true && step.Land == true)
yield return Land();
}
}
private sealed class MoveInternal : ITask, IToastAware
{
private readonly string _cannotExecuteAtThisTime;
private readonly MovementController _movementController;
private readonly ILogger<MoveInternal> _logger;
private readonly ICondition _condition;
private readonly Action _startAction;
private readonly Vector3 _destination;
public MoveInternal(MoveParams moveParams,
MovementController movementController,
GameFunctions gameFunctions,
ILogger<MoveInternal> logger,
ICondition condition,
IDataManager dataManager)
{
_movementController = movementController;
_logger = logger;
_condition = condition;
_cannotExecuteAtThisTime = dataManager.GetString<LogMessage>(579, x => x.Text)!;
_destination = moveParams.Destination;
if (!gameFunctions.IsFlyingUnlocked(moveParams.TerritoryId))
{
moveParams = moveParams with { Fly = false, Land = false };
}
if (!moveParams.DisableNavMesh)
{
_startAction = () =>
_movementController.NavigateTo(EMovementType.Quest, moveParams.DataId, _destination,
fly: moveParams.Fly,
sprint: moveParams.Sprint,
stopDistance: moveParams.StopDistance,
ignoreDistanceToObject: moveParams.IgnoreDistanceToObject,
land: moveParams.Land);
}
else
{
_startAction = () =>
_movementController.NavigateTo(EMovementType.Quest, moveParams.DataId, [_destination],
fly: moveParams.Fly,
sprint: moveParams.Sprint,
stopDistance: moveParams.StopDistance,
ignoreDistanceToObject: moveParams.IgnoreDistanceToObject,
land: moveParams.Land);
}
}
public bool Start()
{
_logger.LogInformation("Moving to {Destination}", _destination.ToString("G", CultureInfo.InvariantCulture));
_startAction();
return true;
}
public ETaskResult Update()
{
if (_movementController.IsPathfinding || _movementController.IsPathRunning)
return ETaskResult.StillRunning;
DateTime movementStartedAt = _movementController.MovementStartedAt;
if (movementStartedAt == DateTime.MaxValue || movementStartedAt.AddSeconds(2) >= DateTime.Now)
return ETaskResult.StillRunning;
return ETaskResult.TaskComplete;
}
public override string ToString() => $"MoveTo({_destination.ToString("G", CultureInfo.InvariantCulture)})";
public bool OnErrorToast(SeString message)
{
if (GameFunctions.GameStringEquals(_cannotExecuteAtThisTime, message.TextValue) &&
_condition[ConditionFlag.Diving])
return true;
return false;
}
}
internal sealed record MoveParams(
ushort TerritoryId,
Vector3 Destination,
float? StopDistance = null,
uint? DataId = null,
bool DisableNavMesh = false,
bool Sprint = true,
bool Fly = false,
bool Land = false,
bool IgnoreDistanceToObject = false)
{
public MoveParams(QuestStep step, Vector3 destination)
: this(step.TerritoryId,
destination,
step.CalculateActualStopDistance(),
step.DataId,
step.DisableNavmesh,
step.Sprint != false,
step.Fly == true,
step.Land == true,
step.IgnoreDistanceToObject == true)
{
}
}
private sealed class WaitForNearDataId(
uint dataId,
float stopDistance,
GameFunctions gameFunctions,
IClientState clientState) : ITask
{
public bool Start() => true;
public ETaskResult Update()
{
IGameObject? gameObject = gameFunctions.FindObjectByDataId(dataId);
if (gameObject == null ||
(gameObject.Position - clientState.LocalPlayer!.Position).Length() > stopDistance)
{
throw new TaskException("Object not found or too far away, no position so we can't move");
}
return ETaskResult.TaskComplete;
}
}
private sealed class LandTask(IClientState clientState, ICondition condition, ILogger<LandTask> logger) : ITask
{
private bool _landing;
private DateTime _continueAt;
public bool Start()
{
if (!condition[ConditionFlag.InFlight])
{
logger.LogInformation("Not flying, not attempting to land");
return false;
}
_landing = AttemptLanding();
_continueAt = DateTime.Now.AddSeconds(0.25);
return true;
}
public ETaskResult Update()
{
if (DateTime.Now < _continueAt)
return ETaskResult.StillRunning;
if (condition[ConditionFlag.InFlight])
{
if (!_landing)
{
_landing = AttemptLanding();
_continueAt = DateTime.Now.AddSeconds(0.25);
}
return ETaskResult.StillRunning;
}
return ETaskResult.TaskComplete;
}
private unsafe bool AttemptLanding()
{
var character = (Character*)(clientState.LocalPlayer?.Address ?? 0);
if (character != null)
{
if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 23) == 0)
{
logger.LogInformation("Attempting to land");
return ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23);
}
}
return false;
}
}
}

Some files were not shown because too many files have changed in this diff Show More