mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
use ref-like keyword to accelerate runtime speed
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.Identity;
|
||||
|
||||
[Generator(LanguageNames.CSharp)]
|
||||
internal sealed class IdentityGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string FileName = "IdentityStructs.json";
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
IncrementalValueProvider<ImmutableArray<AdditionalText>> provider = context.AdditionalTextsProvider.Where(MatchFileName).Collect();
|
||||
|
||||
context.RegisterImplementationSourceOutput(provider, GenerateIdentityStructs);
|
||||
}
|
||||
|
||||
private static bool MatchFileName(AdditionalText text)
|
||||
{
|
||||
return Path.GetFileName(text.Path) == FileName;
|
||||
}
|
||||
|
||||
private static void GenerateIdentityStructs(SourceProductionContext context, ImmutableArray<AdditionalText> texts)
|
||||
{
|
||||
AdditionalText jsonFile = texts.Single();
|
||||
|
||||
string identityJson = jsonFile.GetText(context.CancellationToken)!.ToString();
|
||||
List<IdentityStructMetadata> identities = identityJson.FromJson<List<IdentityStructMetadata>>()!;
|
||||
|
||||
if (identities.Any())
|
||||
{
|
||||
GenerateIdentityConverter(context);
|
||||
|
||||
foreach (IdentityStructMetadata identityStruct in identities)
|
||||
{
|
||||
GenerateIdentityStruct(context, identityStruct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateIdentityConverter(SourceProductionContext context)
|
||||
{
|
||||
string source = $$"""
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.ExpressionService;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// Id 转换器
|
||||
/// </summary>
|
||||
/// <typeparam name="TWrapper">包装类型</typeparam>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(IdentityGenerator)}}","1.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
internal sealed class IdentityConverter<TWrapper> : JsonConverter<TWrapper>
|
||||
where TWrapper : struct
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override TWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return CastTo<TWrapper>.From(reader.GetInt32());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(CastTo<int>.From(value));
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
context.AddSource("IdentityConverter.g.cs", source);
|
||||
}
|
||||
|
||||
private static void GenerateIdentityStruct(SourceProductionContext context, IdentityStructMetadata identityStruct)
|
||||
{
|
||||
string name = identityStruct.Name;
|
||||
string type = identityStruct.Type;
|
||||
|
||||
string source = $$"""
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// {{identityStruct.Documentation}}
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(IdentityConverter<{{name}}>))]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(IdentityGenerator)}}","1.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
internal readonly struct {{name}} : IEquatable<{{name}}>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly {{type}} Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="{{name}}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public {{name}}({{type}} value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator {{type}}({{name}} value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator {{name}}({{type}} value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==({{name}} left, {{name}} right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=({{name}} left, {{name}} right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals({{name}} other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is {{name}} other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
context.AddSource($"{name}.g.cs", source);
|
||||
}
|
||||
|
||||
private sealed class IdentityStructMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 基底类型
|
||||
/// </summary>
|
||||
public string Type { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 文档
|
||||
/// </summary>
|
||||
public string? Documentation { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration;
|
||||
namespace Snap.Hutao.SourceGeneration.Reliquary;
|
||||
|
||||
[Generator(LanguageNames.CSharp)]
|
||||
internal sealed class ReliquaryWeightConfigurationGenerator : IIncrementalGenerator
|
||||
@@ -25,13 +25,13 @@ internal sealed class ReliquaryWeightConfigurationGenerator : IIncrementalGenera
|
||||
return Path.GetFileName(text.Path) == FileName;
|
||||
}
|
||||
|
||||
private static void GenerateReliquaryWeightConfiguration(SourceProductionContext context,ImmutableArray<AdditionalText> additionalTexts)
|
||||
private static void GenerateReliquaryWeightConfiguration(SourceProductionContext context, ImmutableArray<AdditionalText> texts)
|
||||
{
|
||||
AdditionalText jsonFile = additionalTexts.Single();
|
||||
AdditionalText jsonFile = texts.Single();
|
||||
|
||||
string configurationJson = jsonFile.GetText(context.CancellationToken)!.ToString();
|
||||
Dictionary<string, ReliquaryWeightConfigurationMetadata> metadataMap =
|
||||
JsonParser.FromJson<Dictionary<string, ReliquaryWeightConfigurationMetadata>>(configurationJson)!;
|
||||
configurationJson.FromJson<Dictionary<string, ReliquaryWeightConfigurationMetadata>>()!;
|
||||
|
||||
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
@@ -1,70 +0,0 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration;
|
||||
|
||||
/// <summary>
|
||||
/// 类型应为内部分析器
|
||||
/// </summary>
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
internal sealed class TypeInternalAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private static readonly DiagnosticDescriptor typeInternalDescriptor = new("SH001", "Type should be internal", "Type should be internal", "Quality", DiagnosticSeverity.Info, true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(typeInternalDescriptor);
|
||||
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterCompilationStartAction(CompilationStart);
|
||||
}
|
||||
|
||||
private void CompilationStart(CompilationStartAnalysisContext context)
|
||||
{
|
||||
context.RegisterSyntaxNodeAction(HandleSyntax<ClassDeclarationSyntax>, SyntaxKind.ClassDeclaration);
|
||||
context.RegisterSyntaxNodeAction(HandleSyntax<InterfaceDeclarationSyntax>, SyntaxKind.InterfaceDeclaration);
|
||||
context.RegisterSyntaxNodeAction(HandleSyntax<StructDeclarationSyntax>, SyntaxKind.StructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(HandleSyntax<EnumDeclarationSyntax>, SyntaxKind.EnumDeclaration);
|
||||
}
|
||||
|
||||
private void HandleSyntax<TSyntax>(SyntaxNodeAnalysisContext classSyntax)
|
||||
where TSyntax : BaseTypeDeclarationSyntax
|
||||
{
|
||||
TSyntax syntax = (TSyntax)classSyntax.Node;
|
||||
|
||||
bool privateExists = false;
|
||||
bool internalExists = false;
|
||||
bool fileExists = false;
|
||||
|
||||
foreach(SyntaxToken token in syntax.Modifiers)
|
||||
{
|
||||
if (token.IsKind(SyntaxKind.PrivateKeyword))
|
||||
{
|
||||
privateExists = true;
|
||||
}
|
||||
|
||||
if (token.IsKind(SyntaxKind.InternalKeyword))
|
||||
{
|
||||
internalExists = true;
|
||||
}
|
||||
|
||||
if (token.IsKind(SyntaxKind.FileKeyword))
|
||||
{
|
||||
fileExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!privateExists && !internalExists && !fileExists)
|
||||
{
|
||||
Location location = syntax.Identifier.GetLocation();
|
||||
Diagnostic diagnostic = Diagnostic.Create(typeInternalDescriptor, location);
|
||||
classSyntax.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs
Normal file
134
src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration;
|
||||
|
||||
/// <summary>
|
||||
/// 通用分析器
|
||||
/// </summary>
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
internal sealed class UniversalAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private static readonly DiagnosticDescriptor typeInternalDescriptor = new("SH001", "Type should be internal", "Type [{0}] should be internal", "Quality", DiagnosticSeverity.Info, true);
|
||||
private static readonly DiagnosticDescriptor readOnlyStructRefDescriptor = new("SH002", "ReadOnly struct should be passed with ref-like key word", "ReadOnly Struct [{0}] should be passed with ref-like key word", "Quality", DiagnosticSeverity.Info, true);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DiagnosticDescriptor[]
|
||||
{
|
||||
typeInternalDescriptor,
|
||||
readOnlyStructRefDescriptor,
|
||||
}.ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterCompilationStartAction(CompilationStart);
|
||||
}
|
||||
|
||||
private void CompilationStart(CompilationStartAnalysisContext context)
|
||||
{
|
||||
SyntaxKind[] types = new SyntaxKind[]
|
||||
{
|
||||
SyntaxKind.ClassDeclaration,
|
||||
SyntaxKind.InterfaceDeclaration,
|
||||
SyntaxKind.StructDeclaration,
|
||||
SyntaxKind.EnumDeclaration
|
||||
};
|
||||
|
||||
context.RegisterSyntaxNodeAction(HandleTypeDeclaration, types);
|
||||
|
||||
context.RegisterSyntaxNodeAction(CollectReadOnlyStruct, SyntaxKind.StructDeclaration);
|
||||
context.RegisterSyntaxNodeAction(HandleMethodDeclaration, SyntaxKind.MethodDeclaration);
|
||||
}
|
||||
|
||||
private void HandleTypeDeclaration(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
BaseTypeDeclarationSyntax syntax = (BaseTypeDeclarationSyntax)context.Node;
|
||||
|
||||
bool privateExists = false;
|
||||
bool internalExists = false;
|
||||
bool fileExists = false;
|
||||
|
||||
foreach (SyntaxToken token in syntax.Modifiers)
|
||||
{
|
||||
if (token.IsKind(SyntaxKind.PrivateKeyword))
|
||||
{
|
||||
privateExists = true;
|
||||
}
|
||||
|
||||
if (token.IsKind(SyntaxKind.InternalKeyword))
|
||||
{
|
||||
internalExists = true;
|
||||
}
|
||||
|
||||
if (token.IsKind(SyntaxKind.FileKeyword))
|
||||
{
|
||||
fileExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!privateExists && !internalExists && !fileExists)
|
||||
{
|
||||
Location location = syntax.Identifier.GetLocation();
|
||||
Diagnostic diagnostic = Diagnostic.Create(typeInternalDescriptor, location, syntax.Identifier);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly HashSet<string> readOnlyStructs = new();
|
||||
|
||||
private void CollectReadOnlyStruct(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
StructDeclarationSyntax structSyntax = (StructDeclarationSyntax)context.Node;
|
||||
|
||||
if (structSyntax.Modifiers.Any(token => token.IsKind(SyntaxKind.ReadOnlyKeyword)))
|
||||
{
|
||||
if (context.SemanticModel.GetDeclaredSymbol(structSyntax) is INamedTypeSymbol symbol)
|
||||
{
|
||||
readOnlyStructs.Add(symbol.ToDisplayString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
MethodDeclarationSyntax methodSyntax = (MethodDeclarationSyntax)context.Node;
|
||||
|
||||
// 跳过异步方法,因为异步方法无法使用 ref in out
|
||||
if (methodSyntax.Modifiers.Any(token => token.IsKind(SyntaxKind.AsyncKeyword)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 跳过方法定义 如 接口
|
||||
if (methodSyntax.Body == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ParameterSyntax parameter in methodSyntax.ParameterList.Parameters)
|
||||
{
|
||||
if (context.SemanticModel.GetDeclaredSymbol(parameter) is IParameterSymbol symbol)
|
||||
{
|
||||
if (readOnlyStructs.Contains(symbol.Type.ToDisplayString()) && symbol.RefKind == RefKind.None)
|
||||
{
|
||||
Location location = parameter.GetLocation();
|
||||
Diagnostic diagnostic = Diagnostic.Create(readOnlyStructRefDescriptor, location, symbol.Type);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Core.IO;
|
||||
internal static class PickerExtension
|
||||
{
|
||||
/// <inheritdoc cref="FileOpenPicker.PickSingleFileAsync"/>
|
||||
public static async Task<ValueResult<bool, FilePath>> TryPickSingleFileAsync(this FileOpenPicker picker)
|
||||
public static async Task<ValueResult<bool, ValueFile>> TryPickSingleFileAsync(this FileOpenPicker picker)
|
||||
{
|
||||
StorageFile? file;
|
||||
Exception? exception = null;
|
||||
@@ -38,7 +38,7 @@ internal static class PickerExtension
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="FileSavePicker.PickSaveFileAsync"/>
|
||||
public static async Task<ValueResult<bool, FilePath>> TryPickSaveFileAsync(this FileSavePicker picker)
|
||||
public static async Task<ValueResult<bool, ValueFile>> TryPickSaveFileAsync(this FileSavePicker picker)
|
||||
{
|
||||
StorageFile? file;
|
||||
Exception? exception = null;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Core.IO;
|
||||
/// <summary>
|
||||
/// 文件路径
|
||||
/// </summary>
|
||||
internal readonly struct FilePath : IEquatable<FilePath>
|
||||
internal readonly struct ValueFile
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
@@ -16,30 +16,30 @@ internal readonly struct FilePath : IEquatable<FilePath>
|
||||
public readonly string Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FilePath"/> struct.
|
||||
/// Initializes a new instance of the <see cref="ValueFile"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public FilePath(string value)
|
||||
public ValueFile(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator string(FilePath value)
|
||||
public static implicit operator string(ValueFile value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator FilePath(string value)
|
||||
public static implicit operator ValueFile(string value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(FilePath left, FilePath right)
|
||||
public static bool operator ==(ValueFile left, ValueFile right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(FilePath left, FilePath right)
|
||||
public static bool operator !=(ValueFile left, ValueFile right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
@@ -92,16 +92,10 @@ internal readonly struct FilePath : IEquatable<FilePath>
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(FilePath other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is FilePath other && Equals(other);
|
||||
return obj is ValueFile other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -24,7 +24,7 @@ internal static class Persistence
|
||||
/// </summary>
|
||||
/// <param name="options">选项</param>
|
||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
||||
public static void RecoverOrInit<TWindow>(WindowOptions<TWindow> options)
|
||||
public static void RecoverOrInit<TWindow>(in WindowOptions<TWindow> options)
|
||||
where TWindow : Window, IExtendedWindowSource
|
||||
{
|
||||
// Set first launch size.
|
||||
@@ -50,7 +50,7 @@ internal static class Persistence
|
||||
/// </summary>
|
||||
/// <param name="options">选项</param>
|
||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
||||
public static void Save<TWindow>(WindowOptions<TWindow> options)
|
||||
public static void Save<TWindow>(in WindowOptions<TWindow> options)
|
||||
where TWindow : Window, IExtendedWindowSource
|
||||
{
|
||||
WINDOWPLACEMENT windowPlacement = StructMarshal.WINDOWPLACEMENT();
|
||||
|
||||
47
src/Snap.Hutao/Snap.Hutao/IdentityStructs.json
Normal file
47
src/Snap.Hutao/Snap.Hutao/IdentityStructs.json
Normal file
@@ -0,0 +1,47 @@
|
||||
[
|
||||
{
|
||||
"Name": "AvatarId",
|
||||
"Type": "int",
|
||||
"Documentation": "8λ <20><>ɫId"
|
||||
},
|
||||
{
|
||||
"Name": "EquipAffixId",
|
||||
"Type": "int",
|
||||
"Documentation": "6λ װ<><D7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Id"
|
||||
},
|
||||
{
|
||||
"Name": "ExtendedEquipAffixId",
|
||||
"Type": "int",
|
||||
"Documentation": "7λ װ<><D7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Id"
|
||||
},
|
||||
{
|
||||
"Name": "MaterialId",
|
||||
"Type": "int",
|
||||
"Documentation": "3-6λ <20><><EFBFBD><EFBFBD>Id"
|
||||
},
|
||||
{
|
||||
"Name": "MonsterId",
|
||||
"Type": "int",
|
||||
"Documentation": "8λ <20><><EFBFBD><EFBFBD>Id"
|
||||
},
|
||||
{
|
||||
"Name": "PromoteId",
|
||||
"Type": "int",
|
||||
"Documentation": "1-5λ <20><>ɫͻ<C9AB><CDBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Id"
|
||||
},
|
||||
{
|
||||
"Name": "ReliquaryAffixId",
|
||||
"Type": "int",
|
||||
"Documentation": "6λ ʥ<><CAA5><EFBFBD>︱<EFBFBD><EFB8B1><EFBFBD><EFBFBD>Id"
|
||||
},
|
||||
{
|
||||
"Name": "ReliquaryMainAffixId",
|
||||
"Type": "int",
|
||||
"Documentation": "5λ ʥ<><CAA5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Id"
|
||||
},
|
||||
{
|
||||
"Name": "WeaponId",
|
||||
"Type": "int",
|
||||
"Documentation": "5λ <20><><EFBFBD><EFBFBD>Id"
|
||||
}
|
||||
]
|
||||
@@ -64,7 +64,7 @@ internal static class AvatarIds
|
||||
public static readonly AvatarId Yae = 10000058;
|
||||
public static readonly AvatarId Heizo = 10000059;
|
||||
public static readonly AvatarId Yelan = 10000060;
|
||||
|
||||
public static readonly AvatarId Kirara = 10000061;
|
||||
public static readonly AvatarId Aloy = 10000062;
|
||||
public static readonly AvatarId Shenhe = 10000063;
|
||||
public static readonly AvatarId Yunjin = 10000064;
|
||||
@@ -84,13 +84,15 @@ internal static class AvatarIds
|
||||
public static readonly AvatarId Alhaitham = 10000078;
|
||||
public static readonly AvatarId Dehya = 10000079;
|
||||
public static readonly AvatarId Mika = 10000080;
|
||||
public static readonly AvatarId Kaveh = 10000081;
|
||||
public static readonly AvatarId Baizhuer = 10000082;
|
||||
|
||||
/// <summary>
|
||||
/// 检查该角色是否为主角
|
||||
/// </summary>
|
||||
/// <param name="avatarId">角色Id</param>
|
||||
/// <returns>角色是否为主角</returns>
|
||||
public static bool IsPlayer(AvatarId avatarId)
|
||||
public static bool IsPlayer(in AvatarId avatarId)
|
||||
{
|
||||
return avatarId == PlayerBoy || avatarId == PlayerGirl;
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 8位 角色Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<AvatarId>))]
|
||||
internal readonly struct AvatarId : IEquatable<AvatarId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AvatarId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public AvatarId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(AvatarId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator AvatarId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(AvatarId left, AvatarId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(AvatarId left, AvatarId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(AvatarId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is AvatarId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.ExpressionService;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// Id 转换器
|
||||
/// </summary>
|
||||
/// <typeparam name="TWrapper">包装类型</typeparam>
|
||||
[HighQuality]
|
||||
internal sealed class IdentityConverter<TWrapper> : JsonConverter<TWrapper>
|
||||
where TWrapper : struct
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override TWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return CastTo<TWrapper>.From(reader.GetInt32());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(CastTo<int>.From(value));
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 6位 装备属性Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<EquipAffixId>))]
|
||||
internal readonly struct EquipAffixId : IEquatable<EquipAffixId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EquipAffixId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public EquipAffixId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(EquipAffixId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator EquipAffixId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(EquipAffixId left, EquipAffixId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(EquipAffixId left, EquipAffixId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(EquipAffixId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is EquipAffixId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 7位 装备属性Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<ExtendedEquipAffixId>))]
|
||||
internal readonly struct ExtendedEquipAffixId : IEquatable<ExtendedEquipAffixId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtendedEquipAffixId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public ExtendedEquipAffixId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(ExtendedEquipAffixId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator ExtendedEquipAffixId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(ExtendedEquipAffixId left, ExtendedEquipAffixId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(ExtendedEquipAffixId left, ExtendedEquipAffixId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(ExtendedEquipAffixId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is ExtendedEquipAffixId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 3-6位 材料Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<MaterialId>))]
|
||||
internal readonly struct MaterialId : IEquatable<MaterialId>, IComparable<MaterialId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MaterialId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public MaterialId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(MaterialId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator MaterialId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(MaterialId left, MaterialId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(MaterialId left, MaterialId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int CompareTo(MaterialId other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(MaterialId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is MaterialId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 8位 怪物Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<MonsterId>))]
|
||||
internal readonly struct MonsterId : IEquatable<MonsterId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MonsterId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public MonsterId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(MonsterId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator MonsterId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(MonsterId left, MonsterId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(MonsterId left, MonsterId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(MonsterId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is MonsterId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 1-5位 角色突破提升Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<PromoteId>))]
|
||||
internal readonly struct PromoteId : IEquatable<PromoteId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PromoteId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public PromoteId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(PromoteId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator PromoteId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(PromoteId left, PromoteId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(PromoteId left, PromoteId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(PromoteId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is PromoteId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 6位 圣遗物副词条Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<ReliquaryAffixId>))]
|
||||
internal readonly struct ReliquaryAffixId : IEquatable<ReliquaryAffixId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReliquaryAffixId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public ReliquaryAffixId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(ReliquaryAffixId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator ReliquaryAffixId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(ReliquaryAffixId left, ReliquaryAffixId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(ReliquaryAffixId left, ReliquaryAffixId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(ReliquaryAffixId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is ReliquaryAffixId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 5位 圣遗物主属性Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<ReliquaryMainAffixId>))]
|
||||
internal readonly struct ReliquaryMainAffixId : IEquatable<ReliquaryMainAffixId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReliquaryMainAffixId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public ReliquaryMainAffixId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(ReliquaryMainAffixId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator ReliquaryMainAffixId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(ReliquaryMainAffixId left, ReliquaryMainAffixId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(ReliquaryMainAffixId left, ReliquaryMainAffixId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(ReliquaryMainAffixId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is ReliquaryMainAffixId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 5位 武器Id
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[JsonConverter(typeof(IdentityConverter<WeaponId>))]
|
||||
internal readonly struct WeaponId : IEquatable<WeaponId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeaponId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public WeaponId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(WeaponId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator WeaponId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(WeaponId left, WeaponId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(WeaponId left, WeaponId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(WeaponId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is WeaponId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,13 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Globalization;
|
||||
using WinRT;
|
||||
|
||||
[assembly:InternalsVisibleTo("Snap.Hutao.Test")]
|
||||
|
||||
namespace Snap.Hutao;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -35,7 +35,7 @@ internal static class ElementMastery
|
||||
/// <param name="mastery">元素精通</param>
|
||||
/// <param name="coeff">参数</param>
|
||||
/// <returns>差异</returns>
|
||||
public static float GetDelta(float mastery, ElementMasteryCoefficient coeff)
|
||||
public static float GetDelta(float mastery, in ElementMasteryCoefficient coeff)
|
||||
{
|
||||
return mastery + coeff.P2 == 0 ? 0 : MathF.Max(mastery * coeff.P1 / (mastery + coeff.P2), 0);
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ internal sealed class CultivationService : ICultivationService
|
||||
.ToList();
|
||||
|
||||
List<InventoryItemView> results = new();
|
||||
foreach (Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id))
|
||||
foreach (Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id.Value))
|
||||
{
|
||||
InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.Create(projectId, meta.Id);
|
||||
results.Add(new(entity, meta, saveCommand));
|
||||
|
||||
@@ -47,7 +47,7 @@ internal sealed class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
private static GachaStatistics CreateCore(
|
||||
IOrderedQueryable<GachaItem> items,
|
||||
List<HistoryWishBuilder> historyWishBuilders,
|
||||
GachaLogServiceContext context,
|
||||
in GachaLogServiceContext context,
|
||||
bool isEmptyHistoryWishVisible)
|
||||
{
|
||||
TypedWishSummaryBuilder standardWishBuilder = new(SH.ServiceGachaLogFactoryPermanentWishName, TypedWishSummaryBuilder.IsStandardWish, 90, 10);
|
||||
|
||||
@@ -111,7 +111,7 @@ internal sealed class LaunchScheme
|
||||
/// </summary>
|
||||
/// <param name="multiChannel">多通道</param>
|
||||
/// <returns>是否相等</returns>
|
||||
public bool MultiChannelEqual(MultiChannel multiChannel)
|
||||
public bool MultiChannelEqual(in MultiChannel multiChannel)
|
||||
{
|
||||
return Channel == multiChannel.Channel && SubChannel == multiChannel.SubChannel;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ internal sealed class ManualGameLocator : IGameLocator
|
||||
SH.ServiceGameLocatorFileOpenPickerCommitText,
|
||||
".exe");
|
||||
|
||||
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
|
||||
(bool isPickerOk, ValueFile file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
|
||||
|
||||
if (isPickerOk)
|
||||
{
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<None Remove="Assets\Wide310x150Logo.scale-400.png" />
|
||||
<None Remove="Control\Panel\PanelSelector.xaml" />
|
||||
<None Remove="Control\Theme\FontStyle.xaml" />
|
||||
<None Remove="IdentityStructs.json" />
|
||||
<None Remove="LaunchGameWindow.xaml" />
|
||||
<None Remove="NativeMethods.json" />
|
||||
<None Remove="NativeMethods.txt" />
|
||||
@@ -106,6 +107,7 @@
|
||||
<None Remove="Resource\Segoe Fluent Icons.ttf" />
|
||||
<None Remove="Resource\WelcomeView_Background.png" />
|
||||
<None Remove="stylecop.json" />
|
||||
<None Remove="View\Card\AchievementCard.xaml" />
|
||||
<None Remove="View\Card\GachaStatisticsCard.xaml" />
|
||||
<None Remove="View\Card\LaunchGameCard.xaml" />
|
||||
<None Remove="View\Control\BaseValueSlider.xaml" />
|
||||
@@ -157,6 +159,7 @@
|
||||
|
||||
<!-- Analyzer Files -->
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="IdentityStructs.json" />
|
||||
<AdditionalFiles Include="NativeMethods.json" />
|
||||
<AdditionalFiles Include="NativeMethods.txt" />
|
||||
<AdditionalFiles Include="ReliquaryWeightConfiguration.json" />
|
||||
@@ -296,6 +299,12 @@
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="View\Card\AchievementCard.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="View\Card\GachaStatisticsCard.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
11
src/Snap.Hutao/Snap.Hutao/View/Card/AchievementCard.xaml
Normal file
11
src/Snap.Hutao/Snap.Hutao/View/Card/AchievementCard.xaml
Normal file
@@ -0,0 +1,11 @@
|
||||
<Button
|
||||
x:Class="Snap.Hutao.View.Card.AchievementCard"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Snap.Hutao.View.Card"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid/>
|
||||
</Button>
|
||||
20
src/Snap.Hutao/Snap.Hutao/View/Card/AchievementCard.xaml.cs
Normal file
20
src/Snap.Hutao/Snap.Hutao/View/Card/AchievementCard.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Snap.Hutao.View.Card;
|
||||
|
||||
/// <summary>
|
||||
/// 成就卡片
|
||||
/// </summary>
|
||||
internal sealed partial class AchievementCard : Button
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的成就卡片
|
||||
/// </summary>
|
||||
public AchievementCard()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.View.Control;
|
||||
/// <summary>
|
||||
/// <20>ļ<F2B5A5B5><C4BC><EFBFBD><EFBFBD><EFBFBD>ͼ
|
||||
/// </summary>
|
||||
public sealed partial class LoadingViewSlim : Loading
|
||||
internal sealed partial class LoadingViewSlim : Loading
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µļļ<F2B5A5B5><C4BC><EFBFBD><EFBFBD><EFBFBD>ͼ
|
||||
|
||||
@@ -73,7 +73,7 @@ internal sealed class AchievementImporter
|
||||
{
|
||||
if (achievementService.CurrentArchive != null)
|
||||
{
|
||||
(bool isPickerOk, FilePath file) = await serviceProvider
|
||||
(bool isPickerOk, ValueFile file) = await serviceProvider
|
||||
.GetRequiredService<IPickerFactory>()
|
||||
.GetFileOpenPicker(PickerLocationId.Desktop, SH.FilePickerImportCommit, ".json")
|
||||
.TryPickSingleFileAsync()
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI.UI;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Core.Database;
|
||||
@@ -327,7 +326,7 @@ internal sealed class AchievementViewModel : Abstraction.ViewModel, INavigationR
|
||||
picker.CommitButtonText = SH.FilePickerExportCommit;
|
||||
picker.SuggestedFileName = $"{achievementService.CurrentArchive?.Name}.json";
|
||||
|
||||
(bool isPickerOk, FilePath file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
|
||||
(bool isPickerOk, ValueFile file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
|
||||
if (isPickerOk)
|
||||
{
|
||||
UIAF uiaf = await achievementService.ExportToUIAFAsync(SelectedArchive).ConfigureAwait(false);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.ViewModel.Achievement;
|
||||
|
||||
/// <summary>
|
||||
/// 简化的成就视图模型
|
||||
/// </summary>
|
||||
internal sealed class AchievementViewModelSlim : Abstraction.ViewModelSlim<View.Page.AchievementPage>
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的简化的成就视图模型
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">服务提供器</param>
|
||||
public AchievementViewModelSlim(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ internal sealed class GachaLogViewModel : Abstraction.ViewModel
|
||||
private async Task ImportFromUIGFJsonAsync()
|
||||
{
|
||||
FileOpenPicker picker = pickerFactory.GetFileOpenPicker(PickerLocationId.Desktop, SH.FilePickerImportCommit, ".json");
|
||||
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
|
||||
(bool isPickerOk, ValueFile file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
|
||||
if (isPickerOk)
|
||||
{
|
||||
(bool isOk, UIGF? uigf) = await file.DeserializeFromJsonAsync<UIGF>(options).ConfigureAwait(false);
|
||||
@@ -267,7 +267,7 @@ internal sealed class GachaLogViewModel : Abstraction.ViewModel
|
||||
picker.CommitButtonText = SH.FilePickerExportCommit;
|
||||
picker.FileTypeChoices.Add(SH.ViewModelGachaLogExportFileType, ".json".Enumerate().ToList());
|
||||
|
||||
(bool isPickerOk, FilePath file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
|
||||
(bool isPickerOk, ValueFile file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
|
||||
|
||||
if (isPickerOk)
|
||||
{
|
||||
|
||||
@@ -116,7 +116,7 @@ internal static class ApiEndpoints
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>角色基本信息字符串</returns>
|
||||
public static string GameRecordRoleBasicInfo(PlayerUid uid)
|
||||
public static string GameRecordRoleBasicInfo(in PlayerUid uid)
|
||||
{
|
||||
return $"{ApiTakumiRecordApi}/roleBasicInfo?role_id={uid.Value}&server={uid.Region}";
|
||||
}
|
||||
@@ -131,7 +131,7 @@ internal static class ApiEndpoints
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>游戏记录实时便笺字符串</returns>
|
||||
public static string GameRecordDailyNote(PlayerUid uid)
|
||||
public static string GameRecordDailyNote(in PlayerUid uid)
|
||||
{
|
||||
return $"{ApiTakumiRecordApi}/dailyNote?server={uid.Region}&role_id={uid.Value}";
|
||||
}
|
||||
@@ -141,7 +141,7 @@ internal static class ApiEndpoints
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>游戏记录主页字符串</returns>
|
||||
public static string GameRecordIndex(PlayerUid uid)
|
||||
public static string GameRecordIndex(in PlayerUid uid)
|
||||
{
|
||||
return $"{ApiTakumiRecordApi}/index?server={uid.Region}&role_id={uid.Value}";
|
||||
}
|
||||
@@ -152,7 +152,7 @@ internal static class ApiEndpoints
|
||||
/// <param name="scheduleType">深渊类型</param>
|
||||
/// <param name="uid">Uid</param>
|
||||
/// <returns>深渊信息字符串</returns>
|
||||
public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, PlayerUid uid)
|
||||
public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, in PlayerUid uid)
|
||||
{
|
||||
return $"{ApiTakumiRecordApi}/spiralAbyss?schedule_type={(int)scheduleType}&role_id={uid.Value}&server={uid.Region}";
|
||||
}
|
||||
@@ -208,7 +208,7 @@ internal static class ApiEndpoints
|
||||
/// <param name="avatarId">角色Id</param>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>角色详情</returns>
|
||||
public static string CalculateSyncAvatarDetail(AvatarId avatarId, PlayerUid uid)
|
||||
public static string CalculateSyncAvatarDetail(AvatarId avatarId, in PlayerUid uid)
|
||||
{
|
||||
return $"{ApiTakumiEventCalculate}/v1/sync/avatar/detail?avatar_id={avatarId.Value}&uid={uid.Value}®ion={uid.Region}";
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ internal static class ApiOsEndpoints
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>角色基本信息字符串</returns>
|
||||
public static string GameRecordRoleBasicInfo(PlayerUid uid)
|
||||
public static string GameRecordRoleBasicInfo(in PlayerUid uid)
|
||||
{
|
||||
return $"{BbsApiOsGameRecordApi}/roleBasicInfo?role_id={uid.Value}&server={uid.Region}";
|
||||
}
|
||||
@@ -139,7 +139,7 @@ internal static class ApiOsEndpoints
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>游戏记录实时便笺字符串</returns>
|
||||
public static string GameRecordDailyNote(PlayerUid uid)
|
||||
public static string GameRecordDailyNote(in PlayerUid uid)
|
||||
{
|
||||
return $"{BbsApiOsGameRecordApi}/dailyNote?server={uid.Region}&role_id={uid.Value}";
|
||||
}
|
||||
@@ -149,7 +149,7 @@ internal static class ApiOsEndpoints
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>游戏记录主页字符串</returns>
|
||||
public static string GameRecordIndex(PlayerUid uid)
|
||||
public static string GameRecordIndex(in PlayerUid uid)
|
||||
{
|
||||
return $"{BbsApiOsGameRecordApi}/index?server={uid.Region}&role_id={uid.Value}";
|
||||
}
|
||||
@@ -160,7 +160,7 @@ internal static class ApiOsEndpoints
|
||||
/// <param name="scheduleType">深渊类型</param>
|
||||
/// <param name="uid">Uid</param>
|
||||
/// <returns>深渊信息字符串</returns>
|
||||
public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, PlayerUid uid)
|
||||
public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, in PlayerUid uid)
|
||||
{
|
||||
return $"{BbsApiOsGameRecordApi}/spiralAbyss?schedule_type={(int)scheduleType}&role_id={uid.Value}&server={uid.Region}";
|
||||
}
|
||||
@@ -208,7 +208,7 @@ internal static class ApiOsEndpoints
|
||||
/// <param name="avatarId">角色Id</param>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>角色详情</returns>
|
||||
public static string CalculateSyncAvatarDetail(AvatarId avatarId, PlayerUid uid)
|
||||
public static string CalculateSyncAvatarDetail(AvatarId avatarId, in PlayerUid uid)
|
||||
{
|
||||
return $"{SgPublicApi}/event/calculateos/sync/avatar/detail?avatar_id={avatarId.Value}&uid={uid.Value}®ion={uid.Region}";
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ internal sealed class EnkaClient
|
||||
/// <param name="playerUid">玩家Uid</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>Enka API 响应</returns>
|
||||
public Task<EnkaResponse?> GetForwardDataAsync(PlayerUid playerUid, CancellationToken token = default)
|
||||
public Task<EnkaResponse?> GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||
{
|
||||
return httpClient.TryCatchGetFromJsonAsync<EnkaResponse>(string.Format(EnkaAPIHutaoForward, playerUid.Value), options, logger, token);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ internal sealed class EnkaClient
|
||||
/// <param name="playerUid">玩家Uid</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>Enka API 响应</returns>
|
||||
public Task<EnkaResponse?> GetDataAsync(PlayerUid playerUid, CancellationToken token = default)
|
||||
public Task<EnkaResponse?> GetDataAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||
{
|
||||
return httpClient.TryCatchGetFromJsonAsync<EnkaResponse>(string.Format(EnkaAPI, playerUid.Value), options, logger, token);
|
||||
}
|
||||
|
||||
@@ -51,10 +51,7 @@ internal sealed partial class Cookie
|
||||
{
|
||||
string name = parts[0].Trim();
|
||||
string value = parts.Length == 1 ? string.Empty : parts[1].Trim();
|
||||
|
||||
// System.ArgumentException: An item with the same key has already been added.
|
||||
// cookieMap.Add(name, value);
|
||||
cookieMap[name] = value;
|
||||
cookieMap.TryAdd(name, value);
|
||||
}
|
||||
|
||||
return new(cookieMap);
|
||||
@@ -73,7 +70,7 @@ internal sealed partial class Cookie
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从登录结果创建Cookie
|
||||
/// 从登录结果创建 Cookie
|
||||
/// </summary>
|
||||
/// <param name="loginResult">登录结果</param>
|
||||
/// <returns>Cookie</returns>
|
||||
@@ -94,6 +91,12 @@ internal sealed partial class Cookie
|
||||
return new(cookieMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 SToken 创建 Cookie
|
||||
/// </summary>
|
||||
/// <param name="stuid">stuid</param>
|
||||
/// <param name="stoken">stoken</param>
|
||||
/// <returns>Cookie</returns>
|
||||
public static Cookie FromSToken(string stuid, string stoken)
|
||||
{
|
||||
SortedDictionary<string, string> cookieMap = new()
|
||||
|
||||
@@ -29,7 +29,6 @@ internal sealed class GenAuthKeyData
|
||||
/// App Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("auth_appid")]
|
||||
|
||||
public string AuthAppId { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
@@ -55,7 +54,7 @@ internal sealed class GenAuthKeyData
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>验证密钥提交数据</returns>
|
||||
public static GenAuthKeyData CreateForWebViewGacha(PlayerUid uid)
|
||||
public static GenAuthKeyData CreateForWebViewGacha(in PlayerUid uid)
|
||||
{
|
||||
return new("webview_gacha", "hk4e_cn", uid);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user