--- /dev/null
+<Project Sdk="Dalamud.NET.Sdk/10.0.0">
+ <Import Project="..\LLib\LLib.targets"/>
+</Project>
--- /dev/null
+using Dalamud.Plugin;
+
+namespace GatheringPathRenderer;
+
+public sealed class RendererPlugin : IDalamudPlugin
+{
+ public void Dispose()
+ {
+
+ }
+}
--- /dev/null
+{
+ "version": 1,
+ "dependencies": {
+ "net8.0-windows7.0": {
+ "DalamudPackager": {
+ "type": "Direct",
+ "requested": "[2.1.13, )",
+ "resolved": "2.1.13",
+ "contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
+ },
+ "DotNet.ReproducibleBuilds": {
+ "type": "Direct",
+ "requested": "[1.1.1, )",
+ "resolved": "1.1.1",
+ "contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
+ "dependencies": {
+ "Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
+ "Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
+ "Microsoft.SourceLink.GitHub": "1.1.1",
+ "Microsoft.SourceLink.GitLab": "1.1.1"
+ }
+ },
+ "Microsoft.SourceLink.Gitea": {
+ "type": "Direct",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.0",
+ "contentHash": "KOBodmDnlWGIqZt2hT47Q69TIoGhIApDVLCyyj9TT5ct8ju16AbHYcB4XeknoHX562wO1pMS/1DfBIZK+V+sxg==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "8.0.0",
+ "Microsoft.SourceLink.Common": "8.0.0"
+ }
+ },
+ "Microsoft.Build.Tasks.Git": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+ },
+ "Microsoft.SourceLink.AzureRepos.Git": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "1.1.1",
+ "Microsoft.SourceLink.Common": "1.1.1"
+ }
+ },
+ "Microsoft.SourceLink.Bitbucket.Git": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "1.1.1",
+ "Microsoft.SourceLink.Common": "1.1.1"
+ }
+ },
+ "Microsoft.SourceLink.Common": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+ },
+ "Microsoft.SourceLink.GitHub": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "1.1.1",
+ "Microsoft.SourceLink.Common": "1.1.1"
+ }
+ },
+ "Microsoft.SourceLink.GitLab": {
+ "type": "Transitive",
+ "resolved": "1.1.1",
+ "contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "1.1.1",
+ "Microsoft.SourceLink.Common": "1.1.1"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 957,
+ "AetheryteShortcut": "Thavnair - Great Work",
+ "Nodes": [
+ {
+ "DataId": 33918,
+ "Position": {
+ "X": -582.5132,
+ "Y": 40.54578,
+ "Z": -426.0171
+ }
+ },
+ {
+ "DataId": 33919,
+ "Position": {
+ "X": -578.2101,
+ "Y": 41.27147,
+ "Z": -447.6376
+ }
+ },
+ {
+ "DataId": 33920,
+ "Position": {
+ "X": -488.2276,
+ "Y": 34.71221,
+ "Z": -359.6945
+ }
+ },
+ {
+ "DataId": 33921,
+ "Position": {
+ "X": -498.8687,
+ "Y": 31.08014,
+ "Z": -351.9397
+ }
+ },
+ {
+ "DataId": 33922,
+ "Position": {
+ "X": -304.0609,
+ "Y": 68.76999,
+ "Z": -479.1875
+ }
+ },
+ {
+ "DataId": 33923,
+ "Position": {
+ "X": -293.6989,
+ "Y": 68.77935,
+ "Z": -484.2256
+ }
+ }
+ ]
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using Questionable.Model.Gathering;
+
+namespace Questionable.GatheringPaths;
+
+[SuppressMessage("ReSharper", "PartialTypeWithSinglePart", Justification = "Required for RELEASE")]
+public static partial class AssemblyGatheringLocationLoader
+{
+ private static Dictionary<ushort, GatheringRoot>? _locations;
+
+ public static IReadOnlyDictionary<ushort, GatheringRoot> GetLocations()
+ {
+ if (_locations == null)
+ {
+ _locations = [];
+#if RELEASE
+ LoadLocations();
+#endif
+ }
+
+ return _locations ?? throw new InvalidOperationException("quest data is not initialized");
+ }
+
+ public static Stream QuestSchema =>
+ typeof(AssemblyGatheringLocationLoader).Assembly.GetManifestResourceStream("Questionable.GatheringPaths.GatheringLocationSchema")!;
+
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ private static void AddLocation(ushort questId, GatheringRoot root) => _locations![questId] = root;
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>net8.0-windows</TargetFramework>
+ <LangVersion>12</LangVersion>
+ <Nullable>enable</Nullable>
+ <RootNamespace>Questionable.GatheringPaths</RootNamespace>
+ <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
+ <DebugType>none</DebugType>
+ <PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
+ <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
+ <Platforms>x64</Platforms>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Questionable.Model\Questionable.Model.csproj" />
+ <ProjectReference Include="..\QuestPathGenerator\QuestPathGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <None Remove="gatheringlocation-v1.json" />
+ <EmbeddedResource Include="gatheringlocation-v1.json">
+ <LogicalName>Questionable.GatheringPaths.GatheringLocationSchema</LogicalName>
+ </EmbeddedResource>
+ <AdditionalFiles Include="gatheringlocation-v1.json" />
+ <AdditionalFiles Include="..\Questionable.Model\common-schema.json" />
+ </ItemGroup>
+
+ <ItemGroup Condition="'$(Configuration)' == 'Release'">
+ <None Remove="2.x - A Realm Reborn" />
+ <None Remove="3.x - Heavensward" />
+ <None Remove="4.x - Stormblood" />
+ <None Remove="5.x - Shadowbringers" />
+ <None Remove="6.x - Endwalker" />
+ <None Remove="7.x - Dawntrail" />
+ <AdditionalFiles Include="2.x - A Realm Reborn\**\*.json" />
+ <AdditionalFiles Include="3.x - Heavensward\**\*.json" />
+ <AdditionalFiles Include="4.x - Stormblood\**\*.json" />
+ <AdditionalFiles Include="5.x - Shadowbringers\**\*.json" />
+ <AdditionalFiles Include="6.x - Endwalker\**\*.json" />
+ <AdditionalFiles Include="7.x - Dawntrail\**\*.json" />
+ </ItemGroup>
+</Project>
--- /dev/null
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "title": "Gathering Location V1",
+ "description": "A series of gathering locationsk",
+ "type": "object",
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "const": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json"
+ },
+ "Author": {
+ "description": "Author of the gathering location data",
+ "type": [
+ "string",
+ "array"
+ ],
+ "items": {
+ "type": "string"
+ }
+ },
+ "TerritoryId": {
+ "type": "number"
+ },
+ "AetheryteShortcut": {
+ "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/Aetheryte"
+ },
+ "Nodes": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "DataId": {
+ "type": "number",
+ "minimum": 30000,
+ "maximum": 50000
+ },
+ "Position": {
+ "$ref": "#/$defs/Vector3"
+ },
+ "MinimumAngle": {
+ "type": "number",
+ "minimum": -360,
+ "maximum": 360
+ },
+ "MaximumAngle": {
+ "type": "number",
+ "minimum": -360,
+ "maximum": 360
+ },
+ "MinimumDistance": {
+ "type": "number",
+ "minimum": 0
+ },
+ "MaximumDistance": {
+ "type": "number",
+ "exclusiveMinimum": 0
+ }
+ },
+ "required": [
+ "DataId",
+ "Position"
+ ],
+ "additionalProperties": false
+ }
+ }
+ },
+ "required": [
+ "$schema",
+ "Author",
+ "TerritoryId",
+ "AetheryteShortcut",
+ "Nodes"
+ ],
+ "additionalProperties": false,
+ "$defs": {
+ "Vector3": {
+ "type": "object",
+ "description": "Position to (typically) walk to",
+ "properties": {
+ "X": {
+ "type": "number"
+ },
+ "Y": {
+ "type": "number"
+ },
+ "Z": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "X",
+ "Y",
+ "Z"
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "version": 1,
+ "dependencies": {
+ "net8.0-windows7.0": {
+ "System.Text.Encodings.Web": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
+ },
+ "System.Text.Json": {
+ "type": "Transitive",
+ "resolved": "8.0.4",
+ "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==",
+ "dependencies": {
+ "System.Text.Encodings.Web": "8.0.0"
+ }
+ },
+ "questionable.model": {
+ "type": "Project",
+ "dependencies": {
+ "System.Text.Json": "[8.0.4, )"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
using Questionable.QuestPathGenerator;
using Xunit;
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using Json.Schema;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Questionable.Model.Gathering;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+using static Questionable.QuestPathGenerator.RoslynShortcuts;
+
+namespace Questionable.QuestPathGenerator;
+
+[Generator]
+[SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008")]
+public class GatheringSourceGenerator : ISourceGenerator
+{
+ private static readonly DiagnosticDescriptor InvalidJson = new("GPG0001",
+ "Invalid JSON",
+ "Invalid gathering file: {0}",
+ nameof(GatheringSourceGenerator),
+ DiagnosticSeverity.Error,
+ true);
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ // No initialization required for this generator.
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ // Find schema definition
+ AdditionalText? gatheringSchema =
+ context.AdditionalFiles.SingleOrDefault(x => Path.GetFileName(x.Path) == "gatheringlocation-v1.json");
+ if (gatheringSchema != null)
+ GenerateGatheringSource(context, gatheringSchema);
+ }
+
+ private void GenerateGatheringSource(GeneratorExecutionContext context, AdditionalText jsonSchemaFile)
+ {
+ var gatheringSchema = JsonSchema.FromText(jsonSchemaFile.GetText()!.ToString());
+
+ List<(ushort, GatheringRoot)> gatheringLocations = [];
+ foreach (var (id, node) in Utils.GetAdditionalFiles(context, jsonSchemaFile, gatheringSchema, InvalidJson))
+ {
+ var gatheringLocation = node.Deserialize<GatheringRoot>()!;
+ gatheringLocations.Add((id, gatheringLocation));
+ }
+
+ if (gatheringLocations.Count == 0)
+ return;
+
+ var partitionedLocations = gatheringLocations
+ .OrderBy(x => x.Item1)
+ .GroupBy(x => $"LoadLocation{x.Item1 / 100}")
+ .ToList();
+
+ var methods = Utils.CreateMethods("LoadLocations", partitionedLocations, CreateInitializer);
+
+ var code =
+ CompilationUnit()
+ .WithUsings(
+ List(
+ new[]
+ {
+ UsingDirective(
+ IdentifierName("System")),
+ UsingDirective(
+ QualifiedName(
+ IdentifierName("System"),
+ IdentifierName("Numerics"))),
+ UsingDirective(
+ QualifiedName(
+ IdentifierName("System"),
+ IdentifierName("IO"))),
+ UsingDirective(
+ QualifiedName(
+ QualifiedName(
+ IdentifierName("System"), IdentifierName("Collections")),
+ IdentifierName("Generic"))),
+ UsingDirective(
+ QualifiedName(
+ QualifiedName(
+ IdentifierName("Questionable"),
+ IdentifierName("Model")),
+ IdentifierName("Gathering"))),
+ UsingDirective(
+ QualifiedName(
+ QualifiedName(
+ IdentifierName("Questionable"),
+ IdentifierName("Model")),
+ IdentifierName("Common")))
+ }))
+ .WithMembers(
+ SingletonList<MemberDeclarationSyntax>(
+ FileScopedNamespaceDeclaration(
+ QualifiedName(
+ IdentifierName("Questionable"),
+ IdentifierName("GatheringPaths")))
+ .WithMembers(
+ SingletonList<MemberDeclarationSyntax>(
+ ClassDeclaration("AssemblyGatheringLocationLoader")
+ .WithModifiers(
+ TokenList(Token(SyntaxKind.PartialKeyword)))
+ .WithMembers(List<MemberDeclarationSyntax>(methods))))))
+ .NormalizeWhitespace();
+
+ // Add the source code to the compilation.
+ context.AddSource("AssemblyGatheringLocationLoader.g.cs", code.ToFullString());
+ }
+
+ private static StatementSyntax[] CreateInitializer(List<(ushort QuestId, GatheringRoot Root)> quests)
+ {
+ List<StatementSyntax> statements = [];
+
+ foreach (var quest in quests)
+ {
+ statements.Add(
+ ExpressionStatement(
+ InvocationExpression(
+ IdentifierName("AddLocation"))
+ .WithArgumentList(
+ ArgumentList(
+ SeparatedList<ArgumentSyntax>(
+ new SyntaxNodeOrToken[]
+ {
+ Argument(
+ LiteralExpression(SyntaxKind.NumericLiteralExpression,
+ Literal(quest.QuestId))),
+ Token(SyntaxKind.CommaToken),
+ Argument(CreateGatheringRootExpression(quest.QuestId, quest.Root))
+ })))));
+ }
+
+ return statements.ToArray();
+ }
+
+ private static ObjectCreationExpressionSyntax CreateGatheringRootExpression(ushort locationId, GatheringRoot root)
+ {
+ try
+ {
+ return ObjectCreationExpression(
+ IdentifierName(nameof(GatheringRoot)))
+ .WithInitializer(
+ InitializerExpression(
+ SyntaxKind.ObjectInitializerExpression,
+ SeparatedList<ExpressionSyntax>(
+ SyntaxNodeList(
+ AssignmentList(nameof(GatheringRoot.Author), root.Author).AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheringRoot.TerritoryId), root.TerritoryId, default)
+ .AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheringRoot.AetheryteShortcut), root.AetheryteShortcut, null),
+ AssignmentList(nameof(GatheringRoot.Nodes), root.Nodes).AsSyntaxNodeOrToken()))));
+ }
+ catch (Exception e)
+ {
+ throw new Exception($"GatheringGen[{locationId}]: {e.Message}", e);
+ }
+ }
+}
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json;
-using System.Text.Json.Nodes;
using Json.Schema;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Questionable.QuestPathGenerator.RoslynShortcuts;
public void Execute(GeneratorExecutionContext context)
{
- List<(ushort, QuestRoot)> quests = [];
-
// Find schema definition
- AdditionalText jsonSchemaFile =
- context.AdditionalFiles.Single(x => Path.GetFileName(x.Path) == "quest-v1.json");
+ AdditionalText? questSchema =
+ context.AdditionalFiles.SingleOrDefault(x => Path.GetFileName(x.Path) == "quest-v1.json");
+ if (questSchema != null)
+ GenerateQuestSource(context, questSchema);
+ }
+
+ private void GenerateQuestSource(GeneratorExecutionContext context, AdditionalText jsonSchemaFile)
+ {
var questSchema = JsonSchema.FromText(jsonSchemaFile.GetText()!.ToString());
- // Go through all files marked as an Additional File in file properties.
- foreach (var additionalFile in context.AdditionalFiles)
+ List<(ushort, QuestRoot)> quests = [];
+ foreach (var (id, node) in Utils.GetAdditionalFiles(context, jsonSchemaFile, questSchema, InvalidJson))
{
- if (additionalFile == null || additionalFile == jsonSchemaFile)
- continue;
-
- if (Path.GetExtension(additionalFile.Path) != ".json")
- continue;
-
- string name = Path.GetFileName(additionalFile.Path);
- if (!name.Contains('_'))
- continue;
-
- ushort id = ushort.Parse(name.Substring(0, name.IndexOf('_')));
-
- var text = additionalFile.GetText();
- if (text == null)
- continue;
-
- var questNode = JsonNode.Parse(text.ToString());
- var evaluationResult = questSchema.Evaluate(questNode, new EvaluationOptions
- {
- Culture = CultureInfo.InvariantCulture,
- OutputFormat = OutputFormat.List
- });
- if (!evaluationResult.IsValid)
- {
- var error = Diagnostic.Create(InvalidJson,
- null,
- Path.GetFileName(additionalFile.Path));
- context.ReportDiagnostic(error);
- }
-
- var quest = questNode.Deserialize<QuestRoot>()!;
+ var quest = node.Deserialize<QuestRoot>()!;
if (quest.Disabled)
{
quest.Author = [];
.GroupBy(x => $"LoadQuests{x.Item1 / 50}")
.ToList();
- List<MethodDeclarationSyntax> methods =
- [
- MethodDeclaration(
- PredefinedType(
- Token(SyntaxKind.VoidKeyword)),
- Identifier("LoadQuests"))
- .WithModifiers(
- TokenList(
- Token(SyntaxKind.PrivateKeyword),
- Token(SyntaxKind.StaticKeyword)))
- .WithBody(
- Block(
- partitionedQuests
- .Select(x =>
- ExpressionStatement(
- InvocationExpression(
- IdentifierName(x.Key))))))
- ];
-
- foreach (var partition in partitionedQuests)
- {
- methods.Add(MethodDeclaration(
- PredefinedType(
- Token(SyntaxKind.VoidKeyword)),
- Identifier(partition.Key))
- .WithModifiers(
- TokenList(
- Token(SyntaxKind.PrivateKeyword),
- Token(SyntaxKind.StaticKeyword)))
- .WithBody(
- Block(CreateInitializer(partition.ToList()))));
- }
+ var methods = Utils.CreateMethods("LoadQuests", partitionedQuests, CreateInitializer);
var code =
CompilationUnit()
QualifiedName(
IdentifierName("Questionable"),
IdentifierName("Model")),
- IdentifierName("V1")))
+ IdentifierName("Questing"))),
+ UsingDirective(
+ QualifiedName(
+ QualifiedName(
+ IdentifierName("Questionable"),
+ IdentifierName("Model")),
+ IdentifierName("Common")))
}))
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
+using Questionable.Model.Gathering;
+using Questionable.Model.Questing;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Questionable.QuestPathGenerator;
Assignment(nameof(SkipAetheryteCondition.InSameTerritory),
skipAetheryteCondition.InSameTerritory, emptyAetheryte.InSameTerritory)))));
}
+ else if (value is GatheringNodeLocation nodeLocation)
+ {
+ var emptyLocation = new GatheringNodeLocation();
+ return ObjectCreationExpression(
+ IdentifierName(nameof(GatheringNodeLocation)))
+ .WithInitializer(
+ InitializerExpression(
+ SyntaxKind.ObjectInitializerExpression,
+ SeparatedList<ExpressionSyntax>(
+ SyntaxNodeList(
+ Assignment(nameof(GatheringNodeLocation.DataId), nodeLocation.DataId,
+ emptyLocation.DataId)
+ .AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheringNodeLocation.Position), nodeLocation.Position,
+ emptyLocation.Position).AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheringNodeLocation.MinimumAngle), nodeLocation.MinimumAngle,
+ emptyLocation.MinimumAngle).AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheringNodeLocation.MaximumAngle), nodeLocation.MaximumAngle,
+ emptyLocation.MaximumAngle).AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheringNodeLocation.MinimumDistance),
+ nodeLocation.MinimumDistance, emptyLocation.MinimumDistance)
+ .AsSyntaxNodeOrToken(),
+ Assignment(nameof(GatheringNodeLocation.MaximumDistance),
+ nodeLocation.MaximumDistance, emptyLocation.MaximumDistance)
+ .AsSyntaxNodeOrToken()))));
+ }
else if (value is null)
return LiteralExpression(SyntaxKind.NullLiteralExpression);
}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.Json.Nodes;
+using Json.Schema;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Questionable.QuestPathGenerator;
+
+public static class Utils
+{
+ public static IEnumerable<(ushort, JsonNode)> GetAdditionalFiles(GeneratorExecutionContext context,
+ AdditionalText jsonSchemaFile, JsonSchema jsonSchema, DiagnosticDescriptor invalidJson)
+ {
+ var commonSchemaFile = context.AdditionalFiles.Single(x => Path.GetFileName(x.Path) == "common-schema.json");
+ List<AdditionalText> jsonSchemaFiles = [jsonSchemaFile, commonSchemaFile];
+
+ SchemaRegistry.Global.Register(
+ new Uri("https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json"),
+ JsonSchema.FromText(commonSchemaFile.GetText()!.ToString()));
+
+ foreach (var additionalFile in context.AdditionalFiles)
+ {
+ if (additionalFile == null || jsonSchemaFiles.Contains(additionalFile))
+ continue;
+
+ if (Path.GetExtension(additionalFile.Path) != ".json")
+ continue;
+
+ string name = Path.GetFileName(additionalFile.Path);
+ if (!name.Contains("_"))
+ continue;
+
+ ushort id = ushort.Parse(name.Substring(0, name.IndexOf('_')));
+
+ var text = additionalFile.GetText();
+ if (text == null)
+ continue;
+
+ var node = JsonNode.Parse(text.ToString());
+ if (node == null)
+ continue;
+
+ string? schemaLocation = node["$schema"]?.GetValue<string?>();
+ if (schemaLocation == null || new Uri(schemaLocation) != jsonSchema.GetId())
+ continue;
+
+ var evaluationResult = jsonSchema.Evaluate(node, new EvaluationOptions
+ {
+ Culture = CultureInfo.InvariantCulture,
+ OutputFormat = OutputFormat.List,
+ });
+ if (!evaluationResult.IsValid)
+ {
+ var error = Diagnostic.Create(invalidJson,
+ null,
+ Path.GetFileName(additionalFile.Path));
+ context.ReportDiagnostic(error);
+ }
+
+ yield return (id, node);
+ }
+ }
+
+ public static List<MethodDeclarationSyntax> CreateMethods<T>(string prefix,
+ List<IGrouping<string, (ushort, T)>> partitions,
+ Func<List<(ushort, T)>, StatementSyntax[]> toInitializers)
+ {
+ List<MethodDeclarationSyntax> methods =
+ [
+ MethodDeclaration(
+ PredefinedType(
+ Token(SyntaxKind.VoidKeyword)),
+ Identifier(prefix))
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.PrivateKeyword),
+ Token(SyntaxKind.StaticKeyword)))
+ .WithBody(
+ Block(
+ partitions
+ .Select(x =>
+ ExpressionStatement(
+ InvocationExpression(
+ IdentifierName(x.Key))))))
+ ];
+
+ foreach (var partition in partitions)
+ {
+ methods.Add(MethodDeclaration(
+ PredefinedType(
+ Token(SyntaxKind.VoidKeyword)),
+ Identifier(partition.Key))
+ .WithModifiers(
+ TokenList(
+ Token(SyntaxKind.PrivateKeyword),
+ Token(SyntaxKind.StaticKeyword)))
+ .WithBody(
+ Block(toInitializers(partition.ToList()))));
+ }
+
+ return methods;
+ }
+}
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
-using System.Reflection;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.QuestPaths;
<LogicalName>Questionable.QuestPaths.QuestSchema</LogicalName>
</EmbeddedResource>
<AdditionalFiles Include="quest-v1.json" />
+ <AdditionalFiles Include="..\Questionable.Model\common-schema.json" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
},
"AetheryteShortcut": {
"description": "The Aetheryte to teleport to (before moving)",
- "$ref": "#/$defs/Aetheryte"
+ "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/Aetheryte"
},
"AethernetShortcut": {
"type": "array",
"minItems": 2,
"maxItems": 2,
"items": {
- "$ref": "#/$defs/AethernetShard"
+ "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/AethernetShard"
}
},
"ItemId": {
"type": "boolean"
},
"CompletionQuestVariablesFlags": {
- "$ref": "#/$defs/CompletionFlags"
+ "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/CompletionFlags"
},
"Flying": {
"type": "string",
"additionalProperties": false
},
"CompletionQuestVariablesFlags": {
- "$ref": "#/$defs/CompletionFlags"
+ "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/CompletionFlags"
},
"RequiredQuestVariables": {
"type": "array",
"then": {
"properties": {
"Aetheryte": {
- "$ref": "#/$defs/Aetheryte"
+ "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/Aetheryte"
},
"DataId": {
"type": "null"
"then": {
"properties": {
"AethernetShard": {
- "$ref": "#/$defs/AethernetShard"
+ "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/AethernetShard"
},
"DataId": {
"type": "null"
"type": "integer"
},
"CompletionQuestVariablesFlags": {
- "$ref": "#/$defs/CompletionFlags"
+ "$ref": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json#/$defs/CompletionFlags"
},
"IgnoreQuestMarker": {
"type": "boolean"
"QuestSequence",
"Author"
],
- "additionalProperties": false,
- "$defs": {
- "Aetheryte": {
- "type": "string",
- "enum": [
- "Gridania",
- "Central Shroud - Bentbranch Meadows",
- "East Shroud - Hawthorne Hut",
- "South Shroud - Quarrymill",
- "South Shroud - Camp Tranquil",
- "North Shroud - Fallgourd Float",
- "Ul'dah",
- "Western Thanalan - Horizon",
- "Central Thanalan - Black Brush Station",
- "Eastern Thanalan - Camp Drybone",
- "Southern Thanalan - Little Ala Mhigo",
- "Southern Thanalan - Forgotten Springs",
- "Northern Thanalan - Camp Bluefog",
- "Northern Thanalan - Ceruleum Processing Plant",
- "Limsa Lominsa",
- "Middle La Noscea - Summerford Farms",
- "Lower La Noscea - Moraby Drydocks",
- "Eastern La Noscea - Costa Del Sol",
- "Eastern La Noscea - Wineport",
- "Western La Noscea - Swiftperch",
- "Western La Noscea - Aleport",
- "Upper La Noscea - Camp Bronze Lake",
- "Outer La Noscea - Camp Overlook",
- "Coerthas Central Highlands - Camp Dragonhead",
- "Mor Dhona",
- "Gold Saucer",
- "Wolves' Den Pier",
- "Ishgard",
- "Idyllshire",
- "Coerthas Western Highlands - Falcon's Nest",
- "The Sea of Clouds - Camp Cloudtop",
- "The Sea of Clouds - Ok' Zundu",
- "Azys Lla - Helix",
- "The Dravanian Forelands - Tailfeather",
- "The Dravanian Forelands - Anyx Trine",
- "The Churning Mists - Moghome",
- "The Churning Mists - Zenith",
- "Rhalgr's Reach",
- "Fringes - Castrum Oriens",
- "Fringes - Peering Stones",
- "Peaks - Ala Gannha",
- "Peaks - Ala Ghiri",
- "Lochs - Porta Praetoria",
- "Lochs - Ala Mhigan Quarter",
- "Kugane",
- "Ruby Sea - Tamamizu",
- "Ruby Sea - Onokoro",
- "Yanxia - Namai",
- "Yanxia - House of the Fierce",
- "Azim Steppe - Reunion",
- "Azim Steppe - Dawn Throne",
- "Azim Steppe - Dhoro Iloh",
- "Doman Enclave",
- "Crystarium",
- "Eulmore",
- "Lakeland - Fort Jobb",
- "Lakeland - Ostall Imperative",
- "Kholusia - Stilltide",
- "Kholusia - Wright",
- "Kholusia - Tomra",
- "Amh Araeng - Mord Souq",
- "Amh Araeng - Inn at Journey's Head",
- "Amh Araeng - Twine",
- "Rak'tika - Slitherbough",
- "Rak'tika - Fanow",
- "Il Mheg - Lydha Lran",
- "Il Mheg - Pia Enni",
- "Il Mheg - Wolekdorf",
- "Tempest - Ondo Cups",
- "Tempest - Macarenses Angle",
- "Old Sharlayan",
- "Radz-at-Han",
- "Labyrinthos - Archeion",
- "Labyrinthos - Sharlayan Hamlet",
- "Labyrinthos - Aporia",
- "Thavnair - Yedlihmad",
- "Thavnair - Great Work",
- "Thavnair - Palaka's Stand",
- "Garlemald - Camp Broken Glass",
- "Garlemald - Tertium",
- "Mare Lamentorum - Sinus Lacrimarum",
- "Mare Lamentorum - Bestways Burrow",
- "Elpis - Anagnorisis",
- "Elpis - Twelve Wonders",
- "Elpis - Poieten Oikos",
- "Ultima Thule - Reah Tahra",
- "Ultima Thule - Abode of the Ea",
- "Ultima Thule - Base Omicron",
- "Tuliyollal",
- "Solution Nine",
- "Urqopacha - Wachunpelo",
- "Urqopacha - Worlar's Echo",
- "Kozama'uka - Ok'hanu",
- "Kozama'uka - Many Fires",
- "Kozama'uka - Earthenshire",
- "Yak T'el - Iq Br'aax",
- "Yak T'el - Mamook",
- "Shaaloani - Hhusatahwi",
- "Shaaloani - Sheshenewezi Springs",
- "Shaaloani - Mehwahhetsoan",
- "Heritage Found - Yyasulani Station",
- "Heritage Found - The Outskirts",
- "Heritage Found - Electrope Strike",
- "Living Memory - Leynode Mnemo",
- "Living Memory - Leynode Pyro",
- "Living Memory - Leynode Aero"
- ]
- },
- "AethernetShard": {
- "type": "string",
- "enum": [
- "[Gridania] Aetheryte Plaza",
- "[Gridania] Archers' Guild",
- "[Gridania] Leatherworkers' Guild & Shaded Bower",
- "[Gridania] Lancers' Guild",
- "[Gridania] Conjurers' Guild",
- "[Gridania] Botanists' Guild",
- "[Gridania] Mih Khetto's Amphitheatre",
- "[Gridania] Blue Badger Gate (Central Shroud)",
- "[Gridania] Yellow Serpent Gate (North Shroud)",
- "[Gridania] White Wolf Gate (Central Shroud)",
- "[Gridania] Airship Landing",
- "[Ul'dah] Aetheryte Plaza",
- "[Ul'dah] Adventurers' Guild",
- "[Ul'dah] Thaumaturges' Guild",
- "[Ul'dah] Gladiators' Guild",
- "[Ul'dah] Miners' Guild",
- "[Ul'dah] Weavers' Guild",
- "[Ul'dah] Goldsmiths' Guild",
- "[Ul'dah] Sapphire Avenue Exchange",
- "[Ul'dah] Alchemists' Guild",
- "[Ul'dah] Gate of the Sultana (Western Thanalan)",
- "[Ul'dah] Gate of Nald (Central Thanalan)",
- "[Ul'dah] Gate of Thal (Central Thanalan)",
- "[Ul'dah] The Chamber of Rule",
- "[Ul'dah] Airship Landing",
- "[Limsa Lominsa] Aetheryte Plaza",
- "[Limsa Lominsa] Arcanists' Guild",
- "[Limsa Lominsa] Fishermens' Guild",
- "[Limsa Lominsa] Hawkers' Alley",
- "[Limsa Lominsa] The Aftcastle",
- "[Limsa Lominsa] Culinarians' Guild",
- "[Limsa Lominsa] Marauders' Guild",
- "[Limsa Lominsa] Zephyr Gate (Middle La Noscea)",
- "[Limsa Lominsa] Tempest Gate (Lower La Noscea)",
- "[Limsa Lominsa] Airship Landing",
- "[Ishgard] Aetheryte Plaza",
- "[Ishgard] The Forgotten Knight",
- "[Ishgard] Skysteel Manufactory",
- "[Ishgard] The Brume",
- "[Ishgard] Athenaeum Astrologicum",
- "[Ishgard] The Jeweled Crozier",
- "[Ishgard] Saint Reymanaud's Cathedral",
- "[Ishgard] The Tribunal",
- "[Ishgard] The Last Vigil",
- "[Ishgard] The Gates of Judgement (Coerthas Central Highlands)",
- "[Idyllshire] Aetheryte Plaza",
- "[Idyllshire] West Idyllshire",
- "[Idyllshire] Prologue Gate (Western Hinterlands)",
- "[Idyllshire] Epilogue Gate (Eastern Hinterlands)",
- "[Rhalgr's Reach] Aetheryte Plaza",
- "[Rhalgr's Reach] Western Rhalgr's Reach",
- "[Rhalgr's Reach] Northeastern Rhalgr's Reach",
- "[Rhalgr's Reach] Fringes Gate",
- "[Rhalgr's Reach] Peaks Gate",
- "[Kugane] Aetheryte Plaza",
- "[Kugane] Shiokaze Hostelry",
- "[Kugane] Pier #1",
- "[Kugane] Thavnairian Consulate",
- "[Kugane] Kogane Dori Markets",
- "[Kugane] Bokairo Inn",
- "[Kugane] The Ruby Bazaar",
- "[Kugane] Sekiseigumi Barracks",
- "[Kugane] Rakuza District",
- "[Kugane] The Ruby Price",
- "[Kugane] Airship Landing",
- "[Crystarium] Aetheryte Plaza",
- "[Crystarium] Musica Universalis Markets",
- "[Crystarium] Temenos Rookery",
- "[Crystarium] The Dossal Gate",
- "[Crystarium] The Pendants",
- "[Crystarium] The Amaro Launch",
- "[Crystarium] The Crystalline Mean",
- "[Crystarium] The Cabinet of Curiosity",
- "[Crystarium] Tessellation (Lakeland)",
- "[Eulmore] Aetheryte Plaza",
- "[Eulmore] Southeast Derelicts",
- "[Eulmore] Nightsoil Pots",
- "[Eulmore] The Glory Gate",
- "[Eulmore] The Mainstay",
- "[Eulmore] The Path to Glory (Kholusia)",
- "[Old Sharlayan] Aetheryte Plaza",
- "[Old Sharlayan] The Studium",
- "[Old Sharlayan] The Baldesion Annex",
- "[Old Sharlayan] The Rostra",
- "[Old Sharlayan] The Leveilleur Estate",
- "[Old Sharlayan] Journey's End",
- "[Old Sharlayan] Scholar's Harbor",
- "[Old Sharlayan] The Hall of Artifice (Labyrinthos)",
- "[Radz-at-Han] Aetheryte Plaza",
- "[Radz-at-Han] Meghaduta",
- "[Radz-at-Han] Ruveydah Fibers",
- "[Radz-at-Han] Airship Landing",
- "[Radz-at-Han] Alzadaal's Peace",
- "[Radz-at-Han] Hall of the Radiant Host",
- "[Radz-at-Han] Mehryde's Meyhane",
- "[Radz-at-Han] Kama",
- "[Radz-at-Han] The High Crucible of Al-Kimiya",
- "[Radz-at-Han] The Gate of First Sight (Thavnair)",
- "[Tuliyollal] Aetheryte Plaza",
- "[Tuliyollal] Dirigible Landing",
- "[Tuliyollal] The Resplendent Quarter",
- "[Tuliyollal] The For'ard Cabins",
- "[Tuliyollal] Bayside Bevy Marketplace",
- "[Tuliyollal] Vollok Shoonsa",
- "[Tuliyollal] Wachumeqimeqi",
- "[Tuliyollal] Brightploom Post",
- "[Tuliyollal] Arch of the Dawn (Urqopacha)",
- "[Tuliyollal] Arch of the Dawn (Kozama'uka)",
- "[Tuliyollal] Ihuykatumu (Kozama'uka)",
- "[Tuliyollal] Dirigible Landing (Yak T'el)",
- "[Tuliyollal] Xak Tural Skygate (Shaaloani)",
- "[Solution Nine] Aetheryte Plaza",
- "[Solution Nine] Information Center",
- "[Solution Nine] True Vue",
- "[Solution Nine] Neon Stein",
- "[Solution Nine] The Arcadion",
- "[Solution Nine] Resolution",
- "[Solution Nine] Nexus Arcade",
- "[Solution Nine] Residential Sector",
- "[Solution Nine] Scanning Port Nine (Heritage Found)"
- ]
- },
- "CompletionFlags": {
- "type": "array",
- "description": "Quest Variables that dictate whether or not this step is skipped: null is don't check, positive values need to be set, negative values need to be unset",
- "items": {
- "oneOf": [
- {
- "type": "object",
- "properties": {
- "High": {
- "type": [
- "number",
- "null"
- ],
- "minimum": 0,
- "maximum": 15
- },
- "Low": {
- "type": [
- "number",
- "null"
- ],
- "minimum": 0,
- "maximum": 15
- },
- "Negative": {
- "type": "boolean"
- },
- "Mode": {
- "type": "string",
- "enum": [
- "Bitwise",
- "Exact"
- ]
- }
- }
- },
- {
- "type": "number",
- "enum": [
- 1,
- 2,
- 4,
- 8,
- 16,
- 32,
- 64,
- 128
- ]
- },
- {
- "type": "null"
- }
- ]
- },
- "minItems": 6,
- "maxItems": 6
- }
- }
+ "additionalProperties": false
}
--- /dev/null
+using System.IO;
+
+namespace Questionable.Model;
+
+public static class AssemblyModelLoader
+{
+ public static Stream CommonSchema =>
+ typeof(AssemblyModelLoader).Assembly.GetManifestResourceStream("Questionable.Model.CommonSchema")!;
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Common.Converter;
+
+public sealed class AetheryteConverter() : EnumConverter<EAetheryteLocation>(Values)
+{
+ private static readonly Dictionary<EAetheryteLocation, string> Values = new()
+ {
+ { EAetheryteLocation.Gridania, "Gridania" },
+ { EAetheryteLocation.CentralShroudBentbranchMeadows, "Central Shroud - Bentbranch Meadows" },
+ { EAetheryteLocation.EastShroudHawthorneHut, "East Shroud - Hawthorne Hut" },
+ { EAetheryteLocation.SouthShroudQuarrymill, "South Shroud - Quarrymill" },
+ { EAetheryteLocation.SouthShroudCampTranquil, "South Shroud - Camp Tranquil" },
+ { EAetheryteLocation.NorthShroudFallgourdFloat, "North Shroud - Fallgourd Float" },
+
+ { EAetheryteLocation.Uldah, "Ul'dah" },
+ { EAetheryteLocation.WesternThanalanHorizon, "Western Thanalan - Horizon" },
+ { EAetheryteLocation.CentralThanalanBlackBrushStation, "Central Thanalan - Black Brush Station" },
+ { EAetheryteLocation.EasternThanalanCampDrybone, "Eastern Thanalan - Camp Drybone" },
+ { EAetheryteLocation.SouthernThanalanLittleAlaMhigo, "Southern Thanalan - Little Ala Mhigo" },
+ { EAetheryteLocation.SouthernThanalanForgottenSprings, "Southern Thanalan - Forgotten Springs" },
+ { EAetheryteLocation.NorthernThanalanCampBluefog, "Northern Thanalan - Camp Bluefog" },
+ { EAetheryteLocation.NorthernThanalanCeruleumProcessingPlant, "Northern Thanalan - Ceruleum Processing Plant" },
+
+ { EAetheryteLocation.Limsa, "Limsa Lominsa" },
+ { EAetheryteLocation.MiddleLaNosceaSummerfordFarms, "Middle La Noscea - Summerford Farms" },
+ { EAetheryteLocation.LowerLaNosceaMorabyDrydocks, "Lower La Noscea - Moraby Drydocks" },
+ { EAetheryteLocation.EasternLaNosceaCostaDelSol, "Eastern La Noscea - Costa Del Sol" },
+ { EAetheryteLocation.EasternLaNosceaWineport, "Eastern La Noscea - Wineport" },
+ { EAetheryteLocation.WesternLaNosceaSwiftperch, "Western La Noscea - Swiftperch" },
+ { EAetheryteLocation.WesternLaNosceaAleport, "Western La Noscea - Aleport" },
+ { EAetheryteLocation.UpperLaNosceaCampBronzeLake, "Upper La Noscea - Camp Bronze Lake" },
+ { EAetheryteLocation.OuterLaNosceaCampOverlook, "Outer La Noscea - Camp Overlook" },
+
+ { EAetheryteLocation.CoerthasCentralHighlandsCampDragonhead, "Coerthas Central Highlands - Camp Dragonhead" },
+ { EAetheryteLocation.MorDhona, "Mor Dhona" },
+ { EAetheryteLocation.GoldSaucer, "Gold Saucer" },
+ { EAetheryteLocation.WolvesDenPier, "Wolves' Den Pier" },
+
+ { EAetheryteLocation.Ishgard, "Ishgard" },
+ { EAetheryteLocation.Idyllshire, "Idyllshire" },
+ { EAetheryteLocation.CoerthasWesternHighlandsFalconsNest, "Coerthas Western Highlands - Falcon's Nest" },
+ { EAetheryteLocation.SeaOfCloudsCampCloudtop, "The Sea of Clouds - Camp Cloudtop" },
+ { EAetheryteLocation.SeaOfCloudsOkZundu, "The Sea of Clouds - Ok' Zundu" },
+ { EAetheryteLocation.AzysLlaHelix, "Azys Lla - Helix" },
+ { EAetheryteLocation.DravanianForelandsTailfeather, "The Dravanian Forelands - Tailfeather" },
+ { EAetheryteLocation.DravanianForelandsAnyxTrine, "The Dravanian Forelands - Anyx Trine" },
+ { EAetheryteLocation.ChurningMistsMoghome, "The Churning Mists - Moghome" },
+ { EAetheryteLocation.ChurningMistsZenith, "The Churning Mists - Zenith" },
+
+ { EAetheryteLocation.RhalgrsReach, "Rhalgr's Reach" },
+ { EAetheryteLocation.FringesCastrumOriens, "Fringes - Castrum Oriens" },
+ { EAetheryteLocation.FringesPeeringStones, "Fringes - Peering Stones" },
+ { EAetheryteLocation.PeaksAlaGannha, "Peaks - Ala Gannha" },
+ { EAetheryteLocation.PeaksAlaGhiri, "Peaks - Ala Ghiri" },
+ { EAetheryteLocation.LochsPortaPraetoria, "Lochs - Porta Praetoria" },
+ { EAetheryteLocation.LochsAlaMhiganQuarter, "Lochs - Ala Mhigan Quarter" },
+ { EAetheryteLocation.Kugane, "Kugane" },
+ { EAetheryteLocation.RubySeaTamamizu, "Ruby Sea - Tamamizu" },
+ { EAetheryteLocation.RubySeaOnokoro, "Ruby Sea - Onokoro" },
+ { EAetheryteLocation.YanxiaNamai, "Yanxia - Namai" },
+ { EAetheryteLocation.YanxiaHouseOfTheFierce, "Yanxia - House of the Fierce" },
+ { EAetheryteLocation.AzimSteppeReunion, "Azim Steppe - Reunion" },
+ { EAetheryteLocation.AzimSteppeDawnThrone, "Azim Steppe - Dawn Throne" },
+ { EAetheryteLocation.AzimSteppeDhoroIloh, "Azim Steppe - Dhoro Iloh" },
+ { EAetheryteLocation.DomanEnclave, "Doman Enclave" },
+
+ { EAetheryteLocation.Crystarium, "Crystarium" },
+ { EAetheryteLocation.Eulmore, "Eulmore" },
+ { EAetheryteLocation.LakelandFortJobb, "Lakeland - Fort Jobb" },
+ { EAetheryteLocation.LakelandOstallImperative, "Lakeland - Ostall Imperative" },
+ { EAetheryteLocation.KholusiaStilltide, "Kholusia - Stilltide" },
+ { EAetheryteLocation.KholusiaWright, "Kholusia - Wright" },
+ { EAetheryteLocation.KholusiaTomra, "Kholusia - Tomra" },
+ { EAetheryteLocation.AmhAraengMordSouq, "Amh Araeng - Mord Souq" },
+ { EAetheryteLocation.AmhAraengInnAtJourneysHead, "Amh Araeng - Inn at Journey's Head" },
+ { EAetheryteLocation.AmhAraengTwine, "Amh Araeng - Twine" },
+ { EAetheryteLocation.RaktikaSlitherbough, "Rak'tika - Slitherbough" },
+ { EAetheryteLocation.RaktikaFanow, "Rak'tika - Fanow" },
+ { EAetheryteLocation.IlMhegLydhaLran, "Il Mheg - Lydha Lran" },
+ { EAetheryteLocation.IlMhegPiaEnni, "Il Mheg - Pia Enni" },
+ { EAetheryteLocation.IlMhegWolekdorf, "Il Mheg - Wolekdorf" },
+ { EAetheryteLocation.TempestOndoCups, "Tempest - Ondo Cups" },
+ { EAetheryteLocation.TempestMacarensesAngle, "Tempest - Macarenses Angle" },
+
+ { EAetheryteLocation.OldSharlayan, "Old Sharlayan" },
+ { EAetheryteLocation.RadzAtHan, "Radz-at-Han" },
+ { EAetheryteLocation.LabyrinthosArcheion, "Labyrinthos - Archeion" },
+ { EAetheryteLocation.LabyrinthosSharlayanHamlet, "Labyrinthos - Sharlayan Hamlet" },
+ { EAetheryteLocation.LabyrinthosAporia, "Labyrinthos - Aporia" },
+ { EAetheryteLocation.ThavnairYedlihmad, "Thavnair - Yedlihmad" },
+ { EAetheryteLocation.ThavnairGreatWork, "Thavnair - Great Work" },
+ { EAetheryteLocation.ThavnairPalakasStand, "Thavnair - Palaka's Stand" },
+ { EAetheryteLocation.GarlemaldCampBrokenGlass, "Garlemald - Camp Broken Glass" },
+ { EAetheryteLocation.GarlemaldTertium, "Garlemald - Tertium" },
+ { EAetheryteLocation.MareLamentorumSinusLacrimarum, "Mare Lamentorum - Sinus Lacrimarum" },
+ { EAetheryteLocation.MareLamentorumBestwaysBurrow, "Mare Lamentorum - Bestways Burrow" },
+ { EAetheryteLocation.ElpisAnagnorisis, "Elpis - Anagnorisis" },
+ { EAetheryteLocation.ElpisTwelveWonders, "Elpis - Twelve Wonders" },
+ { EAetheryteLocation.ElpisPoietenOikos, "Elpis - Poieten Oikos" },
+ { EAetheryteLocation.UltimaThuleReahTahra, "Ultima Thule - Reah Tahra" },
+ { EAetheryteLocation.UltimaThuleAbodeOfTheEa, "Ultima Thule - Abode of the Ea" },
+ { EAetheryteLocation.UltimaThuleBaseOmicron, "Ultima Thule - Base Omicron" },
+
+ { EAetheryteLocation.Tuliyollal, "Tuliyollal" },
+ { EAetheryteLocation.SolutionNine, "Solution Nine" },
+ { EAetheryteLocation.UrqopachaWachunpelo, "Urqopacha - Wachunpelo" },
+ { EAetheryteLocation.UrqopachaWorlarsEcho, "Urqopacha - Worlar's Echo" },
+ { EAetheryteLocation.KozamaukaOkHanu, "Kozama'uka - Ok'hanu" },
+ { EAetheryteLocation.KozamaukaManyFires, "Kozama'uka - Many Fires" },
+ { EAetheryteLocation.KozamaukaEarthenshire, "Kozama'uka - Earthenshire" },
+ { EAetheryteLocation.YakTelIqBraax, "Yak T'el - Iq Br'aax" },
+ { EAetheryteLocation.YakTelMamook, "Yak T'el - Mamook" },
+ { EAetheryteLocation.ShaaloaniHhusatahwi, "Shaaloani - Hhusatahwi" },
+ { EAetheryteLocation.ShaaloaniShesheneweziSprings, "Shaaloani - Sheshenewezi Springs" },
+ { EAetheryteLocation.ShaaloaniMehwahhetsoan, "Shaaloani - Mehwahhetsoan" },
+ { EAetheryteLocation.HeritageFoundYyasulaniStation, "Heritage Found - Yyasulani Station" },
+ { EAetheryteLocation.HeritageFoundTheOutskirts, "Heritage Found - The Outskirts" },
+ { EAetheryteLocation.HeritageFoundElectropeStrike, "Heritage Found - Electrope Strike" },
+ { EAetheryteLocation.LivingMemoryLeynodeMnemo, "Living Memory - Leynode Mnemo" },
+ { EAetheryteLocation.LivingMemoryLeynodePyro, "Living Memory - Leynode Pyro" },
+ { EAetheryteLocation.LivingMemoryLeynodeAero, "Living Memory - Leynode Aero" },
+ };
+
+ public static bool IsLargeAetheryte(EAetheryteLocation aetheryte) => Values.ContainsKey(aetheryte);
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Questionable.Model.Common.Converter;
+
+public abstract class EnumConverter<T> : JsonConverter<T>
+ where T : Enum
+{
+ private readonly ReadOnlyDictionary<T, string> _enumToString;
+ private readonly ReadOnlyDictionary<string, T> _stringToEnum;
+
+ protected EnumConverter(IReadOnlyDictionary<T, string> values)
+ {
+ _enumToString = values is IDictionary<T, string> dict
+ ? new ReadOnlyDictionary<T, string>(dict)
+ : new ReadOnlyDictionary<T, string>(values.ToDictionary(x => x.Key, x => x.Value));
+ _stringToEnum = new ReadOnlyDictionary<string, T>(_enumToString.ToDictionary(x => x.Value, x => x.Key));
+ }
+
+ public override T? Read(ref Utf8JsonReader reader, Type typeToConvert,
+ JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.String)
+ throw new JsonException();
+
+ string? str = reader.GetString();
+ if (str == null)
+ throw new JsonException();
+
+ return _stringToEnum.TryGetValue(str, out T? value) ? value : throw new JsonException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(_enumToString[value]);
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Questionable.Model.Common.Converter;
+
+public sealed class StringListOrValueConverter : JsonConverter<List<string>>
+{
+ public override List<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.String)
+ return [reader.GetString()!];
+
+ if (reader.TokenType != JsonTokenType.StartArray)
+ throw new JsonException();
+ reader.Read();
+
+ List<string> value = [];
+ while (reader.TokenType != JsonTokenType.EndArray)
+ {
+ value.Add(reader.GetString()!);
+ reader.Read();
+ }
+
+ return value;
+ }
+
+ public override void Write(Utf8JsonWriter writer, List<string>? value, JsonSerializerOptions options)
+ {
+ if (value == null)
+ writer.WriteNullValue();
+ else if (value.Count == 1)
+ writer.WriteStringValue(value[0]);
+ else
+ {
+ writer.WriteStartArray();
+ foreach (var v in value)
+ writer.WriteStringValue(v);
+ writer.WriteEndArray();
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Numerics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Questionable.Model.Common.Converter;
+
+public sealed class VectorConverter : JsonConverter<Vector3>
+{
+ public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.StartObject)
+ throw new JsonException();
+
+ Vector3 vec = new Vector3();
+ while (reader.Read())
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.PropertyName:
+ string? propertyName = reader.GetString();
+ if (propertyName == null || !reader.Read())
+ throw new JsonException();
+
+ switch (propertyName)
+ {
+ case nameof(Vector3.X):
+ vec.X = reader.GetSingle();
+ break;
+
+ case nameof(Vector3.Y):
+ vec.Y = reader.GetSingle();
+ break;
+
+ case nameof(Vector3.Z):
+ vec.Z = reader.GetSingle();
+ break;
+
+ default:
+ throw new JsonException();
+ }
+
+ break;
+
+ case JsonTokenType.EndObject:
+ return vec;
+
+ default:
+ throw new JsonException();
+ }
+ }
+
+ throw new JsonException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+ writer.WriteNumber(nameof(Vector3.X), value.X);
+ writer.WriteNumber(nameof(Vector3.Y), value.X);
+ writer.WriteNumber(nameof(Vector3.Z), value.X);
+ writer.WriteEndObject();
+ }
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Common.Converter;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Common;
+
+[JsonConverter(typeof(AetheryteConverter))]
+public enum EAetheryteLocation
+{
+ None = 0,
+
+ Gridania = 2,
+ GridaniaArcher = 25,
+ GridaniaLeatherworker = 26,
+ GridaniaLancer = 27,
+ GridaniaConjurer = 28,
+ GridaniaBotanist = 29,
+ GridaniaAmphitheatre = 30,
+ GridaniaBlueBadgerGate = 31,
+ GridaniaYellowSerpentGate = 32,
+ GridaniaWhiteWolfGate = 54,
+ GridaniaAirship = 94,
+
+ CentralShroudBentbranchMeadows = 3,
+ EastShroudHawthorneHut = 4,
+ SouthShroudQuarrymill = 5,
+ SouthShroudCampTranquil = 6,
+ NorthShroudFallgourdFloat = 7,
+
+ Uldah = 9,
+ UldahAdventurers = 33,
+ UldahThaumaturge = 34,
+ UldahGladiator = 35,
+ UldahMiner = 36,
+ UldahAlchemist = 37,
+ UldahWeaver = 47,
+ UldahGoldsmith = 50,
+ UldahChamberOfRule = 51,
+ UldahAirship = 95,
+ UldahGateOfTheSultana = 38,
+ UldahGateOfNald = 39,
+ UldahGateOfThal = 40,
+ UldahSapphireAvenue = 125,
+
+ WesternThanalanHorizon = 17,
+ EasternThanalanCampDrybone = 18,
+ SouthernThanalanLittleAlaMhigo = 19,
+ SouthernThanalanForgottenSprings = 20,
+ NorthernThanalanCampBluefog = 21,
+ NorthernThanalanCeruleumProcessingPlant = 22,
+ CentralThanalanBlackBrushStation = 53,
+
+ Limsa = 8,
+ LimsaAftcastle = 41,
+ LimsaCulinarian = 42,
+ LimsaArcanist = 43,
+ LimsaFisher = 44,
+ LimsaMarauder = 48,
+ LimsaHawkersAlley = 49,
+ LimsaZephyrGate = 45,
+ LimsaTempestGate = 46,
+ LimsaAirship = 93,
+
+ LowerLaNosceaMorabyDrydocks = 10,
+ EasternLaNosceaCostaDelSol = 11,
+ EasternLaNosceaWineport = 12,
+ WesternLaNosceaSwiftperch = 13,
+ WesternLaNosceaAleport = 14,
+ UpperLaNosceaCampBronzeLake = 15,
+ OuterLaNosceaCampOverlook = 16,
+ MiddleLaNosceaSummerfordFarms = 52,
+
+ CoerthasCentralHighlandsCampDragonhead = 23,
+ MorDhona = 24,
+ GoldSaucer = 62,
+ WolvesDenPier = 55,
+
+ Ishgard = 70,
+ IshgardForgottenKnight = 80,
+ IshgardSkysteelManufactory = 81,
+ IshgardBrume = 82,
+ IshgardAthenaeumAstrologicum = 83,
+ IshgardJeweledCrozier = 84,
+ IshgardSaintReymanaudsCathedral = 85,
+ IshgardTribunal = 86,
+ IshgardLastVigil = 87,
+ IshgardGatesOfJudgement = 88,
+
+ Idyllshire = 75,
+ IdyllshireWest = 90,
+ IdyllshirePrologueGate = 91,
+ IdyllshireEpilogueGate = 92,
+
+ CoerthasWesternHighlandsFalconsNest = 71,
+ SeaOfCloudsCampCloudtop = 72,
+ SeaOfCloudsOkZundu = 73,
+ AzysLlaHelix = 74,
+ DravanianForelandsTailfeather = 76,
+ DravanianForelandsAnyxTrine = 77,
+ ChurningMistsMoghome = 78,
+ ChurningMistsZenith = 79,
+
+ RhalgrsReach = 104,
+ RhalgrsReachWest = 121,
+ RhalgrsReachNorthEast = 122,
+ RhalgrsReachFringesGate = 123,
+ RhalgrsReachPeaksGate = 124,
+
+ Kugane = 111,
+ KuganeShiokazeHostelry = 112,
+ KuganePier1 = 113,
+ KuganeThavnairianConsulate = 114,
+ KuganeMarkets = 115,
+ KuganeBokairoInn = 116,
+ KuganeRubyBazaar = 117,
+ KuganeSekiseigumiBarracks = 118,
+ KuganeRakuzaDistrict = 119,
+ KuganeRubyPrice = 120,
+ KuganeAirship = 126,
+
+ FringesCastrumOriens = 98,
+ FringesPeeringStones = 99,
+ PeaksAlaGannha = 100,
+ PeaksAlaGhiri = 101,
+ LochsPortaPraetoria = 102,
+ LochsAlaMhiganQuarter = 103,
+ RubySeaTamamizu = 105,
+ RubySeaOnokoro = 106,
+ YanxiaNamai = 107,
+ YanxiaHouseOfTheFierce = 108,
+ AzimSteppeReunion = 109,
+ AzimSteppeDawnThrone = 110,
+ AzimSteppeDhoroIloh = 128,
+
+ DomanEnclave = 127,
+ DomanEnclaveNorthern = 129,
+ DomanEnclaveSouthern = 130,
+ DomanEnclaveOneRiver = 131,
+ DomanEnclaveDocks = 162,
+
+ Crystarium = 133,
+ CrystariumMarkets = 149,
+ CrystariumTemenosRookery = 150,
+ CrystariumDossalGate = 151,
+ CrystariumPendants = 152,
+ CrystariumAmaroLaunch = 153,
+ CrystariumCrystallineMean = 154,
+ CrystariumCabinetOfCuriosity = 155,
+ CrystariumTessellation = 156,
+
+ Eulmore = 134,
+ EulmoreMainstay = 157,
+ EulmoreNightsoilPots = 158,
+ EulmoreGloryGate = 159,
+ EulmoreSoutheastDerelict = 135,
+ EulmorePathToGlory = 160,
+
+ LakelandFortJobb = 132,
+ LakelandOstallImperative = 136,
+ KholusiaStilltide = 137,
+ KholusiaWright = 138,
+ KholusiaTomra = 139,
+ AmhAraengMordSouq = 140,
+ AmhAraengInnAtJourneysHead = 161,
+ AmhAraengTwine = 141,
+ RaktikaSlitherbough = 142,
+ RaktikaFanow = 143,
+ IlMhegLydhaLran = 144,
+ IlMhegPiaEnni = 145,
+ IlMhegWolekdorf = 146,
+ TempestOndoCups = 147,
+ TempestMacarensesAngle = 148,
+
+ OldSharlayan = 182,
+ OldSharlayanStudium = 184,
+ OldSharlayanBaldesionAnnex = 185,
+ OldSharlayanRostra = 186,
+ OldSharlayanLeveilleurEstate = 187,
+ OldSharlayanJourneysEnd = 188,
+ OldSharlayanScholarsHarbor = 189,
+ OldSharlayanHallOfArtifice = 190,
+
+ RadzAtHan = 183,
+ RadzAtHanMeghaduta = 191,
+ RadzAtHanRuveydahFibers = 192,
+ RadzAtHanAirship = 193,
+ RadzAtHanAlzadaalsPeace = 194,
+ RadzAtHanHallOfTheRadiantHost = 195,
+ RadzAtHanMehrydesMeyhane = 196,
+ RadzAtHanKama = 198,
+ RadzAtHanHighCrucible = 199,
+ RadzAtHanGateOfFirstSight = 197,
+
+ LabyrinthosArcheion = 166,
+ LabyrinthosSharlayanHamlet = 167,
+ LabyrinthosAporia = 168,
+ ThavnairYedlihmad = 169,
+ ThavnairGreatWork = 170,
+ ThavnairPalakasStand = 171,
+ GarlemaldCampBrokenGlass = 172,
+ GarlemaldTertium = 173,
+ MareLamentorumSinusLacrimarum = 174,
+ MareLamentorumBestwaysBurrow = 175,
+ ElpisAnagnorisis = 176,
+ ElpisTwelveWonders = 177,
+ ElpisPoietenOikos = 178,
+ UltimaThuleReahTahra = 179,
+ UltimaThuleAbodeOfTheEa = 180,
+ UltimaThuleBaseOmicron = 181,
+
+ Tuliyollal = 216,
+
+ TuliyollalDirigibleLanding = 218,
+ TuliyollalTheResplendentQuarter = 219,
+ TuliyollalTheForardCabins = 220,
+ TuliyollalBaysideBevyMarketplace = 221,
+ TuliyollalVollokShoonsa = 222,
+ TuliyollalWachumeqimeqi = 223,
+ TuliyollalBrightploomPost = 224,
+ TuliyollalArchOfTheDawnUrqopacha = 225,
+ TuliyollalArchOfTheDawnKozamauka = 226,
+ TuliyollalIhuykatumu = 227,
+ TuliyollalDirigibleLandingYakTel = 228,
+ TuliyollalXakTuralSkygate = 229,
+
+ SolutionNine = 217,
+ SolutionNineInformationCenter = 230,
+ SolutionNineTrueVue = 231,
+ SolutionNineNeonStein = 232,
+ SolutionNineTheArcadion = 233,
+ SolutionNineResolution = 234,
+ SolutionNineNexusArcade = 235,
+ SolutionNineResidentialSector = 236,
+ SolutionNineScanningPortNine = 237,
+
+ UrqopachaWachunpelo = 200,
+ UrqopachaWorlarsEcho = 201,
+ KozamaukaOkHanu = 202,
+ KozamaukaManyFires = 203,
+ KozamaukaEarthenshire = 204,
+ YakTelIqBraax = 205,
+ YakTelMamook = 206,
+ ShaaloaniHhusatahwi = 207,
+ ShaaloaniShesheneweziSprings = 208,
+ ShaaloaniMehwahhetsoan = 209,
+ HeritageFoundYyasulaniStation = 210,
+ HeritageFoundTheOutskirts = 211,
+ HeritageFoundElectropeStrike = 212,
+ LivingMemoryLeynodeMnemo = 213,
+ LivingMemoryLeynodePyro = 214,
+ LivingMemoryLeynodeAero = 215,
+}
--- /dev/null
+using System.Numerics;
+
+namespace Questionable.Model.Gathering;
+
+public sealed class GatheringNodeLocation
+{
+ public uint DataId { get; set; }
+ public Vector3 Position { get; set; }
+ public float? MinimumAngle { get; set; }
+ public float? MaximumAngle { get; set; }
+ public float? MinimumDistance { get; set; } = 0.5f;
+ public float? MaximumDistance { get; set; } = 3f;
+}
--- /dev/null
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using Questionable.Model.Common;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Gathering;
+
+public sealed class GatheringRoot
+{
+ [JsonConverter(typeof(StringListOrValueConverter))]
+ public List<string> Author { get; set; } = [];
+ public ushort TerritoryId { get; set; }
+
+ [JsonConverter(typeof(AetheryteConverter))]
+ public EAetheryteLocation? AetheryteShortcut { get; set; }
+
+ public List<GatheringNodeLocation> Nodes { get; set; } = [];
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Common;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(AethernetShortcutConverter))]
+public sealed class AethernetShortcut
+{
+ public EAetheryteLocation From { get; set; }
+ public EAetheryteLocation To { get; set; }
+}
--- /dev/null
+namespace Questionable.Model.Questing;
+
+public sealed class ChatMessage
+{
+ public string? ExcelSheet { get; set; }
+ public string Key { get; set; } = null!;
+}
--- /dev/null
+using System.Collections.Generic;
+
+namespace Questionable.Model.Questing;
+
+public sealed class ComplexCombatData
+{
+ public uint DataId { get; set; }
+
+ // TODO Use this
+ public uint? MinimumKillCount { get; set; }
+
+ /// <summary>
+ /// If a reward item has been set, this is (ping allowing) given to the player before the dead enemy despawns.
+ /// </summary>
+ public uint? RewardItemId { get; set; }
+ public int? RewardItemCount { get; set; }
+ public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
+ public bool IgnoreQuestMarker { get; set; }
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class ActionConverter() : EnumConverter<EAction>(Values)
+{
+ private static readonly Dictionary<EAction, string> Values = new()
+ {
+ { EAction.Cure, "Cure" },
+ { EAction.Esuna, "Esuna" },
+ { EAction.Physick, "Physick" },
+ { EAction.Buffet, "Buffet" },
+ { EAction.Fumigate, "Fumigate" },
+ { EAction.SiphonSnout, "Siphon Snout" },
+ { EAction.RedGulal, "Red Gulal" },
+ { EAction.YellowGulal, "Yellow Gulal" },
+ { EAction.BlueGulal, "Blue Gulal" },
+ };
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class AethernetShardConverter() : EnumConverter<EAetheryteLocation>(Values)
+{
+ public static readonly Dictionary<EAetheryteLocation, string> Values = new()
+ {
+ { EAetheryteLocation.Gridania, "[Gridania] Aetheryte Plaza" },
+ { EAetheryteLocation.GridaniaArcher, "[Gridania] Archers' Guild" },
+ { EAetheryteLocation.GridaniaLeatherworker, "[Gridania] Leatherworkers' Guild & Shaded Bower" },
+ { EAetheryteLocation.GridaniaLancer, "[Gridania] Lancers' Guild" },
+ { EAetheryteLocation.GridaniaConjurer, "[Gridania] Conjurers' Guild" },
+ { EAetheryteLocation.GridaniaBotanist, "[Gridania] Botanists' Guild" },
+ { EAetheryteLocation.GridaniaAmphitheatre, "[Gridania] Mih Khetto's Amphitheatre" },
+ { EAetheryteLocation.GridaniaBlueBadgerGate, "[Gridania] Blue Badger Gate (Central Shroud)" },
+ { EAetheryteLocation.GridaniaYellowSerpentGate, "[Gridania] Yellow Serpent Gate (North Shroud)" },
+ { EAetheryteLocation.GridaniaWhiteWolfGate, "[Gridania] White Wolf Gate (Central Shroud)" },
+ { EAetheryteLocation.GridaniaAirship, "[Gridania] Airship Landing" },
+
+ { EAetheryteLocation.Uldah, "[Ul'dah] Aetheryte Plaza" },
+ { EAetheryteLocation.UldahAdventurers, "[Ul'dah] Adventurers' Guild" },
+ { EAetheryteLocation.UldahThaumaturge, "[Ul'dah] Thaumaturges' Guild" },
+ { EAetheryteLocation.UldahGladiator, "[Ul'dah] Gladiators' Guild" },
+ { EAetheryteLocation.UldahMiner, "[Ul'dah] Miners' Guild" },
+ { EAetheryteLocation.UldahWeaver, "[Ul'dah] Weavers' Guild" },
+ { EAetheryteLocation.UldahGoldsmith, "[Ul'dah] Goldsmiths' Guild" },
+ { EAetheryteLocation.UldahSapphireAvenue, "[Ul'dah] Sapphire Avenue Exchange" },
+ { EAetheryteLocation.UldahAlchemist, "[Ul'dah] Alchemists' Guild" },
+ { EAetheryteLocation.UldahChamberOfRule, "[Ul'dah] The Chamber of Rule" },
+ { EAetheryteLocation.UldahGateOfTheSultana, "[Ul'dah] Gate of the Sultana (Western Thanalan)" },
+ { EAetheryteLocation.UldahGateOfNald, "[Ul'dah] Gate of Nald (Central Thanalan)" },
+ { EAetheryteLocation.UldahGateOfThal, "[Ul'dah] Gate of Thal (Central Thanalan)" },
+ { EAetheryteLocation.UldahAirship, "[Ul'dah] Airship Landing" },
+
+ { EAetheryteLocation.Limsa, "[Limsa Lominsa] Aetheryte Plaza" },
+ { EAetheryteLocation.LimsaArcanist, "[Limsa Lominsa] Arcanists' Guild" },
+ { EAetheryteLocation.LimsaFisher, "[Limsa Lominsa] Fishermens' Guild" },
+ { EAetheryteLocation.LimsaHawkersAlley, "[Limsa Lominsa] Hawkers' Alley" },
+ { EAetheryteLocation.LimsaAftcastle, "[Limsa Lominsa] The Aftcastle" },
+ { EAetheryteLocation.LimsaCulinarian, "[Limsa Lominsa] Culinarians' Guild" },
+ { EAetheryteLocation.LimsaMarauder, "[Limsa Lominsa] Marauders' Guild" },
+ { EAetheryteLocation.LimsaZephyrGate, "[Limsa Lominsa] Zephyr Gate (Middle La Noscea)" },
+ { EAetheryteLocation.LimsaTempestGate, "[Limsa Lominsa] Tempest Gate (Lower La Noscea)" },
+ { EAetheryteLocation.LimsaAirship, "[Limsa Lominsa] Airship Landing" },
+
+ { EAetheryteLocation.Ishgard, "[Ishgard] Aetheryte Plaza" },
+ { EAetheryteLocation.IshgardForgottenKnight, "[Ishgard] The Forgotten Knight" },
+ { EAetheryteLocation.IshgardSkysteelManufactory, "[Ishgard] Skysteel Manufactory" },
+ { EAetheryteLocation.IshgardBrume, "[Ishgard] The Brume" },
+ { EAetheryteLocation.IshgardAthenaeumAstrologicum, "[Ishgard] Athenaeum Astrologicum" },
+ { EAetheryteLocation.IshgardJeweledCrozier, "[Ishgard] The Jeweled Crozier" },
+ { EAetheryteLocation.IshgardSaintReymanaudsCathedral, "[Ishgard] Saint Reymanaud's Cathedral" },
+ { EAetheryteLocation.IshgardTribunal, "[Ishgard] The Tribunal" },
+ { EAetheryteLocation.IshgardLastVigil, "[Ishgard] The Last Vigil" },
+ { EAetheryteLocation.IshgardGatesOfJudgement, "[Ishgard] The Gates of Judgement (Coerthas Central Highlands)" },
+
+ { EAetheryteLocation.Idyllshire, "[Idyllshire] Aetheryte Plaza" },
+ { EAetheryteLocation.IdyllshireWest, "[Idyllshire] West Idyllshire" },
+ { EAetheryteLocation.IdyllshirePrologueGate, "[Idyllshire] Prologue Gate (Western Hinterlands)" },
+ { EAetheryteLocation.IdyllshireEpilogueGate, "[Idyllshire] Epilogue Gate (Eastern Hinterlands)" },
+
+ { EAetheryteLocation.RhalgrsReach, "[Rhalgr's Reach] Aetheryte Plaza" },
+ { EAetheryteLocation.RhalgrsReachWest, "[Rhalgr's Reach] Western Rhalgr's Reach" },
+ { EAetheryteLocation.RhalgrsReachNorthEast, "[Rhalgr's Reach] Northeastern Rhalgr's Reach" },
+ { EAetheryteLocation.RhalgrsReachFringesGate, "[Rhalgr's Reach] Fringes Gate" },
+ { EAetheryteLocation.RhalgrsReachPeaksGate, "[Rhalgr's Reach] Peaks Gate" },
+
+ { EAetheryteLocation.Kugane, "[Kugane] Aetheryte Plaza" },
+ { EAetheryteLocation.KuganeShiokazeHostelry, "[Kugane] Shiokaze Hostelry" },
+ { EAetheryteLocation.KuganePier1, "[Kugane] Pier #1" },
+ { EAetheryteLocation.KuganeThavnairianConsulate, "[Kugane] Thavnairian Consulate" },
+ { EAetheryteLocation.KuganeMarkets, "[Kugane] Kogane Dori Markets" },
+ { EAetheryteLocation.KuganeBokairoInn, "[Kugane] Bokairo Inn" },
+ { EAetheryteLocation.KuganeRubyBazaar, "[Kugane] The Ruby Bazaar" },
+ { EAetheryteLocation.KuganeSekiseigumiBarracks, "[Kugane] Sekiseigumi Barracks" },
+ { EAetheryteLocation.KuganeRakuzaDistrict, "[Kugane] Rakuza District" },
+ { EAetheryteLocation.KuganeRubyPrice, "[Kugane] The Ruby Price" },
+ { EAetheryteLocation.KuganeAirship, "[Kugane] Airship Landing" },
+
+ { EAetheryteLocation.Crystarium, "[Crystarium] Aetheryte Plaza" },
+ { EAetheryteLocation.CrystariumMarkets, "[Crystarium] Musica Universalis Markets" },
+ { EAetheryteLocation.CrystariumTemenosRookery, "[Crystarium] Temenos Rookery" },
+ { EAetheryteLocation.CrystariumDossalGate, "[Crystarium] The Dossal Gate" },
+ { EAetheryteLocation.CrystariumPendants, "[Crystarium] The Pendants" },
+ { EAetheryteLocation.CrystariumAmaroLaunch, "[Crystarium] The Amaro Launch" },
+ { EAetheryteLocation.CrystariumCrystallineMean, "[Crystarium] The Crystalline Mean" },
+ { EAetheryteLocation.CrystariumCabinetOfCuriosity, "[Crystarium] The Cabinet of Curiosity" },
+ { EAetheryteLocation.CrystariumTessellation, "[Crystarium] Tessellation (Lakeland)" },
+
+ { EAetheryteLocation.Eulmore, "[Eulmore] Aetheryte Plaza" },
+ { EAetheryteLocation.EulmoreSoutheastDerelict, "[Eulmore] Southeast Derelicts" },
+ { EAetheryteLocation.EulmoreNightsoilPots, "[Eulmore] Nightsoil Pots" },
+ { EAetheryteLocation.EulmoreGloryGate, "[Eulmore] The Glory Gate" },
+ { EAetheryteLocation.EulmoreMainstay, "[Eulmore] The Mainstay" },
+ { EAetheryteLocation.EulmorePathToGlory, "[Eulmore] The Path to Glory (Kholusia)" },
+
+ { EAetheryteLocation.OldSharlayan, "[Old Sharlayan] Aetheryte Plaza" },
+ { EAetheryteLocation.OldSharlayanStudium, "[Old Sharlayan] The Studium" },
+ { EAetheryteLocation.OldSharlayanBaldesionAnnex, "[Old Sharlayan] The Baldesion Annex" },
+ { EAetheryteLocation.OldSharlayanRostra, "[Old Sharlayan] The Rostra" },
+ { EAetheryteLocation.OldSharlayanLeveilleurEstate, "[Old Sharlayan] The Leveilleur Estate" },
+ { EAetheryteLocation.OldSharlayanJourneysEnd, "[Old Sharlayan] Journey's End" },
+ { EAetheryteLocation.OldSharlayanScholarsHarbor, "[Old Sharlayan] Scholar's Harbor" },
+ { EAetheryteLocation.OldSharlayanHallOfArtifice, "[Old Sharlayan] The Hall of Artifice (Labyrinthos)" },
+
+ { EAetheryteLocation.RadzAtHan, "[Radz-at-Han] Aetheryte Plaza" },
+ { EAetheryteLocation.RadzAtHanMeghaduta, "[Radz-at-Han] Meghaduta" },
+ { EAetheryteLocation.RadzAtHanRuveydahFibers, "[Radz-at-Han] Ruveydah Fibers" },
+ { EAetheryteLocation.RadzAtHanAirship, "[Radz-at-Han] Airship Landing" },
+ { EAetheryteLocation.RadzAtHanAlzadaalsPeace, "[Radz-at-Han] Alzadaal's Peace" },
+ { EAetheryteLocation.RadzAtHanHallOfTheRadiantHost, "[Radz-at-Han] Hall of the Radiant Host" },
+ { EAetheryteLocation.RadzAtHanMehrydesMeyhane, "[Radz-at-Han] Mehryde's Meyhane" },
+ { EAetheryteLocation.RadzAtHanKama, "[Radz-at-Han] Kama" },
+ { EAetheryteLocation.RadzAtHanHighCrucible, "[Radz-at-Han] The High Crucible of Al-Kimiya" },
+ { EAetheryteLocation.RadzAtHanGateOfFirstSight, "[Radz-at-Han] The Gate of First Sight (Thavnair)" },
+
+ { EAetheryteLocation.Tuliyollal, "[Tuliyollal] Aetheryte Plaza" },
+ { EAetheryteLocation.TuliyollalDirigibleLanding, "[Tuliyollal] Dirigible Landing" },
+ { EAetheryteLocation.TuliyollalTheResplendentQuarter, "[Tuliyollal] The Resplendent Quarter" },
+ { EAetheryteLocation.TuliyollalTheForardCabins, "[Tuliyollal] The For'ard Cabins" },
+ { EAetheryteLocation.TuliyollalBaysideBevyMarketplace, "[Tuliyollal] Bayside Bevy Marketplace" },
+ { EAetheryteLocation.TuliyollalVollokShoonsa, "[Tuliyollal] Vollok Shoonsa" },
+ { EAetheryteLocation.TuliyollalWachumeqimeqi, "[Tuliyollal] Wachumeqimeqi" },
+ { EAetheryteLocation.TuliyollalBrightploomPost, "[Tuliyollal] Brightploom Post" },
+ { EAetheryteLocation.TuliyollalArchOfTheDawnUrqopacha, "[Tuliyollal] Arch of the Dawn (Urqopacha)" },
+ { EAetheryteLocation.TuliyollalArchOfTheDawnKozamauka, "[Tuliyollal] Arch of the Dawn (Kozama'uka)" },
+ { EAetheryteLocation.TuliyollalIhuykatumu, "[Tuliyollal] Ihuykatumu (Kozama'uka)" },
+ { EAetheryteLocation.TuliyollalDirigibleLandingYakTel, "[Tuliyollal] Dirigible Landing (Yak T'el)" },
+ { EAetheryteLocation.TuliyollalXakTuralSkygate, "[Tuliyollal] Xak Tural Skygate (Shaaloani)" },
+
+ { EAetheryteLocation.SolutionNine, "[Solution Nine] Aetheryte Plaza" },
+ { EAetheryteLocation.SolutionNineInformationCenter, "[Solution Nine] Information Center" },
+ { EAetheryteLocation.SolutionNineTrueVue, "[Solution Nine] True Vue" },
+ { EAetheryteLocation.SolutionNineNeonStein, "[Solution Nine] Neon Stein" },
+ { EAetheryteLocation.SolutionNineTheArcadion, "[Solution Nine] The Arcadion" },
+ { EAetheryteLocation.SolutionNineResolution, "[Solution Nine] Resolution" },
+ { EAetheryteLocation.SolutionNineNexusArcade, "[Solution Nine] Nexus Arcade" },
+ { EAetheryteLocation.SolutionNineResidentialSector, "[Solution Nine] Residential Sector" },
+ { EAetheryteLocation.SolutionNineScanningPortNine, "[Solution Nine] Scanning Port Nine (Heritage Found)" },
+ };
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Questionable.Model.Common;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class AethernetShortcutConverter : JsonConverter<AethernetShortcut>
+{
+ private static readonly Dictionary<EAetheryteLocation, string> EnumToString = AethernetShardConverter.Values;
+ private static readonly Dictionary<string, EAetheryteLocation> StringToEnum =
+ EnumToString.ToDictionary(x => x.Value, x => x.Key);
+
+ public override AethernetShortcut Read(ref Utf8JsonReader reader, Type typeToConvert,
+ JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.StartArray)
+ throw new JsonException();
+
+ if (!reader.Read() || reader.TokenType != JsonTokenType.String)
+ throw new JsonException();
+
+ string from = reader.GetString() ?? throw new JsonException();
+
+ if (!reader.Read() || reader.TokenType != JsonTokenType.String)
+ throw new JsonException();
+
+ string to = reader.GetString() ?? throw new JsonException();
+
+ if (!reader.Read() || reader.TokenType != JsonTokenType.EndArray)
+ throw new JsonException();
+
+ return new AethernetShortcut
+ {
+ From = StringToEnum.TryGetValue(from, out var fromEnum) ? fromEnum : throw new JsonException(),
+ To = StringToEnum.TryGetValue(to, out var toEnum) ? toEnum : throw new JsonException()
+ };
+ }
+
+ public override void Write(Utf8JsonWriter writer, AethernetShortcut value, JsonSerializerOptions options)
+ {
+ writer.WriteStartArray();
+ writer.WriteStringValue(EnumToString[value.From]);
+ writer.WriteStringValue(EnumToString[value.To]);
+ writer.WriteEndArray();
+ }
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class DialogueChoiceTypeConverter() : EnumConverter<EDialogChoiceType>(Values)
+{
+ private static readonly Dictionary<EDialogChoiceType, string> Values = new()
+ {
+ { EDialogChoiceType.YesNo, "YesNo" },
+ { EDialogChoiceType.List, "List" },
+ };
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class EmoteConverter() : EnumConverter<EEmote>(Values)
+{
+ private static readonly Dictionary<EEmote, string> Values = new()
+ {
+ { EEmote.Stretch, "stretch" },
+ { EEmote.Wave, "wave" },
+ { EEmote.Rally, "rally" },
+ { EEmote.Deny, "deny" },
+ { EEmote.Pray, "pray" },
+ { EEmote.Slap, "slap" },
+ { EEmote.Doubt, "doubt" },
+ { EEmote.Psych, "psych" },
+ { EEmote.Cheer, "cheer" },
+ { EEmote.Happy, "happy" },
+ { EEmote.Poke, "poke" },
+ { EEmote.Flex, "flex" },
+ { EEmote.Soothe, "soothe" },
+ { EEmote.Me, "me" },
+ { EEmote.Welcome, "welcome" },
+ { EEmote.ImperialSalute, "imperialsalute" },
+ { EEmote.Pet, "pet" },
+ { EEmote.Dance, "dance" },
+ { EEmote.Respect, "respect" },
+ { EEmote.Lookout, "lookout" },
+ { EEmote.Kneel, "kneel" },
+ { EEmote.Bow, "bow" },
+ };
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class EnemySpawnTypeConverter() : EnumConverter<EEnemySpawnType>(Values)
+{
+ private static readonly Dictionary<EEnemySpawnType, string> Values = new()
+ {
+ { EEnemySpawnType.AfterInteraction, "AfterInteraction" },
+ { EEnemySpawnType.AfterItemUse, "AfterItemUse" },
+ { EEnemySpawnType.AutoOnEnterArea, "AutoOnEnterArea" },
+ { EEnemySpawnType.OverworldEnemies, "OverworldEnemies" },
+ };
+}
--- /dev/null
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class ExcelRefConverter : JsonConverter<ExcelRef>
+{
+ public override ExcelRef? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return reader.TokenType switch
+ {
+ JsonTokenType.String => ExcelRef.FromKey(reader.GetString()!),
+ JsonTokenType.Number => ExcelRef.FromRowId(reader.GetUInt32()),
+ _ => null
+ };
+ }
+
+ public override void Write(Utf8JsonWriter writer, ExcelRef? value, JsonSerializerOptions options)
+ {
+ if (value == null)
+ writer.WriteNullValue();
+ else if (value.Type == ExcelRef.EType.Key)
+ writer.WriteStringValue(value.AsKey());
+ else if (value.Type == ExcelRef.EType.RowId)
+ writer.WriteNumberValue(value.AsRowId());
+ else
+ throw new JsonException();
+ }
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class InteractionTypeConverter() : EnumConverter<EInteractionType>(Values)
+{
+ private static readonly Dictionary<EInteractionType, string> Values = new()
+ {
+ { EInteractionType.Interact, "Interact" },
+ { EInteractionType.WalkTo, "WalkTo" },
+ { EInteractionType.AttuneAethernetShard, "AttuneAethernetShard" },
+ { EInteractionType.AttuneAetheryte, "AttuneAetheryte" },
+ { EInteractionType.AttuneAetherCurrent, "AttuneAetherCurrent" },
+ { EInteractionType.Combat, "Combat" },
+ { EInteractionType.UseItem, "UseItem" },
+ { EInteractionType.EquipItem, "EquipItem" },
+ { EInteractionType.Say, "Say" },
+ { EInteractionType.Emote, "Emote" },
+ { EInteractionType.Action, "Action" },
+ { EInteractionType.WaitForObjectAtPosition, "WaitForNpcAtPosition" },
+ { EInteractionType.WaitForManualProgress, "WaitForManualProgress" },
+ { EInteractionType.Duty, "Duty" },
+ { EInteractionType.SinglePlayerDuty, "SinglePlayerDuty" },
+ { EInteractionType.Jump, "Jump" },
+ { EInteractionType.Dive, "Dive" },
+ { EInteractionType.Instruction, "Instruction" },
+ { EInteractionType.AcceptQuest, "AcceptQuest" },
+ { EInteractionType.CompleteQuest, "CompleteQuest" },
+ };
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class JumpTypeConverter() : EnumConverter<EJumpType>(Values)
+{
+ private static readonly Dictionary<EJumpType, string> Values = new()
+ {
+ { EJumpType.SingleJump, "SingleJump" },
+ { EJumpType.RepeatedJumps, "RepeatedJumps" },
+ };
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class LockedSkipConditionConverter() : EnumConverter<ELockedSkipCondition>(Values)
+{
+ private static readonly Dictionary<ELockedSkipCondition, string> Values = new()
+ {
+ { ELockedSkipCondition.Locked, "Locked" },
+ { ELockedSkipCondition.Unlocked, "Unlocked" },
+ };
+}
--- /dev/null
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class QuestWorkConfigConverter : JsonConverter<QuestWorkValue>
+{
+ public override QuestWorkValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.Number)
+ return new QuestWorkValue(reader.GetByte());
+
+ if (reader.TokenType != JsonTokenType.StartObject)
+ throw new JsonException();
+
+ byte? high = null, low = null;
+ EQuestWorkMode mode = EQuestWorkMode.Bitwise;
+ while (reader.Read())
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.PropertyName:
+ string? propertyName = reader.GetString();
+ if (propertyName == null || !reader.Read())
+ throw new JsonException();
+
+ switch (propertyName)
+ {
+ case nameof(QuestWorkValue.High):
+ high = reader.GetByte();
+ break;
+
+ case nameof(QuestWorkValue.Low):
+ low = reader.GetByte();
+ break;
+
+ case nameof(QuestWorkValue.Mode):
+ mode = new QuestWorkModeConverter().Read(ref reader, typeof(EQuestWorkMode), options);
+ break;
+
+ default:
+ throw new JsonException();
+ }
+
+ break;
+
+ case JsonTokenType.EndObject:
+ return new QuestWorkValue(high, low, mode);
+
+ default:
+ throw new JsonException();
+ }
+ }
+
+ throw new JsonException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, QuestWorkValue value, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class QuestWorkModeConverter() : EnumConverter<EQuestWorkMode>(Values)
+{
+ private static readonly Dictionary<EQuestWorkMode, string> Values = new()
+ {
+ { EQuestWorkMode.Bitwise, "Bitwise" },
+ { EQuestWorkMode.Exact, "Exact" },
+ };
+}
--- /dev/null
+using System.Collections.Generic;
+using Questionable.Model.Common.Converter;
+
+namespace Questionable.Model.Questing.Converter;
+
+public sealed class SkipConditionConverter() : EnumConverter<EExtraSkipCondition>(Values)
+{
+ private static readonly Dictionary<EExtraSkipCondition, string> Values = new()
+ {
+ { EExtraSkipCondition.WakingSandsMainArea, "WakingSandsMainArea" },
+ };
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+public sealed class DialogueChoice
+{
+ [JsonConverter(typeof(DialogueChoiceTypeConverter))]
+ public EDialogChoiceType Type { get; set; }
+ public string? ExcelSheet { get; set; }
+
+ [JsonConverter(typeof(ExcelRefConverter))]
+ public ExcelRef? Prompt { get; set; }
+
+ public bool Yes { get; set; } = true;
+
+ [JsonConverter(typeof(ExcelRefConverter))]
+ public ExcelRef? Answer { get; set; }
+
+ /// <summary>
+ /// If set, only applies when focusing the given target id.
+ /// </summary>
+ public uint? DataId { get; set; }
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(ActionConverter))]
+public enum EAction
+{
+ Cure = 120,
+ Esuna = 7568,
+ Physick = 190,
+ Buffet = 4931,
+ Fumigate = 5872,
+ SiphonSnout = 18187,
+ RedGulal = 29382,
+ YellowGulal = 29383,
+ BlueGulal = 29384,
+}
+
+public static class EActionExtensions
+{
+ public static bool RequiresMount(this EAction action)
+ {
+ return action
+ is EAction.Buffet
+ or EAction.Fumigate
+ or EAction.SiphonSnout
+ or EAction.RedGulal
+ or EAction.YellowGulal
+ or EAction.BlueGulal;
+ }
+}
--- /dev/null
+namespace Questionable.Model.Questing;
+
+public enum EDialogChoiceType
+{
+ None,
+ YesNo,
+ List,
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(EmoteConverter))]
+public enum EEmote
+{
+ None = 0,
+
+ Stretch = 37,
+ Wave = 16,
+ Rally = 34,
+ Deny = 25,
+ Pray = 58,
+ Slap = 111,
+ Doubt = 12,
+ Psych = 30,
+ Cheer = 6,
+ Happy = 48,
+ Poke = 28,
+ Flex = 139,
+ Soothe = 35,
+ Me = 23,
+ Welcome = 41,
+ ImperialSalute = 59,
+ Pet = 105,
+ Dance = 11,
+ Respect = 140,
+ Lookout = 22,
+ Kneel = 19,
+ Bow = 5,
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(EnemySpawnTypeConverter))]
+public enum EEnemySpawnType
+{
+ None = 0,
+ AfterInteraction,
+ AfterItemUse,
+ AutoOnEnterArea,
+ OverworldEnemies,
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(SkipConditionConverter))]
+public enum EExtraSkipCondition
+{
+ None,
+ WakingSandsMainArea,
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(InteractionTypeConverter))]
+public enum EInteractionType
+{
+ Interact,
+ WalkTo,
+ AttuneAethernetShard,
+ AttuneAetheryte,
+ AttuneAetherCurrent,
+ Combat,
+ UseItem,
+ EquipItem,
+ Say,
+ Emote,
+ Action,
+ WaitForObjectAtPosition,
+ WaitForManualProgress,
+ Duty,
+ SinglePlayerDuty,
+ Jump,
+ Dive,
+
+ /// <summary>
+ /// Needs to be manually continued.
+ /// </summary>
+ Instruction,
+
+ AcceptQuest,
+ CompleteQuest,
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(JumpTypeConverter))]
+public enum EJumpType
+{
+ SingleJump,
+ RepeatedJumps,
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(LockedSkipConditionConverter))]
+public enum ELockedSkipCondition
+{
+ Locked,
+ Unlocked,
+}
--- /dev/null
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(QuestWorkModeConverter))]
+public enum EQuestWorkMode
+{
+ Bitwise,
+ Exact,
+}
--- /dev/null
+using System;
+
+namespace Questionable.Model.Questing;
+
+public class ExcelRef
+{
+ private readonly string? _stringValue;
+ private readonly uint? _rowIdValue;
+
+ public ExcelRef(string value)
+ {
+ _stringValue = value;
+ _rowIdValue = null;
+ Type = EType.Key;
+ }
+
+ public ExcelRef(uint value)
+ {
+ _stringValue = null;
+ _rowIdValue = value;
+ Type = EType.RowId;
+ }
+
+ private ExcelRef(string? stringValue, uint? rowIdValue, EType type)
+ {
+ _stringValue = stringValue;
+ _rowIdValue = rowIdValue;
+ Type = type;
+ }
+
+ public static ExcelRef FromKey(string value) => new(value, null, EType.Key);
+ public static ExcelRef FromRowId(uint rowId) => new(null, rowId, EType.RowId);
+ public static ExcelRef FromSheetValue(string value) => new(value, null, EType.RawString);
+
+ public EType Type { get; }
+
+ public string AsKey()
+ {
+ if (Type != EType.Key)
+ throw new InvalidOperationException();
+
+ return _stringValue!;
+ }
+
+ public uint AsRowId()
+ {
+ if (Type != EType.RowId)
+ throw new InvalidOperationException();
+
+ return _rowIdValue!.Value;
+ }
+
+ public string AsRawString()
+ {
+ if (Type != EType.RawString)
+ throw new InvalidOperationException();
+
+ return _stringValue!;
+ }
+
+ public enum EType
+ {
+ None,
+ Key,
+ RowId,
+ RawString,
+ }
+}
--- /dev/null
+using System.Numerics;
+using System.Text.Json.Serialization;
+using Questionable.Model.Common.Converter;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+public sealed class JumpDestination
+{
+ [JsonConverter(typeof(VectorConverter))]
+ public Vector3 Position { get; set; }
+
+ public float? StopDistance { get; set; }
+ public float? DelaySeconds { get; set; }
+ public EJumpType Type { get; set; } = EJumpType.SingleJump;
+
+ public float CalculateStopDistance() => StopDistance ?? 1f;
+}
--- /dev/null
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using Questionable.Model.Common.Converter;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+public sealed class QuestRoot
+{
+ [JsonConverter(typeof(StringListOrValueConverter))]
+ public List<string> Author { get; set; } = new();
+
+ /// <summary>
+ /// This is only relevant for release builds.
+ /// </summary>
+ public bool Disabled { get; set; }
+
+ public string? Comment { get; set; }
+ public List<ushort> TerritoryBlacklist { get; set; } = new();
+ public List<QuestSequence> QuestSequence { get; set; } = new();
+}
--- /dev/null
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Questionable.Model.Questing;
+
+public sealed class QuestSequence
+{
+ public int Sequence { get; set; }
+ public string? Comment { get; set; }
+ public List<QuestStep> Steps { get; set; } = new();
+
+ public QuestStep? FindStep(int step)
+ {
+ if (step < 0 || step >= Steps.Count)
+ return null;
+
+ return Steps[step];
+ }
+
+ public QuestStep? LastStep() => Steps.LastOrDefault();
+}
--- /dev/null
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text.Json.Serialization;
+using Questionable.Model.Common;
+using Questionable.Model.Common.Converter;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+public sealed class QuestStep
+{
+ public const float DefaultStopDistance = 3f;
+
+ public EInteractionType InteractionType { get; set; }
+
+ public uint? DataId { get; set; }
+
+ [JsonConverter(typeof(VectorConverter))]
+ public Vector3? Position { get; set; }
+
+ public float? StopDistance { get; set; }
+ public float? NpcWaitDistance { get; set; }
+ public ushort TerritoryId { get; set; }
+ public ushort? TargetTerritoryId { get; set; }
+ public float? DelaySecondsAtStart { get; set; }
+
+ public bool Disabled { get; set; }
+ public bool DisableNavmesh { get; set; }
+ public bool? Mount { get; set; }
+ public bool? Fly { get; set; }
+ public bool? Land { get; set; }
+ public bool? Sprint { get; set; }
+ public bool? IgnoreDistanceToObject { get; set; }
+ public string? Comment { get; set; }
+
+ /// <summary>
+ /// Only used when attuning to an aetheryte.
+ /// </summary>
+ public EAetheryteLocation? Aetheryte { get; set; }
+
+ /// <summary>
+ /// Only used when attuning to an aethernet shard.
+ /// </summary>
+ [JsonConverter(typeof(AethernetShardConverter))]
+ public EAetheryteLocation? AethernetShard { get; set; }
+
+ public EAetheryteLocation? AetheryteShortcut { get; set; }
+
+ public AethernetShortcut? AethernetShortcut { get; set; }
+ public uint? AetherCurrentId { get; set; }
+
+ public uint? ItemId { get; set; }
+ public bool? GroundTarget { get; set; }
+
+ public EEmote? Emote { get; set; }
+ public ChatMessage? ChatMessage { get; set; }
+ public EAction? Action { get; set; }
+
+ public EEnemySpawnType? EnemySpawnType { get; set; }
+ public IList<uint> KillEnemyDataIds { get; set; } = new List<uint>();
+ public IList<ComplexCombatData> ComplexCombatData { get; set; } = new List<ComplexCombatData>();
+ public float? CombatDelaySecondsAtStart { get; set; }
+
+ public JumpDestination? JumpDestination { get; set; }
+ public uint? ContentFinderConditionId { get; set; }
+ public SkipConditions? SkipConditions { get; set; }
+
+ public List<List<QuestWorkValue>?> RequiredQuestVariables { get; set; } = new();
+ public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
+ public IList<DialogueChoice> DialogueChoices { get; set; } = new List<DialogueChoice>();
+ public IList<uint> PointMenuChoices { get; set; } = new List<uint>();
+
+ // TODO: Not implemented
+ public ushort? PickUpQuestId { get; set; }
+
+ public ushort? TurnInQuestId { get; set; }
+ public ushort? NextQuestId { get; set; }
+
+ [JsonConstructor]
+ public QuestStep()
+ {
+ }
+
+ public QuestStep(EInteractionType interactionType, uint? dataId, Vector3? position, ushort territoryId)
+ {
+ InteractionType = interactionType;
+ DataId = dataId;
+ Position = position;
+ TerritoryId = territoryId;
+ }
+
+ public float CalculateActualStopDistance()
+ {
+ if (InteractionType == EInteractionType.WalkTo)
+ return StopDistance ?? 0.25f;
+ if (InteractionType == EInteractionType.AttuneAetheryte)
+ return StopDistance ?? 10f;
+ else
+ return StopDistance ?? DefaultStopDistance;
+ }
+}
--- /dev/null
+using System;
+using System.Text.Json.Serialization;
+using Questionable.Model.Questing.Converter;
+
+namespace Questionable.Model.Questing;
+
+[JsonConverter(typeof(QuestWorkConfigConverter))]
+public sealed class QuestWorkValue(byte? high, byte? low, EQuestWorkMode mode)
+{
+ public QuestWorkValue(byte value)
+ : this((byte)(value >> 4), (byte)(value & 0xF), EQuestWorkMode.Bitwise)
+ {
+ }
+
+ public byte? High { get; set; } = high;
+ public byte? Low { get; set; } = low;
+ public EQuestWorkMode Mode { get; set; } = mode;
+
+ public override string ToString()
+ {
+ if (High != null && Low != null)
+ return ((byte)(High << 4) + Low).ToString();
+ else if (High != null)
+ return High + "H";
+ else if (Low != null)
+ return Low + "L";
+ else
+ return "-";
+ }
+}
--- /dev/null
+namespace Questionable.Model.Questing;
+
+public sealed class SkipAetheryteCondition
+{
+ public bool Never { get; set; }
+ public bool InSameTerritory { get; set; }
+}
--- /dev/null
+namespace Questionable.Model.Questing;
+
+public sealed class SkipConditions
+{
+ public SkipStepConditions? StepIf { get; set; }
+ public SkipAetheryteCondition? AetheryteShortcutIf { get; set; }
+ public SkipAetheryteCondition? AethernetShortcutIf { get; set; }
+}
--- /dev/null
+namespace Questionable.Model.Questing;
+
+public sealed class SkipItemConditions
+{
+ public bool NotInInventory { get; set; }
+}
--- /dev/null
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Questionable.Model.Questing;
+
+public sealed class SkipStepConditions
+{
+ public bool Never { get; set; }
+ public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
+ public ELockedSkipCondition? Flying { get; set; }
+ public ELockedSkipCondition? Chocobo { get; set; }
+ public bool NotTargetable { get; set; }
+ public List<ushort> InTerritory { get; set; } = new();
+ public List<ushort> NotInTerritory { get; set; } = new();
+ public SkipItemConditions? Item { get; set; }
+ public List<ushort> QuestsAccepted { get; set; } = new();
+ public List<ushort> QuestsCompleted { get; set; } = new();
+ public EExtraSkipCondition? ExtraCondition { get; set; }
+
+ public bool HasSkipConditions()
+ {
+ if (Never)
+ return false;
+ return (CompletionQuestVariablesFlags.Count > 0 && CompletionQuestVariablesFlags.Any(x => x != null)) ||
+ Flying != null ||
+ Chocobo != null ||
+ NotTargetable ||
+ InTerritory.Count > 0 ||
+ NotInTerritory.Count > 0 ||
+ Item != null ||
+ QuestsAccepted.Count > 0 ||
+ QuestsCompleted.Count > 0 ||
+ ExtraCondition != null;
+ }
+
+ public override string ToString()
+ {
+ return
+ $"{nameof(Never)}: {Never}, {nameof(CompletionQuestVariablesFlags)}: {CompletionQuestVariablesFlags}, {nameof(Flying)}: {Flying}, {nameof(Chocobo)}: {Chocobo}, {nameof(NotTargetable)}: {NotTargetable}, {nameof(InTerritory)}: {string.Join(" ", InTerritory)}, {nameof(NotInTerritory)}: {string.Join(" ", NotInTerritory)}, {nameof(Item)}: {Item}, {nameof(QuestsAccepted)}: {string.Join(" ", QuestsAccepted)}, {nameof(QuestsCompleted)}: {string.Join(" ", QuestsCompleted)}, {nameof(ExtraCondition)}: {ExtraCondition}";
+ }
+}
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.4" />
</ItemGroup>
+
+
+ <ItemGroup>
+ <None Remove="common-schema.json" />
+ <EmbeddedResource Include="common-schema.json">
+ <LogicalName>Questionable.Model.CommonSchema</LogicalName>
+ </EmbeddedResource>
+ </ItemGroup>
</Project>
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(AethernetShortcutConverter))]
-public sealed class AethernetShortcut
-{
- public EAetheryteLocation From { get; set; }
- public EAetheryteLocation To { get; set; }
-}
+++ /dev/null
-namespace Questionable.Model.V1;
-
-public sealed class ChatMessage
-{
- public string? ExcelSheet { get; set; }
- public string Key { get; set; } = null!;
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1;
-
-public sealed class ComplexCombatData
-{
- public uint DataId { get; set; }
-
- // TODO Use this
- public uint? MinimumKillCount { get; set; }
-
- /// <summary>
- /// If a reward item has been set, this is (ping allowing) given to the player before the dead enemy despawns.
- /// </summary>
- public uint? RewardItemId { get; set; }
- public int? RewardItemCount { get; set; }
- public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
- public bool IgnoreQuestMarker { get; set; }
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class ActionConverter() : EnumConverter<EAction>(Values)
-{
- private static readonly Dictionary<EAction, string> Values = new()
- {
- { EAction.Cure, "Cure" },
- { EAction.Esuna, "Esuna" },
- { EAction.Physick, "Physick" },
- { EAction.Buffet, "Buffet" },
- { EAction.Fumigate, "Fumigate" },
- { EAction.SiphonSnout, "Siphon Snout" },
- { EAction.RedGulal, "Red Gulal" },
- { EAction.YellowGulal, "Yellow Gulal" },
- { EAction.BlueGulal, "Blue Gulal" },
- };
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class AethernetShardConverter() : EnumConverter<EAetheryteLocation>(Values)
-{
- public static readonly Dictionary<EAetheryteLocation, string> Values = new()
- {
- { EAetheryteLocation.Gridania, "[Gridania] Aetheryte Plaza" },
- { EAetheryteLocation.GridaniaArcher, "[Gridania] Archers' Guild" },
- { EAetheryteLocation.GridaniaLeatherworker, "[Gridania] Leatherworkers' Guild & Shaded Bower" },
- { EAetheryteLocation.GridaniaLancer, "[Gridania] Lancers' Guild" },
- { EAetheryteLocation.GridaniaConjurer, "[Gridania] Conjurers' Guild" },
- { EAetheryteLocation.GridaniaBotanist, "[Gridania] Botanists' Guild" },
- { EAetheryteLocation.GridaniaAmphitheatre, "[Gridania] Mih Khetto's Amphitheatre" },
- { EAetheryteLocation.GridaniaBlueBadgerGate, "[Gridania] Blue Badger Gate (Central Shroud)" },
- { EAetheryteLocation.GridaniaYellowSerpentGate, "[Gridania] Yellow Serpent Gate (North Shroud)" },
- { EAetheryteLocation.GridaniaWhiteWolfGate, "[Gridania] White Wolf Gate (Central Shroud)" },
- { EAetheryteLocation.GridaniaAirship, "[Gridania] Airship Landing" },
-
- { EAetheryteLocation.Uldah, "[Ul'dah] Aetheryte Plaza" },
- { EAetheryteLocation.UldahAdventurers, "[Ul'dah] Adventurers' Guild" },
- { EAetheryteLocation.UldahThaumaturge, "[Ul'dah] Thaumaturges' Guild" },
- { EAetheryteLocation.UldahGladiator, "[Ul'dah] Gladiators' Guild" },
- { EAetheryteLocation.UldahMiner, "[Ul'dah] Miners' Guild" },
- { EAetheryteLocation.UldahWeaver, "[Ul'dah] Weavers' Guild" },
- { EAetheryteLocation.UldahGoldsmith, "[Ul'dah] Goldsmiths' Guild" },
- { EAetheryteLocation.UldahSapphireAvenue, "[Ul'dah] Sapphire Avenue Exchange" },
- { EAetheryteLocation.UldahAlchemist, "[Ul'dah] Alchemists' Guild" },
- { EAetheryteLocation.UldahChamberOfRule, "[Ul'dah] The Chamber of Rule" },
- { EAetheryteLocation.UldahGateOfTheSultana, "[Ul'dah] Gate of the Sultana (Western Thanalan)" },
- { EAetheryteLocation.UldahGateOfNald, "[Ul'dah] Gate of Nald (Central Thanalan)" },
- { EAetheryteLocation.UldahGateOfThal, "[Ul'dah] Gate of Thal (Central Thanalan)" },
- { EAetheryteLocation.UldahAirship, "[Ul'dah] Airship Landing" },
-
- { EAetheryteLocation.Limsa, "[Limsa Lominsa] Aetheryte Plaza" },
- { EAetheryteLocation.LimsaArcanist, "[Limsa Lominsa] Arcanists' Guild" },
- { EAetheryteLocation.LimsaFisher, "[Limsa Lominsa] Fishermens' Guild" },
- { EAetheryteLocation.LimsaHawkersAlley, "[Limsa Lominsa] Hawkers' Alley" },
- { EAetheryteLocation.LimsaAftcastle, "[Limsa Lominsa] The Aftcastle" },
- { EAetheryteLocation.LimsaCulinarian, "[Limsa Lominsa] Culinarians' Guild" },
- { EAetheryteLocation.LimsaMarauder, "[Limsa Lominsa] Marauders' Guild" },
- { EAetheryteLocation.LimsaZephyrGate, "[Limsa Lominsa] Zephyr Gate (Middle La Noscea)" },
- { EAetheryteLocation.LimsaTempestGate, "[Limsa Lominsa] Tempest Gate (Lower La Noscea)" },
- { EAetheryteLocation.LimsaAirship, "[Limsa Lominsa] Airship Landing" },
-
- { EAetheryteLocation.Ishgard, "[Ishgard] Aetheryte Plaza" },
- { EAetheryteLocation.IshgardForgottenKnight, "[Ishgard] The Forgotten Knight" },
- { EAetheryteLocation.IshgardSkysteelManufactory, "[Ishgard] Skysteel Manufactory" },
- { EAetheryteLocation.IshgardBrume, "[Ishgard] The Brume" },
- { EAetheryteLocation.IshgardAthenaeumAstrologicum, "[Ishgard] Athenaeum Astrologicum" },
- { EAetheryteLocation.IshgardJeweledCrozier, "[Ishgard] The Jeweled Crozier" },
- { EAetheryteLocation.IshgardSaintReymanaudsCathedral, "[Ishgard] Saint Reymanaud's Cathedral" },
- { EAetheryteLocation.IshgardTribunal, "[Ishgard] The Tribunal" },
- { EAetheryteLocation.IshgardLastVigil, "[Ishgard] The Last Vigil" },
- { EAetheryteLocation.IshgardGatesOfJudgement, "[Ishgard] The Gates of Judgement (Coerthas Central Highlands)" },
-
- { EAetheryteLocation.Idyllshire, "[Idyllshire] Aetheryte Plaza" },
- { EAetheryteLocation.IdyllshireWest, "[Idyllshire] West Idyllshire" },
- { EAetheryteLocation.IdyllshirePrologueGate, "[Idyllshire] Prologue Gate (Western Hinterlands)" },
- { EAetheryteLocation.IdyllshireEpilogueGate, "[Idyllshire] Epilogue Gate (Eastern Hinterlands)" },
-
- { EAetheryteLocation.RhalgrsReach, "[Rhalgr's Reach] Aetheryte Plaza" },
- { EAetheryteLocation.RhalgrsReachWest, "[Rhalgr's Reach] Western Rhalgr's Reach" },
- { EAetheryteLocation.RhalgrsReachNorthEast, "[Rhalgr's Reach] Northeastern Rhalgr's Reach" },
- { EAetheryteLocation.RhalgrsReachFringesGate, "[Rhalgr's Reach] Fringes Gate" },
- { EAetheryteLocation.RhalgrsReachPeaksGate, "[Rhalgr's Reach] Peaks Gate" },
-
- { EAetheryteLocation.Kugane, "[Kugane] Aetheryte Plaza" },
- { EAetheryteLocation.KuganeShiokazeHostelry, "[Kugane] Shiokaze Hostelry" },
- { EAetheryteLocation.KuganePier1, "[Kugane] Pier #1" },
- { EAetheryteLocation.KuganeThavnairianConsulate, "[Kugane] Thavnairian Consulate" },
- { EAetheryteLocation.KuganeMarkets, "[Kugane] Kogane Dori Markets" },
- { EAetheryteLocation.KuganeBokairoInn, "[Kugane] Bokairo Inn" },
- { EAetheryteLocation.KuganeRubyBazaar, "[Kugane] The Ruby Bazaar" },
- { EAetheryteLocation.KuganeSekiseigumiBarracks, "[Kugane] Sekiseigumi Barracks" },
- { EAetheryteLocation.KuganeRakuzaDistrict, "[Kugane] Rakuza District" },
- { EAetheryteLocation.KuganeRubyPrice, "[Kugane] The Ruby Price" },
- { EAetheryteLocation.KuganeAirship, "[Kugane] Airship Landing" },
-
- { EAetheryteLocation.Crystarium, "[Crystarium] Aetheryte Plaza" },
- { EAetheryteLocation.CrystariumMarkets, "[Crystarium] Musica Universalis Markets" },
- { EAetheryteLocation.CrystariumTemenosRookery, "[Crystarium] Temenos Rookery" },
- { EAetheryteLocation.CrystariumDossalGate, "[Crystarium] The Dossal Gate" },
- { EAetheryteLocation.CrystariumPendants, "[Crystarium] The Pendants" },
- { EAetheryteLocation.CrystariumAmaroLaunch, "[Crystarium] The Amaro Launch" },
- { EAetheryteLocation.CrystariumCrystallineMean, "[Crystarium] The Crystalline Mean" },
- { EAetheryteLocation.CrystariumCabinetOfCuriosity, "[Crystarium] The Cabinet of Curiosity" },
- { EAetheryteLocation.CrystariumTessellation, "[Crystarium] Tessellation (Lakeland)" },
-
- { EAetheryteLocation.Eulmore, "[Eulmore] Aetheryte Plaza" },
- { EAetheryteLocation.EulmoreSoutheastDerelict, "[Eulmore] Southeast Derelicts" },
- { EAetheryteLocation.EulmoreNightsoilPots, "[Eulmore] Nightsoil Pots" },
- { EAetheryteLocation.EulmoreGloryGate, "[Eulmore] The Glory Gate" },
- { EAetheryteLocation.EulmoreMainstay, "[Eulmore] The Mainstay" },
- { EAetheryteLocation.EulmorePathToGlory, "[Eulmore] The Path to Glory (Kholusia)" },
-
- { EAetheryteLocation.OldSharlayan, "[Old Sharlayan] Aetheryte Plaza" },
- { EAetheryteLocation.OldSharlayanStudium, "[Old Sharlayan] The Studium" },
- { EAetheryteLocation.OldSharlayanBaldesionAnnex, "[Old Sharlayan] The Baldesion Annex" },
- { EAetheryteLocation.OldSharlayanRostra, "[Old Sharlayan] The Rostra" },
- { EAetheryteLocation.OldSharlayanLeveilleurEstate, "[Old Sharlayan] The Leveilleur Estate" },
- { EAetheryteLocation.OldSharlayanJourneysEnd, "[Old Sharlayan] Journey's End" },
- { EAetheryteLocation.OldSharlayanScholarsHarbor, "[Old Sharlayan] Scholar's Harbor" },
- { EAetheryteLocation.OldSharlayanHallOfArtifice, "[Old Sharlayan] The Hall of Artifice (Labyrinthos)" },
-
- { EAetheryteLocation.RadzAtHan, "[Radz-at-Han] Aetheryte Plaza" },
- { EAetheryteLocation.RadzAtHanMeghaduta, "[Radz-at-Han] Meghaduta" },
- { EAetheryteLocation.RadzAtHanRuveydahFibers, "[Radz-at-Han] Ruveydah Fibers" },
- { EAetheryteLocation.RadzAtHanAirship, "[Radz-at-Han] Airship Landing" },
- { EAetheryteLocation.RadzAtHanAlzadaalsPeace, "[Radz-at-Han] Alzadaal's Peace" },
- { EAetheryteLocation.RadzAtHanHallOfTheRadiantHost, "[Radz-at-Han] Hall of the Radiant Host" },
- { EAetheryteLocation.RadzAtHanMehrydesMeyhane, "[Radz-at-Han] Mehryde's Meyhane" },
- { EAetheryteLocation.RadzAtHanKama, "[Radz-at-Han] Kama" },
- { EAetheryteLocation.RadzAtHanHighCrucible, "[Radz-at-Han] The High Crucible of Al-Kimiya" },
- { EAetheryteLocation.RadzAtHanGateOfFirstSight, "[Radz-at-Han] The Gate of First Sight (Thavnair)" },
-
- { EAetheryteLocation.Tuliyollal, "[Tuliyollal] Aetheryte Plaza" },
- { EAetheryteLocation.TuliyollalDirigibleLanding, "[Tuliyollal] Dirigible Landing" },
- { EAetheryteLocation.TuliyollalTheResplendentQuarter, "[Tuliyollal] The Resplendent Quarter" },
- { EAetheryteLocation.TuliyollalTheForardCabins, "[Tuliyollal] The For'ard Cabins" },
- { EAetheryteLocation.TuliyollalBaysideBevyMarketplace, "[Tuliyollal] Bayside Bevy Marketplace" },
- { EAetheryteLocation.TuliyollalVollokShoonsa, "[Tuliyollal] Vollok Shoonsa" },
- { EAetheryteLocation.TuliyollalWachumeqimeqi, "[Tuliyollal] Wachumeqimeqi" },
- { EAetheryteLocation.TuliyollalBrightploomPost, "[Tuliyollal] Brightploom Post" },
- { EAetheryteLocation.TuliyollalArchOfTheDawnUrqopacha, "[Tuliyollal] Arch of the Dawn (Urqopacha)" },
- { EAetheryteLocation.TuliyollalArchOfTheDawnKozamauka, "[Tuliyollal] Arch of the Dawn (Kozama'uka)" },
- { EAetheryteLocation.TuliyollalIhuykatumu, "[Tuliyollal] Ihuykatumu (Kozama'uka)" },
- { EAetheryteLocation.TuliyollalDirigibleLandingYakTel, "[Tuliyollal] Dirigible Landing (Yak T'el)" },
- { EAetheryteLocation.TuliyollalXakTuralSkygate, "[Tuliyollal] Xak Tural Skygate (Shaaloani)" },
-
- { EAetheryteLocation.SolutionNine, "[Solution Nine] Aetheryte Plaza" },
- { EAetheryteLocation.SolutionNineInformationCenter, "[Solution Nine] Information Center" },
- { EAetheryteLocation.SolutionNineTrueVue, "[Solution Nine] True Vue" },
- { EAetheryteLocation.SolutionNineNeonStein, "[Solution Nine] Neon Stein" },
- { EAetheryteLocation.SolutionNineTheArcadion, "[Solution Nine] The Arcadion" },
- { EAetheryteLocation.SolutionNineResolution, "[Solution Nine] Resolution" },
- { EAetheryteLocation.SolutionNineNexusArcade, "[Solution Nine] Nexus Arcade" },
- { EAetheryteLocation.SolutionNineResidentialSector, "[Solution Nine] Residential Sector" },
- { EAetheryteLocation.SolutionNineScanningPortNine, "[Solution Nine] Scanning Port Nine (Heritage Found)" },
- };
-}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class AethernetShortcutConverter : JsonConverter<AethernetShortcut>
-{
- private static readonly Dictionary<EAetheryteLocation, string> EnumToString = AethernetShardConverter.Values;
- private static readonly Dictionary<string, EAetheryteLocation> StringToEnum =
- EnumToString.ToDictionary(x => x.Value, x => x.Key);
-
- public override AethernetShortcut Read(ref Utf8JsonReader reader, Type typeToConvert,
- JsonSerializerOptions options)
- {
- if (reader.TokenType != JsonTokenType.StartArray)
- throw new JsonException();
-
- if (!reader.Read() || reader.TokenType != JsonTokenType.String)
- throw new JsonException();
-
- string from = reader.GetString() ?? throw new JsonException();
-
- if (!reader.Read() || reader.TokenType != JsonTokenType.String)
- throw new JsonException();
-
- string to = reader.GetString() ?? throw new JsonException();
-
- if (!reader.Read() || reader.TokenType != JsonTokenType.EndArray)
- throw new JsonException();
-
- return new AethernetShortcut
- {
- From = StringToEnum.TryGetValue(from, out var fromEnum) ? fromEnum : throw new JsonException(),
- To = StringToEnum.TryGetValue(to, out var toEnum) ? toEnum : throw new JsonException()
- };
- }
-
- public override void Write(Utf8JsonWriter writer, AethernetShortcut value, JsonSerializerOptions options)
- {
- writer.WriteStartArray();
- writer.WriteStringValue(EnumToString[value.From]);
- writer.WriteStringValue(EnumToString[value.To]);
- writer.WriteEndArray();
- }
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class AetheryteConverter() : EnumConverter<EAetheryteLocation>(Values)
-{
- private static readonly Dictionary<EAetheryteLocation, string> Values = new()
- {
- { EAetheryteLocation.Gridania, "Gridania" },
- { EAetheryteLocation.CentralShroudBentbranchMeadows, "Central Shroud - Bentbranch Meadows" },
- { EAetheryteLocation.EastShroudHawthorneHut, "East Shroud - Hawthorne Hut" },
- { EAetheryteLocation.SouthShroudQuarrymill, "South Shroud - Quarrymill" },
- { EAetheryteLocation.SouthShroudCampTranquil, "South Shroud - Camp Tranquil" },
- { EAetheryteLocation.NorthShroudFallgourdFloat, "North Shroud - Fallgourd Float" },
-
- { EAetheryteLocation.Uldah, "Ul'dah" },
- { EAetheryteLocation.WesternThanalanHorizon, "Western Thanalan - Horizon" },
- { EAetheryteLocation.CentralThanalanBlackBrushStation, "Central Thanalan - Black Brush Station" },
- { EAetheryteLocation.EasternThanalanCampDrybone, "Eastern Thanalan - Camp Drybone" },
- { EAetheryteLocation.SouthernThanalanLittleAlaMhigo, "Southern Thanalan - Little Ala Mhigo" },
- { EAetheryteLocation.SouthernThanalanForgottenSprings, "Southern Thanalan - Forgotten Springs" },
- { EAetheryteLocation.NorthernThanalanCampBluefog, "Northern Thanalan - Camp Bluefog" },
- { EAetheryteLocation.NorthernThanalanCeruleumProcessingPlant, "Northern Thanalan - Ceruleum Processing Plant" },
-
- { EAetheryteLocation.Limsa, "Limsa Lominsa" },
- { EAetheryteLocation.MiddleLaNosceaSummerfordFarms, "Middle La Noscea - Summerford Farms" },
- { EAetheryteLocation.LowerLaNosceaMorabyDrydocks, "Lower La Noscea - Moraby Drydocks" },
- { EAetheryteLocation.EasternLaNosceaCostaDelSol, "Eastern La Noscea - Costa Del Sol" },
- { EAetheryteLocation.EasternLaNosceaWineport, "Eastern La Noscea - Wineport" },
- { EAetheryteLocation.WesternLaNosceaSwiftperch, "Western La Noscea - Swiftperch" },
- { EAetheryteLocation.WesternLaNosceaAleport, "Western La Noscea - Aleport" },
- { EAetheryteLocation.UpperLaNosceaCampBronzeLake, "Upper La Noscea - Camp Bronze Lake" },
- { EAetheryteLocation.OuterLaNosceaCampOverlook, "Outer La Noscea - Camp Overlook" },
-
- { EAetheryteLocation.CoerthasCentralHighlandsCampDragonhead, "Coerthas Central Highlands - Camp Dragonhead" },
- { EAetheryteLocation.MorDhona, "Mor Dhona" },
- { EAetheryteLocation.GoldSaucer, "Gold Saucer" },
- { EAetheryteLocation.WolvesDenPier, "Wolves' Den Pier" },
-
- { EAetheryteLocation.Ishgard, "Ishgard" },
- { EAetheryteLocation.Idyllshire, "Idyllshire" },
- { EAetheryteLocation.CoerthasWesternHighlandsFalconsNest, "Coerthas Western Highlands - Falcon's Nest" },
- { EAetheryteLocation.SeaOfCloudsCampCloudtop, "The Sea of Clouds - Camp Cloudtop" },
- { EAetheryteLocation.SeaOfCloudsOkZundu, "The Sea of Clouds - Ok' Zundu" },
- { EAetheryteLocation.AzysLlaHelix, "Azys Lla - Helix" },
- { EAetheryteLocation.DravanianForelandsTailfeather, "The Dravanian Forelands - Tailfeather" },
- { EAetheryteLocation.DravanianForelandsAnyxTrine, "The Dravanian Forelands - Anyx Trine" },
- { EAetheryteLocation.ChurningMistsMoghome, "The Churning Mists - Moghome" },
- { EAetheryteLocation.ChurningMistsZenith, "The Churning Mists - Zenith" },
-
- { EAetheryteLocation.RhalgrsReach, "Rhalgr's Reach" },
- { EAetheryteLocation.FringesCastrumOriens, "Fringes - Castrum Oriens" },
- { EAetheryteLocation.FringesPeeringStones, "Fringes - Peering Stones" },
- { EAetheryteLocation.PeaksAlaGannha, "Peaks - Ala Gannha" },
- { EAetheryteLocation.PeaksAlaGhiri, "Peaks - Ala Ghiri" },
- { EAetheryteLocation.LochsPortaPraetoria, "Lochs - Porta Praetoria" },
- { EAetheryteLocation.LochsAlaMhiganQuarter, "Lochs - Ala Mhigan Quarter" },
- { EAetheryteLocation.Kugane, "Kugane" },
- { EAetheryteLocation.RubySeaTamamizu, "Ruby Sea - Tamamizu" },
- { EAetheryteLocation.RubySeaOnokoro, "Ruby Sea - Onokoro" },
- { EAetheryteLocation.YanxiaNamai, "Yanxia - Namai" },
- { EAetheryteLocation.YanxiaHouseOfTheFierce, "Yanxia - House of the Fierce" },
- { EAetheryteLocation.AzimSteppeReunion, "Azim Steppe - Reunion" },
- { EAetheryteLocation.AzimSteppeDawnThrone, "Azim Steppe - Dawn Throne" },
- { EAetheryteLocation.AzimSteppeDhoroIloh, "Azim Steppe - Dhoro Iloh" },
- { EAetheryteLocation.DomanEnclave, "Doman Enclave" },
-
- { EAetheryteLocation.Crystarium, "Crystarium" },
- { EAetheryteLocation.Eulmore, "Eulmore" },
- { EAetheryteLocation.LakelandFortJobb, "Lakeland - Fort Jobb" },
- { EAetheryteLocation.LakelandOstallImperative, "Lakeland - Ostall Imperative" },
- { EAetheryteLocation.KholusiaStilltide, "Kholusia - Stilltide" },
- { EAetheryteLocation.KholusiaWright, "Kholusia - Wright" },
- { EAetheryteLocation.KholusiaTomra, "Kholusia - Tomra" },
- { EAetheryteLocation.AmhAraengMordSouq, "Amh Araeng - Mord Souq" },
- { EAetheryteLocation.AmhAraengInnAtJourneysHead, "Amh Araeng - Inn at Journey's Head" },
- { EAetheryteLocation.AmhAraengTwine, "Amh Araeng - Twine" },
- { EAetheryteLocation.RaktikaSlitherbough, "Rak'tika - Slitherbough" },
- { EAetheryteLocation.RaktikaFanow, "Rak'tika - Fanow" },
- { EAetheryteLocation.IlMhegLydhaLran, "Il Mheg - Lydha Lran" },
- { EAetheryteLocation.IlMhegPiaEnni, "Il Mheg - Pia Enni" },
- { EAetheryteLocation.IlMhegWolekdorf, "Il Mheg - Wolekdorf" },
- { EAetheryteLocation.TempestOndoCups, "Tempest - Ondo Cups" },
- { EAetheryteLocation.TempestMacarensesAngle, "Tempest - Macarenses Angle" },
-
- { EAetheryteLocation.OldSharlayan, "Old Sharlayan" },
- { EAetheryteLocation.RadzAtHan, "Radz-at-Han" },
- { EAetheryteLocation.LabyrinthosArcheion, "Labyrinthos - Archeion" },
- { EAetheryteLocation.LabyrinthosSharlayanHamlet, "Labyrinthos - Sharlayan Hamlet" },
- { EAetheryteLocation.LabyrinthosAporia, "Labyrinthos - Aporia" },
- { EAetheryteLocation.ThavnairYedlihmad, "Thavnair - Yedlihmad" },
- { EAetheryteLocation.ThavnairGreatWork, "Thavnair - Great Work" },
- { EAetheryteLocation.ThavnairPalakasStand, "Thavnair - Palaka's Stand" },
- { EAetheryteLocation.GarlemaldCampBrokenGlass, "Garlemald - Camp Broken Glass" },
- { EAetheryteLocation.GarlemaldTertium, "Garlemald - Tertium" },
- { EAetheryteLocation.MareLamentorumSinusLacrimarum, "Mare Lamentorum - Sinus Lacrimarum" },
- { EAetheryteLocation.MareLamentorumBestwaysBurrow, "Mare Lamentorum - Bestways Burrow" },
- { EAetheryteLocation.ElpisAnagnorisis, "Elpis - Anagnorisis" },
- { EAetheryteLocation.ElpisTwelveWonders, "Elpis - Twelve Wonders" },
- { EAetheryteLocation.ElpisPoietenOikos, "Elpis - Poieten Oikos" },
- { EAetheryteLocation.UltimaThuleReahTahra, "Ultima Thule - Reah Tahra" },
- { EAetheryteLocation.UltimaThuleAbodeOfTheEa, "Ultima Thule - Abode of the Ea" },
- { EAetheryteLocation.UltimaThuleBaseOmicron, "Ultima Thule - Base Omicron" },
-
- { EAetheryteLocation.Tuliyollal, "Tuliyollal" },
- { EAetheryteLocation.SolutionNine, "Solution Nine" },
- { EAetheryteLocation.UrqopachaWachunpelo, "Urqopacha - Wachunpelo" },
- { EAetheryteLocation.UrqopachaWorlarsEcho, "Urqopacha - Worlar's Echo" },
- { EAetheryteLocation.KozamaukaOkHanu, "Kozama'uka - Ok'hanu" },
- { EAetheryteLocation.KozamaukaManyFires, "Kozama'uka - Many Fires" },
- { EAetheryteLocation.KozamaukaEarthenshire, "Kozama'uka - Earthenshire" },
- { EAetheryteLocation.YakTelIqBraax, "Yak T'el - Iq Br'aax" },
- { EAetheryteLocation.YakTelMamook, "Yak T'el - Mamook" },
- { EAetheryteLocation.ShaaloaniHhusatahwi, "Shaaloani - Hhusatahwi" },
- { EAetheryteLocation.ShaaloaniShesheneweziSprings, "Shaaloani - Sheshenewezi Springs" },
- { EAetheryteLocation.ShaaloaniMehwahhetsoan, "Shaaloani - Mehwahhetsoan" },
- { EAetheryteLocation.HeritageFoundYyasulaniStation, "Heritage Found - Yyasulani Station" },
- { EAetheryteLocation.HeritageFoundTheOutskirts, "Heritage Found - The Outskirts" },
- { EAetheryteLocation.HeritageFoundElectropeStrike, "Heritage Found - Electrope Strike" },
- { EAetheryteLocation.LivingMemoryLeynodeMnemo, "Living Memory - Leynode Mnemo" },
- { EAetheryteLocation.LivingMemoryLeynodePyro, "Living Memory - Leynode Pyro" },
- { EAetheryteLocation.LivingMemoryLeynodeAero, "Living Memory - Leynode Aero" },
- };
-
- public static bool IsLargeAetheryte(EAetheryteLocation aetheryte) => Values.ContainsKey(aetheryte);
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class DialogueChoiceTypeConverter() : EnumConverter<EDialogChoiceType>(Values)
-{
- private static readonly Dictionary<EDialogChoiceType, string> Values = new()
- {
- { EDialogChoiceType.YesNo, "YesNo" },
- { EDialogChoiceType.List, "List" },
- };
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class EmoteConverter() : EnumConverter<EEmote>(Values)
-{
- private static readonly Dictionary<EEmote, string> Values = new()
- {
- { EEmote.Stretch, "stretch" },
- { EEmote.Wave, "wave" },
- { EEmote.Rally, "rally" },
- { EEmote.Deny, "deny" },
- { EEmote.Pray, "pray" },
- { EEmote.Slap, "slap" },
- { EEmote.Doubt, "doubt" },
- { EEmote.Psych, "psych" },
- { EEmote.Cheer, "cheer" },
- { EEmote.Happy, "happy" },
- { EEmote.Poke, "poke" },
- { EEmote.Flex, "flex" },
- { EEmote.Soothe, "soothe" },
- { EEmote.Me, "me" },
- { EEmote.Welcome, "welcome" },
- { EEmote.ImperialSalute, "imperialsalute" },
- { EEmote.Pet, "pet" },
- { EEmote.Dance, "dance" },
- { EEmote.Respect, "respect" },
- { EEmote.Lookout, "lookout" },
- { EEmote.Kneel, "kneel" },
- { EEmote.Bow, "bow" },
- };
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class EnemySpawnTypeConverter() : EnumConverter<EEnemySpawnType>(Values)
-{
- private static readonly Dictionary<EEnemySpawnType, string> Values = new()
- {
- { EEnemySpawnType.AfterInteraction, "AfterInteraction" },
- { EEnemySpawnType.AfterItemUse, "AfterItemUse" },
- { EEnemySpawnType.AutoOnEnterArea, "AutoOnEnterArea" },
- { EEnemySpawnType.OverworldEnemies, "OverworldEnemies" },
- };
-}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Questionable.Model.V1.Converter;
-
-public abstract class EnumConverter<T> : JsonConverter<T>
- where T : Enum
-{
- private readonly ReadOnlyDictionary<T, string> _enumToString;
- private readonly ReadOnlyDictionary<string, T> _stringToEnum;
-
- protected EnumConverter(IReadOnlyDictionary<T, string> values)
- {
- _enumToString = values is IDictionary<T, string> dict
- ? new ReadOnlyDictionary<T, string>(dict)
- : new ReadOnlyDictionary<T, string>(values.ToDictionary(x => x.Key, x => x.Value));
- _stringToEnum = new ReadOnlyDictionary<string, T>(_enumToString.ToDictionary(x => x.Value, x => x.Key));
- }
-
- public override T? Read(ref Utf8JsonReader reader, Type typeToConvert,
- JsonSerializerOptions options)
- {
- if (reader.TokenType != JsonTokenType.String)
- throw new JsonException();
-
- string? str = reader.GetString();
- if (str == null)
- throw new JsonException();
-
- return _stringToEnum.TryGetValue(str, out T? value) ? value : throw new JsonException();
- }
-
- public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
- {
- writer.WriteStringValue(_enumToString[value]);
- }
-}
+++ /dev/null
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class ExcelRefConverter : JsonConverter<ExcelRef>
-{
- public override ExcelRef? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- return reader.TokenType switch
- {
- JsonTokenType.String => ExcelRef.FromKey(reader.GetString()!),
- JsonTokenType.Number => ExcelRef.FromRowId(reader.GetUInt32()),
- _ => null
- };
- }
-
- public override void Write(Utf8JsonWriter writer, ExcelRef? value, JsonSerializerOptions options)
- {
- if (value == null)
- writer.WriteNullValue();
- else if (value.Type == ExcelRef.EType.Key)
- writer.WriteStringValue(value.AsKey());
- else if (value.Type == ExcelRef.EType.RowId)
- writer.WriteNumberValue(value.AsRowId());
- else
- throw new JsonException();
- }
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class InteractionTypeConverter() : EnumConverter<EInteractionType>(Values)
-{
- private static readonly Dictionary<EInteractionType, string> Values = new()
- {
- { EInteractionType.Interact, "Interact" },
- { EInteractionType.WalkTo, "WalkTo" },
- { EInteractionType.AttuneAethernetShard, "AttuneAethernetShard" },
- { EInteractionType.AttuneAetheryte, "AttuneAetheryte" },
- { EInteractionType.AttuneAetherCurrent, "AttuneAetherCurrent" },
- { EInteractionType.Combat, "Combat" },
- { EInteractionType.UseItem, "UseItem" },
- { EInteractionType.EquipItem, "EquipItem" },
- { EInteractionType.Say, "Say" },
- { EInteractionType.Emote, "Emote" },
- { EInteractionType.Action, "Action" },
- { EInteractionType.WaitForObjectAtPosition, "WaitForNpcAtPosition" },
- { EInteractionType.WaitForManualProgress, "WaitForManualProgress" },
- { EInteractionType.Duty, "Duty" },
- { EInteractionType.SinglePlayerDuty, "SinglePlayerDuty" },
- { EInteractionType.Jump, "Jump" },
- { EInteractionType.Dive, "Dive" },
- { EInteractionType.Instruction, "Instruction" },
- { EInteractionType.AcceptQuest, "AcceptQuest" },
- { EInteractionType.CompleteQuest, "CompleteQuest" },
- };
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class JumpTypeConverter() : EnumConverter<EJumpType>(Values)
-{
- private static readonly Dictionary<EJumpType, string> Values = new()
- {
- { EJumpType.SingleJump, "SingleJump" },
- { EJumpType.RepeatedJumps, "RepeatedJumps" },
- };
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class LockedSkipConditionConverter() : EnumConverter<ELockedSkipCondition>(Values)
-{
- private static readonly Dictionary<ELockedSkipCondition, string> Values = new()
- {
- { ELockedSkipCondition.Locked, "Locked" },
- { ELockedSkipCondition.Unlocked, "Unlocked" },
- };
-}
+++ /dev/null
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class QuestWorkConfigConverter : JsonConverter<QuestWorkValue>
-{
- public override QuestWorkValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- if (reader.TokenType == JsonTokenType.Number)
- return new QuestWorkValue(reader.GetByte());
-
- if (reader.TokenType != JsonTokenType.StartObject)
- throw new JsonException();
-
- byte? high = null, low = null;
- EQuestWorkMode mode = EQuestWorkMode.Bitwise;
- while (reader.Read())
- {
- switch (reader.TokenType)
- {
- case JsonTokenType.PropertyName:
- string? propertyName = reader.GetString();
- if (propertyName == null || !reader.Read())
- throw new JsonException();
-
- switch (propertyName)
- {
- case nameof(QuestWorkValue.High):
- high = reader.GetByte();
- break;
-
- case nameof(QuestWorkValue.Low):
- low = reader.GetByte();
- break;
-
- case nameof(QuestWorkValue.Mode):
- mode = new QuestWorkModeConverter().Read(ref reader, typeof(EQuestWorkMode), options);
- break;
-
- default:
- throw new JsonException();
- }
-
- break;
-
- case JsonTokenType.EndObject:
- return new QuestWorkValue(high, low, mode);
-
- default:
- throw new JsonException();
- }
- }
-
- throw new JsonException();
- }
-
- public override void Write(Utf8JsonWriter writer, QuestWorkValue value, JsonSerializerOptions options)
- {
- throw new NotImplementedException();
- }
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class QuestWorkModeConverter() : EnumConverter<EQuestWorkMode>(Values)
-{
- private static readonly Dictionary<EQuestWorkMode, string> Values = new()
- {
- { EQuestWorkMode.Bitwise, "Bitwise" },
- { EQuestWorkMode.Exact, "Exact" },
- };
-}
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class SkipConditionConverter() : EnumConverter<EExtraSkipCondition>(Values)
-{
- private static readonly Dictionary<EExtraSkipCondition, string> Values = new()
- {
- { EExtraSkipCondition.WakingSandsMainArea, "WakingSandsMainArea" },
- };
-}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class StringListOrValueConverter : JsonConverter<List<string>>
-{
- public override List<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- if (reader.TokenType == JsonTokenType.String)
- return [reader.GetString()!];
-
- if (reader.TokenType != JsonTokenType.StartArray)
- throw new JsonException();
- reader.Read();
-
- List<string> value = [];
- while (reader.TokenType != JsonTokenType.EndArray)
- {
- value.Add(reader.GetString()!);
- reader.Read();
- }
-
- return value;
- }
-
- public override void Write(Utf8JsonWriter writer, List<string>? value, JsonSerializerOptions options)
- {
- if (value == null)
- writer.WriteNullValue();
- else if (value.Count == 1)
- writer.WriteStringValue(value[0]);
- else
- {
- writer.WriteStartArray();
- foreach (var v in value)
- writer.WriteStringValue(v);
- writer.WriteEndArray();
- }
- }
-}
+++ /dev/null
-using System;
-using System.Numerics;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Questionable.Model.V1.Converter;
-
-public sealed class VectorConverter : JsonConverter<Vector3>
-{
- public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- if (reader.TokenType != JsonTokenType.StartObject)
- throw new JsonException();
-
- Vector3 vec = new Vector3();
- while (reader.Read())
- {
- switch (reader.TokenType)
- {
- case JsonTokenType.PropertyName:
- string? propertyName = reader.GetString();
- if (propertyName == null || !reader.Read())
- throw new JsonException();
-
- switch (propertyName)
- {
- case nameof(Vector3.X):
- vec.X = reader.GetSingle();
- break;
-
- case nameof(Vector3.Y):
- vec.Y = reader.GetSingle();
- break;
-
- case nameof(Vector3.Z):
- vec.Z = reader.GetSingle();
- break;
-
- default:
- throw new JsonException();
- }
-
- break;
-
- case JsonTokenType.EndObject:
- return vec;
-
- default:
- throw new JsonException();
- }
- }
-
- throw new JsonException();
- }
-
- public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options)
- {
- writer.WriteStartObject();
- writer.WriteNumber(nameof(Vector3.X), value.X);
- writer.WriteNumber(nameof(Vector3.Y), value.X);
- writer.WriteNumber(nameof(Vector3.Z), value.X);
- writer.WriteEndObject();
- }
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-public sealed class DialogueChoice
-{
- [JsonConverter(typeof(DialogueChoiceTypeConverter))]
- public EDialogChoiceType Type { get; set; }
- public string? ExcelSheet { get; set; }
-
- [JsonConverter(typeof(ExcelRefConverter))]
- public ExcelRef? Prompt { get; set; }
-
- public bool Yes { get; set; } = true;
-
- [JsonConverter(typeof(ExcelRefConverter))]
- public ExcelRef? Answer { get; set; }
-
- /// <summary>
- /// If set, only applies when focusing the given target id.
- /// </summary>
- public uint? DataId { get; set; }
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(ActionConverter))]
-public enum EAction
-{
- Cure = 120,
- Esuna = 7568,
- Physick = 190,
- Buffet = 4931,
- Fumigate = 5872,
- SiphonSnout = 18187,
- RedGulal = 29382,
- YellowGulal = 29383,
- BlueGulal = 29384,
-}
-
-public static class EActionExtensions
-{
- public static bool RequiresMount(this EAction action)
- {
- return action
- is EAction.Buffet
- or EAction.Fumigate
- or EAction.SiphonSnout
- or EAction.RedGulal
- or EAction.YellowGulal
- or EAction.BlueGulal;
- }
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(AetheryteConverter))]
-public enum EAetheryteLocation
-{
- None = 0,
-
- Gridania = 2,
- GridaniaArcher = 25,
- GridaniaLeatherworker = 26,
- GridaniaLancer = 27,
- GridaniaConjurer = 28,
- GridaniaBotanist = 29,
- GridaniaAmphitheatre = 30,
- GridaniaBlueBadgerGate = 31,
- GridaniaYellowSerpentGate = 32,
- GridaniaWhiteWolfGate = 54,
- GridaniaAirship = 94,
-
- CentralShroudBentbranchMeadows = 3,
- EastShroudHawthorneHut = 4,
- SouthShroudQuarrymill = 5,
- SouthShroudCampTranquil = 6,
- NorthShroudFallgourdFloat = 7,
-
- Uldah = 9,
- UldahAdventurers = 33,
- UldahThaumaturge = 34,
- UldahGladiator = 35,
- UldahMiner = 36,
- UldahAlchemist = 37,
- UldahWeaver = 47,
- UldahGoldsmith = 50,
- UldahChamberOfRule = 51,
- UldahAirship = 95,
- UldahGateOfTheSultana = 38,
- UldahGateOfNald = 39,
- UldahGateOfThal = 40,
- UldahSapphireAvenue = 125,
-
- WesternThanalanHorizon = 17,
- EasternThanalanCampDrybone = 18,
- SouthernThanalanLittleAlaMhigo = 19,
- SouthernThanalanForgottenSprings = 20,
- NorthernThanalanCampBluefog = 21,
- NorthernThanalanCeruleumProcessingPlant = 22,
- CentralThanalanBlackBrushStation = 53,
-
- Limsa = 8,
- LimsaAftcastle = 41,
- LimsaCulinarian = 42,
- LimsaArcanist = 43,
- LimsaFisher = 44,
- LimsaMarauder = 48,
- LimsaHawkersAlley = 49,
- LimsaZephyrGate = 45,
- LimsaTempestGate = 46,
- LimsaAirship = 93,
-
- LowerLaNosceaMorabyDrydocks = 10,
- EasternLaNosceaCostaDelSol = 11,
- EasternLaNosceaWineport = 12,
- WesternLaNosceaSwiftperch = 13,
- WesternLaNosceaAleport = 14,
- UpperLaNosceaCampBronzeLake = 15,
- OuterLaNosceaCampOverlook = 16,
- MiddleLaNosceaSummerfordFarms = 52,
-
- CoerthasCentralHighlandsCampDragonhead = 23,
- MorDhona = 24,
- GoldSaucer = 62,
- WolvesDenPier = 55,
-
- Ishgard = 70,
- IshgardForgottenKnight = 80,
- IshgardSkysteelManufactory = 81,
- IshgardBrume = 82,
- IshgardAthenaeumAstrologicum = 83,
- IshgardJeweledCrozier = 84,
- IshgardSaintReymanaudsCathedral = 85,
- IshgardTribunal = 86,
- IshgardLastVigil = 87,
- IshgardGatesOfJudgement = 88,
-
- Idyllshire = 75,
- IdyllshireWest = 90,
- IdyllshirePrologueGate = 91,
- IdyllshireEpilogueGate = 92,
-
- CoerthasWesternHighlandsFalconsNest = 71,
- SeaOfCloudsCampCloudtop = 72,
- SeaOfCloudsOkZundu = 73,
- AzysLlaHelix = 74,
- DravanianForelandsTailfeather = 76,
- DravanianForelandsAnyxTrine = 77,
- ChurningMistsMoghome = 78,
- ChurningMistsZenith = 79,
-
- RhalgrsReach = 104,
- RhalgrsReachWest = 121,
- RhalgrsReachNorthEast = 122,
- RhalgrsReachFringesGate = 123,
- RhalgrsReachPeaksGate = 124,
-
- Kugane = 111,
- KuganeShiokazeHostelry = 112,
- KuganePier1 = 113,
- KuganeThavnairianConsulate = 114,
- KuganeMarkets = 115,
- KuganeBokairoInn = 116,
- KuganeRubyBazaar = 117,
- KuganeSekiseigumiBarracks = 118,
- KuganeRakuzaDistrict = 119,
- KuganeRubyPrice = 120,
- KuganeAirship = 126,
-
- FringesCastrumOriens = 98,
- FringesPeeringStones = 99,
- PeaksAlaGannha = 100,
- PeaksAlaGhiri = 101,
- LochsPortaPraetoria = 102,
- LochsAlaMhiganQuarter = 103,
- RubySeaTamamizu = 105,
- RubySeaOnokoro = 106,
- YanxiaNamai = 107,
- YanxiaHouseOfTheFierce = 108,
- AzimSteppeReunion = 109,
- AzimSteppeDawnThrone = 110,
- AzimSteppeDhoroIloh = 128,
-
- DomanEnclave = 127,
- DomanEnclaveNorthern = 129,
- DomanEnclaveSouthern = 130,
- DomanEnclaveOneRiver = 131,
- DomanEnclaveDocks = 162,
-
- Crystarium = 133,
- CrystariumMarkets = 149,
- CrystariumTemenosRookery = 150,
- CrystariumDossalGate = 151,
- CrystariumPendants = 152,
- CrystariumAmaroLaunch = 153,
- CrystariumCrystallineMean = 154,
- CrystariumCabinetOfCuriosity = 155,
- CrystariumTessellation = 156,
-
- Eulmore = 134,
- EulmoreMainstay = 157,
- EulmoreNightsoilPots = 158,
- EulmoreGloryGate = 159,
- EulmoreSoutheastDerelict = 135,
- EulmorePathToGlory = 160,
-
- LakelandFortJobb = 132,
- LakelandOstallImperative = 136,
- KholusiaStilltide = 137,
- KholusiaWright = 138,
- KholusiaTomra = 139,
- AmhAraengMordSouq = 140,
- AmhAraengInnAtJourneysHead = 161,
- AmhAraengTwine = 141,
- RaktikaSlitherbough = 142,
- RaktikaFanow = 143,
- IlMhegLydhaLran = 144,
- IlMhegPiaEnni = 145,
- IlMhegWolekdorf = 146,
- TempestOndoCups = 147,
- TempestMacarensesAngle = 148,
-
- OldSharlayan = 182,
- OldSharlayanStudium = 184,
- OldSharlayanBaldesionAnnex = 185,
- OldSharlayanRostra = 186,
- OldSharlayanLeveilleurEstate = 187,
- OldSharlayanJourneysEnd = 188,
- OldSharlayanScholarsHarbor = 189,
- OldSharlayanHallOfArtifice = 190,
-
- RadzAtHan = 183,
- RadzAtHanMeghaduta = 191,
- RadzAtHanRuveydahFibers = 192,
- RadzAtHanAirship = 193,
- RadzAtHanAlzadaalsPeace = 194,
- RadzAtHanHallOfTheRadiantHost = 195,
- RadzAtHanMehrydesMeyhane = 196,
- RadzAtHanKama = 198,
- RadzAtHanHighCrucible = 199,
- RadzAtHanGateOfFirstSight = 197,
-
- LabyrinthosArcheion = 166,
- LabyrinthosSharlayanHamlet = 167,
- LabyrinthosAporia = 168,
- ThavnairYedlihmad = 169,
- ThavnairGreatWork = 170,
- ThavnairPalakasStand = 171,
- GarlemaldCampBrokenGlass = 172,
- GarlemaldTertium = 173,
- MareLamentorumSinusLacrimarum = 174,
- MareLamentorumBestwaysBurrow = 175,
- ElpisAnagnorisis = 176,
- ElpisTwelveWonders = 177,
- ElpisPoietenOikos = 178,
- UltimaThuleReahTahra = 179,
- UltimaThuleAbodeOfTheEa = 180,
- UltimaThuleBaseOmicron = 181,
-
- Tuliyollal = 216,
-
- TuliyollalDirigibleLanding = 218,
- TuliyollalTheResplendentQuarter = 219,
- TuliyollalTheForardCabins = 220,
- TuliyollalBaysideBevyMarketplace = 221,
- TuliyollalVollokShoonsa = 222,
- TuliyollalWachumeqimeqi = 223,
- TuliyollalBrightploomPost = 224,
- TuliyollalArchOfTheDawnUrqopacha = 225,
- TuliyollalArchOfTheDawnKozamauka = 226,
- TuliyollalIhuykatumu = 227,
- TuliyollalDirigibleLandingYakTel = 228,
- TuliyollalXakTuralSkygate = 229,
-
- SolutionNine = 217,
- SolutionNineInformationCenter = 230,
- SolutionNineTrueVue = 231,
- SolutionNineNeonStein = 232,
- SolutionNineTheArcadion = 233,
- SolutionNineResolution = 234,
- SolutionNineNexusArcade = 235,
- SolutionNineResidentialSector = 236,
- SolutionNineScanningPortNine = 237,
-
- UrqopachaWachunpelo = 200,
- UrqopachaWorlarsEcho = 201,
- KozamaukaOkHanu = 202,
- KozamaukaManyFires = 203,
- KozamaukaEarthenshire = 204,
- YakTelIqBraax = 205,
- YakTelMamook = 206,
- ShaaloaniHhusatahwi = 207,
- ShaaloaniShesheneweziSprings = 208,
- ShaaloaniMehwahhetsoan = 209,
- HeritageFoundYyasulaniStation = 210,
- HeritageFoundTheOutskirts = 211,
- HeritageFoundElectropeStrike = 212,
- LivingMemoryLeynodeMnemo = 213,
- LivingMemoryLeynodePyro = 214,
- LivingMemoryLeynodeAero = 215,
-}
+++ /dev/null
-namespace Questionable.Model.V1;
-
-public enum EDialogChoiceType
-{
- None,
- YesNo,
- List,
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(EmoteConverter))]
-public enum EEmote
-{
- None = 0,
-
- Stretch = 37,
- Wave = 16,
- Rally = 34,
- Deny = 25,
- Pray = 58,
- Slap = 111,
- Doubt = 12,
- Psych = 30,
- Cheer = 6,
- Happy = 48,
- Poke = 28,
- Flex = 139,
- Soothe = 35,
- Me = 23,
- Welcome = 41,
- ImperialSalute = 59,
- Pet = 105,
- Dance = 11,
- Respect = 140,
- Lookout = 22,
- Kneel = 19,
- Bow = 5,
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(EnemySpawnTypeConverter))]
-public enum EEnemySpawnType
-{
- None = 0,
- AfterInteraction,
- AfterItemUse,
- AutoOnEnterArea,
- OverworldEnemies,
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(SkipConditionConverter))]
-public enum EExtraSkipCondition
-{
- None,
- WakingSandsMainArea,
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(InteractionTypeConverter))]
-public enum EInteractionType
-{
- Interact,
- WalkTo,
- AttuneAethernetShard,
- AttuneAetheryte,
- AttuneAetherCurrent,
- Combat,
- UseItem,
- EquipItem,
- Say,
- Emote,
- Action,
- WaitForObjectAtPosition,
- WaitForManualProgress,
- Duty,
- SinglePlayerDuty,
- Jump,
- Dive,
-
- /// <summary>
- /// Needs to be manually continued.
- /// </summary>
- Instruction,
-
- AcceptQuest,
- CompleteQuest,
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(JumpTypeConverter))]
-public enum EJumpType
-{
- SingleJump,
- RepeatedJumps,
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(LockedSkipConditionConverter))]
-public enum ELockedSkipCondition
-{
- Locked,
- Unlocked,
-}
+++ /dev/null
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(QuestWorkModeConverter))]
-public enum EQuestWorkMode
-{
- Bitwise,
- Exact,
-}
+++ /dev/null
-using System;
-
-namespace Questionable.Model.V1;
-
-public class ExcelRef
-{
- private readonly string? _stringValue;
- private readonly uint? _rowIdValue;
-
- public ExcelRef(string value)
- {
- _stringValue = value;
- _rowIdValue = null;
- Type = EType.Key;
- }
-
- public ExcelRef(uint value)
- {
- _stringValue = null;
- _rowIdValue = value;
- Type = EType.RowId;
- }
-
- private ExcelRef(string? stringValue, uint? rowIdValue, EType type)
- {
- _stringValue = stringValue;
- _rowIdValue = rowIdValue;
- Type = type;
- }
-
- public static ExcelRef FromKey(string value) => new(value, null, EType.Key);
- public static ExcelRef FromRowId(uint rowId) => new(null, rowId, EType.RowId);
- public static ExcelRef FromSheetValue(string value) => new(value, null, EType.RawString);
-
- public EType Type { get; }
-
- public string AsKey()
- {
- if (Type != EType.Key)
- throw new InvalidOperationException();
-
- return _stringValue!;
- }
-
- public uint AsRowId()
- {
- if (Type != EType.RowId)
- throw new InvalidOperationException();
-
- return _rowIdValue!.Value;
- }
-
- public string AsRawString()
- {
- if (Type != EType.RawString)
- throw new InvalidOperationException();
-
- return _stringValue!;
- }
-
- public enum EType
- {
- None,
- Key,
- RowId,
- RawString,
- }
-}
+++ /dev/null
-using System.Numerics;
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-public sealed class JumpDestination
-{
- [JsonConverter(typeof(VectorConverter))]
- public Vector3 Position { get; set; }
-
- public float? StopDistance { get; set; }
- public float? DelaySeconds { get; set; }
- public EJumpType Type { get; set; } = EJumpType.SingleJump;
-
- public float CalculateStopDistance() => StopDistance ?? 1f;
-}
+++ /dev/null
-using System.Collections.Generic;
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-public sealed class QuestRoot
-{
- [JsonConverter(typeof(StringListOrValueConverter))]
- public List<string> Author { get; set; } = new();
-
- /// <summary>
- /// This is only relevant for release builds.
- /// </summary>
- public bool Disabled { get; set; }
-
- public string? Comment { get; set; }
- public List<ushort> TerritoryBlacklist { get; set; } = new();
- public List<QuestSequence> QuestSequence { get; set; } = new();
-}
+++ /dev/null
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Questionable.Model.V1;
-
-public sealed class QuestSequence
-{
- public int Sequence { get; set; }
- public string? Comment { get; set; }
- public List<QuestStep> Steps { get; set; } = new();
-
- public QuestStep? FindStep(int step)
- {
- if (step < 0 || step >= Steps.Count)
- return null;
-
- return Steps[step];
- }
-
- public QuestStep? LastStep() => Steps.LastOrDefault();
-}
+++ /dev/null
-using System.Collections.Generic;
-using System.Numerics;
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-public sealed class QuestStep
-{
- public const float DefaultStopDistance = 3f;
-
- public EInteractionType InteractionType { get; set; }
-
- public uint? DataId { get; set; }
-
- [JsonConverter(typeof(VectorConverter))]
- public Vector3? Position { get; set; }
-
- public float? StopDistance { get; set; }
- public float? NpcWaitDistance { get; set; }
- public ushort TerritoryId { get; set; }
- public ushort? TargetTerritoryId { get; set; }
- public float? DelaySecondsAtStart { get; set; }
-
- public bool Disabled { get; set; }
- public bool DisableNavmesh { get; set; }
- public bool? Mount { get; set; }
- public bool? Fly { get; set; }
- public bool? Land { get; set; }
- public bool? Sprint { get; set; }
- public bool? IgnoreDistanceToObject { get; set; }
- public string? Comment { get; set; }
-
- /// <summary>
- /// Only used when attuning to an aetheryte.
- /// </summary>
- public EAetheryteLocation? Aetheryte { get; set; }
-
- /// <summary>
- /// Only used when attuning to an aethernet shard.
- /// </summary>
- [JsonConverter(typeof(AethernetShardConverter))]
- public EAetheryteLocation? AethernetShard { get; set; }
-
- public EAetheryteLocation? AetheryteShortcut { get; set; }
-
- public AethernetShortcut? AethernetShortcut { get; set; }
- public uint? AetherCurrentId { get; set; }
-
- public uint? ItemId { get; set; }
- public bool? GroundTarget { get; set; }
-
- public EEmote? Emote { get; set; }
- public ChatMessage? ChatMessage { get; set; }
- public EAction? Action { get; set; }
-
- public EEnemySpawnType? EnemySpawnType { get; set; }
- public IList<uint> KillEnemyDataIds { get; set; } = new List<uint>();
- public IList<ComplexCombatData> ComplexCombatData { get; set; } = new List<ComplexCombatData>();
- public float? CombatDelaySecondsAtStart { get; set; }
-
- public JumpDestination? JumpDestination { get; set; }
- public uint? ContentFinderConditionId { get; set; }
- public SkipConditions? SkipConditions { get; set; }
-
- public List<List<QuestWorkValue>?> RequiredQuestVariables { get; set; } = new();
- public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
- public IList<DialogueChoice> DialogueChoices { get; set; } = new List<DialogueChoice>();
- public IList<uint> PointMenuChoices { get; set; } = new List<uint>();
-
- // TODO: Not implemented
- public ushort? PickUpQuestId { get; set; }
-
- public ushort? TurnInQuestId { get; set; }
- public ushort? NextQuestId { get; set; }
-
- [JsonConstructor]
- public QuestStep()
- {
- }
-
- public QuestStep(EInteractionType interactionType, uint? dataId, Vector3? position, ushort territoryId)
- {
- InteractionType = interactionType;
- DataId = dataId;
- Position = position;
- TerritoryId = territoryId;
- }
-
- public float CalculateActualStopDistance()
- {
- if (InteractionType == EInteractionType.WalkTo)
- return StopDistance ?? 0.25f;
- if (InteractionType == EInteractionType.AttuneAetheryte)
- return StopDistance ?? 10f;
- else
- return StopDistance ?? DefaultStopDistance;
- }
-}
+++ /dev/null
-using System;
-using System.Text.Json.Serialization;
-using Questionable.Model.V1.Converter;
-
-namespace Questionable.Model.V1;
-
-[JsonConverter(typeof(QuestWorkConfigConverter))]
-public sealed class QuestWorkValue(byte? high, byte? low, EQuestWorkMode mode)
-{
- public QuestWorkValue(byte value)
- : this((byte)(value >> 4), (byte)(value & 0xF), EQuestWorkMode.Bitwise)
- {
- }
-
- public byte? High { get; set; } = high;
- public byte? Low { get; set; } = low;
- public EQuestWorkMode Mode { get; set; } = mode;
-
- public override string ToString()
- {
- if (High != null && Low != null)
- return ((byte)(High << 4) + Low).ToString();
- else if (High != null)
- return High + "H";
- else if (Low != null)
- return Low + "L";
- else
- return "-";
- }
-}
+++ /dev/null
-namespace Questionable.Model.V1;
-
-public sealed class SkipAetheryteCondition
-{
- public bool Never { get; set; }
- public bool InSameTerritory { get; set; }
-}
+++ /dev/null
-namespace Questionable.Model.V1;
-
-public sealed class SkipConditions
-{
- public SkipStepConditions? StepIf { get; set; }
- public SkipAetheryteCondition? AetheryteShortcutIf { get; set; }
- public SkipAetheryteCondition? AethernetShortcutIf { get; set; }
-}
+++ /dev/null
-namespace Questionable.Model.V1;
-
-public sealed class SkipItemConditions
-{
- public bool NotInInventory { get; set; }
-}
+++ /dev/null
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Questionable.Model.V1;
-
-public sealed class SkipStepConditions
-{
- public bool Never { get; set; }
- public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
- public ELockedSkipCondition? Flying { get; set; }
- public ELockedSkipCondition? Chocobo { get; set; }
- public bool NotTargetable { get; set; }
- public List<ushort> InTerritory { get; set; } = new();
- public List<ushort> NotInTerritory { get; set; } = new();
- public SkipItemConditions? Item { get; set; }
- public List<ushort> QuestsAccepted { get; set; } = new();
- public List<ushort> QuestsCompleted { get; set; } = new();
- public EExtraSkipCondition? ExtraCondition { get; set; }
-
- public bool HasSkipConditions()
- {
- if (Never)
- return false;
- return (CompletionQuestVariablesFlags.Count > 0 && CompletionQuestVariablesFlags.Any(x => x != null)) ||
- Flying != null ||
- Chocobo != null ||
- NotTargetable ||
- InTerritory.Count > 0 ||
- NotInTerritory.Count > 0 ||
- Item != null ||
- QuestsAccepted.Count > 0 ||
- QuestsCompleted.Count > 0 ||
- ExtraCondition != null;
- }
-
- public override string ToString()
- {
- return
- $"{nameof(Never)}: {Never}, {nameof(CompletionQuestVariablesFlags)}: {CompletionQuestVariablesFlags}, {nameof(Flying)}: {Flying}, {nameof(Chocobo)}: {Chocobo}, {nameof(NotTargetable)}: {NotTargetable}, {nameof(InTerritory)}: {string.Join(" ", InTerritory)}, {nameof(NotInTerritory)}: {string.Join(" ", NotInTerritory)}, {nameof(Item)}: {Item}, {nameof(QuestsAccepted)}: {string.Join(" ", QuestsAccepted)}, {nameof(QuestsCompleted)}: {string.Join(" ", QuestsCompleted)}, {nameof(ExtraCondition)}: {ExtraCondition}";
- }
-}
--- /dev/null
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json",
+ "$defs": {
+ "Aetheryte": {
+ "type": "string",
+ "enum": [
+ "Gridania",
+ "Central Shroud - Bentbranch Meadows",
+ "East Shroud - Hawthorne Hut",
+ "South Shroud - Quarrymill",
+ "South Shroud - Camp Tranquil",
+ "North Shroud - Fallgourd Float",
+ "Ul'dah",
+ "Western Thanalan - Horizon",
+ "Central Thanalan - Black Brush Station",
+ "Eastern Thanalan - Camp Drybone",
+ "Southern Thanalan - Little Ala Mhigo",
+ "Southern Thanalan - Forgotten Springs",
+ "Northern Thanalan - Camp Bluefog",
+ "Northern Thanalan - Ceruleum Processing Plant",
+ "Limsa Lominsa",
+ "Middle La Noscea - Summerford Farms",
+ "Lower La Noscea - Moraby Drydocks",
+ "Eastern La Noscea - Costa Del Sol",
+ "Eastern La Noscea - Wineport",
+ "Western La Noscea - Swiftperch",
+ "Western La Noscea - Aleport",
+ "Upper La Noscea - Camp Bronze Lake",
+ "Outer La Noscea - Camp Overlook",
+ "Coerthas Central Highlands - Camp Dragonhead",
+ "Mor Dhona",
+ "Gold Saucer",
+ "Wolves' Den Pier",
+ "Ishgard",
+ "Idyllshire",
+ "Coerthas Western Highlands - Falcon's Nest",
+ "The Sea of Clouds - Camp Cloudtop",
+ "The Sea of Clouds - Ok' Zundu",
+ "Azys Lla - Helix",
+ "The Dravanian Forelands - Tailfeather",
+ "The Dravanian Forelands - Anyx Trine",
+ "The Churning Mists - Moghome",
+ "The Churning Mists - Zenith",
+ "Rhalgr's Reach",
+ "Fringes - Castrum Oriens",
+ "Fringes - Peering Stones",
+ "Peaks - Ala Gannha",
+ "Peaks - Ala Ghiri",
+ "Lochs - Porta Praetoria",
+ "Lochs - Ala Mhigan Quarter",
+ "Kugane",
+ "Ruby Sea - Tamamizu",
+ "Ruby Sea - Onokoro",
+ "Yanxia - Namai",
+ "Yanxia - House of the Fierce",
+ "Azim Steppe - Reunion",
+ "Azim Steppe - Dawn Throne",
+ "Azim Steppe - Dhoro Iloh",
+ "Doman Enclave",
+ "Crystarium",
+ "Eulmore",
+ "Lakeland - Fort Jobb",
+ "Lakeland - Ostall Imperative",
+ "Kholusia - Stilltide",
+ "Kholusia - Wright",
+ "Kholusia - Tomra",
+ "Amh Araeng - Mord Souq",
+ "Amh Araeng - Inn at Journey's Head",
+ "Amh Araeng - Twine",
+ "Rak'tika - Slitherbough",
+ "Rak'tika - Fanow",
+ "Il Mheg - Lydha Lran",
+ "Il Mheg - Pia Enni",
+ "Il Mheg - Wolekdorf",
+ "Tempest - Ondo Cups",
+ "Tempest - Macarenses Angle",
+ "Old Sharlayan",
+ "Radz-at-Han",
+ "Labyrinthos - Archeion",
+ "Labyrinthos - Sharlayan Hamlet",
+ "Labyrinthos - Aporia",
+ "Thavnair - Yedlihmad",
+ "Thavnair - Great Work",
+ "Thavnair - Palaka's Stand",
+ "Garlemald - Camp Broken Glass",
+ "Garlemald - Tertium",
+ "Mare Lamentorum - Sinus Lacrimarum",
+ "Mare Lamentorum - Bestways Burrow",
+ "Elpis - Anagnorisis",
+ "Elpis - Twelve Wonders",
+ "Elpis - Poieten Oikos",
+ "Ultima Thule - Reah Tahra",
+ "Ultima Thule - Abode of the Ea",
+ "Ultima Thule - Base Omicron",
+ "Tuliyollal",
+ "Solution Nine",
+ "Urqopacha - Wachunpelo",
+ "Urqopacha - Worlar's Echo",
+ "Kozama'uka - Ok'hanu",
+ "Kozama'uka - Many Fires",
+ "Kozama'uka - Earthenshire",
+ "Yak T'el - Iq Br'aax",
+ "Yak T'el - Mamook",
+ "Shaaloani - Hhusatahwi",
+ "Shaaloani - Sheshenewezi Springs",
+ "Shaaloani - Mehwahhetsoan",
+ "Heritage Found - Yyasulani Station",
+ "Heritage Found - The Outskirts",
+ "Heritage Found - Electrope Strike",
+ "Living Memory - Leynode Mnemo",
+ "Living Memory - Leynode Pyro",
+ "Living Memory - Leynode Aero"
+ ]
+ },
+ "AethernetShard": {
+ "type": "string",
+ "enum": [
+ "[Gridania] Aetheryte Plaza",
+ "[Gridania] Archers' Guild",
+ "[Gridania] Leatherworkers' Guild & Shaded Bower",
+ "[Gridania] Lancers' Guild",
+ "[Gridania] Conjurers' Guild",
+ "[Gridania] Botanists' Guild",
+ "[Gridania] Mih Khetto's Amphitheatre",
+ "[Gridania] Blue Badger Gate (Central Shroud)",
+ "[Gridania] Yellow Serpent Gate (North Shroud)",
+ "[Gridania] White Wolf Gate (Central Shroud)",
+ "[Gridania] Airship Landing",
+ "[Ul'dah] Aetheryte Plaza",
+ "[Ul'dah] Adventurers' Guild",
+ "[Ul'dah] Thaumaturges' Guild",
+ "[Ul'dah] Gladiators' Guild",
+ "[Ul'dah] Miners' Guild",
+ "[Ul'dah] Weavers' Guild",
+ "[Ul'dah] Goldsmiths' Guild",
+ "[Ul'dah] Sapphire Avenue Exchange",
+ "[Ul'dah] Alchemists' Guild",
+ "[Ul'dah] Gate of the Sultana (Western Thanalan)",
+ "[Ul'dah] Gate of Nald (Central Thanalan)",
+ "[Ul'dah] Gate of Thal (Central Thanalan)",
+ "[Ul'dah] The Chamber of Rule",
+ "[Ul'dah] Airship Landing",
+ "[Limsa Lominsa] Aetheryte Plaza",
+ "[Limsa Lominsa] Arcanists' Guild",
+ "[Limsa Lominsa] Fishermens' Guild",
+ "[Limsa Lominsa] Hawkers' Alley",
+ "[Limsa Lominsa] The Aftcastle",
+ "[Limsa Lominsa] Culinarians' Guild",
+ "[Limsa Lominsa] Marauders' Guild",
+ "[Limsa Lominsa] Zephyr Gate (Middle La Noscea)",
+ "[Limsa Lominsa] Tempest Gate (Lower La Noscea)",
+ "[Limsa Lominsa] Airship Landing",
+ "[Ishgard] Aetheryte Plaza",
+ "[Ishgard] The Forgotten Knight",
+ "[Ishgard] Skysteel Manufactory",
+ "[Ishgard] The Brume",
+ "[Ishgard] Athenaeum Astrologicum",
+ "[Ishgard] The Jeweled Crozier",
+ "[Ishgard] Saint Reymanaud's Cathedral",
+ "[Ishgard] The Tribunal",
+ "[Ishgard] The Last Vigil",
+ "[Ishgard] The Gates of Judgement (Coerthas Central Highlands)",
+ "[Idyllshire] Aetheryte Plaza",
+ "[Idyllshire] West Idyllshire",
+ "[Idyllshire] Prologue Gate (Western Hinterlands)",
+ "[Idyllshire] Epilogue Gate (Eastern Hinterlands)",
+ "[Rhalgr's Reach] Aetheryte Plaza",
+ "[Rhalgr's Reach] Western Rhalgr's Reach",
+ "[Rhalgr's Reach] Northeastern Rhalgr's Reach",
+ "[Rhalgr's Reach] Fringes Gate",
+ "[Rhalgr's Reach] Peaks Gate",
+ "[Kugane] Aetheryte Plaza",
+ "[Kugane] Shiokaze Hostelry",
+ "[Kugane] Pier #1",
+ "[Kugane] Thavnairian Consulate",
+ "[Kugane] Kogane Dori Markets",
+ "[Kugane] Bokairo Inn",
+ "[Kugane] The Ruby Bazaar",
+ "[Kugane] Sekiseigumi Barracks",
+ "[Kugane] Rakuza District",
+ "[Kugane] The Ruby Price",
+ "[Kugane] Airship Landing",
+ "[Crystarium] Aetheryte Plaza",
+ "[Crystarium] Musica Universalis Markets",
+ "[Crystarium] Temenos Rookery",
+ "[Crystarium] The Dossal Gate",
+ "[Crystarium] The Pendants",
+ "[Crystarium] The Amaro Launch",
+ "[Crystarium] The Crystalline Mean",
+ "[Crystarium] The Cabinet of Curiosity",
+ "[Crystarium] Tessellation (Lakeland)",
+ "[Eulmore] Aetheryte Plaza",
+ "[Eulmore] Southeast Derelicts",
+ "[Eulmore] Nightsoil Pots",
+ "[Eulmore] The Glory Gate",
+ "[Eulmore] The Mainstay",
+ "[Eulmore] The Path to Glory (Kholusia)",
+ "[Old Sharlayan] Aetheryte Plaza",
+ "[Old Sharlayan] The Studium",
+ "[Old Sharlayan] The Baldesion Annex",
+ "[Old Sharlayan] The Rostra",
+ "[Old Sharlayan] The Leveilleur Estate",
+ "[Old Sharlayan] Journey's End",
+ "[Old Sharlayan] Scholar's Harbor",
+ "[Old Sharlayan] The Hall of Artifice (Labyrinthos)",
+ "[Radz-at-Han] Aetheryte Plaza",
+ "[Radz-at-Han] Meghaduta",
+ "[Radz-at-Han] Ruveydah Fibers",
+ "[Radz-at-Han] Airship Landing",
+ "[Radz-at-Han] Alzadaal's Peace",
+ "[Radz-at-Han] Hall of the Radiant Host",
+ "[Radz-at-Han] Mehryde's Meyhane",
+ "[Radz-at-Han] Kama",
+ "[Radz-at-Han] The High Crucible of Al-Kimiya",
+ "[Radz-at-Han] The Gate of First Sight (Thavnair)",
+ "[Tuliyollal] Aetheryte Plaza",
+ "[Tuliyollal] Dirigible Landing",
+ "[Tuliyollal] The Resplendent Quarter",
+ "[Tuliyollal] The For'ard Cabins",
+ "[Tuliyollal] Bayside Bevy Marketplace",
+ "[Tuliyollal] Vollok Shoonsa",
+ "[Tuliyollal] Wachumeqimeqi",
+ "[Tuliyollal] Brightploom Post",
+ "[Tuliyollal] Arch of the Dawn (Urqopacha)",
+ "[Tuliyollal] Arch of the Dawn (Kozama'uka)",
+ "[Tuliyollal] Ihuykatumu (Kozama'uka)",
+ "[Tuliyollal] Dirigible Landing (Yak T'el)",
+ "[Tuliyollal] Xak Tural Skygate (Shaaloani)",
+ "[Solution Nine] Aetheryte Plaza",
+ "[Solution Nine] Information Center",
+ "[Solution Nine] True Vue",
+ "[Solution Nine] Neon Stein",
+ "[Solution Nine] The Arcadion",
+ "[Solution Nine] Resolution",
+ "[Solution Nine] Nexus Arcade",
+ "[Solution Nine] Residential Sector",
+ "[Solution Nine] Scanning Port Nine (Heritage Found)"
+ ]
+ },
+ "CompletionFlags": {
+ "type": "array",
+ "description": "Quest Variables that dictate whether or not this step is skipped: null is don't check, positive values need to be set, negative values need to be unset",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "High": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 0,
+ "maximum": 15
+ },
+ "Low": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 0,
+ "maximum": 15
+ },
+ "Negative": {
+ "type": "boolean"
+ },
+ "Mode": {
+ "type": "string",
+ "enum": [
+ "Bitwise",
+ "Exact"
+ ]
+ }
+ }
+ },
+ {
+ "type": "number",
+ "enum": [
+ 1,
+ 2,
+ 4,
+ 8,
+ 16,
+ 32,
+ 64,
+ 128
+ ]
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "minItems": 6,
+ "maxItems": 6
+ }
+ }
+}
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuestPathGenerator.Tests", "QuestPathGenerator.Tests\QuestPathGenerator.Tests.csproj", "{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GatheringPaths", "GatheringPaths\GatheringPaths.csproj", "{8BF98BEF-6F00-4197-91ED-75F8F1C35FFB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GatheringPathRenderer", "GatheringPathRenderer\GatheringPathRenderer.csproj", "{F514DA95-9867-4F3F-8062-ACE0C62E8740}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|x64.Build.0 = Debug|x64
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|x64.ActiveCfg = Release|x64
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|x64.Build.0 = Release|x64
+ {8BF98BEF-6F00-4197-91ED-75F8F1C35FFB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8BF98BEF-6F00-4197-91ED-75F8F1C35FFB}.Debug|x64.Build.0 = Debug|Any CPU
+ {8BF98BEF-6F00-4197-91ED-75F8F1C35FFB}.Release|x64.ActiveCfg = Release|Any CPU
+ {8BF98BEF-6F00-4197-91ED-75F8F1C35FFB}.Release|x64.Build.0 = Release|Any CPU
+ {F514DA95-9867-4F3F-8062-ACE0C62E8740}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F514DA95-9867-4F3F-8062-ACE0C62E8740}.Debug|x64.Build.0 = Debug|Any CPU
+ {F514DA95-9867-4F3F-8062-ACE0C62E8740}.Release|x64.ActiveCfg = Release|Any CPU
+ {F514DA95-9867-4F3F-8062-ACE0C62E8740}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
using FFXIVClientStructs.FFXIV.Client.System.String;
using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.Logging;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable;
using Microsoft.Extensions.Logging;
using Questionable.Controller.CombatModules;
using Questionable.Controller.Utils;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller;
using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.Logging;
using Questionable.Data;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
using Quest = Questionable.Model.Quest;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
using Questionable.Controller.NavigationOverrides;
using Questionable.External;
using Questionable.Model;
-using Questionable.Model.V1;
-using Questionable.Model.V1.Converter;
+using Questionable.Model.Common;
+using Questionable.Model.Common.Converter;
+using Questionable.Model.Questing;
+using Questionable.Model.Questing.Converter;
namespace Questionable.Controller;
using Questionable.Controller.Steps.Shared;
using Questionable.External;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
using Questionable.QuestPaths;
using Questionable.Validation;
using Questionable.Validation.Validators;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Common;
using System.Collections.Generic;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using System;
-using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
namespace Questionable.Controller.Steps.Interactions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Questionable.Controller.Steps.Shared;
using Questionable.Controller.Utils;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.DependencyInjection;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Microsoft.Extensions.DependencyInjection;
using Questionable.Controller.Steps.Common;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
using Quest = Questionable.Model.Quest;
namespace Questionable.Controller.Steps.Interactions;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Shared;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Microsoft.Extensions.DependencyInjection;
using Questionable.Controller.Steps.Common;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Microsoft.Extensions.DependencyInjection;
using Questionable.External;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Interactions;
using Questionable.Controller.Utils;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
using AethernetShortcut = Questionable.Controller.Steps.Shared.AethernetShortcut;
namespace Questionable.Controller.Steps.Interactions;
using Questionable.Data;
using Questionable.External;
using Questionable.Model;
-using Questionable.Model.V1;
-using Questionable.Model.V1.Converter;
+using Questionable.Model.Common;
+using Questionable.Model.Common.Converter;
+using Questionable.Model.Questing;
+using Questionable.Model.Questing.Converter;
namespace Questionable.Controller.Steps.Shared;
using Questionable.Controller.Steps.Common;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
using Questionable.Controller.Steps.Common;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Utils;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
using Questionable.Controller.Utils;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
using Microsoft.Extensions.DependencyInjection;
using Questionable.Controller.Steps.Common;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Steps.Shared;
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Shared;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Controller.Utils;
using System.Numerics;
using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets;
-using Microsoft.Extensions.Logging;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
namespace Questionable.Data;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Questionable.Data;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
namespace Questionable.External;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
using Action = Lumina.Excel.GeneratedSheets2.Action;
using BattleChara = FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara;
using ContentFinderCondition = Lumina.Excel.GeneratedSheets.ContentFinderCondition;
using System.Collections.Generic;
using System.Linq;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Model;
using System.Linq;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Validation.Validators;
using System.Collections.Generic;
using System.Linq;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Validation.Validators;
using System.Numerics;
using Questionable.Controller.Utils;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Validation.Validators;
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Globalization;
using System.Text.Json.Nodes;
using Json.Schema;
private readonly Dictionary<ushort, JsonNode> _questNodes = new();
private JsonSchema? _questSchema;
+ public JsonSchemaValidator()
+ {
+ SchemaRegistry.Global.Register(
+ new Uri("https://git.carvel.li/liza/Questionable/raw/branch/master/Questionable.Model/common-schema.json"),
+ JsonSchema.FromStream(AssemblyModelLoader.CommonSchema).AsTask().Result);
+ }
+
public IEnumerable<ValidationIssue> Validate(Quest quest)
{
_questSchema ??= JsonSchema.FromStream(AssemblyQuestLoader.QuestSchema).AsTask().Result;
};
}
}
-
}
public void Enqueue(ushort questId, JsonNode questNode) => _questNodes[questId] = questNode;
using System.Collections.Generic;
using System.Linq;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Validation.Validators;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Windows;
using ImGuiNET;
using Questionable.Controller;
using Questionable.Controller.Steps.Shared;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
namespace Questionable.Windows.QuestComponents;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
namespace Questionable.Windows.QuestComponents;
"Left click: Copy target position as JSON.\nRight click: Copy target position as C# code.");
if (copy)
{
- string interactionType = gameObject->NamePlateIconId switch
+ var target = _targetManager.Target;
+ if (target.ObjectKind == ObjectKind.GatheringPoint)
{
- 71201 or 71211 or 71221 or 71231 or 71341 or 71351 => "AcceptQuest",
- 71202 or 71212 or 71222 or 71232 or 71342 or 71352 => "AcceptQuest", // repeatable
- 71205 or 71215 or 71225 or 71235 or 71345 or 71355 => "CompleteQuest",
- _ => "Interact",
- };
- ImGui.SetClipboardText($$"""
- "DataId": {{_targetManager.Target.DataId}},
- "Position": {
- "X": {{_targetManager.Target.Position.X.ToString(CultureInfo.InvariantCulture)}},
- "Y": {{_targetManager.Target.Position.Y.ToString(CultureInfo.InvariantCulture)}},
- "Z": {{_targetManager.Target.Position.Z.ToString(CultureInfo.InvariantCulture)}}
- },
- "TerritoryId": {{_clientState.TerritoryType}},
- "InteractionType": "{{interactionType}}"
- """);
+ ImGui.SetClipboardText($$"""
+ "DataId": {{target.DataId}},
+ "Position": {
+ "X": {{target.Position.X.ToString(CultureInfo.InvariantCulture)}},
+ "Y": {{target.Position.Y.ToString(CultureInfo.InvariantCulture)}},
+ "Z": {{target.Position.Z.ToString(CultureInfo.InvariantCulture)}}
+ }
+ """);
+ }
+ else
+ {
+ string interactionType = gameObject->NamePlateIconId switch
+ {
+ 71201 or 71211 or 71221 or 71231 or 71341 or 71351 => "AcceptQuest",
+ 71202 or 71212 or 71222 or 71232 or 71342 or 71352 => "AcceptQuest", // repeatable
+ 71205 or 71215 or 71225 or 71235 or 71345 or 71355 => "CompleteQuest",
+ _ => "Interact",
+ };
+ ImGui.SetClipboardText($$"""
+ "DataId": {{target.DataId}},
+ "Position": {
+ "X": {{target.Position.X.ToString(CultureInfo.InvariantCulture)}},
+ "Y": {{target.Position.Y.ToString(CultureInfo.InvariantCulture)}},
+ "Z": {{target.Position.Z.ToString(CultureInfo.InvariantCulture)}}
+ },
+ "TerritoryId": {{_clientState.TerritoryType}},
+ "InteractionType": "{{interactionType}}"
+ """);
+ }
}
else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
using Questionable.Controller;
using Questionable.Data;
using Questionable.Model;
-using Questionable.Model.V1;
+using Questionable.Model.Questing;
using Questionable.Windows.QuestComponents;
namespace Questionable.Windows;