--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 141,
+ "AetheryteShortcut": "Central Thanalan - Black Brush Station",
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 30425,
+ "Locations": [
+ {
+ "Position": {
+ "X": 118.6389,
+ "Y": 7.583679,
+ "Z": 262.4399
+ },
+ "MinimumAngle": 60,
+ "MaximumAngle": 170
+ },
+ {
+ "Position": {
+ "X": 113.4342,
+ "Y": 4.562373,
+ "Z": 271.4816
+ },
+ "MinimumAngle": 80,
+ "MaximumAngle": 190
+ },
+ {
+ "Position": {
+ "X": 116.9106,
+ "Y": 2.964557,
+ "Z": 285.8209
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 115
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 30427,
+ "Locations": [
+ {
+ "Position": {
+ "X": 127.2449,
+ "Y": 15.54889,
+ "Z": 240.1923
+ },
+ "MinimumAngle": 15,
+ "MaximumAngle": 115
+ },
+ {
+ "Position": {
+ "X": 122.0915,
+ "Y": 14.071,
+ "Z": 225.1131
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 110
+ },
+ {
+ "Position": {
+ "X": 120.8954,
+ "Y": 15.9651,
+ "Z": 213.8515
+ },
+ "MinimumAngle": 30,
+ "MaximumAngle": 115
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 30426,
+ "Locations": [
+ {
+ "Position": {
+ "X": 148.7842,
+ "Y": 16.44447,
+ "Z": 292.8037
+ },
+ "MinimumAngle": 30,
+ "MaximumAngle": 135
+ },
+ {
+ "Position": {
+ "X": 144.9166,
+ "Y": 18.86193,
+ "Z": 264.833
+ },
+ "MinimumAngle": 15,
+ "MaximumAngle": 95
+ },
+ {
+ "Position": {
+ "X": 152.6806,
+ "Y": 16.58945,
+ "Z": 300.3315
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 75
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 30428,
+ "Locations": [
+ {
+ "Position": {
+ "X": 137.6659,
+ "Y": 6.65416,
+ "Z": 311.1226
+ },
+ "MinimumAngle": 15,
+ "MaximumAngle": 135
+ },
+ {
+ "Position": {
+ "X": 141.0331,
+ "Y": 5.844177,
+ "Z": 325.063
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 150
+ },
+ {
+ "Position": {
+ "X": 130.6749,
+ "Y": 5.736229,
+ "Z": 300.4703
+ },
+ "MinimumAngle": -5,
+ "MaximumAngle": 100
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 140,
+ "AetheryteShortcut": "Western Thanalan - Horizon",
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 30414,
+ "Locations": [
+ {
+ "Position": {
+ "X": 249.8601,
+ "Y": 55.12077,
+ "Z": 178.5377
+ },
+ "MinimumAngle": 125,
+ "MaximumAngle": 270
+ },
+ {
+ "Position": {
+ "X": 253.9519,
+ "Y": 55.95691,
+ "Z": 181.4238
+ },
+ "MinimumAngle": 180,
+ "MaximumAngle": 285
+ },
+ {
+ "Position": {
+ "X": 244.4912,
+ "Y": 53.49751,
+ "Z": 169.9265
+ },
+ "MinimumAngle": 150,
+ "MaximumAngle": 250
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 30413,
+ "Locations": [
+ {
+ "Position": {
+ "X": 292.9385,
+ "Y": 59.79165,
+ "Z": 187.855
+ },
+ "MinimumAngle": 45,
+ "MaximumAngle": 165
+ },
+ {
+ "Position": {
+ "X": 300.3293,
+ "Y": 63.1124,
+ "Z": 175.0616
+ },
+ "MinimumAngle": 65,
+ "MaximumAngle": 155
+ },
+ {
+ "Position": {
+ "X": 296.3942,
+ "Y": 61.46834,
+ "Z": 182.3181
+ },
+ "MinimumAngle": 70,
+ "MaximumAngle": 185
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 30415,
+ "Locations": [
+ {
+ "Position": {
+ "X": 262.1395,
+ "Y": 58.70948,
+ "Z": 239.3097
+ },
+ "MinimumAngle": 105,
+ "MaximumAngle": 210
+ },
+ {
+ "Position": {
+ "X": 284.4424,
+ "Y": 59.78878,
+ "Z": 222.5899
+ },
+ "MinimumAngle": 65,
+ "MaximumAngle": 240
+ },
+ {
+ "Position": {
+ "X": 278.6144,
+ "Y": 59.63044,
+ "Z": 231.8303
+ },
+ "MinimumAngle": 95,
+ "MaximumAngle": 185
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 30416,
+ "Locations": [
+ {
+ "Position": {
+ "X": 222.244,
+ "Y": 58.86799,
+ "Z": 244.9212
+ },
+ "MinimumAngle": 135,
+ "MaximumAngle": 275
+ },
+ {
+ "Position": {
+ "X": 212.3073,
+ "Y": 58.06055,
+ "Z": 245.9091
+ },
+ "MinimumAngle": 80,
+ "MaximumAngle": 220
+ },
+ {
+ "Position": {
+ "X": 235.9484,
+ "Y": 58.30469,
+ "Z": 249.0489
+ },
+ "MinimumAngle": 80,
+ "MaximumAngle": 190
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 1073,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 31819,
+ "Locations": [
+ {
+ "Position": {
+ "X": -5.492728,
+ "Y": 499.6548,
+ "Z": 37.58726
+ },
+ "MinimumAngle": 145,
+ "MaximumAngle": 300
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 31700,
+ "Locations": [
+ {
+ "Position": {
+ "X": 27.22863,
+ "Y": 499.8322,
+ "Z": 2.94655
+ },
+ "MinimumAngle": -15,
+ "MaximumAngle": 210
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 31489,
+ "Locations": [
+ {
+ "Position": {
+ "X": 28.38036,
+ "Y": 500.019,
+ "Z": -0.7058061
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 210
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 1073,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 31822,
+ "Locations": [
+ {
+ "Position": {
+ "X": -4.054798,
+ "Y": 494.3483,
+ "Z": -54.37905
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 31821,
+ "Locations": [
+ {
+ "Position": {
+ "X": -5.287843,
+ "Y": 494.2204,
+ "Z": -66.80152
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 31820,
+ "Locations": [
+ {
+ "Position": {
+ "X": -9.143447,
+ "Y": 494.1166,
+ "Z": -82.62958
+ },
+ "MinimumAngle": -75,
+ "MaximumAngle": 95
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 1073,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 33842,
+ "Locations": [
+ {
+ "Position": {
+ "X": -71.72573,
+ "Y": 495.8044,
+ "Z": -23.42241
+ },
+ "MinimumAngle": -15,
+ "MaximumAngle": 90
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33846,
+ "Locations": [
+ {
+ "Position": {
+ "X": -85.765,
+ "Y": 493.9822,
+ "Z": -11.33734
+ },
+ "MinimumAngle": -185,
+ "MaximumAngle": 20
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33847,
+ "Locations": [
+ {
+ "Position": {
+ "X": -100.52,
+ "Y": 493.6702,
+ "Z": -9.731167
+ },
+ "MinimumAngle": -185,
+ "MaximumAngle": -25
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 1073,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 33848,
+ "Locations": [
+ {
+ "Position": {
+ "X": -109.0629,
+ "Y": 491.0458,
+ "Z": 34.78553
+ },
+ "MinimumAngle": -110,
+ "MaximumAngle": 35
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33849,
+ "Locations": [
+ {
+ "Position": {
+ "X": -110.2371,
+ "Y": 491.0116,
+ "Z": 54.68977
+ },
+ "MinimumAngle": -170,
+ "MaximumAngle": -45
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33850,
+ "Locations": [
+ {
+ "Position": {
+ "X": -101.4737,
+ "Y": 490.7073,
+ "Z": 54.9267
+ },
+ "MinimumAngle": 90,
+ "MaximumAngle": 220
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 1073,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 33851,
+ "Locations": [
+ {
+ "Position": {
+ "X": -10.66682,
+ "Y": 499.3763,
+ "Z": 34.01145
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 32269,
+ "Locations": [
+ {
+ "Position": {
+ "X": -16.14751,
+ "Y": 499.7353,
+ "Z": 22.38433
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 32766,
+ "Locations": [
+ {
+ "Position": {
+ "X": -28.72828,
+ "Y": 499.5391,
+ "Z": 33.41306
+ },
+ "MinimumAngle": 125,
+ "MaximumAngle": 335
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 1073,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 34347,
+ "Locations": [
+ {
+ "Position": {
+ "X": -13.64273,
+ "Y": 493.9979,
+ "Z": -58.21632
+ },
+ "MinimumAngle": -5,
+ "MaximumAngle": 210
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34348,
+ "Locations": [
+ {
+ "Position": {
+ "X": -22.23647,
+ "Y": 494.0945,
+ "Z": -59.94842
+ },
+ "MinimumAngle": 125,
+ "MaximumAngle": 360
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34334,
+ "Locations": [
+ {
+ "Position": {
+ "X": -20.58942,
+ "Y": 494.25,
+ "Z": -77.68658
+ },
+ "MinimumAngle": -45,
+ "MaximumAngle": 170
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 1073,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 34353,
+ "Locations": [
+ {
+ "Position": {
+ "X": -59.17508,
+ "Y": 494.132,
+ "Z": -1.865536
+ },
+ "MinimumAngle": -35,
+ "MaximumAngle": 140
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34352,
+ "Locations": [
+ {
+ "Position": {
+ "X": -61.05396,
+ "Y": 495.2124,
+ "Z": -22.16576
+ },
+ "MinimumAngle": -30,
+ "MaximumAngle": 90
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34354,
+ "Locations": [
+ {
+ "Position": {
+ "X": -90.96052,
+ "Y": 493.2197,
+ "Z": -20.74431
+ },
+ "MinimumAngle": -70,
+ "MaximumAngle": 75
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 1073,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 34357,
+ "Locations": [
+ {
+ "Position": {
+ "X": -91.21072,
+ "Y": 490.3782,
+ "Z": 41.27306
+ },
+ "MinimumAngle": 140,
+ "MaximumAngle": 360
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34355,
+ "Locations": [
+ {
+ "Position": {
+ "X": -118.7086,
+ "Y": 490.4538,
+ "Z": 34.37638
+ },
+ "MinimumAngle": 40,
+ "MaximumAngle": 345
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34356,
+ "Locations": [
+ {
+ "Position": {
+ "X": -105.7026,
+ "Y": 490.5684,
+ "Z": 58.49864
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 210
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
AssignmentList(nameof(QuestStep.RequiredQuestVariables),
step.RequiredQuestVariables)
.AsSyntaxNodeOrToken(),
+ AssignmentList(nameof(QuestStep.RequiredGatheredItems),
+ step.RequiredGatheredItems),
AssignmentList(nameof(QuestStep.CompletionQuestVariablesFlags),
step.CompletionQuestVariablesFlags)
.AsSyntaxNodeOrToken(),
Assignment(nameof(SkipAetheryteCondition.Never), skipAetheryteCondition.Never,
emptyAetheryte.Never),
Assignment(nameof(SkipAetheryteCondition.InSameTerritory),
- skipAetheryteCondition.InSameTerritory, emptyAetheryte.InSameTerritory)))));
+ skipAetheryteCondition.InSameTerritory, emptyAetheryte.InSameTerritory),
+ AssignmentList(nameof(SkipAetheryteCondition.InTerritory),
+ skipAetheryteCondition.InTerritory)))));
+ }
+ else if (value is GatheredItem gatheredItem)
+ {
+ var emptyItem = new GatheredItem();
+ return ObjectCreationExpression(
+ IdentifierName(nameof(GatheredItem)))
+ .WithInitializer(
+ InitializerExpression(
+ SyntaxKind.ObjectInitializerExpression,
+ SeparatedList<ExpressionSyntax>(
+ SyntaxNodeList(
+ Assignment(nameof(GatheredItem.ItemId), gatheredItem.ItemId, emptyItem.ItemId)
+ .AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheredItem.ItemCount), gatheredItem.ItemCount,
+ emptyItem.ItemCount)
+ .AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheredItem.Collectability), gatheredItem.Collectability,
+ emptyItem.Collectability)
+ .AsSyntaxNodeOrToken()))));
}
else if (value is GatheringNodeGroup nodeGroup)
{
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "liza",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1002282,
+ "Position": {
+ "X": 3.5552979,
+ "Y": 7.5999613,
+ "Z": 153.2157
+ },
+ "TerritoryId": 131,
+ "InteractionType": "AcceptQuest",
+ "DialogueChoices": [
+ {
+ "Type": "YesNo",
+ "Prompt": "TEXT_CLSMIN001_00192_Q1_000_1",
+ "Yes": true
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "liza",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1002282,
+ "Position": {
+ "X": 3.5552979,
+ "Y": 7.5999613,
+ "Z": 153.2157
+ },
+ "TerritoryId": 131,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1002298,
+ "Position": {
+ "X": -18.997498,
+ "Y": 6.2,
+ "Z": 157.42725
+ },
+ "TerritoryId": 131,
+ "InteractionType": "CompleteQuest",
+ "DialogueChoices": [
+ {
+ "Type": "YesNo",
+ "Prompt": "TEXT_CLSMIN011_00597_Q1_000_1",
+ "Yes": true
+ }
+ ],
+ "NextQuestId": 599
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "liza",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1002298,
+ "Position": {
+ "X": -18.997498,
+ "Y": 6.2,
+ "Z": 157.42725
+ },
+ "TerritoryId": 131,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1002298,
+ "Position": {
+ "X": -18.997498,
+ "Y": 6.2,
+ "Z": 157.42725
+ },
+ "TerritoryId": 131,
+ "InteractionType": "CompleteQuest",
+ "AetheryteShortcut": "Ul'dah",
+ "AethernetShortcut": [
+ "[Ul'dah] Aetheryte Plaza",
+ "[Ul'dah] Miners' Guild"
+ ],
+ "SkipConditions": {
+ "AetheryteShortcutIf": {
+ "InTerritory": [
+ 130,
+ 131
+ ]
+ }
+ },
+ "RequiredGatheredItems": [
+ {
+ "ItemId": 5106,
+ "ItemCount": 10
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "liza",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1002298,
+ "Position": {
+ "X": -18.997498,
+ "Y": 6.2,
+ "Z": 157.42725
+ },
+ "TerritoryId": 131,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1002298,
+ "Position": {
+ "X": -18.997498,
+ "Y": 6.2,
+ "Z": 157.42725
+ },
+ "TerritoryId": 131,
+ "InteractionType": "CompleteQuest",
+ "AetheryteShortcut": "Ul'dah",
+ "AethernetShortcut": [
+ "[Ul'dah] Aetheryte Plaza",
+ "[Ul'dah] Miners' Guild"
+ ],
+ "SkipConditions": {
+ "AetheryteShortcutIf": {
+ "InTerritory": [
+ 130,
+ 131
+ ]
+ }
+ },
+ "RequiredGatheredItems": [
+ {
+ "ItemId": 5432,
+ "ItemCount": 10
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
},
"InSameTerritory": {
"type": "boolean"
+ },
+ "InTerritory": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
},
"additionalProperties": false
--- /dev/null
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Questionable.Model.Questing.Converter;
+
+public class ElementIdConverter : JsonConverter<ElementId>
+{
+ public override ElementId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ uint value = reader.GetUInt32();
+ return ElementId.From(value);
+ }
+
+ public override void Write(Utf8JsonWriter writer, ElementId value, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+}
+++ /dev/null
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Questionable.Model.Questing.Converter;
-
-public class IdConverter : JsonConverter<IId>
-{
- public override IId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- uint value = reader.GetUInt32();
- return Id.From(value);
- }
-
- public override void Write(Utf8JsonWriter writer, IId value, JsonSerializerOptions options)
- {
- throw new NotImplementedException();
- }
-}
--- /dev/null
+using System;
+using System.Globalization;
+
+namespace Questionable.Model.Questing;
+
+public abstract class ElementId : IComparable<ElementId>, IEquatable<ElementId>
+{
+ protected ElementId(ushort value)
+ {
+ Value = value;
+ }
+
+ public ushort Value { get; }
+
+ public int CompareTo(ElementId? other)
+ {
+ if (ReferenceEquals(this, other)) return 0;
+ if (ReferenceEquals(null, other)) return 1;
+ return Value.CompareTo(other.Value);
+ }
+
+ public bool Equals(ElementId? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ if (other.GetType() != GetType()) return false;
+ return Value == other.Value;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((ElementId)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return Value.GetHashCode();
+ }
+
+ public static bool operator ==(ElementId? left, ElementId? right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(ElementId? left, ElementId? right)
+ {
+ return !Equals(left, right);
+ }
+
+ public static ElementId From(uint value)
+ {
+ if (value >= 100_000 && value < 200_000)
+ return new LeveId((ushort)(value - 100_000));
+ else
+ return new QuestId((ushort)value);
+ }
+}
+
+public sealed class QuestId : ElementId
+{
+ public QuestId(ushort value)
+ : base(value)
+ {
+ }
+
+ public override string ToString()
+ {
+ return Value.ToString(CultureInfo.InvariantCulture);
+ }
+}
+
+public sealed class LeveId : ElementId
+{
+ public LeveId(ushort value)
+ : base(value)
+ {
+ }
+
+ public override string ToString()
+ {
+ return "L" + Value.ToString(CultureInfo.InvariantCulture);
+ }
+}
+++ /dev/null
-using System;
-using System.Globalization;
-
-namespace Questionable.Model.Questing
-{
- public interface IId : IComparable<IId>
- {
- public ushort Value { get; }
- }
-
- public static class Id
- {
- public static IId From(uint value)
- {
- if (value >= 100_000 && value < 200_000)
- return new LeveId((ushort)(value - 100_000));
- else
- return new QuestId((ushort)value);
- }
- }
-
- public sealed record QuestId(ushort Value) : IId
- {
- public override string ToString()
- {
- return "Q" + Value.ToString(CultureInfo.InvariantCulture);
- }
-
- public int CompareTo(IId? other)
- {
- if (ReferenceEquals(this, other)) return 0;
- if (ReferenceEquals(null, other)) return 1;
- return Value.CompareTo(other.Value);
- }
- }
-
- public sealed record LeveId(ushort Value) : IId
- {
- public override string ToString()
- {
- return "L" + Value.ToString(CultureInfo.InvariantCulture);
- }
-
- public int CompareTo(IId? other)
- {
- if (ReferenceEquals(this, other)) return 0;
- if (ReferenceEquals(null, other)) return 1;
- return Value.CompareTo(other.Value);
- }
- }
-}
-
-namespace System.Runtime.CompilerServices
-{
- internal static class IsExternalInit
- {
- }
-}
public IList<uint> PointMenuChoices { get; set; } = new List<uint>();
// TODO: Not implemented
- [JsonConverter(typeof(IdConverter))]
- public IId? PickUpQuestId { get; set; }
+ [JsonConverter(typeof(ElementIdConverter))]
+ public ElementId? PickUpQuestId { get; set; }
- [JsonConverter(typeof(IdConverter))]
- public IId? TurnInQuestId { get; set; }
+ [JsonConverter(typeof(ElementIdConverter))]
+ public ElementId? TurnInQuestId { get; set; }
- [JsonConverter(typeof(IdConverter))]
- public IId? NextQuestId { get; set; }
+ [JsonConverter(typeof(ElementIdConverter))]
+ public ElementId? NextQuestId { get; set; }
[JsonConstructor]
public QuestStep()
-namespace Questionable.Model.Questing;
+using System.Collections.Generic;
+
+namespace Questionable.Model.Questing;
public sealed class SkipAetheryteCondition
{
public bool Never { get; set; }
public bool InSameTerritory { get; set; }
+ public List<ushort> InTerritory { get; set; } = new();
}
public List<ushort> InTerritory { get; set; } = new();
public List<ushort> NotInTerritory { get; set; } = new();
public SkipItemConditions? Item { get; set; }
- public List<IId> QuestsAccepted { get; set; } = new();
- public List<IId> QuestsCompleted { get; set; } = new();
+ public List<ElementId> QuestsAccepted { get; set; } = new();
+ public List<ElementId> QuestsCompleted { get; set; } = new();
public EExtraSkipCondition? ExtraCondition { get; set; }
public bool HasSkipConditions()
}
}
- if (QuestWorkUtils.HasCompletionFlags(condition.CompletionQuestVariablesFlags) && _currentFight.Data.QuestId is QuestId questId)
+ if (QuestWorkUtils.HasCompletionFlags(condition.CompletionQuestVariablesFlags) && _currentFight.Data.QuestElementId is QuestId questId)
{
var questWork = _gameFunctions.GetQuestEx(questId);
if (questWork != null && QuestWorkUtils.MatchesQuestWork(condition.CompletionQuestVariablesFlags,
public sealed class CombatData
{
- public required IId QuestId { get; init; }
+ public required ElementId QuestElementId { get; init; }
public required EEnemySpawnType SpawnType { get; init; }
public required List<uint> KillEnemyDataIds { get; init; }
public required List<ComplexCombatData> ComplexCombatDatas { get; init; }
if (arguments.Length >= 1 && uint.TryParse(arguments[0], out uint questId))
{
- if (_questRegistry.TryGetQuest(Id.From(questId), out Quest? quest))
+ if (_questRegistry.TryGetQuest(ElementId.From(questId), out Quest? quest))
{
- _debugOverlay.HighlightedQuest = quest.QuestId;
+ _debugOverlay.HighlightedQuest = quest.QuestElementId;
_chatGui.Print($"[Questionable] Set highlighted quest to {questId} ({quest.Info.Name}).");
}
else
{
if (arguments.Length >= 1 && uint.TryParse(arguments[0], out uint questId))
{
- if (_gameFunctions.IsQuestLocked(Id.From(questId)))
+ if (_gameFunctions.IsQuestLocked(ElementId.From(questId)))
_chatGui.PrintError($"[Questionable] Quest {questId} is locked.");
- else if (_questRegistry.TryGetQuest(Id.From(questId), out Quest? quest))
+ else if (_questRegistry.TryGetQuest(ElementId.From(questId), out Quest? quest))
{
_questController.SetNextQuest(quest);
_chatGui.Print($"[Questionable] Set next quest to {questId} ({quest.Info.Name}).");
{
if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId))
{
- if (_questRegistry.TryGetQuest(Id.From(questId), out Quest? quest))
+ if (_questRegistry.TryGetQuest(ElementId.From(questId), out Quest? quest))
{
_questController.SimulateQuest(quest);
_chatGui.Print($"[Questionable] Simulating quest {questId} ({quest.Info.Name}).");
private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
{
- if (_questController.StartedQuest?.Quest.QuestId.Value == 4526)
+ if (_questController.StartedQuest?.Quest.QuestElementId.Value == 4526)
{
_logger.LogInformation("Closing Unending Codex");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
{
- if (_questController.StartedQuest?.Quest.QuestId.Value == 245)
+ if (_questController.StartedQuest?.Quest.QuestElementId.Value == 245)
{
_logger.LogInformation("Closing ContentsTutorial");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
/// </summary>
private unsafe void MultipleHelpWindowPostSetup(AddonEvent type, AddonArgs args)
{
- if (_questController.StartedQuest?.Quest.QuestId.Value == 245)
+ if (_questController.StartedQuest?.Quest.QuestElementId.Value == 245)
{
_logger.LogInformation("Closing MultipleHelpWindow");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
{
if (_simulatedQuest != null)
return (_simulatedQuest, CurrentQuestType.Simulated);
- else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.QuestId))
+ else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.QuestElementId))
return (_nextQuest, CurrentQuestType.Next);
else if (_startedQuest != null)
return (_startedQuest, CurrentQuestType.Normal);
// if the quest is accepted, we no longer track it
bool canUseNextQuest;
if (_nextQuest.Quest.Info.IsRepeatable)
- canUseNextQuest = !_gameFunctions.IsQuestAccepted(_nextQuest.Quest.QuestId);
+ canUseNextQuest = !_gameFunctions.IsQuestAccepted(_nextQuest.Quest.QuestElementId);
else
- canUseNextQuest = !_gameFunctions.IsQuestAcceptedOrComplete(_nextQuest.Quest.QuestId);
+ canUseNextQuest = !_gameFunctions.IsQuestAcceptedOrComplete(_nextQuest.Quest.QuestElementId);
if (!canUseNextQuest)
{
- _logger.LogInformation("Next quest {QuestId} accepted or completed", _nextQuest.Quest.QuestId);
+ _logger.LogInformation("Next quest {QuestId} accepted or completed", _nextQuest.Quest.QuestElementId);
_nextQuest = null;
}
}
currentSequence = _simulatedQuest.Sequence;
questToRun = _simulatedQuest;
}
- else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.QuestId))
+ else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.QuestElementId))
{
questToRun = _nextQuest;
currentSequence = _nextQuest.Sequence; // by definition, this should always be 0
}
else
{
- (IId? currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest();
+ (ElementId? currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest();
if (currentQuestId == null || currentQuestId.Value == 0)
{
if (_startedQuest != null)
questToRun = null;
}
- else if (_startedQuest == null || _startedQuest.Quest.QuestId != currentQuestId)
+ else if (_startedQuest == null || _startedQuest.Quest.QuestElementId != currentQuestId)
{
if (_questRegistry.TryGetQuest(currentQuestId, out var quest))
{
return (seq, seq.Steps[CurrentQuest.Step]);
}
- public void IncreaseStepCount(IId? questId, int? sequence, bool shouldContinue = false)
+ public void IncreaseStepCount(ElementId? questId, int? sequence, bool shouldContinue = false)
{
lock (_progressLock)
{
return;
}
- if (questId != null && CurrentQuest.Quest.QuestId != questId)
+ if (questId != null && CurrentQuest.Quest.QuestElementId != questId)
{
_logger.LogWarning(
"Ignoring 'increase step count' for different quest (expected {ExpectedQuestId}, but we are at {CurrentQuestId}",
- questId, CurrentQuest.Quest.QuestId);
+ questId, CurrentQuest.Quest.QuestElementId);
return;
}
public void SimulateQuest(Quest? quest)
{
- _logger.LogInformation("SimulateQuest: {QuestId}", quest?.QuestId);
+ _logger.LogInformation("SimulateQuest: {QuestId}", quest?.QuestElementId);
if (quest != null)
_simulatedQuest = new QuestProgress(quest);
else
public void SetNextQuest(Quest? quest)
{
- _logger.LogInformation("NextQuest: {QuestId}", quest?.QuestId);
+ _logger.LogInformation("NextQuest: {QuestId}", quest?.QuestElementId);
if (quest != null)
_nextQuest = new QuestProgress(quest);
else
protected override void OnNextStep(ILastTask task)
{
- IncreaseStepCount(task.QuestId, task.Sequence, true);
+ IncreaseStepCount(task.QuestElementId, task.Sequence, true);
}
public void ExecuteNextStep(bool automatic)
if (CurrentQuest == null || seq == null || step == null)
{
_logger.LogWarning("Could not retrieve next quest step, not doing anything [{QuestId}, {Sequence}, {Step}]",
- CurrentQuest?.Quest.QuestId, CurrentQuest?.Sequence, CurrentQuest?.Step);
+ CurrentQuest?.Quest.QuestElementId, CurrentQuest?.Sequence, CurrentQuest?.Step);
return;
}
}
_logger.LogInformation("Tasks for {QuestId}, {Sequence}, {Step}: {Tasks}",
- CurrentQuest.Quest.QuestId, seq.Sequence, seq.Steps.IndexOf(step),
+ CurrentQuest.Quest.QuestElementId, seq.Sequence, seq.Steps.IndexOf(step),
string.Join(", ", newTasks.Select(x => x.ToString())));
foreach (var task in newTasks)
_taskQueue.Enqueue(task);
}
}
- public void Skip(IId questQuestId, byte currentQuestSequence)
+ public void Skip(ElementId questQuestElementId, byte currentQuestSequence)
{
lock (_progressLock)
{
if (_taskQueue.Count == 0)
{
Stop("Skip");
- IncreaseStepCount(questQuestId, currentQuestSequence);
+ IncreaseStepCount(questQuestElementId, currentQuestSequence);
}
}
else
{
Stop("SkipNx");
- IncreaseStepCount(questQuestId, currentQuestSequence);
+ IncreaseStepCount(questQuestElementId, currentQuestSequence);
}
}
}
private readonly ILogger<QuestRegistry> _logger;
private readonly ICallGateProvider<object> _reloadDataIpc;
- private readonly Dictionary<IId, Quest> _quests = new();
+ private readonly Dictionary<ElementId, Quest> _quests = new();
public QuestRegistry(IDalamudPluginInterface pluginInterface, QuestData questData,
QuestValidator questValidator, JsonSchemaValidator jsonSchemaValidator,
{
Quest quest = new()
{
- QuestId = new QuestId(questId),
+ QuestElementId = new QuestId(questId),
Root = questRoot,
Info = _questData.GetQuestInfo(new QuestId(questId)),
ReadOnly = true,
};
- _quests[quest.QuestId] = quest;
+ _quests[quest.QuestElementId] = quest;
}
_logger.LogInformation("Loaded {Count} quests from assembly", _quests.Count);
private void LoadQuestFromStream(string fileName, Stream stream)
{
_logger.LogTrace("Loading quest from '{FileName}'", fileName);
- IId? questId = ExtractQuestIdFromName(fileName);
+ ElementId? questId = ExtractQuestIdFromName(fileName);
if (questId == null)
return;
Quest quest = new Quest
{
- QuestId = questId,
+ QuestElementId = questId,
Root = questNode.Deserialize<QuestRoot>()!,
Info = _questData.GetQuestInfo(questId),
ReadOnly = false,
};
- _quests[quest.QuestId] = quest;
+ _quests[quest.QuestElementId] = quest;
}
private void LoadFromDirectory(DirectoryInfo directory, LogLevel logLevel = LogLevel.Information)
LoadFromDirectory(childDirectory, logLevel);
}
- private static IId? ExtractQuestIdFromName(string resourceName)
+ private static ElementId? ExtractQuestIdFromName(string resourceName)
{
string name = resourceName.Substring(0, resourceName.Length - ".json".Length);
name = name.Substring(name.LastIndexOf('.') + 1);
return null;
string[] parts = name.Split('_', 2);
- return Id.From(uint.Parse(parts[0], CultureInfo.InvariantCulture));
+ return ElementId.From(uint.Parse(parts[0], CultureInfo.InvariantCulture));
}
- public bool IsKnownQuest(IId id)
+ public bool IsKnownQuest(ElementId elementId)
{
- if (id is QuestId questId)
+ if (elementId is QuestId questId)
return IsKnownQuest(questId);
else
return false;
public bool IsKnownQuest(QuestId questId) => _quests.ContainsKey(questId);
- public bool TryGetQuest(IId id, [NotNullWhen(true)] out Quest? quest)
+ public bool TryGetQuest(ElementId elementId, [NotNullWhen(true)] out Quest? quest)
{
- if (id is QuestId questId)
+ if (elementId is QuestId questId)
return TryGetQuest(questId, out quest);
else
{
if (step.NextQuestId == null)
return null;
- if (step.NextQuestId == quest.QuestId)
+ if (step.NextQuestId == quest.QuestElementId)
return null;
return serviceProvider.GetRequiredService<SetQuest>()
- .With(step.NextQuestId, quest.QuestId);
+ .With(step.NextQuestId, quest.QuestElementId);
}
}
internal sealed class SetQuest(QuestRegistry questRegistry, QuestController questController, GameFunctions gameFunctions, ILogger<SetQuest> logger) : ITask
{
- public IId NextQuestId { get; set; } = null!;
- public IId CurrentQuestId { get; set; } = null!;
+ public ElementId NextQuestElementId { get; set; } = null!;
+ public ElementId CurrentQuestElementId { get; set; } = null!;
- public ITask With(IId nextQuestId, IId currentQuestId)
+ public ITask With(ElementId nextQuestElementId, ElementId currentQuestElementId)
{
- NextQuestId = nextQuestId;
- CurrentQuestId = currentQuestId;
+ NextQuestElementId = nextQuestElementId;
+ CurrentQuestElementId = currentQuestElementId;
return this;
}
public bool Start()
{
- if (gameFunctions.IsQuestLocked(NextQuestId, CurrentQuestId))
+ if (gameFunctions.IsQuestLocked(NextQuestElementId, CurrentQuestElementId))
{
- 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", NextQuestElementId);
}
- else if (questRegistry.TryGetQuest(NextQuestId, out Quest? quest))
+ else if (questRegistry.TryGetQuest(NextQuestElementId, out Quest? quest))
{
- logger.LogInformation("Setting next quest to {QuestId}: '{QuestName}'", NextQuestId, quest.Info.Name);
+ logger.LogInformation("Setting next quest to {QuestId}: '{QuestName}'", NextQuestElementId, quest.Info.Name);
questController.SetNextQuest(quest);
}
else
{
- logger.LogInformation("Next quest with id {QuestId} not found", NextQuestId);
+ logger.LogInformation("Next quest with id {QuestId} not found", NextQuestElementId);
questController.SetNextQuest(null);
}
public ETaskResult Update() => ETaskResult.TaskComplete;
- public override string ToString() => $"SetNextQuest({NextQuestId})";
+ public override string ToString() => $"SetNextQuest({NextQuestElementId})";
}
}
internal interface ILastTask : ITask
{
- public IId QuestId { get; }
+ public ElementId QuestElementId { get; }
public int Sequence { get; }
}
ArgumentNullException.ThrowIfNull(step.ItemId);
yield return serviceProvider.GetRequiredService<UseItem.UseOnObject>()
- .With(quest.QuestId, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags,
+ .With(quest.QuestElementId, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags,
true);
yield return CreateTask(quest, sequence, step);
break;
bool isLastStep = sequence.Steps.Last() == step;
return serviceProvider.GetRequiredService<HandleCombat>()
- .With(quest.QuestId, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds,
+ .With(quest.QuestElementId, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds,
step.CompletionQuestVariablesFlags, step.ComplexCombatData);
}
}
private CombatController.CombatData _combatData = null!;
private IList<QuestWorkValue?> _completionQuestVariableFlags = null!;
- public ITask With(IId questId, bool isLastStep, EEnemySpawnType enemySpawnType, IList<uint> killEnemyDataIds,
+ public ITask With(ElementId questElementId, bool isLastStep, EEnemySpawnType enemySpawnType, IList<uint> killEnemyDataIds,
IList<QuestWorkValue?> completionQuestVariablesFlags, IList<ComplexCombatData> complexCombatData)
{
_isLastStep = isLastStep;
_combatData = new CombatController.CombatData
{
- QuestId = questId,
+ QuestElementId = questElementId,
SpawnType = enemySpawnType,
KillEnemyDataIds = killEnemyDataIds.ToList(),
ComplexCombatDatas = complexCombatData.ToList(),
return ETaskResult.StillRunning;
// if our quest step has any completion flags, we need to check if they are set
- if (QuestWorkUtils.HasCompletionFlags(_completionQuestVariableFlags) && _combatData.QuestId is QuestId questId)
+ if (QuestWorkUtils.HasCompletionFlags(_completionQuestVariableFlags) && _combatData.QuestElementId is QuestId questId)
{
var questWork = gameFunctions.GetQuestEx(questId);
if (questWork == null)
}
var task = serviceProvider.GetRequiredService<Use>()
- .With(quest.QuestId, step.ItemId.Value, step.CompletionQuestVariablesFlags);
+ .With(quest.QuestElementId, step.ItemId.Value, step.CompletionQuestVariablesFlags);
return
[
unmount, task,
ITask task;
if (step.DataId != null)
task = serviceProvider.GetRequiredService<UseOnGround>()
- .With(quest.QuestId, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
+ .With(quest.QuestElementId, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
else
{
ArgumentNullException.ThrowIfNull(step.Position);
task = serviceProvider.GetRequiredService<UseOnPosition>()
- .With(quest.QuestId, step.Position.Value, step.ItemId.Value,
+ .With(quest.QuestElementId, step.Position.Value, step.ItemId.Value,
step.CompletionQuestVariablesFlags);
}
else if (step.DataId != null)
{
var task = serviceProvider.GetRequiredService<UseOnObject>()
- .With(quest.QuestId, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
+ .With(quest.QuestElementId, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
return [unmount, task];
}
else
{
var task = serviceProvider.GetRequiredService<Use>()
- .With(quest.QuestId, step.ItemId.Value, step.CompletionQuestVariablesFlags);
+ .With(quest.QuestElementId, step.ItemId.Value, step.CompletionQuestVariablesFlags);
return [unmount, task];
}
}
private DateTime _continueAt;
private int _itemCount;
- public IId? QuestId { get; set; }
+ public ElementId? QuestId { get; set; }
public uint ItemId { get; set; }
public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
public bool StartingCombat { get; set; }
public uint DataId { get; set; }
- public ITask With(IId? questId, uint dataId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
+ public ITask With(ElementId? questId, uint dataId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
{
QuestId = questId;
DataId = dataId;
public Vector3 Position { get; set; }
- public ITask With(IId? questId, Vector3 position, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
+ public ITask With(ElementId? questId, Vector3 position, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
{
QuestId = questId;
Position = position;
public uint DataId { get; set; }
- public ITask With(IId? questId, uint dataId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags,
+ public ITask With(ElementId? questId, uint dataId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags,
bool startingCombat = false)
{
QuestId = questId;
{
private readonly GameFunctions _gameFunctions = gameFunctions;
- public ITask With(IId? questId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
+ public ITask With(ElementId? questId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
{
QuestId = questId;
ItemId = itemId;
{
_continueAt = DateTime.Now.AddSeconds(8);
ushort territoryType = clientState.TerritoryType;
- if (Step != null && ExpectedTerritoryId == territoryType)
+ if (Step != null)
{
var skipConditions = Step.SkipConditions?.AetheryteShortcutIf ?? new();
- if (!skipConditions.Never)
+ if (skipConditions is { Never: false, InTerritory.Count: > 0 })
{
- if (skipConditions is { InSameTerritory: true })
+ if (skipConditions.InTerritory.Contains(territoryType))
{
- logger.LogInformation("Skipping aetheryte teleport due to SkipCondition");
+ logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (InTerritory)");
return false;
}
+ }
- Vector3 pos = clientState.LocalPlayer!.Position;
- if (Step.Position != null &&
- (pos - Step.Position.Value).Length() < Step.CalculateActualStopDistance())
+ if (ExpectedTerritoryId == territoryType)
+ {
+ if (!skipConditions.Never)
{
- logger.LogInformation("Skipping aetheryte teleport, we're near the target");
- return false;
- }
+ if (skipConditions is { InSameTerritory: true })
+ {
+ logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (InSameTerritory)");
+ return false;
+ }
- if (aetheryteData.CalculateDistance(pos, territoryType, TargetAetheryte) < 20 ||
- (Step.AethernetShortcut != null &&
- (aetheryteData.CalculateDistance(pos, territoryType, Step.AethernetShortcut.From) < 20 ||
- aetheryteData.CalculateDistance(pos, territoryType, Step.AethernetShortcut.To) < 20)))
- {
- logger.LogInformation("Skipping aetheryte teleport");
- return false;
+ Vector3 pos = clientState.LocalPlayer!.Position;
+ if (Step.Position != null &&
+ (pos - Step.Position.Value).Length() < Step.CalculateActualStopDistance())
+ {
+ logger.LogInformation("Skipping aetheryte teleport, we're near the target");
+ return false;
+ }
+
+ if (aetheryteData.CalculateDistance(pos, territoryType, TargetAetheryte) < 20 ||
+ (Step.AethernetShortcut != null &&
+ (aetheryteData.CalculateDistance(pos, territoryType, Step.AethernetShortcut.From) < 20 ||
+ aetheryteData.CalculateDistance(pos, territoryType, Step.AethernetShortcut.To) < 20)))
+ {
+ logger.LogInformation("Skipping aetheryte teleport");
+ return false;
+ }
}
}
}
return null;
return serviceProvider.GetRequiredService<CheckSkip>()
- .With(step, skipConditions ?? new(), quest.QuestId);
+ .With(step, skipConditions ?? new(), quest.QuestElementId);
}
}
{
public QuestStep Step { get; set; } = null!;
public SkipStepConditions SkipConditions { get; set; } = null!;
- public IId QuestId { get; set; } = null!;
+ public ElementId QuestElementId { get; set; } = null!;
- public ITask With(QuestStep step, SkipStepConditions skipConditions, IId questId)
+ public ITask With(QuestStep step, SkipStepConditions skipConditions, ElementId questElementId)
{
Step = step;
SkipConditions = skipConditions;
- QuestId = questId;
+ QuestElementId = questElementId;
return this;
}
return true;
}
- if (QuestId is QuestId questId)
+ if (QuestElementId is QuestId questId)
{
QuestWork? questWork = gameFunctions.GetQuestEx(questId);
if (QuestWorkUtils.HasCompletionFlags(Step.CompletionQuestVariablesFlags) && questWork != null)
if (step.CompletionQuestVariablesFlags.Count == 6 && QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags))
{
var task = serviceProvider.GetRequiredService<WaitForCompletionFlags>()
- .With((QuestId)quest.QuestId, step);
+ .With((QuestId)quest.QuestElementId, step);
var delay = serviceProvider.GetRequiredService<WaitDelay>();
return [task, delay, Next(quest, sequence)];
}
case EInteractionType.AcceptQuest:
{
var accept = serviceProvider.GetRequiredService<WaitQuestAccepted>()
- .With(step.PickUpQuestId ?? quest.QuestId);
+ .With(step.PickUpQuestId ?? quest.QuestElementId);
var delay = serviceProvider.GetRequiredService<WaitDelay>();
if (step.PickUpQuestId != null)
return [accept, delay, Next(quest, sequence)];
case EInteractionType.CompleteQuest:
{
var complete = serviceProvider.GetRequiredService<WaitQuestCompleted>()
- .With(step.TurnInQuestId ?? quest.QuestId);
+ .With(step.TurnInQuestId ?? quest.QuestElementId);
var delay = serviceProvider.GetRequiredService<WaitDelay>();
if (step.TurnInQuestId != null)
return [complete, delay, Next(quest, sequence)];
private static NextStep Next(Quest quest, QuestSequence sequence)
{
- return new NextStep(quest.QuestId, sequence.Sequence);
+ return new NextStep(quest.QuestElementId, sequence.Sequence);
}
}
internal sealed class WaitQuestAccepted(GameFunctions gameFunctions) : ITask
{
- public IId QuestId { get; set; } = null!;
+ public ElementId QuestElementId { get; set; } = null!;
- public ITask With(IId questId)
+ public ITask With(ElementId questElementId)
{
- QuestId = questId;
+ QuestElementId = questElementId;
return this;
}
public ETaskResult Update()
{
- return gameFunctions.IsQuestAccepted(QuestId)
+ return gameFunctions.IsQuestAccepted(QuestElementId)
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
}
- public override string ToString() => $"WaitQuestAccepted({QuestId})";
+ public override string ToString() => $"WaitQuestAccepted({QuestElementId})";
}
internal sealed class WaitQuestCompleted(GameFunctions gameFunctions) : ITask
{
- public IId QuestId { get; set; } = null!;
+ public ElementId QuestElementId { get; set; } = null!;
- public ITask With(IId questId)
+ public ITask With(ElementId questElementId)
{
- QuestId = questId;
+ QuestElementId = questElementId;
return this;
}
public ETaskResult Update()
{
- return gameFunctions.IsQuestComplete(QuestId) ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
+ return gameFunctions.IsQuestComplete(QuestElementId) ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
}
- public override string ToString() => $"WaitQuestComplete({QuestId})";
+ public override string ToString() => $"WaitQuestComplete({QuestElementId})";
}
- internal sealed class NextStep(IId questId, int sequence) : ILastTask
+ internal sealed class NextStep(ElementId questElementId, int sequence) : ILastTask
{
- public IId QuestId { get; } = questId;
+ public ElementId QuestElementId { get; } = questElementId;
public int Sequence { get; } = sequence;
public bool Start() => true;
internal sealed class EndAutomation : ILastTask
{
- public IId QuestId => throw new InvalidOperationException();
+ public ElementId QuestElementId => throw new InvalidOperationException();
public int Sequence => throw new InvalidOperationException();
public bool Start() => true;
.ToImmutableDictionary(x => x.QuestId, x => x);
}
- public QuestInfo GetQuestInfo(IId id)
+ public QuestInfo GetQuestInfo(ElementId elementId)
{
- if (id is QuestId questId)
+ if (elementId is QuestId questId)
return GetQuestInfo(questId);
- throw new ArgumentException("Invalid id", nameof(id));
+ throw new ArgumentException("Invalid id", nameof(elementId));
}
public QuestInfo GetQuestInfo(QuestId questId)
public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
- public (IId? CurrentQuest, byte Sequence) GetCurrentQuest()
+ public (ElementId? CurrentQuest, byte Sequence) GetCurrentQuest()
{
var (currentQuest, sequence) = GetCurrentQuestInternal();
PlayerState* playerState = PlayerState.Instance();
return (currentQuest, sequence);
}
- public (IId? CurrentQuest, byte Sequence) GetCurrentQuestInternal()
+ public (ElementId? CurrentQuest, byte Sequence) GetCurrentQuestInternal()
{
var questManager = QuestManager.Instance();
if (questManager != null)
// If no quests are marked as 'priority', accepting a new quest adds it to the top of the list.
for (int i = questManager->TrackedQuests.Length - 1; i >= 0; --i)
{
- IId currentQuest;
+ ElementId currentQuest;
var trackedQuest = questManager->TrackedQuests[i];
switch (trackedQuest.QuestType)
{
return questWork != null ? *questWork : null;
}
- public bool IsReadyToAcceptQuest(IId id)
+ public bool IsReadyToAcceptQuest(ElementId elementId)
{
- if (id is QuestId questId)
+ if (elementId is QuestId questId)
return IsReadyToAcceptQuest(questId);
return false;
}
return true;
}
- public bool IsQuestAcceptedOrComplete(IId questId)
+ public bool IsQuestAcceptedOrComplete(ElementId questElementId)
{
- return IsQuestComplete(questId) || IsQuestAccepted(questId);
+ return IsQuestComplete(questElementId) || IsQuestAccepted(questElementId);
}
- public bool IsQuestAccepted(IId id)
+ public bool IsQuestAccepted(ElementId elementId)
{
- if (id is QuestId questId)
+ if (elementId is QuestId questId)
return IsQuestAccepted(questId);
return false;
}
return questManager->IsQuestAccepted(questId.Value);
}
- public bool IsQuestComplete(IId id)
+ public bool IsQuestComplete(ElementId elementId)
{
- if (id is QuestId questId)
+ if (elementId is QuestId questId)
return IsQuestComplete(questId);
return false;
}
return QuestManager.IsQuestComplete(questId.Value);
}
- public bool IsQuestLocked(IId id, IId? extraCompletedQuest = null)
+ public bool IsQuestLocked(ElementId elementId, ElementId? extraCompletedQuest = null)
{
- if (id is QuestId questId)
+ if (elementId is QuestId questId)
return IsQuestLocked(questId, extraCompletedQuest);
return false;
}
- public bool IsQuestLocked(QuestId questId, IId? extraCompletedQuest = null)
+ public bool IsQuestLocked(QuestId questId, ElementId? extraCompletedQuest = null)
{
var questInfo = _questData.GetQuestInfo(questId);
if (questInfo.QuestLocks.Count > 0)
return !HasCompletedPreviousQuests(questInfo, extraCompletedQuest) || !HasCompletedPreviousInstances(questInfo);
}
- private bool HasCompletedPreviousQuests(QuestInfo questInfo, IId? extraCompletedQuest)
+ private bool HasCompletedPreviousQuests(QuestInfo questInfo, ElementId? extraCompletedQuest)
{
if (questInfo.PreviousQuests.Count == 0)
return true;
if (excelSheetName == null)
{
var questRow =
- _dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestId.Value +
+ _dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestElementId.Value +
0x10000);
if (questRow == null)
{
- _logger.LogError("Could not find quest row for {QuestId}", currentQuest.QuestId);
+ _logger.LogError("Could not find quest row for {QuestId}", currentQuest.QuestElementId);
return null;
}
- excelSheetName = $"quest/{(currentQuest.QuestId.Value / 100):000}/{questRow.Id}";
+ excelSheetName = $"quest/{(currentQuest.QuestElementId.Value / 100):000}/{questRow.Id}";
}
var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(excelSheetName);
internal sealed class Quest
{
- public required IId QuestId { get; init; }
+ public required ElementId QuestElementId { get; init; }
public required QuestRoot Root { get; init; }
public required QuestInfo Info { get; init; }
public required bool ReadOnly { get; init; }
<Project Sdk="Dalamud.NET.Sdk/10.0.0">
<PropertyGroup>
- <Version>1.24</Version>
+ <Version>2.0</Version>
<OutputPath>dist</OutputPath>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<Platforms>x64</Platforms>
internal sealed record ValidationIssue
{
- public required IId? QuestId { get; init; }
+ public required ElementId? QuestId { get; init; }
public required byte? Sequence { get; init; }
public required int? Step { get; init; }
public EBeastTribe BeastTribe { get; init; } = EBeastTribe.None;
public IEnumerable<ValidationIssue> Validate(Quest quest)
{
return quest.AllSteps()
- .Select(x => Validate(quest.QuestId, x.Sequence.Sequence, x.StepId, x.Step.AethernetShortcut))
+ .Select(x => Validate(quest.QuestElementId, x.Sequence.Sequence, x.StepId, x.Step.AethernetShortcut))
.Where(x => x != null)
.Cast<ValidationIssue>();
}
- private ValidationIssue? Validate(IId questId, int sequenceNo, int stepId, AethernetShortcut? aethernetShortcut)
+ private ValidationIssue? Validate(ElementId questElementId, int sequenceNo, int stepId, AethernetShortcut? aethernetShortcut)
{
if (aethernetShortcut == null)
return null;
{
return new ValidationIssue
{
- QuestId = questId,
+ QuestId = questElementId,
Sequence = (byte)sequenceNo,
Step = stepId,
Type = EIssueType.InvalidAethernetShortcut,
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = 0,
Step = null,
Type = EIssueType.MissingSequence0,
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = (byte)sequence.Sequence,
Step = null,
Type = EIssueType.InstantQuestWithMultipleSteps,
{
return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = (byte)sequenceNo,
Step = null,
Type = EIssueType.MissingSequence,
{
return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = (byte)sequenceNo,
Step = null,
Type = EIssueType.DuplicateSequence,
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = (byte)sequence.Sequence,
Step = i,
Type = EIssueType.DuplicateCompletionFlags,
internal sealed class JsonSchemaValidator : IQuestValidator
{
- private readonly Dictionary<IId, JsonNode> _questNodes = new();
+ private readonly Dictionary<ElementId, JsonNode> _questNodes = new();
private JsonSchema? _questSchema;
public JsonSchemaValidator()
{
_questSchema ??= JsonSchema.FromStream(AssemblyQuestLoader.QuestSchema).AsTask().Result;
- if (_questNodes.TryGetValue(quest.QuestId, out JsonNode? questNode))
+ if (_questNodes.TryGetValue(quest.QuestElementId, out JsonNode? questNode))
{
var evaluationResult = _questSchema.Evaluate(questNode, new EvaluationOptions
{
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = null,
Step = null,
Type = EIssueType.InvalidJsonSchema,
}
}
- public void Enqueue(IId questId, JsonNode questNode) => _questNodes[questId] = questNode;
+ public void Enqueue(ElementId questElementId, JsonNode questNode) => _questNodes[questElementId] = questNode;
public void Reset() => _questNodes.Clear();
}
{
public IEnumerable<ValidationIssue> Validate(Quest quest)
{
- foreach (var invalidNextQuest in quest.AllSteps().Where(x => x.Step.NextQuestId == quest.QuestId))
+ foreach (var invalidNextQuest in quest.AllSteps().Where(x => x.Step.NextQuestId == quest.QuestElementId))
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = (byte)invalidNextQuest.Sequence.Sequence,
Step = invalidNextQuest.StepId,
Type = EIssueType.InvalidNextQuestId,
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = null,
Step = null,
Type = EIssueType.QuestDisabled,
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = (byte)accept.Sequence.Sequence,
Step = accept.StepId,
Type = EIssueType.UnexpectedAcceptQuestStep,
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = 0,
Step = null,
Type = EIssueType.MissingQuestAccept,
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = (byte)complete.Sequence.Sequence,
Step = complete.StepId,
Type = EIssueType.UnexpectedCompleteQuestStep,
{
yield return new ValidationIssue
{
- QuestId = quest.QuestId,
+ QuestId = quest.QuestElementId,
Sequence = 255,
Step = null,
Type = EIssueType.MissingQuestComplete,
IsOpen = true;
}
- public IId? HighlightedQuest { get; set; }
+ public ElementId? HighlightedQuest { get; set; }
public override bool DrawConditions() => _configuration.Advanced.DebugOverlay;
QuestStep? step = sequence.FindStep(i);
if (step != null && TryGetPosition(step, out Vector3? position))
{
- DrawStep($"{quest.QuestId} / {sequence.Sequence} / {i}", step, position.Value, 0xFFFFFFFF);
+ DrawStep($"{quest.QuestElementId} / {sequence.Sequence} / {i}", step, position.Value, 0xFFFFFFFF);
}
}
}
private QuestWork? DrawQuestWork(QuestController.QuestProgress currentQuest)
{
- if (currentQuest.Quest.QuestId is not QuestId questId)
+ if (currentQuest.Quest.QuestElementId is not QuestId questId)
return null;
var questWork = _gameFunctions.GetQuestEx(questId);
{
using var disabled = ImRaii.Disabled();
- if (currentQuest.Quest.QuestId == _questController.NextQuest?.Quest.QuestId)
+ if (currentQuest.Quest.QuestElementId == _questController.NextQuest?.Quest.QuestElementId)
ImGui.TextUnformatted("(Next quest in story line not accepted)");
else
ImGui.TextUnformatted("(Not accepted)");
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.ArrowCircleRight, "Skip"))
{
_movementController.Stop();
- _questController.Skip(currentQuest.Quest.QuestId, currentQuest.Sequence);
+ _questController.Skip(currentQuest.Quest.QuestElementId, currentQuest.Sequence);
}
if (colored)
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas))
_commandManager.DispatchCommand("/questinfo",
- currentQuest.Quest.QuestId.ToString() ?? string.Empty, commandInfo);
+ currentQuest.Quest.QuestElementId.ToString() ?? string.Empty, commandInfo);
}
bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
_quests = _questRegistry.AllQuests
.Where(x => x.FindSequence(0)?.FindStep(0)?.TerritoryId == territoryId)
- .Select(x => _questData.GetQuestInfo(x.QuestId))
+ .Select(x => _questData.GetQuestInfo(x.QuestElementId))
.ToList();
foreach (var unacceptedQuest in Map.Instance()->UnacceptedQuestMarkers)
_pluginInterface = pluginInterface;
}
- public (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(IId questId)
+ public (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(ElementId questElementId)
{
- if (_gameFunctions.IsQuestAccepted(questId))
+ if (_gameFunctions.IsQuestAccepted(questElementId))
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running, "Active");
- else if (_gameFunctions.IsQuestAcceptedOrComplete(questId))
+ else if (_gameFunctions.IsQuestAcceptedOrComplete(questElementId))
return (ImGuiColors.ParsedGreen, FontAwesomeIcon.Check, "Complete");
- else if (_gameFunctions.IsQuestLocked(questId))
+ else if (_gameFunctions.IsQuestLocked(questElementId))
return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times, "Locked");
else
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight, "Available");