diff --git a/.gitignore b/.gitignore index 05dc5493..f0fb17c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -/.idea +obj/ +bin/ +/.idea +/.vs *.user diff --git a/QuestPathGenerator/QuestPathGenerator.csproj b/QuestPathGenerator/QuestPathGenerator.csproj new file mode 100644 index 00000000..b62d2878 --- /dev/null +++ b/QuestPathGenerator/QuestPathGenerator.csproj @@ -0,0 +1,37 @@ + + + netstandard2.0 + false + 12 + enable + true + Questionable.QuestPathGenerator + portable + + true + true + + QuestPathGenerator + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + diff --git a/QuestPathGenerator/QuestSourceGenerator.cs b/QuestPathGenerator/QuestSourceGenerator.cs new file mode 100644 index 00000000..9bc6cc29 --- /dev/null +++ b/QuestPathGenerator/QuestSourceGenerator.cs @@ -0,0 +1,316 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Questionable.Model.V1; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static Questionable.QuestPathGenerator.RoslynShortcuts; + +namespace Questionable.QuestPathGenerator; + +/// +/// A sample source generator that creates C# classes based on the text file (in this case, Domain Driven Design ubiquitous language registry). +/// When using a simple text file as a baseline, we can create a non-incremental source generator. +/// +[Generator] +public class QuestSourceGenerator : ISourceGenerator +{ + public void Initialize(GeneratorInitializationContext context) + { + // No initialization required for this generator. + } + + public void Execute(GeneratorExecutionContext context) + { + List<(ushort, QuestData)> quests = []; + + // Go through all files marked as an Additional File in file properties. + foreach (var additionalFile in context.AdditionalFiles) + { + if (additionalFile == null) + continue; + + if (Path.GetExtension(additionalFile.Path) != ".json") + continue; + + string name = Path.GetFileName(additionalFile.Path); + ushort id = ushort.Parse(name.Substring(0, name.IndexOf('_'))); + + var text = additionalFile.GetText(); + if (text == null) + continue; + + var quest = JsonSerializer.Deserialize(text.ToString())!; + quests.Add((id, quest)); + } + + quests = quests.OrderBy(x => x.Item1).ToList(); + + 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("V1"))) + })) + .WithMembers( + SingletonList( + FileScopedNamespaceDeclaration( + QualifiedName( + IdentifierName("Questionable"), + IdentifierName("QuestPaths"))) + .WithMembers( + SingletonList( + ClassDeclaration("AssemblyQuestLoader") + .WithModifiers( + TokenList( + [ + Token(SyntaxKind.PartialKeyword) + ])) + .WithMembers( + SingletonList( + FieldDeclaration( + VariableDeclaration( + GenericName( + Identifier("IReadOnlyDictionary")) + .WithTypeArgumentList( + TypeArgumentList( + SeparatedList( + new SyntaxNodeOrToken[] + { + PredefinedType( + Token(SyntaxKind + .UShortKeyword)), + Token(SyntaxKind.CommaToken), + IdentifierName("QuestData") + })))) + .WithVariables( + SingletonSeparatedList( + VariableDeclarator( + Identifier("Quests")) + .WithInitializer( + EqualsValueClause( + ObjectCreationExpression( + GenericName( + Identifier( + "Dictionary")) + .WithTypeArgumentList( + TypeArgumentList( + SeparatedList< + TypeSyntax>( + new + SyntaxNodeOrToken + [] + { + PredefinedType( + Token( + SyntaxKind + .UShortKeyword)), + Token( + SyntaxKind + .CommaToken), + IdentifierName( + "QuestData") + })))) + .WithArgumentList( + ArgumentList()) + .WithInitializer( + InitializerExpression( + SyntaxKind + .CollectionInitializerExpression, + SeparatedList< + ExpressionSyntax>( + quests.SelectMany(x => + CreateQuestInitializer( + x.Item1, + x.Item2) + .ToArray()))))))))) + .WithModifiers( + TokenList( + [ + Token(SyntaxKind.InternalKeyword), + Token(SyntaxKind.StaticKeyword) + ])))))))) + .NormalizeWhitespace(); + + // Add the source code to the compilation. + context.AddSource("AssemblyQuestLoader.g.cs", code.ToFullString()); + } + + private static IEnumerable CreateQuestInitializer(ushort questId, QuestData quest) + { + return new SyntaxNodeOrToken[] + { + InitializerExpression( + SyntaxKind.ComplexElementInitializerExpression, + SeparatedList( + new SyntaxNodeOrToken[] + { + LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(questId)), + Token(SyntaxKind.CommaToken), + ObjectCreationExpression( + IdentifierName(nameof(QuestData))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + SyntaxNodeList( + Assignment(nameof(QuestData.Author), quest.Author, null) + .AsSyntaxNodeOrToken(), + AssignmentList(nameof(QuestData.Contributors), quest.Contributors) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestData.Comment), quest.Comment, null) + .AsSyntaxNodeOrToken(), + AssignmentList(nameof(QuestData.TerritoryBlacklist), + quest.TerritoryBlacklist).AsSyntaxNodeOrToken(), + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(nameof(QuestData.QuestSequence)), + CreateQuestSequence(quest.QuestSequence)) + )))) + })), + Token(SyntaxKind.CommaToken) + }; + } + + private static ExpressionSyntax CreateQuestSequence(List sequences) + { + return CollectionExpression( + SeparatedList( + sequences.SelectMany(sequence => new SyntaxNodeOrToken[] + { + ExpressionElement( + ObjectCreationExpression( + IdentifierName(nameof(QuestSequence))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + SyntaxNodeList( + Assignment(nameof(QuestSequence.Sequence), sequence.Sequence, null) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestSequence.Comment), sequence.Comment, null) + .AsSyntaxNodeOrToken(), + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(nameof(QuestSequence.Steps)), + CreateQuestSteps(sequence.Steps))))))), + Token(SyntaxKind.CommaToken), + }.ToArray()))); + } + + private static ExpressionSyntax CreateQuestSteps(List steps) + { + QuestStep emptyStep = new(); + return CollectionExpression( + SeparatedList( + steps.SelectMany(step => new SyntaxNodeOrToken[] + { + ExpressionElement( + ObjectCreationExpression( + IdentifierName(nameof(QuestStep))) + .WithArgumentList( + ArgumentList( + SeparatedList( + new SyntaxNodeOrToken[] + { + Argument(LiteralValue(step.InteractionType)), + Token(SyntaxKind.CommaToken), + Argument(LiteralValue(step.DataId)), + Token(SyntaxKind.CommaToken), + Argument(LiteralValue(step.Position)), + Token(SyntaxKind.CommaToken), + Argument(LiteralValue(step.TerritoryId)) + }))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + SyntaxNodeList( + Assignment(nameof(QuestStep.StopDistance), step.StopDistance, + emptyStep.StopDistance) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.TargetTerritoryId), step.TargetTerritoryId, + emptyStep.TargetTerritoryId) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.Disabled), step.Disabled, emptyStep.Disabled) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.DisableNavmesh), step.DisableNavmesh, + emptyStep.DisableNavmesh) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.Mount), step.Mount, emptyStep.Mount) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.Fly), step.Fly, emptyStep.Fly) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.Sprint), step.Sprint, emptyStep.Sprint) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.Comment), step.Comment, emptyStep.Comment) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.AetheryteShortcut), step.AetheryteShortcut, + emptyStep.AetheryteShortcut) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.AethernetShortcut), step.AethernetShortcut, + emptyStep.AethernetShortcut) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.AetherCurrentId), step.AetherCurrentId, + emptyStep.AetherCurrentId) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.ItemId), step.ItemId, emptyStep.ItemId) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.GroundTarget), step.GroundTarget, + emptyStep.GroundTarget) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.Emote), step.Emote, emptyStep.Emote) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.ChatMessage), step.ChatMessage, + emptyStep.ChatMessage) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.EnemySpawnType), step.EnemySpawnType, + emptyStep.EnemySpawnType) + .AsSyntaxNodeOrToken(), + AssignmentList(nameof(QuestStep.KillEnemyDataIds), step.KillEnemyDataIds) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.JumpDestination), step.JumpDestination, + emptyStep.JumpDestination) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestStep.ContentFinderConditionId), + step.ContentFinderConditionId, emptyStep.ContentFinderConditionId) + .AsSyntaxNodeOrToken(), + AssignmentList(nameof(QuestStep.SkipIf), step.SkipIf) + .AsSyntaxNodeOrToken(), + AssignmentList(nameof(QuestStep.CompletionQuestVariablesFlags), + step.CompletionQuestVariablesFlags) + .AsSyntaxNodeOrToken(), + AssignmentList(nameof(QuestStep.DialogueChoices), step.DialogueChoices) + .AsSyntaxNodeOrToken()))))), + Token(SyntaxKind.CommaToken), + }.ToArray()))); + } +} diff --git a/QuestPathGenerator/RoslynShortcuts.cs b/QuestPathGenerator/RoslynShortcuts.cs new file mode 100644 index 00000000..545cb27a --- /dev/null +++ b/QuestPathGenerator/RoslynShortcuts.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Questionable.Model.V1; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Questionable.QuestPathGenerator; + +public static class RoslynShortcuts +{ + public static IEnumerable SyntaxNodeList(params SyntaxNodeOrToken?[] nodes) + { + nodes = nodes.Where(x => x != null).ToArray(); + if (nodes.Length == 0) + return []; + + List list = new(); + for (int i = 0; i < nodes.Length; ++i) + { + if (i > 0) + list.Add(Token(SyntaxKind.CommaToken)); + list.Add(nodes[i].GetValueOrDefault()); + } + + return list; + } + + public static ExpressionSyntax LiteralValue(T? value) + { + if (value is string s) + return LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(s)); + else if (value is bool b) + return LiteralExpression(b ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression); + else if (value is short i16) + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(i16)); + else if (value is int i32) + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(i32)); + else if (value is ushort u16) + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(u16)); + else if (value is uint u32) + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(u32)); + else if (value is float f) + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(f)); + else if (value != null && value.GetType().IsEnum) + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(value.GetType().Name), + IdentifierName(value.GetType().GetEnumName(value)!)); + else if (value is Vector3 vector) + { + return ObjectCreationExpression( + IdentifierName(nameof(Vector3))) + .WithArgumentList( + ArgumentList( + SeparatedList( + new SyntaxNodeOrToken[] + { + Argument(LiteralValue(vector.X)), + Token(SyntaxKind.CommaToken), + Argument(LiteralValue(vector.Y)), + Token(SyntaxKind.CommaToken), + Argument(LiteralValue(vector.Z)) + }))); + } + else if (value is AethernetShortcut aethernetShortcut) + { + return ObjectCreationExpression( + IdentifierName(nameof(AethernetShortcut))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + SyntaxNodeList( + Assignment(nameof(AethernetShortcut.From), aethernetShortcut.From, + null) + .AsSyntaxNodeOrToken(), + Assignment(nameof(AethernetShortcut.To), aethernetShortcut.To, + null) + .AsSyntaxNodeOrToken())))); + } + else if (value is ChatMessage chatMessage) + { + ChatMessage emptyMessage = new(); + return ObjectCreationExpression( + IdentifierName(nameof(ChatMessage))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + SyntaxNodeList( + Assignment(nameof(ChatMessage.ExcelSheet), chatMessage.ExcelSheet, + emptyMessage.ExcelSheet) + .AsSyntaxNodeOrToken(), + Assignment(nameof(ChatMessage.Key), chatMessage.Key, + emptyMessage.Key) + .AsSyntaxNodeOrToken())))); + } + else if (value is DialogueChoice dialogueChoice) + { + DialogueChoice emptyChoice = new(); + return ObjectCreationExpression( + IdentifierName(nameof(DialogueChoice))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + SyntaxNodeList( + Assignment(nameof(DialogueChoice.Type), dialogueChoice.Type, null) + .AsSyntaxNodeOrToken(), + Assignment(nameof(DialogueChoice.ExcelSheet), dialogueChoice.ExcelSheet, + emptyChoice.ExcelSheet) + .AsSyntaxNodeOrToken(), + Assignment(nameof(DialogueChoice.Prompt), dialogueChoice.Prompt, emptyChoice.Prompt) + .AsSyntaxNodeOrToken(), + Assignment(nameof(DialogueChoice.Yes), dialogueChoice.Yes, emptyChoice.Yes) + .AsSyntaxNodeOrToken(), + Assignment(nameof(DialogueChoice.Answer), dialogueChoice.Answer, emptyChoice.Answer) + .AsSyntaxNodeOrToken(), + Assignment(nameof(DialogueChoice.DataId), dialogueChoice.DataId, emptyChoice.DataId) + .AsSyntaxNodeOrToken())))); + } + else if (value is JumpDestination jumpDestination) + { + return ObjectCreationExpression( + IdentifierName(nameof(JumpDestination))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + SyntaxNodeList( + Assignment(nameof(JumpDestination.Position), jumpDestination.Position, null) + .AsSyntaxNodeOrToken(), + Assignment(nameof(JumpDestination.StopDistance), jumpDestination.StopDistance, null) + .AsSyntaxNodeOrToken(), + Assignment(nameof(JumpDestination.DelaySeconds), jumpDestination.DelaySeconds, null) + .AsSyntaxNodeOrToken())))); + } + else if (value is ExcelRef excelRef) + { + if (excelRef.Type == ExcelRef.EType.Key) + { + return ObjectCreationExpression( + IdentifierName(nameof(ExcelRef))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument(LiteralValue(excelRef.AsKey()))))); + } + else if (excelRef.Type == ExcelRef.EType.RowId) + { + return ObjectCreationExpression( + IdentifierName(nameof(ExcelRef))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument(LiteralValue(excelRef.AsRowId()))))); + } + else + throw new Exception($"Unsupported ExcelRef type {excelRef.Type}"); + } + else if (value is null) + return LiteralExpression(SyntaxKind.NullLiteralExpression); + else + throw new Exception($"Unsupported data type {value.GetType()} = {value}"); + } + + public static AssignmentExpressionSyntax? Assignment(string name, T? value, T? defaultValue) + { + if (value == null && defaultValue == null) + return null; + + if (value != null && defaultValue != null && value.Equals(defaultValue)) + return null; + + return AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(name), + LiteralValue(value)); + } + + public static AssignmentExpressionSyntax? AssignmentList(string name, IEnumerable value) + { + IEnumerable list = value.ToList(); + if (!list.Any()) + return null; + + return AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(name), + CollectionExpression( + SeparatedList( + SyntaxNodeList(list.Select(x => ExpressionElement( + LiteralValue(x)).AsSyntaxNodeOrToken()).ToArray()) + ))); + } + + public static SyntaxNodeOrToken? AsSyntaxNodeOrToken(this SyntaxNode? node) + { + if (node == null) + return null; + + return node; + } +} diff --git a/QuestPathGenerator/packages.lock.json b/QuestPathGenerator/packages.lock.json new file mode 100644 index 00000000..a9d05083 --- /dev/null +++ b/QuestPathGenerator/packages.lock.json @@ -0,0 +1,244 @@ +{ + "version": 1, + "dependencies": { + ".NETStandard,Version=v2.0": { + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Direct", + "requested": "[3.3.4, )", + "resolved": "3.3.4", + "contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g==" + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Direct", + "requested": "[4.9.2, )", + "resolved": "4.9.2", + "contentHash": "HGIo7E9Mf3exAJbUdYpDFfLoYkSVaHDJXPyusWTYUTBaOPCowGw+Gap5McE1w+K+ryIXre72oiqL88sQHmHBmg==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.9.2]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Direct", + "requested": "[4.9.2, )", + "resolved": "4.9.2", + "contentHash": "c74oxEil3fiZ3nXchnIgY6mXS4roHGiQBT6p3X6dMWokVqluHiqi3PNcXyxH8N/w28rQeXprF3mca83rPPNrMw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.9.2]", + "Microsoft.CodeAnalysis.Common": "[4.9.2]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.9.2]" + } + }, + "NETStandard.Library": { + "type": "Direct", + "requested": "[2.0.3, )", + "resolved": "2.0.3", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "System.Text.Json": { + "type": "Direct", + "requested": "[8.0.3, )", + "resolved": "8.0.3", + "contentHash": "hpagS9joOwv6efWfrMmV9MjQXpiXZH72PgN067Ysfr6AWMSD1/1hEcvh/U5mUpPLezEWsOJSuVrmqDIVD958iA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "8.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.9.2", + "contentHash": "M5PThug7b2AdxL7xKmQs50KzAQTl9jENw5jMT3iUt16k+DAFlw1S87juU3UuPs3gvBm8trMBSOEvSFDr31c9Vw==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.4", + "System.Collections.Immutable": "8.0.0", + "System.Memory": "4.5.5", + "System.Reflection.Metadata": "8.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "8.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.9.2", + "contentHash": "sgBlkBjKwUdpbtwM7SnBdOxvQxuaTtO9F8QgvKY5cH/OnlwDTZqmkK8hfDbhxv9wnN2wME10BL2vIv1fLJwFGA==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.CodeAnalysis.Common": "[4.9.2]", + "System.Composition": "8.0.0", + "System.IO.Pipelines": "8.0.0", + "System.Threading.Channels": "8.0.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Composition": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "E9oO9olNNxA39J8CxQwf7ceIPm+j/B/PhYpyK9M4LhN/OLLRw6u5fNInkhVqaWueMB9iXxYqnwqwgz+W91loIA==", + "dependencies": { + "System.Composition.AttributedModel": "8.0.0", + "System.Composition.Convention": "8.0.0", + "System.Composition.Hosting": "8.0.0", + "System.Composition.Runtime": "8.0.0", + "System.Composition.TypedParts": "8.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "NyElSuvmBMYdn2iPG0n29i7Igu0bq99izOP3MAtEwskY3OP9jqsavvVmPn9lesVaj/KT/o/QkNjA43dOJTsDQw==" + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UuVkc1B3vQU/LzEbWLMZ1aYVssv4rpShzf8wPEyrUqoGNqdYKREmB8bXR73heOMKkwS6ZnPz3PjGODT2MenukQ==", + "dependencies": { + "System.Composition.AttributedModel": "8.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "qwbONqoxlazxcbiohvb3t1JWZgKIKcRdXS5uEeLbo5wtuBupIbAvdC3PYTAeBCZrZeERvrtAbhYHuuS43Zr1bQ==", + "dependencies": { + "System.Composition.Runtime": "8.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "G+kRyB5/6+3ucRRQz+DF4uSHGqpkK8Q4ilVdbt4zvxpmvLVZNmSkyFAQpJLcbOyVF85aomJx0m+TGMDVlwx7ZQ==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "DsSklhuA+Dsgo3ZZrar8hjBFvq1wa1grrkNCTt+6SoX3vq0Vy+HXJnVXrU/nNH1BjlGH684A7h4hJQHZd/u5mA==", + "dependencies": { + "System.Composition.AttributedModel": "8.0.0", + "System.Composition.Hosting": "8.0.0", + "System.Composition.Runtime": "8.0.0" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", + "dependencies": { + "System.Collections.Immutable": "8.0.0", + "System.Memory": "4.5.5" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "questionable.model": { + "type": "Project", + "dependencies": { + "System.Text.Json": "[8.0.3, )" + } + } + } + } +} \ No newline at end of file diff --git a/QuestPaths/AssemblyQuestLoader.cs b/QuestPaths/AssemblyQuestLoader.cs index 95faeb46..9601c4dc 100644 --- a/QuestPaths/AssemblyQuestLoader.cs +++ b/QuestPaths/AssemblyQuestLoader.cs @@ -1,30 +1,11 @@ -#if RELEASE -using System; -using System.IO; -using System.IO.Compression; +using System.Collections.Generic; +using Questionable.Model.V1; +#if RELEASE namespace Questionable.QuestPaths; -public static class AssemblyQuestLoader +public static partial class AssemblyQuestLoader { - public static void LoadQuestsFromEmbeddedResources(Action loadFunction) - { - foreach (string resourceName in typeof(AssemblyQuestLoader).Assembly.GetManifestResourceNames()) - { - if (resourceName.EndsWith(".zip")) - { - using ZipArchive zipArchive = - new ZipArchive(typeof(AssemblyQuestLoader).Assembly.GetManifestResourceStream(resourceName)!); - foreach (ZipArchiveEntry entry in zipArchive.Entries) - { - if (entry.Name.EndsWith(".json")) - { - using Stream stream = entry.Open(); - loadFunction(entry.Name, stream); - } - } - } - } - } + public static IReadOnlyDictionary GetQuests() => Quests; } #endif diff --git a/QuestPaths/QuestPaths.csproj b/QuestPaths/QuestPaths.csproj index 3ea68608..2a204647 100644 --- a/QuestPaths/QuestPaths.csproj +++ b/QuestPaths/QuestPaths.csproj @@ -8,29 +8,22 @@ true none $(SolutionDir)=X:\ + true - - - - - - - - - - - + + + + - - - + + + + + + diff --git a/QuestPaths/packages.lock.json b/QuestPaths/packages.lock.json index f9ae1775..aced35f4 100644 --- a/QuestPaths/packages.lock.json +++ b/QuestPaths/packages.lock.json @@ -1,6 +1,105 @@ { "version": 1, "dependencies": { - "net8.0-windows7.0": {} + "net8.0-windows7.0": { + "Dalamud.Extensions.MicrosoftLogging": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "fMEL2ajtF/30SBBku7vMyG0yye5eHN/A9fgT//1CEjUth/Wz2CYco5Ehye21T8KN1IuAPwoqJuu49rB71j+8ug==", + "dependencies": { + "Microsoft.Extensions.Logging": "8.0.0" + } + }, + "DalamudPackager": { + "type": "Transitive", + "resolved": "2.1.12", + "contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg==" + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2023.3.0", + "contentHash": "PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.3", + "contentHash": "hpagS9joOwv6efWfrMmV9MjQXpiXZH72PgN067Ysfr6AWMSD1/1hEcvh/U5mUpPLezEWsOJSuVrmqDIVD958iA==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, + "llib": { + "type": "Project" + }, + "questionable": { + "type": "Project", + "dependencies": { + "Dalamud.Extensions.MicrosoftLogging": "[4.0.1, )", + "DalamudPackager": "[2.1.12, )", + "JetBrains.Annotations": "[2023.3.0, )", + "LLib": "[1.0.0, )", + "Microsoft.Extensions.DependencyInjection": "[8.0.0, )", + "QuestPaths": "[1.0.0, )", + "Questionable.Model": "[1.0.0, )", + "System.Text.Json": "[8.0.3, )" + } + }, + "questionable.model": { + "type": "Project", + "dependencies": { + "System.Text.Json": "[8.0.3, )" + } + } + } } } \ No newline at end of file diff --git a/Questionable.Model/Questionable.Model.csproj b/Questionable.Model/Questionable.Model.csproj new file mode 100644 index 00000000..c1fcd0f1 --- /dev/null +++ b/Questionable.Model/Questionable.Model.csproj @@ -0,0 +1,15 @@ + + + netstandard2.0 + 12 + enable + true + $(SolutionDir)=X:\ + true + portable + + + + + + diff --git a/Questionable/Model/V1/AethernetShortcut.cs b/Questionable.Model/V1/AethernetShortcut.cs similarity index 86% rename from Questionable/Model/V1/AethernetShortcut.cs rename to Questionable.Model/V1/AethernetShortcut.cs index 2fd707a1..40514b58 100644 --- a/Questionable/Model/V1/AethernetShortcut.cs +++ b/Questionable.Model/V1/AethernetShortcut.cs @@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; [JsonConverter(typeof(AethernetShortcutConverter))] -internal sealed class AethernetShortcut +public sealed class AethernetShortcut { public EAetheryteLocation From { get; set; } public EAetheryteLocation To { get; set; } diff --git a/Questionable.Model/V1/ChatMessage.cs b/Questionable.Model/V1/ChatMessage.cs new file mode 100644 index 00000000..71721380 --- /dev/null +++ b/Questionable.Model/V1/ChatMessage.cs @@ -0,0 +1,7 @@ +namespace Questionable.Model.V1; + +public sealed class ChatMessage +{ + public string? ExcelSheet { get; set; } + public string Key { get; set; } = null!; +} diff --git a/Questionable/Model/V1/Converter/AethernetShortcutConverter.cs b/Questionable.Model/V1/Converter/AethernetShortcutConverter.cs similarity index 98% rename from Questionable/Model/V1/Converter/AethernetShortcutConverter.cs rename to Questionable.Model/V1/Converter/AethernetShortcutConverter.cs index 9393e9c3..76a06550 100644 --- a/Questionable/Model/V1/Converter/AethernetShortcutConverter.cs +++ b/Questionable.Model/V1/Converter/AethernetShortcutConverter.cs @@ -6,7 +6,7 @@ using System.Text.Json.Serialization; namespace Questionable.Model.V1.Converter; -internal sealed class AethernetShortcutConverter : JsonConverter +public sealed class AethernetShortcutConverter : JsonConverter { private static readonly Dictionary EnumToString = new() { @@ -151,9 +151,6 @@ internal sealed class AethernetShortcutConverter : JsonConverter(Values) +public sealed class AetheryteConverter() : EnumConverter(Values) { private static readonly Dictionary Values = new() { diff --git a/Questionable/Model/V1/Converter/DialogueChoiceTypeConverter.cs b/Questionable.Model/V1/Converter/DialogueChoiceTypeConverter.cs similarity index 73% rename from Questionable/Model/V1/Converter/DialogueChoiceTypeConverter.cs rename to Questionable.Model/V1/Converter/DialogueChoiceTypeConverter.cs index b40304d7..ea832cac 100644 --- a/Questionable/Model/V1/Converter/DialogueChoiceTypeConverter.cs +++ b/Questionable.Model/V1/Converter/DialogueChoiceTypeConverter.cs @@ -2,7 +2,7 @@ namespace Questionable.Model.V1.Converter; -internal sealed class DialogueChoiceTypeConverter() : EnumConverter(Values) +public sealed class DialogueChoiceTypeConverter() : EnumConverter(Values) { private static readonly Dictionary Values = new() { diff --git a/Questionable/Model/V1/Converter/EmoteConverter.cs b/Questionable.Model/V1/Converter/EmoteConverter.cs similarity index 86% rename from Questionable/Model/V1/Converter/EmoteConverter.cs rename to Questionable.Model/V1/Converter/EmoteConverter.cs index bf8e62de..8cc50027 100644 --- a/Questionable/Model/V1/Converter/EmoteConverter.cs +++ b/Questionable.Model/V1/Converter/EmoteConverter.cs @@ -2,7 +2,7 @@ namespace Questionable.Model.V1.Converter; -internal sealed class EmoteConverter() : EnumConverter(Values) +public sealed class EmoteConverter() : EnumConverter(Values) { private static readonly Dictionary Values = new() { diff --git a/Questionable/Model/V1/Converter/EnemySpawnTypeConverter.cs b/Questionable.Model/V1/Converter/EnemySpawnTypeConverter.cs similarity index 82% rename from Questionable/Model/V1/Converter/EnemySpawnTypeConverter.cs rename to Questionable.Model/V1/Converter/EnemySpawnTypeConverter.cs index a0ffe843..5c5de532 100644 --- a/Questionable/Model/V1/Converter/EnemySpawnTypeConverter.cs +++ b/Questionable.Model/V1/Converter/EnemySpawnTypeConverter.cs @@ -2,7 +2,7 @@ namespace Questionable.Model.V1.Converter; -internal sealed class EnemySpawnTypeConverter() : EnumConverter(Values) +public sealed class EnemySpawnTypeConverter() : EnumConverter(Values) { private static readonly Dictionary Values = new() { diff --git a/Questionable/Model/V1/Converter/EnumConverter.cs b/Questionable.Model/V1/Converter/EnumConverter.cs similarity index 79% rename from Questionable/Model/V1/Converter/EnumConverter.cs rename to Questionable.Model/V1/Converter/EnumConverter.cs index afd37fde..3ed1267b 100644 --- a/Questionable/Model/V1/Converter/EnumConverter.cs +++ b/Questionable.Model/V1/Converter/EnumConverter.cs @@ -7,7 +7,7 @@ using System.Text.Json.Serialization; namespace Questionable.Model.V1.Converter; -internal abstract class EnumConverter : JsonConverter +public abstract class EnumConverter : JsonConverter where T : Enum { private readonly ReadOnlyDictionary _enumToString; @@ -17,9 +17,8 @@ internal abstract class EnumConverter : JsonConverter { _enumToString = values is IDictionary dict ? new ReadOnlyDictionary(dict) - : values.ToDictionary(x => x.Key, x => x.Value).AsReadOnly(); - _stringToEnum = _enumToString.ToDictionary(x => x.Value, x => x.Key) - .AsReadOnly(); + : new ReadOnlyDictionary(values.ToDictionary(x => x.Key, x => x.Value)); + _stringToEnum = new ReadOnlyDictionary(_enumToString.ToDictionary(x => x.Value, x => x.Key)); } public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, @@ -37,7 +36,6 @@ internal abstract class EnumConverter : JsonConverter public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { - ArgumentNullException.ThrowIfNull(writer); writer.WriteStringValue(_enumToString[value]); } } diff --git a/Questionable/Model/V1/Converter/ExcelRefConverter.cs b/Questionable.Model/V1/Converter/ExcelRefConverter.cs similarity index 93% rename from Questionable/Model/V1/Converter/ExcelRefConverter.cs rename to Questionable.Model/V1/Converter/ExcelRefConverter.cs index 0c446e4e..0c48e5bb 100644 --- a/Questionable/Model/V1/Converter/ExcelRefConverter.cs +++ b/Questionable.Model/V1/Converter/ExcelRefConverter.cs @@ -4,7 +4,7 @@ using System.Text.Json.Serialization; namespace Questionable.Model.V1.Converter; -internal sealed class ExcelRefConverter : JsonConverter +public sealed class ExcelRefConverter : JsonConverter { public override ExcelRef? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/Questionable/Model/V1/Converter/InteractionTypeConverter.cs b/Questionable.Model/V1/Converter/InteractionTypeConverter.cs similarity index 92% rename from Questionable/Model/V1/Converter/InteractionTypeConverter.cs rename to Questionable.Model/V1/Converter/InteractionTypeConverter.cs index d67435f5..6c43696f 100644 --- a/Questionable/Model/V1/Converter/InteractionTypeConverter.cs +++ b/Questionable.Model/V1/Converter/InteractionTypeConverter.cs @@ -2,7 +2,7 @@ namespace Questionable.Model.V1.Converter; -internal sealed class InteractionTypeConverter() : EnumConverter(Values) +public sealed class InteractionTypeConverter() : EnumConverter(Values) { private static readonly Dictionary Values = new() { diff --git a/Questionable/Model/V1/Converter/SkipConditionConverter.cs b/Questionable.Model/V1/Converter/SkipConditionConverter.cs similarity index 79% rename from Questionable/Model/V1/Converter/SkipConditionConverter.cs rename to Questionable.Model/V1/Converter/SkipConditionConverter.cs index a65d5044..dd38ac4f 100644 --- a/Questionable/Model/V1/Converter/SkipConditionConverter.cs +++ b/Questionable.Model/V1/Converter/SkipConditionConverter.cs @@ -2,7 +2,7 @@ namespace Questionable.Model.V1.Converter; -internal sealed class SkipConditionConverter() : EnumConverter(Values) +public sealed class SkipConditionConverter() : EnumConverter(Values) { private static readonly Dictionary Values = new() { diff --git a/Questionable/Model/V1/Converter/VectorConverter.cs b/Questionable.Model/V1/Converter/VectorConverter.cs similarity index 94% rename from Questionable/Model/V1/Converter/VectorConverter.cs rename to Questionable.Model/V1/Converter/VectorConverter.cs index 575fd885..f75ffe6a 100644 --- a/Questionable/Model/V1/Converter/VectorConverter.cs +++ b/Questionable.Model/V1/Converter/VectorConverter.cs @@ -5,7 +5,7 @@ using System.Text.Json.Serialization; namespace Questionable.Model.V1.Converter; -internal sealed class VectorConverter : JsonConverter +public sealed class VectorConverter : JsonConverter { public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { @@ -55,8 +55,6 @@ internal sealed class VectorConverter : JsonConverter public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options) { - ArgumentNullException.ThrowIfNull(writer); - writer.WriteStartObject(); writer.WriteNumber(nameof(Vector3.X), value.X); writer.WriteNumber(nameof(Vector3.Y), value.X); diff --git a/Questionable/Model/V1/DialogueChoice.cs b/Questionable.Model/V1/DialogueChoice.cs similarity index 81% rename from Questionable/Model/V1/DialogueChoice.cs rename to Questionable.Model/V1/DialogueChoice.cs index ddfc0ae0..80b49d05 100644 --- a/Questionable/Model/V1/DialogueChoice.cs +++ b/Questionable.Model/V1/DialogueChoice.cs @@ -1,11 +1,9 @@ using System.Text.Json.Serialization; -using JetBrains.Annotations; using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; -[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)] -internal sealed class DialogueChoice +public sealed class DialogueChoice { [JsonConverter(typeof(DialogueChoiceTypeConverter))] public EDialogChoiceType Type { get; set; } diff --git a/Questionable/Model/V1/EAetheryteLocation.cs b/Questionable.Model/V1/EAetheryteLocation.cs similarity index 99% rename from Questionable/Model/V1/EAetheryteLocation.cs rename to Questionable.Model/V1/EAetheryteLocation.cs index c6ec79f9..a49a471e 100644 --- a/Questionable/Model/V1/EAetheryteLocation.cs +++ b/Questionable.Model/V1/EAetheryteLocation.cs @@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; [JsonConverter(typeof(AetheryteConverter))] -internal enum EAetheryteLocation +public enum EAetheryteLocation { None = 0, diff --git a/Questionable/Model/V1/EDialogChoiceType.cs b/Questionable.Model/V1/EDialogChoiceType.cs similarity index 69% rename from Questionable/Model/V1/EDialogChoiceType.cs rename to Questionable.Model/V1/EDialogChoiceType.cs index e8f18dda..066639a3 100644 --- a/Questionable/Model/V1/EDialogChoiceType.cs +++ b/Questionable.Model/V1/EDialogChoiceType.cs @@ -1,6 +1,6 @@ namespace Questionable.Model.V1; -internal enum EDialogChoiceType +public enum EDialogChoiceType { None, YesNo, diff --git a/Questionable/Model/V1/EEmote.cs b/Questionable.Model/V1/EEmote.cs similarity index 93% rename from Questionable/Model/V1/EEmote.cs rename to Questionable.Model/V1/EEmote.cs index 2e751d36..3ad0afbb 100644 --- a/Questionable/Model/V1/EEmote.cs +++ b/Questionable.Model/V1/EEmote.cs @@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; [JsonConverter(typeof(EmoteConverter))] -internal enum EEmote +public enum EEmote { None = 0, diff --git a/Questionable/Model/V1/EEnemySpawnType.cs b/Questionable.Model/V1/EEnemySpawnType.cs similarity index 89% rename from Questionable/Model/V1/EEnemySpawnType.cs rename to Questionable.Model/V1/EEnemySpawnType.cs index 1c19132d..8465f011 100644 --- a/Questionable/Model/V1/EEnemySpawnType.cs +++ b/Questionable.Model/V1/EEnemySpawnType.cs @@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; [JsonConverter(typeof(EnemySpawnTypeConverter))] -internal enum EEnemySpawnType +public enum EEnemySpawnType { None = 0, AfterInteraction, diff --git a/Questionable/Model/V1/EInteractionType.cs b/Questionable.Model/V1/EInteractionType.cs similarity index 95% rename from Questionable/Model/V1/EInteractionType.cs rename to Questionable.Model/V1/EInteractionType.cs index edb27cee..6e3b7da8 100644 --- a/Questionable/Model/V1/EInteractionType.cs +++ b/Questionable.Model/V1/EInteractionType.cs @@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; [JsonConverter(typeof(InteractionTypeConverter))] -internal enum EInteractionType +public enum EInteractionType { Interact, WalkTo, diff --git a/Questionable/Model/V1/ESkipCondition.cs b/Questionable.Model/V1/ESkipCondition.cs similarity index 88% rename from Questionable/Model/V1/ESkipCondition.cs rename to Questionable.Model/V1/ESkipCondition.cs index f7e173c9..4f2639d4 100644 --- a/Questionable/Model/V1/ESkipCondition.cs +++ b/Questionable.Model/V1/ESkipCondition.cs @@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; [JsonConverter(typeof(SkipConditionConverter))] -internal enum ESkipCondition +public enum ESkipCondition { None, Never, diff --git a/Questionable/Model/V1/ExcelRef.cs b/Questionable.Model/V1/ExcelRef.cs similarity index 100% rename from Questionable/Model/V1/ExcelRef.cs rename to Questionable.Model/V1/ExcelRef.cs diff --git a/Questionable/Model/V1/JumpDestination.cs b/Questionable.Model/V1/JumpDestination.cs similarity index 68% rename from Questionable/Model/V1/JumpDestination.cs rename to Questionable.Model/V1/JumpDestination.cs index 1827f879..e6a709e7 100644 --- a/Questionable/Model/V1/JumpDestination.cs +++ b/Questionable.Model/V1/JumpDestination.cs @@ -1,12 +1,10 @@ using System.Numerics; using System.Text.Json.Serialization; -using JetBrains.Annotations; using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; -[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)] -internal sealed class JumpDestination +public sealed class JumpDestination { [JsonConverter(typeof(VectorConverter))] public Vector3 Position { get; set; } diff --git a/Questionable.Model/V1/QuestData.cs b/Questionable.Model/V1/QuestData.cs new file mode 100644 index 00000000..111bd257 --- /dev/null +++ b/Questionable.Model/V1/QuestData.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Questionable.Model.V1; + +public sealed class QuestData +{ + public string Author { get; set; } = null!; + public List Contributors { get; set; } = new(); + public string? Comment { get; set; } + public List TerritoryBlacklist { get; set; } = new(); + public List QuestSequence { get; set; } = new(); +} diff --git a/Questionable/Model/V1/QuestSequence.cs b/Questionable.Model/V1/QuestSequence.cs similarity index 62% rename from Questionable/Model/V1/QuestSequence.cs rename to Questionable.Model/V1/QuestSequence.cs index 58393c32..62736416 100644 --- a/Questionable/Model/V1/QuestSequence.cs +++ b/Questionable.Model/V1/QuestSequence.cs @@ -1,12 +1,10 @@ using System.Collections.Generic; -using JetBrains.Annotations; namespace Questionable.Model.V1; -[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)] -internal sealed class QuestSequence +public sealed class QuestSequence { - public required int Sequence { get; set; } + public int Sequence { get; set; } public string? Comment { get; set; } public List Steps { get; set; } = new(); diff --git a/Questionable/Model/V1/QuestStep.cs b/Questionable.Model/V1/QuestStep.cs similarity index 54% rename from Questionable/Model/V1/QuestStep.cs rename to Questionable.Model/V1/QuestStep.cs index 44e3ce44..029060e7 100644 --- a/Questionable/Model/V1/QuestStep.cs +++ b/Questionable.Model/V1/QuestStep.cs @@ -3,14 +3,11 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Text.Json.Serialization; -using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; -using JetBrains.Annotations; using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; -[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)] -internal sealed class QuestStep +public sealed class QuestStep { public EInteractionType InteractionType { get; set; } @@ -42,8 +39,8 @@ internal sealed class QuestStep public ChatMessage? ChatMessage { get; set; } public EEnemySpawnType? EnemySpawnType { get; set; } - public IList KillEnemyDataIds { get; set; } = new List(); + public JumpDestination? JumpDestination { get; set; } public uint? ContentFinderConditionId { get; set; } @@ -51,37 +48,16 @@ internal sealed class QuestStep public IList CompletionQuestVariablesFlags { get; set; } = new List(); public IList DialogueChoices { get; set; } = new List(); - /// - /// Positive values: Must be set to this value; will wait for the step to have these set. - /// Negative values: Will skip if set to this value, won't wait for this to be set. - /// - public unsafe bool MatchesQuestVariables(QuestWork questWork, bool forSkip) + [JsonConstructor] + public QuestStep() { - if (CompletionQuestVariablesFlags.Count != 6) - return false; + } - for (int i = 0; i < 6; ++i) - { - short? check = CompletionQuestVariablesFlags[i]; - if (check == null) - continue; - - byte actualValue = questWork.Variables[i]; - byte checkByte = check > 0 ? (byte)check : (byte)-check; - if (forSkip) - { - byte expectedValue = (byte)Math.Abs(check.Value); - if ((actualValue & checkByte) != expectedValue) - return false; - } - else if (!forSkip && check > 0) - { - byte expectedValue = check > 0 ? (byte)check : (byte)0; - if ((actualValue & checkByte) != expectedValue) - return false; - } - } - - return true; + public QuestStep(EInteractionType interactionType, uint? dataId, Vector3? position, ushort territoryId) + { + InteractionType = interactionType; + DataId = dataId; + Position = position; + TerritoryId = territoryId; } } diff --git a/Questionable.Model/packages.lock.json b/Questionable.Model/packages.lock.json new file mode 100644 index 00000000..7bcc7e48 --- /dev/null +++ b/Questionable.Model/packages.lock.json @@ -0,0 +1,86 @@ +{ + "version": 1, + "dependencies": { + ".NETStandard,Version=v2.0": { + "NETStandard.Library": { + "type": "Direct", + "requested": "[2.0.3, )", + "resolved": "2.0.3", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "System.Text.Json": { + "type": "Direct", + "requested": "[8.0.3, )", + "resolved": "8.0.3", + "contentHash": "hpagS9joOwv6efWfrMmV9MjQXpiXZH72PgN067Ysfr6AWMSD1/1hEcvh/U5mUpPLezEWsOJSuVrmqDIVD958iA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "8.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + } + } + } +} \ No newline at end of file diff --git a/Questionable.sln b/Questionable.sln index 1ad54b6d..5a420a9c 100644 --- a/Questionable.sln +++ b/Questionable.sln @@ -6,6 +6,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLib", "LLib\LLib.csproj", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPaths", "QuestPaths\QuestPaths.csproj", "{7A136F28-8D5C-478D-B993-0F39F1451A47}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPaths", "QuestPathGenerator\QuestPathGenerator.csproj", "{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Questionable.Model", "Questionable.Model\Questionable.Model.csproj", "{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,5 +28,13 @@ Global {7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|Any CPU.Build.0 = Debug|Any CPU {7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|Any CPU.ActiveCfg = Release|Any CPU {7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|Any CPU.Build.0 = Release|Any CPU + {DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|Any CPU.Build.0 = Release|Any CPU + {E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Questionable/.gitignore b/Questionable/.gitignore index 958518b5..a60a458a 100644 --- a/Questionable/.gitignore +++ b/Questionable/.gitignore @@ -1,3 +1 @@ /dist -/obj -/bin diff --git a/Questionable/Controller/QuestRegistry.cs b/Questionable/Controller/QuestRegistry.cs index 1bc3a44e..71b91a19 100644 --- a/Questionable/Controller/QuestRegistry.cs +++ b/Questionable/Controller/QuestRegistry.cs @@ -33,7 +33,17 @@ internal sealed class QuestRegistry #if RELEASE _logger.LogInformation("Loading quests from assembly"); - QuestPaths.AssemblyQuestLoader.LoadQuestsFromEmbeddedResources(LoadQuestFromStream); + + foreach ((ushort questId, QuestData questData) in QuestPaths.AssemblyQuestLoader.GetQuests()) + { + Quest quest = new() + { + QuestId = questId, + Name = string.Empty, + Data = questData, + }; + _quests[questId] = quest; + } #else DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation?.Directory?.Parent?.Parent; if (solutionDirectory != null) @@ -48,6 +58,7 @@ internal sealed class QuestRegistry } } #endif + LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "Quests"))); foreach (var (questId, quest) in _quests) @@ -60,6 +71,8 @@ internal sealed class QuestRegistry quest.Name = questData.Name.ToString(); quest.Level = questData.ClassJobLevel0; } + + _logger.LogInformation("Loaded {Count} quests", _quests.Count); } diff --git a/Questionable/Model/V1/ChatMessage.cs b/Questionable/Model/V1/ChatMessage.cs deleted file mode 100644 index c33bf43a..00000000 --- a/Questionable/Model/V1/ChatMessage.cs +++ /dev/null @@ -1,10 +0,0 @@ -using JetBrains.Annotations; - -namespace Questionable.Model.V1; - -[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)] -internal sealed class ChatMessage -{ - public string? ExcelSheet { get; set; } - public string Key { get; set; } = null!; -} diff --git a/Questionable/Model/V1/QuestData.cs b/Questionable/Model/V1/QuestData.cs deleted file mode 100644 index d484481b..00000000 --- a/Questionable/Model/V1/QuestData.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using JetBrains.Annotations; - -namespace Questionable.Model.V1; - -[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)] -internal sealed class QuestData -{ - public required string Author { get; set; } - public List Contributors { get; set; } = new(); - public string? Comment { get; set; } - public List TerritoryBlacklist { get; set; } = new(); - public required List QuestSequence { get; set; } = new(); -} diff --git a/Questionable/Model/V1/QuestStepExtensions.cs b/Questionable/Model/V1/QuestStepExtensions.cs new file mode 100644 index 00000000..dedf2ebc --- /dev/null +++ b/Questionable/Model/V1/QuestStepExtensions.cs @@ -0,0 +1,41 @@ +using System; +using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; + +namespace Questionable.Model.V1; + +internal static class QuestStepExtensions +{ + /// + /// Positive values: Must be set to this value; will wait for the step to have these set. + /// Negative values: Will skip if set to this value, won't wait for this to be set. + /// + public static unsafe bool MatchesQuestVariables(this QuestStep step, QuestWork questWork, bool forSkip) + { + if (step.CompletionQuestVariablesFlags.Count != 6) + return false; + + for (int i = 0; i < 6; ++i) + { + short? check = step.CompletionQuestVariablesFlags[i]; + if (check == null) + continue; + + byte actualValue = questWork.Variables[i]; + byte checkByte = check > 0 ? (byte)check : (byte)-check; + if (forSkip) + { + byte expectedValue = (byte)Math.Abs(check.Value); + if ((actualValue & checkByte) != expectedValue) + return false; + } + else if (!forSkip && check > 0) + { + byte expectedValue = check > 0 ? (byte)check : (byte)0; + if ((actualValue & checkByte) != expectedValue) + return false; + } + } + + return true; + } +} diff --git a/Questionable/Questionable.csproj b/Questionable/Questionable.csproj index 2e9031b4..64a7caf1 100644 --- a/Questionable/Questionable.csproj +++ b/Questionable/Questionable.csproj @@ -59,6 +59,7 @@ - + + diff --git a/Questionable/packages.lock.json b/Questionable/packages.lock.json index e4dec3d5..f9ea92b0 100644 --- a/Questionable/packages.lock.json +++ b/Questionable/packages.lock.json @@ -86,8 +86,17 @@ "llib": { "type": "Project" }, + "questionable.model": { + "type": "Project", + "dependencies": { + "System.Text.Json": "[8.0.3, )" + } + }, "questpaths": { - "type": "Project" + "type": "Project", + "dependencies": { + "Questionable.Model": "[1.0.0, )" + } } } }