Add basic support for gathering custom delivery items automatically
This commit is contained in:
parent
2f4f4e24e2
commit
09f11d1914
@ -92,12 +92,6 @@
|
|||||||
"ecommons": {
|
"ecommons": {
|
||||||
"type": "Project"
|
"type": "Project"
|
||||||
},
|
},
|
||||||
"gatheringpaths": {
|
|
||||||
"type": "Project",
|
|
||||||
"dependencies": {
|
|
||||||
"Questionable.Model": "[1.0.0, )"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"questionable.model": {
|
"questionable.model": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
115
GatheringPaths/4.x - Stormblood/Yanxia/731_Yuzuka Manor_BTN.json
Normal file
115
GatheringPaths/4.x - Stormblood/Yanxia/731_Yuzuka Manor_BTN.json
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"TerritoryId": 614,
|
||||||
|
"AetheryteShortcut": "Yanxia - Namai",
|
||||||
|
"Groups": [
|
||||||
|
{
|
||||||
|
"Nodes": [
|
||||||
|
{
|
||||||
|
"DataId": 33334,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -222.386,
|
||||||
|
"Y": 23.28162,
|
||||||
|
"Z": 425.76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -209.1725,
|
||||||
|
"Y": 22.35068,
|
||||||
|
"Z": 425.5524
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DataId": 33333,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -219.9592,
|
||||||
|
"Y": 22.78741,
|
||||||
|
"Z": 431.5036
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Nodes": [
|
||||||
|
{
|
||||||
|
"DataId": 33335,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -349.8553,
|
||||||
|
"Y": 33.90925,
|
||||||
|
"Z": 452.5893
|
||||||
|
},
|
||||||
|
"MinimumAngle": -90,
|
||||||
|
"MaximumAngle": 90
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DataId": 33336,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -361.5062,
|
||||||
|
"Y": 33.49068,
|
||||||
|
"Z": 453.4639
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -359.826,
|
||||||
|
"Y": 35.47207,
|
||||||
|
"Z": 442.164
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Nodes": [
|
||||||
|
{
|
||||||
|
"DataId": 33331,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -231.3864,
|
||||||
|
"Y": 17.74118,
|
||||||
|
"Z": 511.0694
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DataId": 33332,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -219.0789,
|
||||||
|
"Y": 18.05494,
|
||||||
|
"Z": 525.418
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -220.9139,
|
||||||
|
"Y": 17.97838,
|
||||||
|
"Z": 514.0063
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -71.31451,
|
||||||
|
"Y": 206.56206,
|
||||||
|
"Z": 29.3684
|
||||||
|
},
|
||||||
|
"TerritoryId": 478,
|
||||||
|
"InteractionType": "WalkTo",
|
||||||
|
"RequiredGatheredItems": [],
|
||||||
|
"AetheryteShortcut": "Idyllshire"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DataId": 1019615,
|
||||||
|
"Position": {
|
||||||
|
"X": -71.763245,
|
||||||
|
"Y": 206.50021,
|
||||||
|
"Z": 32.638916
|
||||||
|
},
|
||||||
|
"StopDistance": 5,
|
||||||
|
"TerritoryId": 478,
|
||||||
|
"InteractionType": "Interact"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1018393,
|
||||||
|
"Position": {
|
||||||
|
"X": -60.380005,
|
||||||
|
"Y": 206.50021,
|
||||||
|
"Z": 26.16919
|
||||||
|
},
|
||||||
|
"TerritoryId": 478,
|
||||||
|
"InteractionType": "Interact",
|
||||||
|
"RequiredGatheredItems": [],
|
||||||
|
"AetheryteShortcut": "Idyllshire"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1025878,
|
||||||
|
"Position": {
|
||||||
|
"X": 343.984,
|
||||||
|
"Y": -120.32947,
|
||||||
|
"Z": -306.0197
|
||||||
|
},
|
||||||
|
"TerritoryId": 613,
|
||||||
|
"InteractionType": "Interact",
|
||||||
|
"RequiredGatheredItems": [],
|
||||||
|
"AetheryteShortcut": "Ruby Sea - Tamamizu"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1020337,
|
||||||
|
"Position": {
|
||||||
|
"X": 171.31299,
|
||||||
|
"Y": 13.02367,
|
||||||
|
"Z": -89.951965
|
||||||
|
},
|
||||||
|
"TerritoryId": 635,
|
||||||
|
"InteractionType": "Interact",
|
||||||
|
"RequiredGatheredItems": [],
|
||||||
|
"AetheryteShortcut": "Rhalgr's Reach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1035211,
|
||||||
|
"Position": {
|
||||||
|
"X": -116.96039,
|
||||||
|
"Y": 0,
|
||||||
|
"Z": -133.95898
|
||||||
|
},
|
||||||
|
"TerritoryId": 886,
|
||||||
|
"InteractionType": "Interact",
|
||||||
|
"AetheryteShortcut": "Ishgard",
|
||||||
|
"AethernetShortcut": [
|
||||||
|
"[Ishgard] Aetheryte Plaza",
|
||||||
|
"[Ishgard] Firmament"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1033543,
|
||||||
|
"Position": {
|
||||||
|
"X": 113.38977,
|
||||||
|
"Y": -20,
|
||||||
|
"Z": -0.96136475
|
||||||
|
},
|
||||||
|
"TerritoryId": 886,
|
||||||
|
"InteractionType": "Interact",
|
||||||
|
"AetheryteShortcut": "Ishgard",
|
||||||
|
"AethernetShortcut": [
|
||||||
|
"[Ishgard] Aetheryte Plaza",
|
||||||
|
"[Ishgard] Firmament"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1031801,
|
||||||
|
"Position": {
|
||||||
|
"X": 52.8114,
|
||||||
|
"Y": 83.001076,
|
||||||
|
"Z": -65.38495
|
||||||
|
},
|
||||||
|
"TerritoryId": 820,
|
||||||
|
"InteractionType": "Interact",
|
||||||
|
"RequiredGatheredItems": [],
|
||||||
|
"AetheryteShortcut": "Eulmore"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1042241,
|
||||||
|
"Position": {
|
||||||
|
"X": 222.85791,
|
||||||
|
"Y": 24.942732,
|
||||||
|
"Z": -197.77222
|
||||||
|
},
|
||||||
|
"TerritoryId": 962,
|
||||||
|
"InteractionType": "Interact",
|
||||||
|
"RequiredGatheredItems": [],
|
||||||
|
"AetheryteShortcut": "Old Sharlayan",
|
||||||
|
"AethernetShortcut": [
|
||||||
|
"[Old Sharlayan] Aetheryte Plaza",
|
||||||
|
"[Old Sharlayan] The Leveilleur Estate"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1044547,
|
||||||
|
"Position": {
|
||||||
|
"X": -241.68768,
|
||||||
|
"Y": 51.058994,
|
||||||
|
"Z": 620.8744
|
||||||
|
},
|
||||||
|
"TerritoryId": 816,
|
||||||
|
"InteractionType": "Interact",
|
||||||
|
"RequiredGatheredItems": [],
|
||||||
|
"AetheryteShortcut": "Il Mheg - Lydha Lran",
|
||||||
|
"Fly": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -44.066154,
|
||||||
|
"Y": -29.530005,
|
||||||
|
"Z": -55.85129
|
||||||
|
},
|
||||||
|
"TerritoryId": 956,
|
||||||
|
"InteractionType": "WalkTo",
|
||||||
|
"AetheryteShortcut": "Labyrinthos - Sharlayan Hamlet",
|
||||||
|
"RequiredGatheredItems": [],
|
||||||
|
"Fly": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DataId": 1046073,
|
||||||
|
"Position": {
|
||||||
|
"X": -53.635498,
|
||||||
|
"Y": -29.497286,
|
||||||
|
"Z": -65.14081
|
||||||
|
},
|
||||||
|
"TerritoryId": 956,
|
||||||
|
"InteractionType": "Interact"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -40,8 +40,4 @@
|
|||||||
<AdditionalFiles Include="6.x - Endwalker\**\*.json" />
|
<AdditionalFiles Include="6.x - Endwalker\**\*.json" />
|
||||||
<AdditionalFiles Include="7.x - Dawntrail\**\*.json" />
|
<AdditionalFiles Include="7.x - Dawntrail\**\*.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="7.x - Dawntrail\Custom Deliveries\" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1041,7 +1041,8 @@
|
|||||||
"PickUpQuestId": {
|
"PickUpQuestId": {
|
||||||
"type": [
|
"type": [
|
||||||
"null",
|
"null",
|
||||||
"number"
|
"number",
|
||||||
|
"string"
|
||||||
],
|
],
|
||||||
"description": "Determines the quest which should be accepted. If empty/null, accepts the quest corresponding to the file name."
|
"description": "Determines the quest which should be accepted. If empty/null, accepts the quest corresponding to the file name."
|
||||||
}
|
}
|
||||||
@ -1061,14 +1062,16 @@
|
|||||||
"TurnInQuestId": {
|
"TurnInQuestId": {
|
||||||
"type": [
|
"type": [
|
||||||
"null",
|
"null",
|
||||||
"number"
|
"number",
|
||||||
|
"string"
|
||||||
],
|
],
|
||||||
"description": "Determines the quest which should be turned in. If empty/null, turns in the quest corresponding to the file name."
|
"description": "Determines the quest which should be turned in. If empty/null, turns in the quest corresponding to the file name."
|
||||||
},
|
},
|
||||||
"NextQuestId": {
|
"NextQuestId": {
|
||||||
"type": [
|
"type": [
|
||||||
"null",
|
"null",
|
||||||
"number"
|
"number",
|
||||||
|
"string"
|
||||||
],
|
],
|
||||||
"description": "For quest chains (e.g. DT healer role quests) Which quest to do next, given that you meet the required level."
|
"description": "For quest chains (e.g. DT healer role quests) Which quest to do next, given that you meet the required level."
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,7 @@ public enum EAetheryteLocation
|
|||||||
IshgardTribunal = 86,
|
IshgardTribunal = 86,
|
||||||
IshgardLastVigil = 87,
|
IshgardLastVigil = 87,
|
||||||
IshgardGatesOfJudgement = 88,
|
IshgardGatesOfJudgement = 88,
|
||||||
|
IshgardFirmament = 100001,
|
||||||
|
|
||||||
Idyllshire = 75,
|
Idyllshire = 75,
|
||||||
IdyllshireWest = 90,
|
IdyllshireWest = 90,
|
||||||
|
@ -56,6 +56,7 @@ public sealed class AethernetShardConverter() : EnumConverter<EAetheryteLocation
|
|||||||
{ EAetheryteLocation.IshgardTribunal, "[Ishgard] The Tribunal" },
|
{ EAetheryteLocation.IshgardTribunal, "[Ishgard] The Tribunal" },
|
||||||
{ EAetheryteLocation.IshgardLastVigil, "[Ishgard] The Last Vigil" },
|
{ EAetheryteLocation.IshgardLastVigil, "[Ishgard] The Last Vigil" },
|
||||||
{ EAetheryteLocation.IshgardGatesOfJudgement, "[Ishgard] The Gates of Judgement (Coerthas Central Highlands)" },
|
{ EAetheryteLocation.IshgardGatesOfJudgement, "[Ishgard] The Gates of Judgement (Coerthas Central Highlands)" },
|
||||||
|
{ EAetheryteLocation.IshgardFirmament, "[Ishgard] Firmament" },
|
||||||
|
|
||||||
{ EAetheryteLocation.Idyllshire, "[Idyllshire] Aetheryte Plaza" },
|
{ EAetheryteLocation.Idyllshire, "[Idyllshire] Aetheryte Plaza" },
|
||||||
{ EAetheryteLocation.IdyllshireWest, "[Idyllshire] West Idyllshire" },
|
{ EAetheryteLocation.IdyllshireWest, "[Idyllshire] West Idyllshire" },
|
||||||
|
@ -4,12 +4,14 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace Questionable.Model.Questing.Converter;
|
namespace Questionable.Model.Questing.Converter;
|
||||||
|
|
||||||
public class ElementIdConverter : JsonConverter<ElementId>
|
public sealed class ElementIdConverter : JsonConverter<ElementId>
|
||||||
{
|
{
|
||||||
public override ElementId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override ElementId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
uint value = reader.GetUInt32();
|
if (reader.TokenType == JsonTokenType.Number)
|
||||||
return ElementId.From(value);
|
return new QuestId(reader.GetUInt16());
|
||||||
|
else
|
||||||
|
return ElementId.FromString(reader.GetString() ?? throw new JsonException());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, ElementId value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, ElementId value, JsonSerializerOptions options)
|
||||||
|
@ -50,37 +50,51 @@ public abstract class ElementId : IComparable<ElementId>, IEquatable<ElementId>
|
|||||||
return !Equals(left, right);
|
return !Equals(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ElementId From(uint value)
|
public static ElementId FromString(string value)
|
||||||
{
|
{
|
||||||
if (value >= 100_000 && value < 200_000)
|
if (value.StartsWith("L"))
|
||||||
return new LeveId((ushort)(value - 100_000));
|
return new LeveId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
|
||||||
|
else if (value.StartsWith("S"))
|
||||||
|
return new SatisfactionSupplyNpcId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
|
||||||
else
|
else
|
||||||
return new QuestId((ushort)value);
|
return new QuestId(ushort.Parse(value, CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryFromString(string value, out ElementId? elementId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
elementId = FromString(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
elementId = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class QuestId : ElementId
|
public sealed class QuestId(ushort value) : ElementId(value)
|
||||||
{
|
{
|
||||||
public QuestId(ushort value)
|
|
||||||
: base(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Value.ToString(CultureInfo.InvariantCulture);
|
return Value.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class LeveId : ElementId
|
public sealed class LeveId(ushort value) : ElementId(value)
|
||||||
{
|
{
|
||||||
public LeveId(ushort value)
|
|
||||||
: base(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return "L" + Value.ToString(CultureInfo.InvariantCulture);
|
return "L" + Value.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class SatisfactionSupplyNpcId(ushort value) : ElementId(value)
|
||||||
|
{
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "S" + Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -161,6 +161,7 @@
|
|||||||
"[Ishgard] The Tribunal",
|
"[Ishgard] The Tribunal",
|
||||||
"[Ishgard] The Last Vigil",
|
"[Ishgard] The Last Vigil",
|
||||||
"[Ishgard] The Gates of Judgement (Coerthas Central Highlands)",
|
"[Ishgard] The Gates of Judgement (Coerthas Central Highlands)",
|
||||||
|
"[Ishgard] Firmament",
|
||||||
"[Idyllshire] Aetheryte Plaza",
|
"[Idyllshire] Aetheryte Plaza",
|
||||||
"[Idyllshire] West Idyllshire",
|
"[Idyllshire] West Idyllshire",
|
||||||
"[Idyllshire] Prologue Gate (Western Hinterlands)",
|
"[Idyllshire] Prologue Gate (Western Hinterlands)",
|
||||||
|
@ -77,7 +77,7 @@ internal sealed class CommandHandler : IDisposable
|
|||||||
|
|
||||||
case "start":
|
case "start":
|
||||||
_questWindow.IsOpen = true;
|
_questWindow.IsOpen = true;
|
||||||
_questController.ExecuteNextStep(true);
|
_questController.ExecuteNextStep(QuestController.EAutomationType.Automatic);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "stop":
|
case "stop":
|
||||||
@ -128,11 +128,11 @@ internal sealed class CommandHandler : IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arguments.Length >= 1 && uint.TryParse(arguments[0], out uint questId))
|
if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId? questId) && questId != null)
|
||||||
{
|
{
|
||||||
if (_questRegistry.TryGetQuest(ElementId.From(questId), out Quest? quest))
|
if (_questRegistry.TryGetQuest(questId, out Quest? quest))
|
||||||
{
|
{
|
||||||
_debugOverlay.HighlightedQuest = quest.QuestElementId;
|
_debugOverlay.HighlightedQuest = quest.Id;
|
||||||
_chatGui.Print($"[Questionable] Set highlighted quest to {questId} ({quest.Info.Name}).");
|
_chatGui.Print($"[Questionable] Set highlighted quest to {questId} ({quest.Info.Name}).");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -147,11 +147,11 @@ internal sealed class CommandHandler : IDisposable
|
|||||||
|
|
||||||
private void SetNextQuest(string[] arguments)
|
private void SetNextQuest(string[] arguments)
|
||||||
{
|
{
|
||||||
if (arguments.Length >= 1 && uint.TryParse(arguments[0], out uint questId))
|
if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId? questId) && questId != null)
|
||||||
{
|
{
|
||||||
if (_gameFunctions.IsQuestLocked(ElementId.From(questId)))
|
if (_gameFunctions.IsQuestLocked(questId))
|
||||||
_chatGui.PrintError($"[Questionable] Quest {questId} is locked.");
|
_chatGui.PrintError($"[Questionable] Quest {questId} is locked.");
|
||||||
else if (_questRegistry.TryGetQuest(ElementId.From(questId), out Quest? quest))
|
else if (_questRegistry.TryGetQuest(questId, out Quest? quest))
|
||||||
{
|
{
|
||||||
_questController.SetNextQuest(quest);
|
_questController.SetNextQuest(quest);
|
||||||
_chatGui.Print($"[Questionable] Set next quest to {questId} ({quest.Info.Name}).");
|
_chatGui.Print($"[Questionable] Set next quest to {questId} ({quest.Info.Name}).");
|
||||||
@ -170,9 +170,9 @@ internal sealed class CommandHandler : IDisposable
|
|||||||
|
|
||||||
private void SetSimulatedQuest(string[] arguments)
|
private void SetSimulatedQuest(string[] arguments)
|
||||||
{
|
{
|
||||||
if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId))
|
if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId? questId) && questId != null)
|
||||||
{
|
{
|
||||||
if (_questRegistry.TryGetQuest(ElementId.From(questId), out Quest? quest))
|
if (_questRegistry.TryGetQuest(questId, out Quest? quest))
|
||||||
{
|
{
|
||||||
_questController.SimulateQuest(quest);
|
_questController.SimulateQuest(quest);
|
||||||
_chatGui.Print($"[Questionable] Simulating quest {questId} ({quest.Info.Name}).");
|
_chatGui.Print($"[Questionable] Simulating quest {questId} ({quest.Info.Name}).");
|
||||||
|
111
Questionable/Controller/ContextMenuController.cs
Normal file
111
Questionable/Controller/ContextMenuController.cs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Game.Gui.ContextMenu;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Questionable.Data;
|
||||||
|
using Questionable.Model;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
|
namespace Questionable.Controller;
|
||||||
|
|
||||||
|
internal sealed class ContextMenuController : IDisposable
|
||||||
|
{
|
||||||
|
private readonly IContextMenu _contextMenu;
|
||||||
|
private readonly QuestController _questController;
|
||||||
|
private readonly GatheringData _gatheringData;
|
||||||
|
private readonly QuestRegistry _questRegistry;
|
||||||
|
private readonly QuestData _questData;
|
||||||
|
private readonly IGameGui _gameGui;
|
||||||
|
private readonly IChatGui _chatGui;
|
||||||
|
private readonly IClientState _clientState;
|
||||||
|
private readonly ILogger<ContextMenuController> _logger;
|
||||||
|
|
||||||
|
public ContextMenuController(
|
||||||
|
IContextMenu contextMenu,
|
||||||
|
QuestController questController,
|
||||||
|
GatheringData gatheringData,
|
||||||
|
QuestRegistry questRegistry,
|
||||||
|
QuestData questData,
|
||||||
|
IGameGui gameGui,
|
||||||
|
IChatGui chatGui,
|
||||||
|
IClientState clientState,
|
||||||
|
ILogger<ContextMenuController> logger)
|
||||||
|
{
|
||||||
|
_contextMenu = contextMenu;
|
||||||
|
_questController = questController;
|
||||||
|
_gatheringData = gatheringData;
|
||||||
|
_questRegistry = questRegistry;
|
||||||
|
_questData = questData;
|
||||||
|
_gameGui = gameGui;
|
||||||
|
_chatGui = chatGui;
|
||||||
|
_clientState = clientState;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
_contextMenu.OnMenuOpened += MenuOpened;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MenuOpened(IMenuOpenedArgs args)
|
||||||
|
{
|
||||||
|
uint itemId = (uint) _gameGui.HoveredItem;
|
||||||
|
if (itemId == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (itemId > 1_000_000)
|
||||||
|
itemId -= 1_000_000;
|
||||||
|
|
||||||
|
if (itemId >= 500_000)
|
||||||
|
itemId -= 500_000;
|
||||||
|
|
||||||
|
if (!_gatheringData.TryGetGatheringPointId(itemId, _clientState.LocalPlayer!.ClassJob.Id, out _))
|
||||||
|
{
|
||||||
|
_logger.LogInformation("No gathering point found for current job.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_gatheringData.TryGetCustomDeliveryNpc(itemId, out uint npcId))
|
||||||
|
{
|
||||||
|
ushort collectability = _gatheringData.GetRecommendedCollectability(itemId);
|
||||||
|
int quantityToGather = collectability > 0 ? 6 : int.MaxValue;
|
||||||
|
if (collectability == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.AddMenuItem(new MenuItem
|
||||||
|
{
|
||||||
|
Prefix = SeIconChar.Hyadelyn,
|
||||||
|
PrefixColor = 52,
|
||||||
|
Name = "Gather with Questionable",
|
||||||
|
OnClicked = _ => StartGathering(npcId, itemId, quantityToGather, collectability),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartGathering(uint npcId, uint itemId, int quantity, ushort collectability)
|
||||||
|
{
|
||||||
|
var info = (SatisfactionSupplyInfo)_questData.GetAllByIssuerDataId(npcId).Single(x => x is SatisfactionSupplyInfo);
|
||||||
|
if (_questRegistry.TryGetQuest(info.QuestId, out Quest? quest))
|
||||||
|
{
|
||||||
|
var step = quest.FindSequence(0)!.FindStep(0)!;
|
||||||
|
step.RequiredGatheredItems =
|
||||||
|
[
|
||||||
|
new GatheredItem
|
||||||
|
{
|
||||||
|
ItemId = itemId,
|
||||||
|
ItemCount = quantity,
|
||||||
|
Collectability = collectability
|
||||||
|
}
|
||||||
|
];
|
||||||
|
_questController.SetNextQuest(quest);
|
||||||
|
_questController.ExecuteNextStep(QuestController.EAutomationType.CurrentQuestOnly);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_chatGui.PrintError($"No associated quest ({info.QuestId}).", "Questionable");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_contextMenu.OnMenuOpened -= MenuOpened;
|
||||||
|
}
|
||||||
|
}
|
@ -600,7 +600,7 @@ internal sealed class GameUiController : IDisposable
|
|||||||
|
|
||||||
private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
|
private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
|
||||||
{
|
{
|
||||||
if (_questController.StartedQuest?.Quest.QuestElementId.Value == 4526)
|
if (_questController.StartedQuest?.Quest.Id.Value == 4526)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Closing Unending Codex");
|
_logger.LogInformation("Closing Unending Codex");
|
||||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||||
@ -610,7 +610,7 @@ internal sealed class GameUiController : IDisposable
|
|||||||
|
|
||||||
private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
|
private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
|
||||||
{
|
{
|
||||||
if (_questController.StartedQuest?.Quest.QuestElementId.Value == 245)
|
if (_questController.StartedQuest?.Quest.Id.Value == 245)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Closing ContentsTutorial");
|
_logger.LogInformation("Closing ContentsTutorial");
|
||||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||||
@ -623,7 +623,7 @@ internal sealed class GameUiController : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe void MultipleHelpWindowPostSetup(AddonEvent type, AddonArgs args)
|
private unsafe void MultipleHelpWindowPostSetup(AddonEvent type, AddonArgs args)
|
||||||
{
|
{
|
||||||
if (_questController.StartedQuest?.Quest.QuestElementId.Value == 245)
|
if (_questController.StartedQuest?.Quest.Id.Value == 245)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Closing MultipleHelpWindow");
|
_logger.LogInformation("Closing MultipleHelpWindow");
|
||||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||||
|
@ -33,7 +33,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
private QuestProgress? _startedQuest;
|
private QuestProgress? _startedQuest;
|
||||||
private QuestProgress? _nextQuest;
|
private QuestProgress? _nextQuest;
|
||||||
private QuestProgress? _simulatedQuest;
|
private QuestProgress? _simulatedQuest;
|
||||||
private bool _automatic;
|
private EAutomationType _automationType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Some combat encounters finish relatively early (i.e. they're done as part of progressing the quest, but not
|
/// Some combat encounters finish relatively early (i.e. they're done as part of progressing the quest, but not
|
||||||
@ -71,16 +71,16 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
_taskFactories = taskFactories.ToList().AsReadOnly();
|
_taskFactories = taskFactories.ToList().AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
public (QuestProgress Progress, CurrentQuestType Type)? CurrentQuestDetails
|
public (QuestProgress Progress, ECurrentQuestType Type)? CurrentQuestDetails
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_simulatedQuest != null)
|
if (_simulatedQuest != null)
|
||||||
return (_simulatedQuest, CurrentQuestType.Simulated);
|
return (_simulatedQuest, ECurrentQuestType.Simulated);
|
||||||
else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.QuestElementId))
|
else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.Id))
|
||||||
return (_nextQuest, CurrentQuestType.Next);
|
return (_nextQuest, ECurrentQuestType.Next);
|
||||||
else if (_startedQuest != null)
|
else if (_startedQuest != null)
|
||||||
return (_startedQuest, CurrentQuestType.Normal);
|
return (_startedQuest, ECurrentQuestType.Normal);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -153,10 +153,11 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
if (CurrentQuest != null && CurrentQuest.Quest.Root.TerritoryBlacklist.Contains(_clientState.TerritoryType))
|
if (CurrentQuest != null && CurrentQuest.Quest.Root.TerritoryBlacklist.Contains(_clientState.TerritoryType))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_automatic && ((_currentTask == null && _taskQueue.Count == 0) ||
|
if (_automationType == EAutomationType.Automatic &&
|
||||||
_currentTask is WaitAtEnd.WaitQuestAccepted)
|
((_currentTask == null && _taskQueue.Count == 0) ||
|
||||||
&& CurrentQuest is { Sequence: 0, Step: 0 } or { Sequence: 0, Step: 255 }
|
_currentTask is WaitAtEnd.WaitQuestAccepted)
|
||||||
&& DateTime.Now >= CurrentQuest.StepProgress.StartedAt.AddSeconds(15))
|
&& CurrentQuest is { Sequence: 0, Step: 0 } or { Sequence: 0, Step: 255 }
|
||||||
|
&& DateTime.Now >= CurrentQuest.StepProgress.StartedAt.AddSeconds(15))
|
||||||
{
|
{
|
||||||
lock (_progressLock)
|
lock (_progressLock)
|
||||||
{
|
{
|
||||||
@ -164,7 +165,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
CurrentQuest.SetStep(0);
|
CurrentQuest.SetStep(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecuteNextStep(true);
|
ExecuteNextStep(_automationType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,13 +183,14 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
// if the quest is accepted, we no longer track it
|
// if the quest is accepted, we no longer track it
|
||||||
bool canUseNextQuest;
|
bool canUseNextQuest;
|
||||||
if (_nextQuest.Quest.Info.IsRepeatable)
|
if (_nextQuest.Quest.Info.IsRepeatable)
|
||||||
canUseNextQuest = !_gameFunctions.IsQuestAccepted(_nextQuest.Quest.QuestElementId);
|
canUseNextQuest = !_gameFunctions.IsQuestAccepted(_nextQuest.Quest.Id);
|
||||||
else
|
else
|
||||||
canUseNextQuest = !_gameFunctions.IsQuestAcceptedOrComplete(_nextQuest.Quest.QuestElementId);
|
canUseNextQuest = !_gameFunctions.IsQuestAcceptedOrComplete(_nextQuest.Quest.Id);
|
||||||
|
|
||||||
if (!canUseNextQuest)
|
if (!canUseNextQuest)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Next quest {QuestId} accepted or completed", _nextQuest.Quest.QuestElementId);
|
_logger.LogInformation("Next quest {QuestId} accepted or completed",
|
||||||
|
_nextQuest.Quest.Id);
|
||||||
_nextQuest = null;
|
_nextQuest = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,12 +202,15 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
currentSequence = _simulatedQuest.Sequence;
|
currentSequence = _simulatedQuest.Sequence;
|
||||||
questToRun = _simulatedQuest;
|
questToRun = _simulatedQuest;
|
||||||
}
|
}
|
||||||
else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.QuestElementId))
|
else if (_nextQuest != null && _gameFunctions.IsReadyToAcceptQuest(_nextQuest.Quest.Id))
|
||||||
{
|
{
|
||||||
questToRun = _nextQuest;
|
questToRun = _nextQuest;
|
||||||
currentSequence = _nextQuest.Sequence; // by definition, this should always be 0
|
currentSequence = _nextQuest.Sequence; // by definition, this should always be 0
|
||||||
if (_nextQuest.Step == 0 && _currentTask == null && _taskQueue.Count == 0 && _automatic)
|
if (_nextQuest.Step == 0 &&
|
||||||
ExecuteNextStep(true);
|
_currentTask == null &&
|
||||||
|
_taskQueue.Count == 0 &&
|
||||||
|
_automationType == EAutomationType.Automatic)
|
||||||
|
ExecuteNextStep(_automationType);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -221,7 +226,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
|
|
||||||
questToRun = null;
|
questToRun = null;
|
||||||
}
|
}
|
||||||
else if (_startedQuest == null || _startedQuest.Quest.QuestElementId != currentQuestId)
|
else if (_startedQuest == null || _startedQuest.Quest.Id != currentQuestId)
|
||||||
{
|
{
|
||||||
if (_questRegistry.TryGetQuest(currentQuestId, out var quest))
|
if (_questRegistry.TryGetQuest(currentQuestId, out var quest))
|
||||||
{
|
{
|
||||||
@ -341,11 +346,11 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (questId != null && CurrentQuest.Quest.QuestElementId != questId)
|
if (questId != null && CurrentQuest.Quest.Id != questId)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"Ignoring 'increase step count' for different quest (expected {ExpectedQuestId}, but we are at {CurrentQuestId}",
|
"Ignoring 'increase step count' for different quest (expected {ExpectedQuestId}, but we are at {CurrentQuestId}",
|
||||||
questId, CurrentQuest.Quest.QuestElementId);
|
questId, CurrentQuest.Quest.Id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,8 +368,8 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
CurrentQuest.SetStep(255);
|
CurrentQuest.SetStep(255);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldContinue && _automatic)
|
if (shouldContinue && _automationType != EAutomationType.Manual)
|
||||||
ExecuteNextStep(true);
|
ExecuteNextStep(_automationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearTasksInternal()
|
private void ClearTasksInternal()
|
||||||
@ -387,17 +392,17 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
ClearTasksInternal();
|
ClearTasksInternal();
|
||||||
|
|
||||||
// reset task queue
|
// reset task queue
|
||||||
if (continueIfAutomatic && _automatic)
|
if (continueIfAutomatic && _automationType == EAutomationType.Automatic)
|
||||||
{
|
{
|
||||||
if (CurrentQuest?.Step is >= 0 and < 255)
|
if (CurrentQuest?.Step is >= 0 and < 255)
|
||||||
ExecuteNextStep(true);
|
ExecuteNextStep(_automationType);
|
||||||
else
|
else
|
||||||
_logger.LogInformation("Couldn't execute next step during Stop() call");
|
_logger.LogInformation("Couldn't execute next step during Stop() call");
|
||||||
}
|
}
|
||||||
else if (_automatic)
|
else if (_automationType != EAutomationType.Manual)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Stopping automatic questing");
|
_logger.LogInformation("Stopping automatic questing");
|
||||||
_automatic = false;
|
_automationType = EAutomationType.Manual;
|
||||||
_nextQuest = null;
|
_nextQuest = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,7 +411,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
|
|
||||||
public void SimulateQuest(Quest? quest)
|
public void SimulateQuest(Quest? quest)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("SimulateQuest: {QuestId}", quest?.QuestElementId);
|
_logger.LogInformation("SimulateQuest: {QuestId}", quest?.Id);
|
||||||
if (quest != null)
|
if (quest != null)
|
||||||
_simulatedQuest = new QuestProgress(quest);
|
_simulatedQuest = new QuestProgress(quest);
|
||||||
else
|
else
|
||||||
@ -415,7 +420,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
|
|
||||||
public void SetNextQuest(Quest? quest)
|
public void SetNextQuest(Quest? quest)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("NextQuest: {QuestId}", quest?.QuestElementId);
|
_logger.LogInformation("NextQuest: {QuestId}", quest?.Id);
|
||||||
if (quest != null)
|
if (quest != null)
|
||||||
_nextQuest = new QuestProgress(quest);
|
_nextQuest = new QuestProgress(quest);
|
||||||
else
|
else
|
||||||
@ -441,10 +446,10 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
IncreaseStepCount(task.QuestElementId, task.Sequence, true);
|
IncreaseStepCount(task.QuestElementId, task.Sequence, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecuteNextStep(bool automatic)
|
public void ExecuteNextStep(EAutomationType automatic)
|
||||||
{
|
{
|
||||||
ClearTasksInternal();
|
ClearTasksInternal();
|
||||||
_automatic = automatic;
|
_automationType = automatic;
|
||||||
|
|
||||||
if (TryPickPriorityQuest())
|
if (TryPickPriorityQuest())
|
||||||
_logger.LogInformation("Using priority quest over current quest");
|
_logger.LogInformation("Using priority quest over current quest");
|
||||||
@ -452,8 +457,21 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
(QuestSequence? seq, QuestStep? step) = GetNextStep();
|
(QuestSequence? seq, QuestStep? step) = GetNextStep();
|
||||||
if (CurrentQuest == null || seq == null || step == null)
|
if (CurrentQuest == null || seq == null || step == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Could not retrieve next quest step, not doing anything [{QuestId}, {Sequence}, {Step}]",
|
if (CurrentQuestDetails?.Progress.Quest.Id is SatisfactionSupplyNpcId &&
|
||||||
CurrentQuest?.Quest.QuestElementId, CurrentQuest?.Sequence, CurrentQuest?.Step);
|
CurrentQuestDetails?.Progress.Sequence == 0 &&
|
||||||
|
CurrentQuestDetails?.Progress.Step == 255 &&
|
||||||
|
CurrentQuestDetails?.Type == ECurrentQuestType.Next)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Completed delivery quest");
|
||||||
|
SetNextQuest(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
"Could not retrieve next quest step, not doing anything [{QuestId}, {Sequence}, {Step}]",
|
||||||
|
CurrentQuest?.Quest.Id, CurrentQuest?.Sequence, CurrentQuest?.Step);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,7 +506,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Tasks for {QuestId}, {Sequence}, {Step}: {Tasks}",
|
_logger.LogInformation("Tasks for {QuestId}, {Sequence}, {Step}: {Tasks}",
|
||||||
CurrentQuest.Quest.QuestElementId, seq.Sequence, seq.Steps.IndexOf(step),
|
CurrentQuest.Quest.Id, seq.Sequence, seq.Steps.IndexOf(step),
|
||||||
string.Join(", ", newTasks.Select(x => x.ToString())));
|
string.Join(", ", newTasks.Select(x => x.ToString())));
|
||||||
foreach (var task in newTasks)
|
foreach (var task in newTasks)
|
||||||
_taskQueue.Enqueue(task);
|
_taskQueue.Enqueue(task);
|
||||||
@ -587,7 +605,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var (currentQuest, type) = details.Value;
|
var (currentQuest, type) = details.Value;
|
||||||
if (type != CurrentQuestType.Normal)
|
if (type != ECurrentQuestType.Normal)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QuestSequence? currentSequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
|
QuestSequence? currentSequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
|
||||||
@ -628,10 +646,17 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
DateTime StartedAt,
|
DateTime StartedAt,
|
||||||
int PointMenuCounter = 0);
|
int PointMenuCounter = 0);
|
||||||
|
|
||||||
public enum CurrentQuestType
|
public enum ECurrentQuestType
|
||||||
{
|
{
|
||||||
Normal,
|
Normal,
|
||||||
Next,
|
Next,
|
||||||
Simulated,
|
Simulated,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum EAutomationType
|
||||||
|
{
|
||||||
|
Manual,
|
||||||
|
Automatic,
|
||||||
|
CurrentQuestOnly,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,12 +91,12 @@ internal sealed class QuestRegistry
|
|||||||
{
|
{
|
||||||
Quest quest = new()
|
Quest quest = new()
|
||||||
{
|
{
|
||||||
QuestElementId = new QuestId(questId),
|
Id = new QuestId(questId),
|
||||||
Root = questRoot,
|
Root = questRoot,
|
||||||
Info = _questData.GetQuestInfo(new QuestId(questId)),
|
Info = _questData.GetQuestInfo(new QuestId(questId)),
|
||||||
ReadOnly = true,
|
ReadOnly = true,
|
||||||
};
|
};
|
||||||
_quests[quest.QuestElementId] = quest;
|
_quests[quest.Id] = quest;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Loaded {Count} quests from assembly", _quests.Count);
|
_logger.LogInformation("Loaded {Count} quests from assembly", _quests.Count);
|
||||||
@ -145,12 +145,12 @@ internal sealed class QuestRegistry
|
|||||||
|
|
||||||
Quest quest = new Quest
|
Quest quest = new Quest
|
||||||
{
|
{
|
||||||
QuestElementId = questId,
|
Id = questId,
|
||||||
Root = questNode.Deserialize<QuestRoot>()!,
|
Root = questNode.Deserialize<QuestRoot>()!,
|
||||||
Info = _questData.GetQuestInfo(questId),
|
Info = _questData.GetQuestInfo(questId),
|
||||||
ReadOnly = false,
|
ReadOnly = false,
|
||||||
};
|
};
|
||||||
_quests[quest.QuestElementId] = quest;
|
_quests[quest.Id] = quest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadFromDirectory(DirectoryInfo directory, LogLevel logLevel = LogLevel.Information)
|
private void LoadFromDirectory(DirectoryInfo directory, LogLevel logLevel = LogLevel.Information)
|
||||||
@ -188,30 +188,11 @@ internal sealed class QuestRegistry
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
string[] parts = name.Split('_', 2);
|
string[] parts = name.Split('_', 2);
|
||||||
return ElementId.From(uint.Parse(parts[0], CultureInfo.InvariantCulture));
|
return ElementId.FromString(parts[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsKnownQuest(ElementId elementId)
|
public bool IsKnownQuest(ElementId questId) => _quests.ContainsKey(questId);
|
||||||
{
|
|
||||||
if (elementId is QuestId questId)
|
|
||||||
return IsKnownQuest(questId);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsKnownQuest(QuestId questId) => _quests.ContainsKey(questId);
|
public bool TryGetQuest(ElementId questId, [NotNullWhen(true)] out Quest? quest)
|
||||||
|
|
||||||
public bool TryGetQuest(ElementId elementId, [NotNullWhen(true)] out Quest? quest)
|
|
||||||
{
|
|
||||||
if (elementId is QuestId questId)
|
|
||||||
return TryGetQuest(questId, out quest);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
quest = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetQuest(QuestId questId, [NotNullWhen(true)] out Quest? quest)
|
|
||||||
=> _quests.TryGetValue(questId, out quest);
|
=> _quests.TryGetValue(questId, out quest);
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,11 @@ internal static class NextQuest
|
|||||||
if (step.NextQuestId == null)
|
if (step.NextQuestId == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (step.NextQuestId == quest.QuestElementId)
|
if (step.NextQuestId == quest.Id)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return serviceProvider.GetRequiredService<SetQuest>()
|
return serviceProvider.GetRequiredService<SetQuest>()
|
||||||
.With(step.NextQuestId, quest.QuestElementId);
|
.With(step.NextQuestId, quest.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ internal static class Combat
|
|||||||
ArgumentNullException.ThrowIfNull(step.ItemId);
|
ArgumentNullException.ThrowIfNull(step.ItemId);
|
||||||
|
|
||||||
yield return serviceProvider.GetRequiredService<UseItem.UseOnObject>()
|
yield return serviceProvider.GetRequiredService<UseItem.UseOnObject>()
|
||||||
.With(quest.QuestElementId, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags,
|
.With(quest.Id, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags,
|
||||||
true);
|
true);
|
||||||
yield return CreateTask(quest, sequence, step);
|
yield return CreateTask(quest, sequence, step);
|
||||||
break;
|
break;
|
||||||
@ -73,7 +73,7 @@ internal static class Combat
|
|||||||
|
|
||||||
bool isLastStep = sequence.Steps.Last() == step;
|
bool isLastStep = sequence.Steps.Last() == step;
|
||||||
return serviceProvider.GetRequiredService<HandleCombat>()
|
return serviceProvider.GetRequiredService<HandleCombat>()
|
||||||
.With(quest.QuestElementId, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds,
|
.With(quest.Id, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds,
|
||||||
step.CompletionQuestVariablesFlags, step.ComplexCombatData);
|
step.CompletionQuestVariablesFlags, step.ComplexCombatData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,8 @@ internal static class Interact
|
|||||||
yield return serviceProvider.GetRequiredService<WaitAtEnd.WaitDelay>();
|
yield return serviceProvider.GetRequiredService<WaitAtEnd.WaitDelay>();
|
||||||
|
|
||||||
yield return serviceProvider.GetRequiredService<DoInteract>()
|
yield return serviceProvider.GetRequiredService<DoInteract>()
|
||||||
.With(step.DataId.Value, step.TargetTerritoryId != null);
|
.With(step.DataId.Value,
|
||||||
|
step.TargetTerritoryId != null || quest.Id is SatisfactionSupplyNpcId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
public ITask CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||||
|
@ -48,7 +48,7 @@ internal static class UseItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
var task = serviceProvider.GetRequiredService<Use>()
|
var task = serviceProvider.GetRequiredService<Use>()
|
||||||
.With(quest.QuestElementId, step.ItemId.Value, step.CompletionQuestVariablesFlags);
|
.With(quest.Id, step.ItemId.Value, step.CompletionQuestVariablesFlags);
|
||||||
return
|
return
|
||||||
[
|
[
|
||||||
unmount, task,
|
unmount, task,
|
||||||
@ -65,12 +65,12 @@ internal static class UseItem
|
|||||||
ITask task;
|
ITask task;
|
||||||
if (step.DataId != null)
|
if (step.DataId != null)
|
||||||
task = serviceProvider.GetRequiredService<UseOnGround>()
|
task = serviceProvider.GetRequiredService<UseOnGround>()
|
||||||
.With(quest.QuestElementId, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
|
.With(quest.Id, step.DataId.Value, step.ItemId.Value, step.CompletionQuestVariablesFlags);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(step.Position);
|
ArgumentNullException.ThrowIfNull(step.Position);
|
||||||
task = serviceProvider.GetRequiredService<UseOnPosition>()
|
task = serviceProvider.GetRequiredService<UseOnPosition>()
|
||||||
.With(quest.QuestElementId, step.Position.Value, step.ItemId.Value,
|
.With(quest.Id, step.Position.Value, step.ItemId.Value,
|
||||||
step.CompletionQuestVariablesFlags);
|
step.CompletionQuestVariablesFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,13 +79,13 @@ internal static class UseItem
|
|||||||
else if (step.DataId != null)
|
else if (step.DataId != null)
|
||||||
{
|
{
|
||||||
var task = serviceProvider.GetRequiredService<UseOnObject>()
|
var task = serviceProvider.GetRequiredService<UseOnObject>()
|
||||||
.With(quest.QuestElementId, 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 = serviceProvider.GetRequiredService<Use>()
|
||||||
.With(quest.QuestElementId, step.ItemId.Value, step.CompletionQuestVariablesFlags);
|
.With(quest.Id, step.ItemId.Value, step.CompletionQuestVariablesFlags);
|
||||||
return [unmount, task];
|
return [unmount, task];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ internal static class GatheringRequiredItems
|
|||||||
|
|
||||||
if (!AssemblyGatheringLocationLoader.GetLocations()
|
if (!AssemblyGatheringLocationLoader.GetLocations()
|
||||||
.TryGetValue(gatheringPointId, out GatheringRoot? gatheringRoot))
|
.TryGetValue(gatheringPointId, out GatheringRoot? gatheringRoot))
|
||||||
throw new TaskException("No path found for gathering point");
|
throw new TaskException($"No path found for gathering point {gatheringPointId}");
|
||||||
|
|
||||||
if (HasRequiredItems(requiredGatheredItems))
|
if (HasRequiredItems(requiredGatheredItems))
|
||||||
continue;
|
continue;
|
||||||
|
@ -34,7 +34,7 @@ internal static class SkipCondition
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
return serviceProvider.GetRequiredService<CheckSkip>()
|
return serviceProvider.GetRequiredService<CheckSkip>()
|
||||||
.With(step, skipConditions ?? new(), quest.QuestElementId);
|
.With(step, skipConditions ?? new(), quest.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ internal static class WaitAtEnd
|
|||||||
if (step.CompletionQuestVariablesFlags.Count == 6 && QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags))
|
if (step.CompletionQuestVariablesFlags.Count == 6 && QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags))
|
||||||
{
|
{
|
||||||
var task = serviceProvider.GetRequiredService<WaitForCompletionFlags>()
|
var task = serviceProvider.GetRequiredService<WaitForCompletionFlags>()
|
||||||
.With((QuestId)quest.QuestElementId, step);
|
.With((QuestId)quest.Id, step);
|
||||||
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
||||||
return [task, delay, Next(quest, sequence)];
|
return [task, delay, Next(quest, sequence)];
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ internal static class WaitAtEnd
|
|||||||
case EInteractionType.AcceptQuest:
|
case EInteractionType.AcceptQuest:
|
||||||
{
|
{
|
||||||
var accept = serviceProvider.GetRequiredService<WaitQuestAccepted>()
|
var accept = serviceProvider.GetRequiredService<WaitQuestAccepted>()
|
||||||
.With(step.PickUpQuestId ?? quest.QuestElementId);
|
.With(step.PickUpQuestId ?? quest.Id);
|
||||||
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
||||||
if (step.PickUpQuestId != null)
|
if (step.PickUpQuestId != null)
|
||||||
return [accept, delay, Next(quest, sequence)];
|
return [accept, delay, Next(quest, sequence)];
|
||||||
@ -121,7 +121,7 @@ internal static class WaitAtEnd
|
|||||||
case EInteractionType.CompleteQuest:
|
case EInteractionType.CompleteQuest:
|
||||||
{
|
{
|
||||||
var complete = serviceProvider.GetRequiredService<WaitQuestCompleted>()
|
var complete = serviceProvider.GetRequiredService<WaitQuestCompleted>()
|
||||||
.With(step.TurnInQuestId ?? quest.QuestElementId);
|
.With(step.TurnInQuestId ?? quest.Id);
|
||||||
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
||||||
if (step.TurnInQuestId != null)
|
if (step.TurnInQuestId != null)
|
||||||
return [complete, delay, Next(quest, sequence)];
|
return [complete, delay, Next(quest, sequence)];
|
||||||
@ -140,7 +140,7 @@ internal static class WaitAtEnd
|
|||||||
|
|
||||||
private static NextStep Next(Quest quest, QuestSequence sequence)
|
private static NextStep Next(Quest quest, QuestSequence sequence)
|
||||||
{
|
{
|
||||||
return new NextStep(quest.QuestElementId, sequence.Sequence);
|
return new NextStep(quest.Id, sequence.Sequence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,10 @@ internal sealed class AetheryteData
|
|||||||
aethernetGroups[(EAetheryteLocation)aetheryte.RowId] = aetheryte.AethernetGroup;
|
aethernetGroups[(EAetheryteLocation)aetheryte.RowId] = aetheryte.AethernetGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aethernetNames[EAetheryteLocation.IshgardFirmament] = "Firmament";
|
||||||
|
territoryIds[EAetheryteLocation.IshgardFirmament] = 886;
|
||||||
|
aethernetGroups[EAetheryteLocation.IshgardFirmament] = aethernetGroups[EAetheryteLocation.Ishgard];
|
||||||
|
|
||||||
AethernetNames = aethernetNames.AsReadOnly();
|
AethernetNames = aethernetNames.AsReadOnly();
|
||||||
TerritoryIds = territoryIds.AsReadOnly();
|
TerritoryIds = territoryIds.AsReadOnly();
|
||||||
AethernetGroups = aethernetGroups.AsReadOnly();
|
AethernetGroups = aethernetGroups.AsReadOnly();
|
||||||
@ -267,6 +271,7 @@ internal sealed class AetheryteData
|
|||||||
{ EAetheryteLocation.GridaniaAirship, new(24.86354f, -19.000002f, 96f) },
|
{ EAetheryteLocation.GridaniaAirship, new(24.86354f, -19.000002f, 96f) },
|
||||||
{ EAetheryteLocation.UldahAirship, new(-16.954851f, 82.999985f, -9.421141f) },
|
{ EAetheryteLocation.UldahAirship, new(-16.954851f, 82.999985f, -9.421141f) },
|
||||||
{ EAetheryteLocation.KuganeAirship, new(-55.72525f, 79.10602f, 46.23109f) },
|
{ EAetheryteLocation.KuganeAirship, new(-55.72525f, 79.10602f, 46.23109f) },
|
||||||
|
{ EAetheryteLocation.IshgardFirmament, new(9.92315f, -15.2f, 173.5059f) },
|
||||||
}.AsReadOnly();
|
}.AsReadOnly();
|
||||||
|
|
||||||
public ReadOnlyDictionary<EAetheryteLocation, string> AethernetNames { get; }
|
public ReadOnlyDictionary<EAetheryteLocation, string> AethernetNames { get; }
|
||||||
@ -298,6 +303,9 @@ internal sealed class AetheryteData
|
|||||||
|
|
||||||
public bool IsCityAetheryte(EAetheryteLocation aetheryte)
|
public bool IsCityAetheryte(EAetheryteLocation aetheryte)
|
||||||
{
|
{
|
||||||
|
if (aetheryte == EAetheryteLocation.IshgardFirmament)
|
||||||
|
return true;
|
||||||
|
|
||||||
var territoryId = TerritoryIds[aetheryte];
|
var territoryId = TerritoryIds[aetheryte];
|
||||||
return TownTerritoryIds.Contains(territoryId);
|
return TownTerritoryIds.Contains(territoryId);
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Questionable.Data;
|
namespace Questionable.Data;
|
||||||
|
|
||||||
internal sealed class GatheringData
|
internal sealed class GatheringData
|
||||||
{
|
{
|
||||||
private readonly Dictionary<uint, uint> _gatheringItemToItem;
|
|
||||||
private readonly Dictionary<uint, ushort> _minerGatheringPoints = [];
|
private readonly Dictionary<uint, ushort> _minerGatheringPoints = [];
|
||||||
private readonly Dictionary<uint, ushort> _botanistGatheringPoints = [];
|
private readonly Dictionary<uint, ushort> _botanistGatheringPoints = [];
|
||||||
|
private readonly Dictionary<uint, ushort> _itemIdToCollectability;
|
||||||
|
private readonly Dictionary<uint, uint> _npcForCustomDeliveries;
|
||||||
|
|
||||||
public GatheringData(IDataManager dataManager)
|
public GatheringData(IDataManager dataManager)
|
||||||
{
|
{
|
||||||
_gatheringItemToItem = dataManager.GetExcelSheet<GatheringItem>()!
|
Dictionary<uint, uint> gatheringItemToItem = dataManager.GetExcelSheet<GatheringItem>()!
|
||||||
.Where(x => x.RowId != 0 && x.Item != 0)
|
.Where(x => x.RowId != 0 && x.Item != 0)
|
||||||
.ToDictionary(x => x.RowId, x => (uint)x.Item);
|
.ToDictionary(x => x.RowId, x => (uint)x.Item);
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ internal sealed class GatheringData
|
|||||||
{
|
{
|
||||||
foreach (var gatheringItemId in gatheringPointBase.Item.Where(x => x != 0))
|
foreach (var gatheringItemId in gatheringPointBase.Item.Where(x => x != 0))
|
||||||
{
|
{
|
||||||
if (_gatheringItemToItem.TryGetValue((uint)gatheringItemId, out uint itemId))
|
if (gatheringItemToItem.TryGetValue((uint)gatheringItemId, out uint itemId))
|
||||||
{
|
{
|
||||||
if (gatheringPointBase.GatheringType.Row is 0 or 1)
|
if (gatheringPointBase.GatheringType.Row is 0 or 1)
|
||||||
_minerGatheringPoints[itemId] = (ushort)gatheringPointBase.RowId;
|
_minerGatheringPoints[itemId] = (ushort)gatheringPointBase.RowId;
|
||||||
@ -31,8 +32,31 @@ internal sealed class GatheringData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
_itemIdToCollectability = dataManager.GetExcelSheet<SatisfactionSupply>()!
|
||||||
|
.Where(x => x.RowId > 0)
|
||||||
|
.Where(x => x.Slot is 2)
|
||||||
|
.Select(x => new
|
||||||
|
{
|
||||||
|
ItemId = x.Item.Row,
|
||||||
|
Collectability = x.CollectabilityHigh,
|
||||||
|
})
|
||||||
|
.Distinct()
|
||||||
|
.ToDictionary(x => x.ItemId, x => x.Collectability);
|
||||||
|
|
||||||
|
_npcForCustomDeliveries = dataManager.GetExcelSheet<SatisfactionNpc>()!
|
||||||
|
.Where(x => x.RowId > 0)
|
||||||
|
.SelectMany(x => dataManager.GetExcelSheet<SatisfactionSupply>()!
|
||||||
|
.Where(y => y.RowId == x.SupplyIndex.Last())
|
||||||
|
.Select(y => new
|
||||||
|
{
|
||||||
|
ItemId = y.Item.Row,
|
||||||
|
NpcId = x.Npc.Row
|
||||||
|
}))
|
||||||
|
.Where(x => x.ItemId > 0)
|
||||||
|
.Distinct()
|
||||||
|
.ToDictionary(x => x.ItemId, x => x.NpcId);
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetGatheringPointId(uint itemId, uint classJobId, out ushort gatheringPointId)
|
public bool TryGetGatheringPointId(uint itemId, uint classJobId, out ushort gatheringPointId)
|
||||||
{
|
{
|
||||||
@ -46,4 +70,10 @@ internal sealed class GatheringData
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ushort GetRecommendedCollectability(uint itemId)
|
||||||
|
=> _itemIdToCollectability.GetValueOrDefault(itemId);
|
||||||
|
|
||||||
|
public bool TryGetCustomDeliveryNpc(uint itemId, out uint npcId)
|
||||||
|
=> _npcForCustomDeliveries.TryGetValue(itemId, out npcId);
|
||||||
}
|
}
|
||||||
|
@ -22,17 +22,17 @@ internal sealed class JournalData
|
|||||||
var genreLimsa = new Genre(uint.MaxValue - 3, "Starting in Limsa Lominsa", 1,
|
var genreLimsa = new Genre(uint.MaxValue - 3, "Starting in Limsa Lominsa", 1,
|
||||||
new uint[] { 108, 109 }.Concat(limsaStart.Quest.Select(x => x.Row))
|
new uint[] { 108, 109 }.Concat(limsaStart.Quest.Select(x => x.Row))
|
||||||
.Where(x => x != 0)
|
.Where(x => x != 0)
|
||||||
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
.Select(x => (QuestInfo)questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
||||||
.ToList());
|
.ToList());
|
||||||
var genreGridania = new Genre(uint.MaxValue - 2, "Starting in Gridania", 1,
|
var genreGridania = new Genre(uint.MaxValue - 2, "Starting in Gridania", 1,
|
||||||
new uint[] { 85, 123, 124 }.Concat(gridaniaStart.Quest.Select(x => x.Row))
|
new uint[] { 85, 123, 124 }.Concat(gridaniaStart.Quest.Select(x => x.Row))
|
||||||
.Where(x => x != 0)
|
.Where(x => x != 0)
|
||||||
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
.Select(x => (QuestInfo)questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
||||||
.ToList());
|
.ToList());
|
||||||
var genreUldah = new Genre(uint.MaxValue - 1, "Starting in Ul'dah", 1,
|
var genreUldah = new Genre(uint.MaxValue - 1, "Starting in Ul'dah", 1,
|
||||||
new uint[] { 568, 569, 570 }.Concat(uldahStart.Quest.Select(x => x.Row))
|
new uint[] { 568, 569, 570 }.Concat(uldahStart.Quest.Select(x => x.Row))
|
||||||
.Where(x => x != 0)
|
.Where(x => x != 0)
|
||||||
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
.Select(x => (QuestInfo)questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
||||||
.ToList());
|
.ToList());
|
||||||
genres.InsertRange(0, [genreLimsa, genreGridania, genreUldah]);
|
genres.InsertRange(0, [genreLimsa, genreGridania, genreUldah]);
|
||||||
genres.Single(x => x.Id == 1)
|
genres.Single(x => x.Id == 1)
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Questionable.Model;
|
using Questionable.Model;
|
||||||
using Questionable.Model.Questing;
|
using Questionable.Model.Questing;
|
||||||
using Quest = Lumina.Excel.GeneratedSheets.Quest;
|
using Quest = Lumina.Excel.GeneratedSheets.Quest;
|
||||||
@ -11,32 +12,30 @@ namespace Questionable.Data;
|
|||||||
|
|
||||||
internal sealed class QuestData
|
internal sealed class QuestData
|
||||||
{
|
{
|
||||||
private readonly ImmutableDictionary<QuestId, QuestInfo> _quests;
|
private readonly Dictionary<ElementId, IQuestInfo> _quests;
|
||||||
|
|
||||||
public QuestData(IDataManager dataManager)
|
public QuestData(IDataManager dataManager)
|
||||||
{
|
{
|
||||||
_quests = dataManager.GetExcelSheet<Quest>()!
|
List<IQuestInfo> quests =
|
||||||
.Where(x => x.RowId > 0)
|
[
|
||||||
.Where(x => x.IssuerLocation.Row > 0)
|
..dataManager.GetExcelSheet<Quest>()!
|
||||||
.Where(x => x.Festival.Row == 0)
|
.Where(x => x.RowId > 0)
|
||||||
.Select(x => new QuestInfo(x))
|
.Where(x => x.IssuerLocation.Row > 0)
|
||||||
.ToImmutableDictionary(x => x.QuestId, x => x);
|
.Where(x => x.Festival.Row == 0)
|
||||||
|
.Select(x => new QuestInfo(x)),
|
||||||
|
..dataManager.GetExcelSheet<SatisfactionNpc>()!
|
||||||
|
.Where(x => x.RowId > 0)
|
||||||
|
.Select(x => new SatisfactionSupplyInfo(x))
|
||||||
|
];
|
||||||
|
_quests = quests.ToDictionary(x => x.QuestId, x => x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuestInfo GetQuestInfo(ElementId elementId)
|
public IQuestInfo GetQuestInfo(ElementId elementId)
|
||||||
{
|
{
|
||||||
if (elementId is QuestId questId)
|
return _quests[elementId] ?? throw new ArgumentOutOfRangeException(nameof(elementId));
|
||||||
return GetQuestInfo(questId);
|
|
||||||
|
|
||||||
throw new ArgumentException("Invalid id", nameof(elementId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuestInfo GetQuestInfo(QuestId questId)
|
public List<IQuestInfo> GetAllByIssuerDataId(uint targetId)
|
||||||
{
|
|
||||||
return _quests[questId] ?? throw new ArgumentOutOfRangeException(nameof(questId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<QuestInfo> GetAllByIssuerDataId(uint targetId)
|
|
||||||
{
|
{
|
||||||
return _quests.Values
|
return _quests.Values
|
||||||
.Where(x => x.IssuerDataId == targetId)
|
.Where(x => x.IssuerDataId == targetId)
|
||||||
@ -48,6 +47,8 @@ internal sealed class QuestData
|
|||||||
public List<QuestInfo> GetAllByJournalGenre(uint journalGenre)
|
public List<QuestInfo> GetAllByJournalGenre(uint journalGenre)
|
||||||
{
|
{
|
||||||
return _quests.Values
|
return _quests.Values
|
||||||
|
.Where(x => x is QuestInfo)
|
||||||
|
.Cast<QuestInfo>()
|
||||||
.Where(x => x.JournalGenre == journalGenre)
|
.Where(x => x.JournalGenre == journalGenre)
|
||||||
.OrderBy(x => x.SortKey)
|
.OrderBy(x => x.SortKey)
|
||||||
.ThenBy(x => x.QuestId)
|
.ThenBy(x => x.QuestId)
|
||||||
|
6
Questionable/External/LifestreamIpc.cs
vendored
6
Questionable/External/LifestreamIpc.cs
vendored
@ -18,6 +18,12 @@ internal sealed class LifestreamIpc
|
|||||||
|
|
||||||
public bool Teleport(EAetheryteLocation aetheryteLocation)
|
public bool Teleport(EAetheryteLocation aetheryteLocation)
|
||||||
{
|
{
|
||||||
|
if (aetheryteLocation == EAetheryteLocation.IshgardFirmament)
|
||||||
|
{
|
||||||
|
// TODO does this even work on non-EN clients?
|
||||||
|
return _aethernetTeleport.InvokeFunc("Firmament");
|
||||||
|
}
|
||||||
|
|
||||||
if (!_aetheryteData.AethernetNames.TryGetValue(aetheryteLocation, out string? name))
|
if (!_aetheryteData.AethernetNames.TryGetValue(aetheryteLocation, out string? name))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -246,7 +246,10 @@ internal sealed unsafe class GameFunctions
|
|||||||
{
|
{
|
||||||
if (elementId is QuestId questId)
|
if (elementId is QuestId questId)
|
||||||
return IsReadyToAcceptQuest(questId);
|
return IsReadyToAcceptQuest(questId);
|
||||||
return false;
|
else if (elementId is SatisfactionSupplyNpcId)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(elementId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsReadyToAcceptQuest(QuestId questId)
|
public bool IsReadyToAcceptQuest(QuestId questId)
|
||||||
@ -283,7 +286,10 @@ internal sealed unsafe class GameFunctions
|
|||||||
{
|
{
|
||||||
if (elementId is QuestId questId)
|
if (elementId is QuestId questId)
|
||||||
return IsQuestAccepted(questId);
|
return IsQuestAccepted(questId);
|
||||||
return false;
|
else if (elementId is SatisfactionSupplyNpcId)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(elementId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsQuestAccepted(QuestId questId)
|
public bool IsQuestAccepted(QuestId questId)
|
||||||
@ -296,7 +302,10 @@ internal sealed unsafe class GameFunctions
|
|||||||
{
|
{
|
||||||
if (elementId is QuestId questId)
|
if (elementId is QuestId questId)
|
||||||
return IsQuestComplete(questId);
|
return IsQuestComplete(questId);
|
||||||
return false;
|
else if (elementId is SatisfactionSupplyNpcId)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(elementId));
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Performance", "CA1822")]
|
[SuppressMessage("Performance", "CA1822")]
|
||||||
@ -309,12 +318,15 @@ internal sealed unsafe class GameFunctions
|
|||||||
{
|
{
|
||||||
if (elementId is QuestId questId)
|
if (elementId is QuestId questId)
|
||||||
return IsQuestLocked(questId, extraCompletedQuest);
|
return IsQuestLocked(questId, extraCompletedQuest);
|
||||||
return false;
|
else if (elementId is SatisfactionSupplyNpcId)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(elementId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsQuestLocked(QuestId questId, ElementId? extraCompletedQuest = null)
|
public bool IsQuestLocked(QuestId questId, ElementId? extraCompletedQuest = null)
|
||||||
{
|
{
|
||||||
var questInfo = _questData.GetQuestInfo(questId);
|
var questInfo = (QuestInfo) _questData.GetQuestInfo(questId);
|
||||||
if (questInfo.QuestLocks.Count > 0)
|
if (questInfo.QuestLocks.Count > 0)
|
||||||
{
|
{
|
||||||
var completedQuests = questInfo.QuestLocks.Count(x => IsQuestComplete(x) || x.Equals(extraCompletedQuest));
|
var completedQuests = questInfo.QuestLocks.Count(x => IsQuestComplete(x) || x.Equals(extraCompletedQuest));
|
||||||
@ -369,7 +381,11 @@ internal sealed unsafe class GameFunctions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAetheryteUnlocked(EAetheryteLocation aetheryteLocation)
|
public bool IsAetheryteUnlocked(EAetheryteLocation aetheryteLocation)
|
||||||
=> IsAetheryteUnlocked((uint)aetheryteLocation, out _);
|
{
|
||||||
|
if (aetheryteLocation == EAetheryteLocation.IshgardFirmament)
|
||||||
|
return IsQuestComplete(new QuestId(3672));
|
||||||
|
return IsAetheryteUnlocked((uint)aetheryteLocation, out _);
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanTeleport(EAetheryteLocation aetheryteLocation)
|
public bool CanTeleport(EAetheryteLocation aetheryteLocation)
|
||||||
{
|
{
|
||||||
@ -707,15 +723,15 @@ internal sealed unsafe class GameFunctions
|
|||||||
if (excelSheetName == null)
|
if (excelSheetName == null)
|
||||||
{
|
{
|
||||||
var questRow =
|
var questRow =
|
||||||
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestElementId.Value +
|
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.Id.Value +
|
||||||
0x10000);
|
0x10000);
|
||||||
if (questRow == null)
|
if (questRow == null)
|
||||||
{
|
{
|
||||||
_logger.LogError("Could not find quest row for {QuestId}", currentQuest.QuestElementId);
|
_logger.LogError("Could not find quest row for {QuestId}", currentQuest.Id);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
excelSheetName = $"quest/{(currentQuest.QuestElementId.Value / 100):000}/{questRow.Id}";
|
excelSheetName = $"quest/{(currentQuest.Id.Value / 100):000}/{questRow.Id}";
|
||||||
}
|
}
|
||||||
|
|
||||||
var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(excelSheetName);
|
var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(excelSheetName);
|
||||||
|
20
Questionable/Model/IQuestInfo.cs
Normal file
20
Questionable/Model/IQuestInfo.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
|
namespace Questionable.Model;
|
||||||
|
|
||||||
|
public interface IQuestInfo
|
||||||
|
{
|
||||||
|
public ElementId QuestId { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public uint IssuerDataId { get; }
|
||||||
|
public bool IsRepeatable { get; }
|
||||||
|
public ushort Level { get; }
|
||||||
|
public EBeastTribe BeastTribe { get; }
|
||||||
|
public bool IsMainScenarioQuest { get; }
|
||||||
|
|
||||||
|
public string SimplifiedName => Name
|
||||||
|
.Replace(".", "", StringComparison.Ordinal)
|
||||||
|
.TrimStart(SeIconChar.QuestSync.ToIconChar(), SeIconChar.QuestRepeatable.ToIconChar(), ' ');
|
||||||
|
}
|
@ -6,9 +6,9 @@ namespace Questionable.Model;
|
|||||||
|
|
||||||
internal sealed class Quest
|
internal sealed class Quest
|
||||||
{
|
{
|
||||||
public required ElementId QuestElementId { get; init; }
|
public required ElementId Id { get; init; }
|
||||||
public required QuestRoot Root { get; init; }
|
public required QuestRoot Root { get; init; }
|
||||||
public required QuestInfo Info { get; init; }
|
public required IQuestInfo Info { get; init; }
|
||||||
public required bool ReadOnly { get; init; }
|
public required bool ReadOnly { get; init; }
|
||||||
|
|
||||||
public QuestSequence? FindSequence(byte currentSequence)
|
public QuestSequence? FindSequence(byte currentSequence)
|
||||||
|
@ -10,7 +10,7 @@ using ExcelQuest = Lumina.Excel.GeneratedSheets.Quest;
|
|||||||
|
|
||||||
namespace Questionable.Model;
|
namespace Questionable.Model;
|
||||||
|
|
||||||
internal sealed class QuestInfo
|
internal sealed class QuestInfo : IQuestInfo
|
||||||
{
|
{
|
||||||
public QuestInfo(ExcelQuest quest)
|
public QuestInfo(ExcelQuest quest)
|
||||||
{
|
{
|
||||||
@ -56,7 +56,7 @@ internal sealed class QuestInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public QuestId QuestId { get; }
|
public ElementId QuestId { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public ushort Level { get; }
|
public ushort Level { get; }
|
||||||
public uint IssuerDataId { get; }
|
public uint IssuerDataId { get; }
|
||||||
@ -74,10 +74,6 @@ internal sealed class QuestInfo
|
|||||||
public GrandCompany GrandCompany { get; }
|
public GrandCompany GrandCompany { get; }
|
||||||
public EBeastTribe BeastTribe { get; }
|
public EBeastTribe BeastTribe { get; }
|
||||||
|
|
||||||
public string SimplifiedName => Name
|
|
||||||
.Replace(".", "", StringComparison.Ordinal)
|
|
||||||
.TrimStart(SeIconChar.QuestSync.ToIconChar(), SeIconChar.QuestRepeatable.ToIconChar(), ' ');
|
|
||||||
|
|
||||||
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Members)]
|
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Members)]
|
||||||
public enum QuestJoin : byte
|
public enum QuestJoin : byte
|
||||||
{
|
{
|
||||||
|
23
Questionable/Model/SatisfactionSupplyInfo.cs
Normal file
23
Questionable/Model/SatisfactionSupplyInfo.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
|
namespace Questionable.Model;
|
||||||
|
|
||||||
|
internal sealed class SatisfactionSupplyInfo : IQuestInfo
|
||||||
|
{
|
||||||
|
public SatisfactionSupplyInfo(SatisfactionNpc npc)
|
||||||
|
{
|
||||||
|
QuestId = new SatisfactionSupplyNpcId((ushort)npc.RowId);
|
||||||
|
Name = npc.Npc.Value!.Singular;
|
||||||
|
IssuerDataId = npc.Npc.Row;
|
||||||
|
Level = npc.LevelUnlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElementId QuestId { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public uint IssuerDataId { get; }
|
||||||
|
public bool IsRepeatable => true;
|
||||||
|
public ushort Level { get; }
|
||||||
|
public EBeastTribe BeastTribe => EBeastTribe.None;
|
||||||
|
public bool IsMainScenarioQuest => false;
|
||||||
|
}
|
@ -43,7 +43,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
|||||||
IChatGui chatGui,
|
IChatGui chatGui,
|
||||||
ICommandManager commandManager,
|
ICommandManager commandManager,
|
||||||
IAddonLifecycle addonLifecycle,
|
IAddonLifecycle addonLifecycle,
|
||||||
IKeyState keyState)
|
IKeyState keyState,
|
||||||
|
IContextMenu contextMenu)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(pluginInterface);
|
ArgumentNullException.ThrowIfNull(pluginInterface);
|
||||||
|
|
||||||
@ -66,6 +67,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
|||||||
serviceCollection.AddSingleton(commandManager);
|
serviceCollection.AddSingleton(commandManager);
|
||||||
serviceCollection.AddSingleton(addonLifecycle);
|
serviceCollection.AddSingleton(addonLifecycle);
|
||||||
serviceCollection.AddSingleton(keyState);
|
serviceCollection.AddSingleton(keyState);
|
||||||
|
serviceCollection.AddSingleton(contextMenu);
|
||||||
serviceCollection.AddSingleton(new WindowSystem(nameof(Questionable)));
|
serviceCollection.AddSingleton(new WindowSystem(nameof(Questionable)));
|
||||||
serviceCollection.AddSingleton((Configuration?)pluginInterface.GetPluginConfig() ?? new Configuration());
|
serviceCollection.AddSingleton((Configuration?)pluginInterface.GetPluginConfig() ?? new Configuration());
|
||||||
|
|
||||||
@ -81,6 +83,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
|||||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
_serviceProvider.GetRequiredService<QuestRegistry>().Reload();
|
_serviceProvider.GetRequiredService<QuestRegistry>().Reload();
|
||||||
_serviceProvider.GetRequiredService<CommandHandler>();
|
_serviceProvider.GetRequiredService<CommandHandler>();
|
||||||
|
_serviceProvider.GetRequiredService<ContextMenuController>();
|
||||||
_serviceProvider.GetRequiredService<DalamudInitializer>();
|
_serviceProvider.GetRequiredService<DalamudInitializer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +159,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
|||||||
serviceCollection.AddSingleton<NavigationShortcutController>();
|
serviceCollection.AddSingleton<NavigationShortcutController>();
|
||||||
serviceCollection.AddSingleton<CombatController>();
|
serviceCollection.AddSingleton<CombatController>();
|
||||||
serviceCollection.AddSingleton<GatheringController>();
|
serviceCollection.AddSingleton<GatheringController>();
|
||||||
|
serviceCollection.AddSingleton<ContextMenuController>();
|
||||||
|
|
||||||
serviceCollection.AddSingleton<ICombatModule, RotationSolverRebornModule>();
|
serviceCollection.AddSingleton<ICombatModule, RotationSolverRebornModule>();
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ internal sealed class AethernetShortcutValidator : IQuestValidator
|
|||||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||||
{
|
{
|
||||||
return quest.AllSteps()
|
return quest.AllSteps()
|
||||||
.Select(x => Validate(quest.QuestElementId, x.Sequence.Sequence, x.StepId, x.Step.AethernetShortcut))
|
.Select(x => Validate(quest.Id, x.Sequence.Sequence, x.StepId, x.Step.AethernetShortcut))
|
||||||
.Where(x => x != null)
|
.Where(x => x != null)
|
||||||
.Cast<ValidationIssue>();
|
.Cast<ValidationIssue>();
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = 0,
|
Sequence = 0,
|
||||||
Step = null,
|
Step = null,
|
||||||
Type = EIssueType.MissingSequence0,
|
Type = EIssueType.MissingSequence0,
|
||||||
@ -28,7 +28,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
|
|||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quest.Info.CompletesInstantly)
|
if (quest.Info is QuestInfo { CompletesInstantly: true })
|
||||||
{
|
{
|
||||||
foreach (var sequence in sequences)
|
foreach (var sequence in sequences)
|
||||||
{
|
{
|
||||||
@ -37,7 +37,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
|
|||||||
|
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = (byte)sequence.Sequence,
|
Sequence = (byte)sequence.Sequence,
|
||||||
Step = null,
|
Step = null,
|
||||||
Type = EIssueType.InstantQuestWithMultipleSteps,
|
Type = EIssueType.InstantQuestWithMultipleSteps,
|
||||||
@ -46,7 +46,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (quest.Info is QuestInfo)
|
||||||
{
|
{
|
||||||
int maxSequence = sequences.Select(x => x.Sequence)
|
int maxSequence = sequences.Select(x => x.Sequence)
|
||||||
.Where(x => x != 255)
|
.Where(x => x != 255)
|
||||||
@ -73,7 +73,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
return new ValidationIssue
|
return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = (byte)sequenceNo,
|
Sequence = (byte)sequenceNo,
|
||||||
Step = null,
|
Step = null,
|
||||||
Type = EIssueType.MissingSequence,
|
Type = EIssueType.MissingSequence,
|
||||||
@ -85,7 +85,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
return new ValidationIssue
|
return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = (byte)sequenceNo,
|
Sequence = (byte)sequenceNo,
|
||||||
Step = null,
|
Step = null,
|
||||||
Type = EIssueType.DuplicateSequence,
|
Type = EIssueType.DuplicateSequence,
|
||||||
|
@ -45,7 +45,7 @@ internal sealed class CompletionFlagsValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = (byte)sequence.Sequence,
|
Sequence = (byte)sequence.Sequence,
|
||||||
Step = i,
|
Step = i,
|
||||||
Type = EIssueType.DuplicateCompletionFlags,
|
Type = EIssueType.DuplicateCompletionFlags,
|
||||||
|
@ -25,7 +25,7 @@ internal sealed class JsonSchemaValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
_questSchema ??= JsonSchema.FromStream(AssemblyQuestLoader.QuestSchema).AsTask().Result;
|
_questSchema ??= JsonSchema.FromStream(AssemblyQuestLoader.QuestSchema).AsTask().Result;
|
||||||
|
|
||||||
if (_questNodes.TryGetValue(quest.QuestElementId, out JsonNode? questNode))
|
if (_questNodes.TryGetValue(quest.Id, out JsonNode? questNode))
|
||||||
{
|
{
|
||||||
var evaluationResult = _questSchema.Evaluate(questNode, new EvaluationOptions
|
var evaluationResult = _questSchema.Evaluate(questNode, new EvaluationOptions
|
||||||
{
|
{
|
||||||
@ -36,7 +36,7 @@ internal sealed class JsonSchemaValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = null,
|
Sequence = null,
|
||||||
Step = null,
|
Step = null,
|
||||||
Type = EIssueType.InvalidJsonSchema,
|
Type = EIssueType.InvalidJsonSchema,
|
||||||
|
@ -8,11 +8,11 @@ internal sealed class NextQuestValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||||
{
|
{
|
||||||
foreach (var invalidNextQuest in quest.AllSteps().Where(x => x.Step.NextQuestId == quest.QuestElementId))
|
foreach (var invalidNextQuest in quest.AllSteps().Where(x => x.Step.NextQuestId == quest.Id))
|
||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = (byte)invalidNextQuest.Sequence.Sequence,
|
Sequence = (byte)invalidNextQuest.Sequence.Sequence,
|
||||||
Step = invalidNextQuest.StepId,
|
Step = invalidNextQuest.StepId,
|
||||||
Type = EIssueType.InvalidNextQuestId,
|
Type = EIssueType.InvalidNextQuestId,
|
||||||
|
@ -11,7 +11,7 @@ internal sealed class QuestDisabledValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = null,
|
Sequence = null,
|
||||||
Step = null,
|
Step = null,
|
||||||
Type = EIssueType.QuestDisabled,
|
Type = EIssueType.QuestDisabled,
|
||||||
|
@ -9,6 +9,9 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||||
{
|
{
|
||||||
|
if (quest.Id is SatisfactionSupplyNpcId)
|
||||||
|
yield break;
|
||||||
|
|
||||||
var questAccepts = FindQuestStepsWithInteractionType(quest, EInteractionType.AcceptQuest)
|
var questAccepts = FindQuestStepsWithInteractionType(quest, EInteractionType.AcceptQuest)
|
||||||
.Where(x => x.Step.PickUpQuestId == null)
|
.Where(x => x.Step.PickUpQuestId == null)
|
||||||
.ToList();
|
.ToList();
|
||||||
@ -18,7 +21,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = (byte)accept.Sequence.Sequence,
|
Sequence = (byte)accept.Sequence.Sequence,
|
||||||
Step = accept.StepId,
|
Step = accept.StepId,
|
||||||
Type = EIssueType.UnexpectedAcceptQuestStep,
|
Type = EIssueType.UnexpectedAcceptQuestStep,
|
||||||
@ -32,7 +35,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = 0,
|
Sequence = 0,
|
||||||
Step = null,
|
Step = null,
|
||||||
Type = EIssueType.MissingQuestAccept,
|
Type = EIssueType.MissingQuestAccept,
|
||||||
@ -50,7 +53,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = (byte)complete.Sequence.Sequence,
|
Sequence = (byte)complete.Sequence.Sequence,
|
||||||
Step = complete.StepId,
|
Step = complete.StepId,
|
||||||
Type = EIssueType.UnexpectedCompleteQuestStep,
|
Type = EIssueType.UnexpectedCompleteQuestStep,
|
||||||
@ -64,7 +67,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
|
|||||||
{
|
{
|
||||||
yield return new ValidationIssue
|
yield return new ValidationIssue
|
||||||
{
|
{
|
||||||
QuestId = quest.QuestElementId,
|
QuestId = quest.Id,
|
||||||
Sequence = 255,
|
Sequence = 255,
|
||||||
Step = null,
|
Step = null,
|
||||||
Type = EIssueType.MissingQuestComplete,
|
Type = EIssueType.MissingQuestComplete,
|
||||||
|
@ -103,7 +103,7 @@ internal sealed class DebugOverlay : Window
|
|||||||
QuestStep? step = sequence.FindStep(i);
|
QuestStep? step = sequence.FindStep(i);
|
||||||
if (step != null && TryGetPosition(step, out Vector3? position))
|
if (step != null && TryGetPosition(step, out Vector3? position))
|
||||||
{
|
{
|
||||||
DrawStep($"{quest.QuestElementId} / {sequence.Sequence} / {i}", step, position.Value, 0xFFFFFFFF);
|
DrawStep($"{quest.Id} / {sequence.Sequence} / {i}", step, position.Value, 0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
|
|
||||||
if (ImGui.IsItemClicked() && _commandManager.Commands.TryGetValue("/questinfo", out var commandInfo))
|
if (ImGui.IsItemClicked() && _commandManager.Commands.TryGetValue("/questinfo", out var commandInfo))
|
||||||
{
|
{
|
||||||
_commandManager.DispatchCommand("/questinfo", questInfo.QuestId.ToString(), commandInfo);
|
_commandManager.DispatchCommand("/questinfo", questInfo.QuestId.ToString() ?? string.Empty, commandInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
|
@ -58,7 +58,7 @@ internal sealed class ActiveQuestComponent
|
|||||||
{
|
{
|
||||||
var currentQuestDetails = _questController.CurrentQuestDetails;
|
var currentQuestDetails = _questController.CurrentQuestDetails;
|
||||||
QuestController.QuestProgress? currentQuest = currentQuestDetails?.Progress;
|
QuestController.QuestProgress? currentQuest = currentQuestDetails?.Progress;
|
||||||
QuestController.CurrentQuestType? currentQuestType = currentQuestDetails?.Type;
|
QuestController.ECurrentQuestType? currentQuestType = currentQuestDetails?.Type;
|
||||||
if (currentQuest != null)
|
if (currentQuest != null)
|
||||||
{
|
{
|
||||||
DrawQuestNames(currentQuest, currentQuestType);
|
DrawQuestNames(currentQuest, currentQuestType);
|
||||||
@ -108,9 +108,9 @@ internal sealed class ActiveQuestComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void DrawQuestNames(QuestController.QuestProgress currentQuest,
|
private void DrawQuestNames(QuestController.QuestProgress currentQuest,
|
||||||
QuestController.CurrentQuestType? currentQuestType)
|
QuestController.ECurrentQuestType? currentQuestType)
|
||||||
{
|
{
|
||||||
if (currentQuestType == QuestController.CurrentQuestType.Simulated)
|
if (currentQuestType == QuestController.ECurrentQuestType.Simulated)
|
||||||
{
|
{
|
||||||
using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
|
using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
|
||||||
ImGui.TextUnformatted(
|
ImGui.TextUnformatted(
|
||||||
@ -151,7 +151,7 @@ internal sealed class ActiveQuestComponent
|
|||||||
|
|
||||||
private QuestWork? DrawQuestWork(QuestController.QuestProgress currentQuest)
|
private QuestWork? DrawQuestWork(QuestController.QuestProgress currentQuest)
|
||||||
{
|
{
|
||||||
if (currentQuest.Quest.QuestElementId is not QuestId questId)
|
if (currentQuest.Quest.Id is not QuestId questId)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var questWork = _gameFunctions.GetQuestEx(questId);
|
var questWork = _gameFunctions.GetQuestEx(questId);
|
||||||
@ -210,7 +210,7 @@ internal sealed class ActiveQuestComponent
|
|||||||
{
|
{
|
||||||
using var disabled = ImRaii.Disabled();
|
using var disabled = ImRaii.Disabled();
|
||||||
|
|
||||||
if (currentQuest.Quest.QuestElementId == _questController.NextQuest?.Quest.QuestElementId)
|
if (currentQuest.Quest.Id == _questController.NextQuest?.Quest.Id)
|
||||||
ImGui.TextUnformatted("(Next quest in story line not accepted)");
|
ImGui.TextUnformatted("(Next quest in story line not accepted)");
|
||||||
else
|
else
|
||||||
ImGui.TextUnformatted("(Not accepted)");
|
ImGui.TextUnformatted("(Not accepted)");
|
||||||
@ -229,14 +229,14 @@ internal sealed class ActiveQuestComponent
|
|||||||
if (questWork == null)
|
if (questWork == null)
|
||||||
_questController.SetNextQuest(currentQuest.Quest);
|
_questController.SetNextQuest(currentQuest.Quest);
|
||||||
|
|
||||||
_questController.ExecuteNextStep(true);
|
_questController.ExecuteNextStep(QuestController.EAutomationType.Automatic);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.StepForward, "Step"))
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.StepForward, "Step"))
|
||||||
{
|
{
|
||||||
_questController.ExecuteNextStep(false);
|
_questController.ExecuteNextStep(QuestController.EAutomationType.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
@ -262,7 +262,7 @@ internal sealed class ActiveQuestComponent
|
|||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.ArrowCircleRight, "Skip"))
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.ArrowCircleRight, "Skip"))
|
||||||
{
|
{
|
||||||
_movementController.Stop();
|
_movementController.Stop();
|
||||||
_questController.Skip(currentQuest.Quest.QuestElementId, currentQuest.Sequence);
|
_questController.Skip(currentQuest.Quest.Id, currentQuest.Sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (colored)
|
if (colored)
|
||||||
@ -274,7 +274,7 @@ internal sealed class ActiveQuestComponent
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas))
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas))
|
||||||
_commandManager.DispatchCommand("/questinfo",
|
_commandManager.DispatchCommand("/questinfo",
|
||||||
currentQuest.Quest.QuestElementId.ToString() ?? string.Empty, commandInfo);
|
currentQuest.Quest.Id.ToString() ?? string.Empty, commandInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
|
bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
|
||||||
|
@ -6,6 +6,7 @@ using ImGuiNET;
|
|||||||
using Questionable.Controller;
|
using Questionable.Controller;
|
||||||
using Questionable.Data;
|
using Questionable.Data;
|
||||||
using Questionable.Model;
|
using Questionable.Model;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
namespace Questionable.Windows.QuestComponents;
|
namespace Questionable.Windows.QuestComponents;
|
||||||
|
|
||||||
@ -31,6 +32,12 @@ internal sealed class QuestTooltipComponent
|
|||||||
_uiUtils = uiUtils;
|
_uiUtils = uiUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Draw(IQuestInfo quest)
|
||||||
|
{
|
||||||
|
if (quest is QuestInfo questInfo)
|
||||||
|
Draw(questInfo);
|
||||||
|
}
|
||||||
|
|
||||||
public void Draw(QuestInfo quest)
|
public void Draw(QuestInfo quest)
|
||||||
{
|
{
|
||||||
using var tooltip = ImRaii.Tooltip();
|
using var tooltip = ImRaii.Tooltip();
|
||||||
@ -93,8 +100,8 @@ internal sealed class QuestTooltipComponent
|
|||||||
|
|
||||||
_uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
|
_uiUtils.ChecklistItem(FormatQuestUnlockName(qInfo), iconColor, icon);
|
||||||
|
|
||||||
if (counter <= 2 || icon != FontAwesomeIcon.Check)
|
if (qInfo is QuestInfo qstInfo && (counter <= 2 || icon != FontAwesomeIcon.Check))
|
||||||
DrawQuestUnlocks(qInfo, counter + 1);
|
DrawQuestUnlocks(qstInfo, counter + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +169,7 @@ internal sealed class QuestTooltipComponent
|
|||||||
ImGui.Unindent();
|
ImGui.Unindent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatQuestUnlockName(QuestInfo questInfo)
|
private static string FormatQuestUnlockName(IQuestInfo questInfo)
|
||||||
{
|
{
|
||||||
if (questInfo.IsMainScenarioQuest)
|
if (questInfo.IsMainScenarioQuest)
|
||||||
return $"{questInfo.Name} ({questInfo.QuestId}, MSQ)";
|
return $"{questInfo.Name} ({questInfo.QuestId}, MSQ)";
|
||||||
|
@ -39,8 +39,8 @@ internal sealed class QuestSelectionWindow : LWindow
|
|||||||
private readonly UiUtils _uiUtils;
|
private readonly UiUtils _uiUtils;
|
||||||
private readonly QuestTooltipComponent _questTooltipComponent;
|
private readonly QuestTooltipComponent _questTooltipComponent;
|
||||||
|
|
||||||
private List<QuestInfo> _quests = [];
|
private List<IQuestInfo> _quests = [];
|
||||||
private List<QuestInfo> _offeredQuests = [];
|
private List<IQuestInfo> _offeredQuests = [];
|
||||||
private bool _onlyAvailableQuests = true;
|
private bool _onlyAvailableQuests = true;
|
||||||
|
|
||||||
public QuestSelectionWindow(QuestData questData, IGameGui gameGui, IChatGui chatGui, GameFunctions gameFunctions,
|
public QuestSelectionWindow(QuestData questData, IGameGui gameGui, IChatGui chatGui, GameFunctions gameFunctions,
|
||||||
@ -105,7 +105,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
|||||||
|
|
||||||
_quests = _questRegistry.AllQuests
|
_quests = _questRegistry.AllQuests
|
||||||
.Where(x => x.FindSequence(0)?.FindStep(0)?.TerritoryId == territoryId)
|
.Where(x => x.FindSequence(0)?.FindStep(0)?.TerritoryId == territoryId)
|
||||||
.Select(x => _questData.GetQuestInfo(x.QuestElementId))
|
.Select(x => _questData.GetQuestInfo(x.Id))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var unacceptedQuest in Map.Instance()->UnacceptedQuestMarkers)
|
foreach (var unacceptedQuest in Map.Instance()->UnacceptedQuestMarkers)
|
||||||
@ -157,11 +157,11 @@ internal sealed class QuestSelectionWindow : LWindow
|
|||||||
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, actionIconSize);
|
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, actionIconSize);
|
||||||
ImGui.TableHeadersRow();
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
foreach (QuestInfo quest in (_offeredQuests.Count != 0 && _onlyAvailableQuests) ? _offeredQuests : _quests)
|
foreach (IQuestInfo quest in (_offeredQuests.Count != 0 && _onlyAvailableQuests) ? _offeredQuests : _quests)
|
||||||
{
|
{
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
string questId = quest.QuestId.ToString();
|
string questId = quest.QuestId.ToString() ?? string.Empty;
|
||||||
bool isKnownQuest = _questRegistry.TryGetQuest(quest.QuestId, out var knownQuest);
|
bool isKnownQuest = _questRegistry.TryGetQuest(quest.QuestId, out var knownQuest);
|
||||||
|
|
||||||
if (ImGui.TableNextColumn())
|
if (ImGui.TableNextColumn())
|
||||||
@ -228,7 +228,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
|||||||
if (startNextQuest)
|
if (startNextQuest)
|
||||||
{
|
{
|
||||||
_questController.SetNextQuest(knownQuest);
|
_questController.SetNextQuest(knownQuest);
|
||||||
_questController.ExecuteNextStep(true);
|
_questController.ExecuteNextStep(QuestController.EAutomationType.Automatic);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
@ -245,7 +245,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyToClipboard(QuestInfo quest, bool suffix)
|
private void CopyToClipboard(IQuestInfo quest, bool suffix)
|
||||||
{
|
{
|
||||||
string fileName = $"{quest.QuestId}_{quest.SimplifiedName}{(suffix ? ".json" : "")}";
|
string fileName = $"{quest.QuestId}_{quest.SimplifiedName}{(suffix ? ".json" : "")}";
|
||||||
ImGui.SetClipboardText(fileName);
|
ImGui.SetClipboardText(fileName);
|
||||||
|
@ -22,13 +22,13 @@ internal sealed class UiUtils
|
|||||||
public (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(ElementId questElementId)
|
public (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(ElementId questElementId)
|
||||||
{
|
{
|
||||||
if (_gameFunctions.IsQuestAccepted(questElementId))
|
if (_gameFunctions.IsQuestAccepted(questElementId))
|
||||||
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running, "Active");
|
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight, "Active");
|
||||||
else if (_gameFunctions.IsQuestAcceptedOrComplete(questElementId))
|
else if (_gameFunctions.IsQuestAcceptedOrComplete(questElementId))
|
||||||
return (ImGuiColors.ParsedGreen, FontAwesomeIcon.Check, "Complete");
|
return (ImGuiColors.ParsedGreen, FontAwesomeIcon.Check, "Complete");
|
||||||
else if (_gameFunctions.IsQuestLocked(questElementId))
|
else if (_gameFunctions.IsQuestLocked(questElementId))
|
||||||
return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times, "Locked");
|
return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times, "Locked");
|
||||||
else
|
else
|
||||||
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight, "Available");
|
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running, "Available");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (Vector4 color, FontAwesomeIcon icon) GetInstanceStyle(ushort instanceId)
|
public static (Vector4 color, FontAwesomeIcon icon) GetInstanceStyle(ushort instanceId)
|
||||||
@ -36,7 +36,7 @@ internal sealed class UiUtils
|
|||||||
if (UIState.IsInstanceContentCompleted(instanceId))
|
if (UIState.IsInstanceContentCompleted(instanceId))
|
||||||
return (ImGuiColors.ParsedGreen, FontAwesomeIcon.Check);
|
return (ImGuiColors.ParsedGreen, FontAwesomeIcon.Check);
|
||||||
else if (UIState.IsInstanceContentUnlocked(instanceId))
|
else if (UIState.IsInstanceContentUnlocked(instanceId))
|
||||||
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.PersonWalkingArrowRight);
|
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running);
|
||||||
else
|
else
|
||||||
return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times);
|
return (ImGuiColors.DalamudRed, FontAwesomeIcon.Times);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user