mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
migrate to metadata2
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.Identity;
|
||||
|
||||
@@ -32,8 +33,6 @@ internal sealed class IdentityGenerator : IIncrementalGenerator
|
||||
|
||||
if (identities.Any())
|
||||
{
|
||||
GenerateIdentityConverter(context);
|
||||
|
||||
foreach (IdentityStructMetadata identityStruct in identities)
|
||||
{
|
||||
GenerateIdentityStruct(context, identityStruct);
|
||||
@@ -41,137 +40,157 @@ internal sealed class IdentityGenerator : IIncrementalGenerator
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateIdentityConverter(SourceProductionContext context)
|
||||
private static void GenerateIdentityStruct(SourceProductionContext context, IdentityStructMetadata metadata)
|
||||
{
|
||||
string source = $$"""
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
string name = metadata.Name;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// Id 转换器
|
||||
/// </summary>
|
||||
/// <typeparam name="TWrapper">包装类型</typeparam>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(IdentityGenerator)}}", "1.0.0.0")]
|
||||
internal unsafe sealed class IdentityConverter<TWrapper> : JsonConverter<TWrapper>
|
||||
where TWrapper : unmanaged
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override TWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Number)
|
||||
{
|
||||
int value = reader.GetInt32();
|
||||
return *(TWrapper*)&value;
|
||||
}
|
||||
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(*(int*)&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 = $$"""
|
||||
StringBuilder sourceBuilder = new StringBuilder().AppendLine($$"""
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// {{identityStruct.Documentation}}
|
||||
/// {{metadata.Documentation}}
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(IdentityConverter<{{name}}>))]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(IdentityGenerator)}}","1.0.0.0")]
|
||||
internal readonly struct {{name}} : IEquatable<{{name}}>
|
||||
internal readonly partial struct {{name}}
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly {{type}} Value;
|
||||
public readonly uint Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="{{name}}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public {{name}}({{type}} value)
|
||||
public {{name}}(uint value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator {{type}}({{name}} value)
|
||||
public static implicit operator uint({{name}} value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator {{name}}({{type}} value)
|
||||
public static implicit operator {{name}}(uint 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);
|
||||
if (metadata.Equatable)
|
||||
{
|
||||
sourceBuilder.AppendLine($$"""
|
||||
|
||||
internal readonly partial struct {{name}} : IEquatable<{{name}}>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is {{name}} other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals({{name}} other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
if (metadata.EqualityOperators)
|
||||
{
|
||||
sourceBuilder.AppendLine($$"""
|
||||
|
||||
internal readonly partial struct {{name}} : IEqualityOperators<{{name}}, {{name}}, bool>, IEqualityOperators<{{name}}, uint, bool>
|
||||
{
|
||||
public static bool operator ==({{name}} left, {{name}} right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator ==({{name}} left, uint right)
|
||||
{
|
||||
return left.Value == right;
|
||||
}
|
||||
|
||||
public static bool operator !=({{name}} left, {{name}} right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static bool operator !=({{name}} left, uint right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
if (metadata.AdditionOperators)
|
||||
{
|
||||
sourceBuilder.AppendLine($$"""
|
||||
|
||||
internal readonly partial struct {{name}} : IAdditionOperators<{{name}}, {{name}}, {{name}}>, IAdditionOperators<{{name}}, uint, {{name}}>
|
||||
{
|
||||
public static {{name}} operator +({{name}} left, {{name}} right)
|
||||
{
|
||||
return left.Value + right.Value;
|
||||
}
|
||||
|
||||
public static {{name}} operator +({{name}} left, uint right)
|
||||
{
|
||||
return left.Value + right;
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
if (metadata.IncrementOperators)
|
||||
{
|
||||
sourceBuilder.AppendLine($$"""
|
||||
|
||||
internal readonly partial struct {{name}} : IIncrementOperators<{{name}}>
|
||||
{
|
||||
public static unsafe {{name}} operator ++({{name}} value)
|
||||
{
|
||||
++*(uint*)&value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
context.AddSource($"{name}.g.cs", sourceBuilder.ToString());
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
public bool Equatable { get; set; }
|
||||
|
||||
public bool EqualityOperators { get; set; }
|
||||
|
||||
public bool AdditionOperators { get; set; }
|
||||
|
||||
public bool IncrementOperators { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Test;
|
||||
@@ -18,6 +19,13 @@ public class JsonSerializeTest
|
||||
}
|
||||
""";
|
||||
|
||||
private const string SmapleNumberDictionaryJson = """
|
||||
{
|
||||
"111" : "12",
|
||||
"222" : "34"
|
||||
}
|
||||
""";
|
||||
|
||||
[TestMethod]
|
||||
public void DelegatePropertyCanSerialize()
|
||||
{
|
||||
@@ -26,11 +34,33 @@ public class JsonSerializeTest
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EmptyStringCanSerializeAsNumber()
|
||||
public void EmptyStringCannotSerializeAsNumber()
|
||||
{
|
||||
// Throw
|
||||
StringNumberSample sample = JsonSerializer.Deserialize<StringNumberSample>(SmapleNumberObjectJson)!;
|
||||
Assert.AreEqual(sample.A, 0);
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
// Throw
|
||||
StringNumberSample sample = JsonSerializer.Deserialize<StringNumberSample>(SmapleNumberObjectJson)!;
|
||||
Assert.AreEqual(sample.A, 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
|
||||
Assert.IsTrue(caught);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NumberStringKeyCanSerializeAsKey()
|
||||
{
|
||||
JsonSerializerOptions options = new()
|
||||
{
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString,
|
||||
};
|
||||
|
||||
Dictionary<int,string> sample = JsonSerializer.Deserialize<Dictionary<int, string>>(SmapleNumberDictionaryJson, options)!;
|
||||
Assert.AreEqual(sample[111], "12");
|
||||
}
|
||||
|
||||
private class Sample
|
||||
|
||||
@@ -14,8 +14,8 @@ internal sealed class UserdataCorruptedException : Exception
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="innerException">内部错误</param>
|
||||
public UserdataCorruptedException(string message, Exception innerException)
|
||||
: base(string.Format(SH.CoreExceptionServiceUserdataCorruptedMessage, $"{message}\n{innerException.Message}"), innerException)
|
||||
public UserdataCorruptedException(string message, Exception? innerException)
|
||||
: base(string.Format(SH.CoreExceptionServiceUserdataCorruptedMessage, $"{message}\n{innerException?.Message}"), innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,38 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Snap.Hutao.Core.IO;
|
||||
namespace Snap.Hutao.Core.IO.Hashing;
|
||||
|
||||
/// <summary>
|
||||
/// 摘要
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal static class Digest
|
||||
internal static class MD5
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步获取文件 Md5 摘要
|
||||
/// 异步获取文件 MD5 摘要
|
||||
/// </summary>
|
||||
/// <param name="filePath">文件路径</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>文件 Md5 摘要</returns>
|
||||
public static async Task<string> GetFileMD5Async(string filePath, CancellationToken token = default)
|
||||
public static async Task<string> HashFileAsync(string filePath, CancellationToken token = default)
|
||||
{
|
||||
using (FileStream stream = File.OpenRead(filePath))
|
||||
{
|
||||
return await GetStreamMD5Async(stream, token).ConfigureAwait(false);
|
||||
return await HashAsync(stream, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取流的 Md5 摘要
|
||||
/// 获取流的 MD5 摘要
|
||||
/// </summary>
|
||||
/// <param name="stream">流</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>流 Md5 摘要</returns>
|
||||
public static async Task<string> GetStreamMD5Async(Stream stream, CancellationToken token = default)
|
||||
public static async Task<string> HashAsync(Stream stream, CancellationToken token = default)
|
||||
{
|
||||
using (MD5 md5 = MD5.Create())
|
||||
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
|
||||
{
|
||||
byte[] bytes = await md5.ComputeHashAsync(stream, token).ConfigureAwait(false);
|
||||
return System.Convert.ToHexString(bytes);
|
||||
41
src/Snap.Hutao/Snap.Hutao/Core/IO/Hashing/XXH64.cs
Normal file
41
src/Snap.Hutao/Snap.Hutao/Core/IO/Hashing/XXH64.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.IO;
|
||||
using System.IO.Hashing;
|
||||
|
||||
namespace Snap.Hutao.Core.IO.Hashing;
|
||||
|
||||
/// <summary>
|
||||
/// XXH64 摘要
|
||||
/// </summary>
|
||||
internal static class XXH64
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取流的 XXH64 摘要
|
||||
/// </summary>
|
||||
/// <param name="stream">流</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>摘要</returns>
|
||||
public static async Task<string> HashAsync(Stream stream, CancellationToken token = default)
|
||||
{
|
||||
XxHash64 xxHash64 = new();
|
||||
await xxHash64.AppendAsync(stream, token).ConfigureAwait(false);
|
||||
byte[] bytes = xxHash64.GetHashAndReset();
|
||||
return System.Convert.ToHexString(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件的 XXH64 摘要
|
||||
/// </summary>
|
||||
/// <param name="path">路径</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>摘要</returns>
|
||||
public static async Task<string> HashFileAsync(string path, CancellationToken token = default)
|
||||
{
|
||||
using (FileStream stream = File.OpenRead(path))
|
||||
{
|
||||
return await HashAsync(stream, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ internal static class JsonOptions
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true,
|
||||
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -15,10 +16,12 @@ internal static partial class EnumerableExtension
|
||||
/// 增加计数
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键类型</typeparam>
|
||||
/// <typeparam name="TValue">值类型</typeparam>
|
||||
/// <param name="dict">字典</param>
|
||||
/// <param name="key">键</param>
|
||||
public static void Increase<TKey>(this Dictionary<TKey, int> dict, TKey key)
|
||||
public static void IncreaseOne<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key)
|
||||
where TKey : notnull
|
||||
where TValue : struct, IIncrementOperators<TValue>
|
||||
{
|
||||
++CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
|
||||
}
|
||||
@@ -27,14 +30,16 @@ internal static partial class EnumerableExtension
|
||||
/// 增加计数
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键类型</typeparam>
|
||||
/// <typeparam name="TValue">值类型</typeparam>
|
||||
/// <param name="dict">字典</param>
|
||||
/// <param name="key">键</param>
|
||||
/// <param name="value">增加的值</param>
|
||||
public static void Increase<TKey>(this Dictionary<TKey, int> dict, TKey key, int value)
|
||||
public static void IncreaseValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
|
||||
where TKey : notnull
|
||||
where TValue : struct, IAdditionOperators<TValue, TValue, TValue>
|
||||
{
|
||||
// ref the value, so that we can manipulate it outside the dict.
|
||||
ref int current = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
|
||||
ref TValue current = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
|
||||
current += value;
|
||||
}
|
||||
|
||||
@@ -42,13 +47,15 @@ internal static partial class EnumerableExtension
|
||||
/// 增加计数
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键类型</typeparam>
|
||||
/// <typeparam name="TValue">值类型</typeparam>
|
||||
/// <param name="dict">字典</param>
|
||||
/// <param name="key">键</param>
|
||||
/// <returns>是否存在键值</returns>
|
||||
public static bool TryIncrease<TKey>(this Dictionary<TKey, int> dict, TKey key)
|
||||
public static bool TryIncreaseOne<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key)
|
||||
where TKey : notnull
|
||||
where TValue : struct, IIncrementOperators<TValue>
|
||||
{
|
||||
ref int value = ref CollectionsMarshal.GetValueRefOrNullRef(dict, key);
|
||||
ref TValue value = ref CollectionsMarshal.GetValueRefOrNullRef(dict, key);
|
||||
if (!Unsafe.IsNullRef(ref value))
|
||||
{
|
||||
++value;
|
||||
|
||||
@@ -17,9 +17,9 @@ internal static class NumberExtension
|
||||
/// <param name="x">给定的整数</param>
|
||||
/// <returns>位数</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Place(in this int x)
|
||||
public static uint Place(in this uint x)
|
||||
{
|
||||
// Benchmarked and compared as a most optimized solution
|
||||
return (int)(MathF.Log10(x) + 1);
|
||||
return (uint)(MathF.Log10(x) + 1);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,166 @@
|
||||
[
|
||||
{
|
||||
"Name": "AchievementGoalId",
|
||||
"Type": "int",
|
||||
"Documentation": "1-2位 成就分类Id"
|
||||
"Documentation": "成就分类 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "AchievementId",
|
||||
"Type": "int",
|
||||
"Documentation": "5位 成就Id"
|
||||
"Documentation": "成就 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "AvatarId",
|
||||
"Type": "int",
|
||||
"Documentation": "8位 角色Id"
|
||||
"Documentation": "角色 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "CostumeId",
|
||||
"Type": "int",
|
||||
"Documentation": "6位 角色装扮Id"
|
||||
"Documentation": "角色装扮 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "EquipAffixId",
|
||||
"Type": "int",
|
||||
"Documentation": "6位 装备属性Id"
|
||||
"Documentation": "装备属性 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "ExtendedEquipAffixId",
|
||||
"Type": "int",
|
||||
"Documentation": "7位 装备属性Id"
|
||||
"Documentation": "装备属性 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FetterLevel",
|
||||
"Documentation": "好感等级 1 - 10",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "Level",
|
||||
"Documentation": "等级 1 - 90",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "MaterialId",
|
||||
"Type": "int",
|
||||
"Documentation": "3-6位 材料Id"
|
||||
"Documentation": "材料 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "MonsterDescribeId",
|
||||
"Documentation": "怪物描述 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "MonsterId",
|
||||
"Type": "int",
|
||||
"Documentation": "8位 怪物Id"
|
||||
"Documentation": "怪物 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "PromoteId",
|
||||
"Type": "int",
|
||||
"Documentation": "1-5位 角色突破提升Id"
|
||||
"Documentation": "角色突破提升 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "ReliquaryAffixId",
|
||||
"Type": "int",
|
||||
"Documentation": "6位 圣遗物副词条Id"
|
||||
"Name": "PromoteLevel",
|
||||
"Documentation": "突破等级 0 - 6",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "ReliquaryLevel",
|
||||
"Documentation": "圣遗物等级 1 - 21",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "ReliquarySubAffixId",
|
||||
"Documentation": "圣遗物副词条 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "ReliquaryId",
|
||||
"Type": "int",
|
||||
"Documentation": "5位 圣遗物Id"
|
||||
"Documentation": "圣遗物 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "ReliquaryMainAffixId",
|
||||
"Type": "int",
|
||||
"Documentation": "5位 圣遗物主属性Id"
|
||||
"Documentation": "圣遗物主属性 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "ReliquarySetId",
|
||||
"Type": "int",
|
||||
"Documentation": "5位 圣遗物套装Id"
|
||||
"Documentation": "圣遗物套装 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "SkillGroupId",
|
||||
"Type": "int",
|
||||
"Documentation": "3-4位 技能组Id"
|
||||
"Documentation": "技能组 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "SkillId",
|
||||
"Type": "int",
|
||||
"Documentation": "5-6位 技能Id"
|
||||
"Documentation": "技能 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "SkillLevel",
|
||||
"Documentation": "技能等级 1 - 15",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true,
|
||||
"AdditionOperators": true,
|
||||
"IncrementOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "TowerLevelId",
|
||||
"Documentation": "深渊间 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "TowerLevelGroupId",
|
||||
"Documentation": "深渊间分组 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "TowerFloorId",
|
||||
"Documentation": "深渊层 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "TowerScheduleId",
|
||||
"Documentation": "深渊计划 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "WeaponAffixLevel",
|
||||
"Documentation": "武器精炼等级 0 - 4",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "WeaponId",
|
||||
"Type": "int",
|
||||
"Documentation": "5位 武器Id"
|
||||
"Documentation": "武器 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
}
|
||||
]
|
||||
@@ -42,7 +42,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
b.ToTable("achievements", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
@@ -60,7 +60,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
b.ToTable("achievement_archives", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
@@ -79,7 +79,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
b.ToTable("avatar_infos", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
@@ -101,7 +101,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("cultivate_entries");
|
||||
b.ToTable("cultivate_entries", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
@@ -126,7 +126,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_items");
|
||||
b.ToTable("cultivate_items", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||
@@ -147,7 +147,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("cultivate_projects");
|
||||
b.ToTable("cultivate_projects", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
@@ -200,7 +200,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
b.ToTable("daily_notes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
@@ -218,7 +218,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
b.ToTable("gacha_archives", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
@@ -249,7 +249,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
b.ToTable("gacha_items", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
@@ -274,7 +274,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
b.ToTable("game_accounts", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
@@ -296,7 +296,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_items");
|
||||
b.ToTable("inventory_items", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
@@ -325,7 +325,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_reliquaries");
|
||||
b.ToTable("inventory_reliquaries", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
@@ -350,7 +350,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_weapons");
|
||||
b.ToTable("inventory_weapons", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||
@@ -366,7 +366,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("object_cache");
|
||||
b.ToTable("object_cache", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
@@ -379,7 +379,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
b.ToTable("settings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||
@@ -401,7 +401,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("spiral_abysses");
|
||||
b.ToTable("spiral_abysses", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
@@ -435,7 +435,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users");
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace Snap.Hutao.Model.Calculable;
|
||||
[HighQuality]
|
||||
internal sealed class CalculableAvatar : ObservableObject, ICalculableAvatar
|
||||
{
|
||||
private int levelCurrent;
|
||||
private int levelTarget;
|
||||
private uint levelCurrent;
|
||||
private uint levelTarget;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的可计算角色
|
||||
@@ -58,10 +58,10 @@ internal sealed class CalculableAvatar : ObservableObject, ICalculableAvatar
|
||||
public AvatarId AvatarId { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelMin { get; }
|
||||
public uint LevelMin { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelMax { get; }
|
||||
public uint LevelMax { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<ICalculableSkill> Skills { get; }
|
||||
@@ -73,11 +73,11 @@ internal sealed class CalculableAvatar : ObservableObject, ICalculableAvatar
|
||||
public Uri Icon { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ItemQuality Quality { get; }
|
||||
public QualityType Quality { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
|
||||
public uint LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
|
||||
public uint LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.ViewModel.AvatarProperty;
|
||||
|
||||
namespace Snap.Hutao.Model.Calculable;
|
||||
@@ -15,8 +16,8 @@ namespace Snap.Hutao.Model.Calculable;
|
||||
[HighQuality]
|
||||
internal sealed class CalculableSkill : ObservableObject, ICalculableSkill
|
||||
{
|
||||
private int levelCurrent;
|
||||
private int levelTarget;
|
||||
private uint levelCurrent;
|
||||
private uint levelTarget;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的可计算的技能
|
||||
@@ -29,7 +30,7 @@ internal sealed class CalculableSkill : ObservableObject, ICalculableSkill
|
||||
LevelMax = 10; // hard coded 10 here
|
||||
Name = skill.Name;
|
||||
Icon = SkillIconConverter.IconNameToUri(skill.Icon);
|
||||
Quality = ItemQuality.QUALITY_NONE;
|
||||
Quality = QualityType.QUALITY_NONE;
|
||||
|
||||
LevelCurrent = LevelMin;
|
||||
LevelTarget = LevelMax;
|
||||
@@ -46,20 +47,20 @@ internal sealed class CalculableSkill : ObservableObject, ICalculableSkill
|
||||
LevelMax = 10; // hard coded 10 here
|
||||
Name = skill.Name;
|
||||
Icon = skill.Icon;
|
||||
Quality = ItemQuality.QUALITY_NONE;
|
||||
Quality = QualityType.QUALITY_NONE;
|
||||
|
||||
LevelCurrent = LevelMin;
|
||||
LevelTarget = LevelMax;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GruopId { get; }
|
||||
public SkillGroupId GruopId { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelMin { get; }
|
||||
public uint LevelMin { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelMax { get; }
|
||||
public uint LevelMax { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; }
|
||||
@@ -68,11 +69,11 @@ internal sealed class CalculableSkill : ObservableObject, ICalculableSkill
|
||||
public Uri Icon { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ItemQuality Quality { get; }
|
||||
public QualityType Quality { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
|
||||
public uint LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
|
||||
public uint LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
|
||||
}
|
||||
@@ -15,8 +15,8 @@ namespace Snap.Hutao.Model.Calculable;
|
||||
[HighQuality]
|
||||
internal class CalculableWeapon : ObservableObject, ICalculableWeapon
|
||||
{
|
||||
private int levelCurrent;
|
||||
private int levelTarget;
|
||||
private uint levelCurrent;
|
||||
private uint levelTarget;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的可计算武器
|
||||
@@ -56,10 +56,10 @@ internal class CalculableWeapon : ObservableObject, ICalculableWeapon
|
||||
public WeaponId WeaponId { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelMin { get; }
|
||||
public uint LevelMin { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelMax { get; }
|
||||
public uint LevelMax { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; }
|
||||
@@ -68,11 +68,11 @@ internal class CalculableWeapon : ObservableObject, ICalculableWeapon
|
||||
public Uri Icon { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ItemQuality Quality { get; }
|
||||
public QualityType Quality { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
|
||||
public uint LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
|
||||
public uint LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
|
||||
}
|
||||
@@ -14,15 +14,15 @@ internal interface ICalculable : INameIcon
|
||||
/// <summary>
|
||||
/// 星级
|
||||
/// </summary>
|
||||
ItemQuality Quality { get; }
|
||||
QualityType Quality { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前等级
|
||||
/// </summary>
|
||||
int LevelCurrent { get; set; }
|
||||
uint LevelCurrent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标等级
|
||||
/// </summary>
|
||||
int LevelTarget { get; set; }
|
||||
uint LevelTarget { get; set; }
|
||||
}
|
||||
@@ -19,12 +19,12 @@ internal interface ICalculableAvatar : ICalculable
|
||||
/// <summary>
|
||||
/// 最小等级
|
||||
/// </summary>
|
||||
int LevelMin { get; }
|
||||
uint LevelMin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大等级
|
||||
/// </summary>
|
||||
int LevelMax { get; }
|
||||
uint LevelMax { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 技能组
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Calculable;
|
||||
|
||||
/// <summary>
|
||||
@@ -12,15 +14,15 @@ internal interface ICalculableSkill : ICalculable
|
||||
/// <summary>
|
||||
/// 技能组Id
|
||||
/// </summary>
|
||||
int GruopId { get; }
|
||||
SkillGroupId GruopId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 最小等级
|
||||
/// </summary>
|
||||
int LevelMin { get; }
|
||||
uint LevelMin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大等级
|
||||
/// </summary>
|
||||
int LevelMax { get; }
|
||||
uint LevelMax { get; }
|
||||
}
|
||||
@@ -19,10 +19,10 @@ internal interface ICalculableWeapon : ICalculable
|
||||
/// <summary>
|
||||
/// 最小等级
|
||||
/// </summary>
|
||||
int LevelMin { get; }
|
||||
uint LevelMin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大等级
|
||||
/// </summary>
|
||||
int LevelMax { get; }
|
||||
uint LevelMax { get; }
|
||||
}
|
||||
@@ -37,12 +37,12 @@ internal sealed class Achievement : IEquatable<Achievement>
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
public uint Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前进度
|
||||
/// </summary>
|
||||
public int Current { get; set; }
|
||||
public uint Current { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 完成时间
|
||||
|
||||
@@ -40,7 +40,7 @@ internal sealed class CultivateEntry
|
||||
/// <summary>
|
||||
/// 角色/武器/家具 Id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
public uint Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的养成入口点
|
||||
@@ -49,7 +49,7 @@ internal sealed class CultivateEntry
|
||||
/// <param name="type">类型</param>
|
||||
/// <param name="id">主Id</param>
|
||||
/// <returns>养成入口点</returns>
|
||||
public static CultivateEntry Create(in Guid projectId, CultivateType type, int id)
|
||||
public static CultivateEntry Create(in Guid projectId, CultivateType type, uint id)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ internal sealed class GachaItem
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
public int ItemId { get; set; }
|
||||
public uint ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取时间
|
||||
@@ -66,12 +66,12 @@ internal sealed class GachaItem
|
||||
/// </summary>
|
||||
/// <param name="itemId">物品Id</param>
|
||||
/// <returns>物品类型字符串</returns>
|
||||
public static string GetItemTypeStringByItemId(int itemId)
|
||||
public static string GetItemTypeStringByItemId(uint itemId)
|
||||
{
|
||||
return itemId.Place() switch
|
||||
{
|
||||
8 => "角色",
|
||||
5 => "武器",
|
||||
8U => "角色",
|
||||
5U => "武器",
|
||||
_ => "未知",
|
||||
};
|
||||
}
|
||||
@@ -83,7 +83,7 @@ internal sealed class GachaItem
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <param name="itemId">物品Id</param>
|
||||
/// <returns>新的祈愿物品</returns>
|
||||
public static GachaItem Create(in Guid archiveId, GachaLogItem item, int itemId)
|
||||
public static GachaItem Create(in Guid archiveId, GachaLogItem item, uint itemId)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@@ -103,7 +103,7 @@ internal sealed class GachaItem
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <param name="itemId">物品Id</param>
|
||||
/// <returns>新的祈愿物品</returns>
|
||||
public static GachaItem CreateForMajor2Minor2OrLower(in Guid archiveId, UIGFItem item, int itemId)
|
||||
public static GachaItem CreateForMajor2Minor2OrLower(in Guid archiveId, UIGFItem item, uint itemId)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@@ -129,7 +129,7 @@ internal sealed class GachaItem
|
||||
ArchiveId = archiveId,
|
||||
GachaType = item.GachaType,
|
||||
QueryType = item.UIGFGachaType,
|
||||
ItemId = int.Parse(item.ItemId),
|
||||
ItemId = uint.Parse(item.ItemId),
|
||||
Time = item.Time,
|
||||
Id = item.Id,
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ internal sealed class InventoryItem
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
public int ItemId { get; set; }
|
||||
public uint ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 个数 4294967295
|
||||
@@ -47,7 +47,7 @@ internal sealed class InventoryItem
|
||||
/// <param name="projectId">项目Id</param>
|
||||
/// <param name="itemId">物品Id</param>
|
||||
/// <returns>新的个数为0的物品</returns>
|
||||
public static InventoryItem Create(in Guid projectId, int itemId)
|
||||
public static InventoryItem Create(in Guid projectId, uint itemId)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ internal sealed class UIAFItem
|
||||
/// 成就Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
public uint Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 完成时间
|
||||
@@ -27,7 +27,7 @@ internal sealed class UIAFItem
|
||||
/// 对于progress为1的项,该属性始终为0
|
||||
/// </summary>
|
||||
[JsonPropertyName("current")]
|
||||
public int Current { get; set; }
|
||||
public uint Current { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 完成状态
|
||||
|
||||
@@ -27,7 +27,6 @@ internal sealed class UIGFInfo
|
||||
/// 导出的时间戳
|
||||
/// </summary>
|
||||
[JsonPropertyName("export_timestamp")]
|
||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
|
||||
public long? ExportTimestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -27,7 +27,6 @@ internal sealed class UIIFInfo
|
||||
/// 导出的时间戳
|
||||
/// </summary>
|
||||
[JsonPropertyName("export_timestamp")]
|
||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
|
||||
public long? ExportTimestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -36,37 +36,45 @@ internal enum GrowCurveType
|
||||
GROW_CURVE_HP_2 = 23,
|
||||
GROW_CURVE_ATTACK_2 = 24,
|
||||
GROW_CURVE_HP_ENVIRONMENT = 25,
|
||||
|
||||
GROW_CURVE_ATTACK_S5 = 31,
|
||||
GROW_CURVE_ATTACK_S4 = 32,
|
||||
GROW_CURVE_ATTACK_S3 = 33,
|
||||
GROW_CURVE_STRIKE_S5 = 34,
|
||||
|
||||
GROW_CURVE_DEFENSE_S5 = 41,
|
||||
GROW_CURVE_DEFENSE_S4 = 42,
|
||||
|
||||
GROW_CURVE_ATTACK_101 = 1101,
|
||||
GROW_CURVE_ATTACK_102 = 1102,
|
||||
GROW_CURVE_ATTACK_103 = 1103,
|
||||
GROW_CURVE_ATTACK_104 = 1104,
|
||||
GROW_CURVE_ATTACK_105 = 1105,
|
||||
|
||||
GROW_CURVE_ATTACK_201 = 1201,
|
||||
GROW_CURVE_ATTACK_202 = 1202,
|
||||
GROW_CURVE_ATTACK_203 = 1203,
|
||||
GROW_CURVE_ATTACK_204 = 1204,
|
||||
GROW_CURVE_ATTACK_205 = 1205,
|
||||
|
||||
GROW_CURVE_ATTACK_301 = 1301,
|
||||
GROW_CURVE_ATTACK_302 = 1302,
|
||||
GROW_CURVE_ATTACK_303 = 1303,
|
||||
GROW_CURVE_ATTACK_304 = 1304,
|
||||
GROW_CURVE_ATTACK_305 = 1305,
|
||||
|
||||
GROW_CURVE_CRITICAL_101 = 2101,
|
||||
GROW_CURVE_CRITICAL_102 = 2102,
|
||||
GROW_CURVE_CRITICAL_103 = 2103,
|
||||
GROW_CURVE_CRITICAL_104 = 2104,
|
||||
GROW_CURVE_CRITICAL_105 = 2105,
|
||||
|
||||
GROW_CURVE_CRITICAL_201 = 2201,
|
||||
GROW_CURVE_CRITICAL_202 = 2202,
|
||||
GROW_CURVE_CRITICAL_203 = 2203,
|
||||
GROW_CURVE_CRITICAL_204 = 2204,
|
||||
GROW_CURVE_CRITICAL_205 = 2205,
|
||||
|
||||
GROW_CURVE_CRITICAL_301 = 2301,
|
||||
GROW_CURVE_CRITICAL_302 = 2302,
|
||||
GROW_CURVE_CRITICAL_303 = 2303,
|
||||
|
||||
@@ -24,7 +24,7 @@ internal static class IntrinsicImmutables
|
||||
/// <summary>
|
||||
/// 物品类型
|
||||
/// </summary>
|
||||
public static readonly ImmutableHashSet<string> ItemQualities = Enum.GetValues<ItemQuality>().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType<string>().ToImmutableHashSet();
|
||||
public static readonly ImmutableHashSet<string> ItemQualities = Enum.GetValues<QualityType>().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType<string>().ToImmutableHashSet();
|
||||
|
||||
/// <summary>
|
||||
/// 身材类型
|
||||
|
||||
@@ -12,6 +12,7 @@ internal enum MaterialType
|
||||
MATERIAL_NONE = 0,
|
||||
MATERIAL_FOOD = 1,
|
||||
MATERIAL_QUEST = 2,
|
||||
|
||||
MATERIAL_EXCHANGE = 4,
|
||||
MATERIAL_CONSUME = 5,
|
||||
MATERIAL_EXP_FRUIT = 6,
|
||||
@@ -34,6 +35,7 @@ internal enum MaterialType
|
||||
MATERIAL_FAKE_ABSORBATE = 23,
|
||||
MATERIAL_CONSUME_BATCH_USE = 24,
|
||||
MATERIAL_WOOD = 25,
|
||||
|
||||
MATERIAL_FURNITURE_FORMULA = 27,
|
||||
MATERIAL_CHANNELLER_SLAB_BUFF = 28,
|
||||
MATERIAL_FURNITURE_SUITE_FORMULA = 29,
|
||||
@@ -41,7 +43,7 @@ internal enum MaterialType
|
||||
MATERIAL_HOME_SEED = 31,
|
||||
MATERIAL_FISH_BAIT = 32,
|
||||
MATERIAL_FISH_ROD = 33,
|
||||
MATERIAL_SUMO_BUFF = 34, // never appear
|
||||
MATERIAL_SUMO_BUFF = 34, // sumo 活动道具,never appear
|
||||
MATERIAL_FIREWORKS = 35,
|
||||
MATERIAL_BGM = 36,
|
||||
MATERIAL_SPICE_FOOD = 37,
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Model.Intrinsic;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Localization]
|
||||
internal enum ItemQuality
|
||||
internal enum QualityType
|
||||
{
|
||||
/// <summary>
|
||||
/// 无
|
||||
@@ -29,5 +29,5 @@ internal class Item : INameIcon
|
||||
/// <summary>
|
||||
/// 星级
|
||||
/// </summary>
|
||||
public ItemQuality Quality { get; set; }
|
||||
public QualityType Quality { get; set; }
|
||||
}
|
||||
@@ -19,5 +19,5 @@ internal interface INameQuality
|
||||
/// <summary>
|
||||
/// 星级
|
||||
/// </summary>
|
||||
ItemQuality Quality { get; }
|
||||
QualityType Quality { get; }
|
||||
}
|
||||
@@ -15,7 +15,7 @@ internal interface ISummaryItemSource
|
||||
/// <summary>
|
||||
/// 星级
|
||||
/// </summary>
|
||||
ItemQuality Quality { get; }
|
||||
QualityType Quality { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 转换到简述统计物品
|
||||
|
||||
@@ -24,7 +24,7 @@ internal sealed class Achievement
|
||||
/// <summary>
|
||||
/// 排序顺序
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
public uint Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标题
|
||||
@@ -44,7 +44,7 @@ internal sealed class Achievement
|
||||
/// <summary>
|
||||
/// 总进度
|
||||
/// </summary>
|
||||
public int Progress { get; set; }
|
||||
public uint Progress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标
|
||||
|
||||
@@ -19,7 +19,7 @@ internal sealed class AchievementGoal
|
||||
/// <summary>
|
||||
/// 排序顺序
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
public uint Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
|
||||
@@ -18,5 +18,5 @@ internal sealed class Reward
|
||||
/// <summary>
|
||||
/// 数量
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
public uint Count { get; set; }
|
||||
}
|
||||
@@ -18,6 +18,7 @@ internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IName
|
||||
{
|
||||
/// <summary>
|
||||
/// [非元数据] 搭配数据
|
||||
/// TODO:Add View suffix.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public AvatarCollocationView? Collocation { get; set; }
|
||||
@@ -37,7 +38,7 @@ internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IName
|
||||
/// 最大等级
|
||||
/// </summary>
|
||||
[SuppressMessage("", "CA1822")]
|
||||
public int MaxLevel { get => 90; }
|
||||
public uint MaxLevel { get => 90U; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableAvatar ToCalculable()
|
||||
|
||||
@@ -26,12 +26,11 @@ internal partial class Avatar
|
||||
/// <summary>
|
||||
/// 排序号
|
||||
/// </summary>
|
||||
public int Sort { get; set; }
|
||||
public uint Sort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 体型
|
||||
/// </summary>
|
||||
[JsonEnum(JsonSerializeType.String)]
|
||||
public BodyType Body { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
@@ -62,7 +61,7 @@ internal partial class Avatar
|
||||
/// <summary>
|
||||
/// 星级
|
||||
/// </summary>
|
||||
public ItemQuality Quality { get; set; }
|
||||
public QualityType Quality { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 武器类型
|
||||
@@ -72,12 +71,12 @@ internal partial class Avatar
|
||||
/// <summary>
|
||||
/// 基础数值
|
||||
/// </summary>
|
||||
public BaseValue BaseValue { get; set; } = default!;
|
||||
public AvatarBaseValue BaseValue { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 生长曲线
|
||||
/// </summary>
|
||||
public Dictionary<FightProperty, GrowCurveType> GrowCurves { get; set; } = default!;
|
||||
public List<TypeValue<FightProperty, GrowCurveType>> GrowCurves { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 技能
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Json.Annotation;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.ViewModel.Wiki;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Avatar;
|
||||
|
||||
/// <summary>
|
||||
/// 角色基础数值
|
||||
/// </summary>
|
||||
internal sealed class AvatarBaseValue : BaseValue
|
||||
{
|
||||
public PropertyCurveValue GetPropertyCurveValue(FightProperty fightProperty)
|
||||
{
|
||||
// TODO: impl
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ internal sealed class Costume
|
||||
/// <summary>
|
||||
/// 图标
|
||||
/// </summary>
|
||||
public string Icon { get; set; } = default!;
|
||||
public string FrontIcon { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 侧面图标
|
||||
|
||||
@@ -25,7 +25,6 @@ internal sealed class FetterInfo
|
||||
/// <summary>
|
||||
/// 地区
|
||||
/// </summary>
|
||||
[JsonEnum(JsonSerializeType.String)]
|
||||
public AssociationType Association { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
@@ -36,12 +35,12 @@ internal sealed class FetterInfo
|
||||
/// <summary>
|
||||
/// 生月
|
||||
/// </summary>
|
||||
public int BirthMonth { get; set; }
|
||||
public uint BirthMonth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生日
|
||||
/// </summary>
|
||||
public int BirthDay { get; set; }
|
||||
public uint BirthDay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 格式化的生日日期
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Snap.Hutao.Model.Metadata;
|
||||
[SuppressMessage("", "SA1600")]
|
||||
internal static class AvatarIds
|
||||
{
|
||||
// 此处的变量名称以 UI_AvatarIcon 为准
|
||||
public static readonly AvatarId Ayaka = 10000002;
|
||||
public static readonly AvatarId Qin = 10000003;
|
||||
|
||||
@@ -106,7 +107,7 @@ internal static class AvatarIds
|
||||
Name = "旅行者",
|
||||
Icon = "UI_AvatarIcon_PlayerBoy",
|
||||
SideIcon = "UI_AvatarIcon_Side_PlayerBoy",
|
||||
Quality = Intrinsic.ItemQuality.QUALITY_ORANGE,
|
||||
Quality = Intrinsic.QualityType.QUALITY_ORANGE,
|
||||
},
|
||||
|
||||
[PlayerGirl] = new()
|
||||
@@ -114,7 +115,7 @@ internal static class AvatarIds
|
||||
Name = "旅行者",
|
||||
Icon = "UI_AvatarIcon_PlayerGirl",
|
||||
SideIcon = "UI_AvatarIcon_Side_PlayerGirl",
|
||||
Quality = Intrinsic.ItemQuality.QUALITY_ORANGE,
|
||||
Quality = Intrinsic.QualityType.QUALITY_ORANGE,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ internal class BaseValue
|
||||
/// </summary>
|
||||
/// <param name="fightProperty">战斗属性</param>
|
||||
/// <returns>值</returns>
|
||||
public float GetValue(FightProperty fightProperty)
|
||||
public virtual float GetValue(FightProperty fightProperty)
|
||||
{
|
||||
return fightProperty switch
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ internal sealed partial class DescriptionsParametersDescriptor : ValueConverter<
|
||||
/// <param name="from">源</param>
|
||||
/// <param name="level">等级</param>
|
||||
/// <returns>特定等级的解释</returns>
|
||||
public static LevelParameters<string, ParameterDescription> Convert(DescriptionsParameters from, int level)
|
||||
public static LevelParameters<string, ParameterDescription> Convert(DescriptionsParameters from, uint level)
|
||||
{
|
||||
LevelParameters<int, float> param = from.Parameters.Single(param => param.Level == level);
|
||||
return new LevelParameters<string, ParameterDescription>($"Lv.{param.Level}", GetParameterDescription(from.Descriptions, param.Parameters));
|
||||
|
||||
@@ -13,18 +13,18 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
/// 品质颜色转换器
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class QualityColorConverter : ValueConverter<ItemQuality, Color>
|
||||
internal sealed class QualityColorConverter : ValueConverter<QualityType, Color>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Color Convert(ItemQuality from)
|
||||
public override Color Convert(QualityType from)
|
||||
{
|
||||
return from switch
|
||||
{
|
||||
ItemQuality.QUALITY_WHITE => StructMarshal.Color(0xFF72778B),
|
||||
ItemQuality.QUALITY_GREEN => StructMarshal.Color(0xFF2A8F72),
|
||||
ItemQuality.QUALITY_BLUE => StructMarshal.Color(0xFF5180CB),
|
||||
ItemQuality.QUALITY_PURPLE => StructMarshal.Color(0xFFA156E0),
|
||||
ItemQuality.QUALITY_ORANGE or ItemQuality.QUALITY_ORANGE_SP => StructMarshal.Color(0xFFBC6932),
|
||||
QualityType.QUALITY_WHITE => StructMarshal.Color(0xFF72778B),
|
||||
QualityType.QUALITY_GREEN => StructMarshal.Color(0xFF2A8F72),
|
||||
QualityType.QUALITY_BLUE => StructMarshal.Color(0xFF5180CB),
|
||||
QualityType.QUALITY_PURPLE => StructMarshal.Color(0xFFA156E0),
|
||||
QualityType.QUALITY_ORANGE or QualityType.QUALITY_ORANGE_SP => StructMarshal.Color(0xFFBC6932),
|
||||
_ => Colors.Transparent,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
/// 物品等级转换器
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class QualityConverter : ValueConverter<ItemQuality, Uri>
|
||||
internal sealed class QualityConverter : ValueConverter<QualityType, Uri>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Uri Convert(ItemQuality from)
|
||||
public override Uri Convert(QualityType from)
|
||||
{
|
||||
string name = Enum.GetName(from) ?? from.ToString();
|
||||
if (name == nameof(ItemQuality.QUALITY_ORANGE_SP))
|
||||
if (name == nameof(QualityType.QUALITY_ORANGE_SP))
|
||||
{
|
||||
name = "QUALITY_RED";
|
||||
}
|
||||
|
||||
@@ -21,11 +21,21 @@ internal sealed class GachaEvent
|
||||
/// </summary>
|
||||
public string Version { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 顺序
|
||||
/// </summary>
|
||||
public uint Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 卡池图
|
||||
/// </summary>
|
||||
public Uri Banner { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 卡池图2
|
||||
/// </summary>
|
||||
public Uri Banner2 { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 开始时间
|
||||
/// </summary>
|
||||
@@ -44,10 +54,10 @@ internal sealed class GachaEvent
|
||||
/// <summary>
|
||||
/// 五星列表
|
||||
/// </summary>
|
||||
public List<int> UpOrangeList { get; set; } = default!;
|
||||
public List<uint> UpOrangeList { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 四星列表
|
||||
/// </summary>
|
||||
public List<int> UpPurpleList { get; set; } = default!;
|
||||
public List<uint> UpPurpleList { get; set; } = default!;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata;
|
||||
|
||||
@@ -11,13 +12,23 @@ namespace Snap.Hutao.Model.Metadata;
|
||||
[HighQuality]
|
||||
internal sealed class GrowCurve
|
||||
{
|
||||
private Dictionary<GrowCurveType, float>? curveMap;
|
||||
|
||||
/// <summary>
|
||||
/// 等级
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
public Level Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 曲线 值相乘
|
||||
/// </summary>
|
||||
public Dictionary<GrowCurveType, float> Curves { get; set; } = default!;
|
||||
public List<TypeValue<GrowCurveType, float>> Curves { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 曲线映射
|
||||
/// </summary>
|
||||
public Dictionary<GrowCurveType, float> CurveMap
|
||||
{
|
||||
get => curveMap ??= Curves.ToDictionary(v => v.Type, v => v.Value);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Model.Metadata.Item;
|
||||
/// 展示物品
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal class Display
|
||||
internal class DisplayItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
@@ -20,7 +20,7 @@ internal class Display
|
||||
/// <summary>
|
||||
/// 等级
|
||||
/// </summary>
|
||||
public ItemQuality RankLevel { get; set; }
|
||||
public QualityType RankLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品类型
|
||||
@@ -11,44 +11,8 @@ namespace Snap.Hutao.Model.Metadata.Item;
|
||||
/// <summary>
|
||||
/// 材料
|
||||
/// </summary>
|
||||
internal sealed class Material : Display
|
||||
internal sealed class Material : DisplayItem
|
||||
{
|
||||
private static readonly ImmutableHashSet<MaterialId> MondayThursdayItems = new HashSet<MaterialId>
|
||||
{
|
||||
104301, 104302, 104303, // 「自由」
|
||||
104310, 104311, 104312, // 「繁荣」
|
||||
104320, 104321, 104322, // 「浮世」
|
||||
104329, 104330, 104331, // 「诤言」
|
||||
114001, 114002, 114003, 114004, // 高塔孤王
|
||||
114013, 114014, 114015, 114016, // 孤云寒林
|
||||
114025, 114026, 114027, 114028, // 远海夷地
|
||||
114037, 114038, 114039, 114040, // 谧林涓露
|
||||
}.ToImmutableHashSet();
|
||||
|
||||
private static readonly ImmutableHashSet<MaterialId> TuesdayFridayItems = new HashSet<MaterialId>
|
||||
{
|
||||
104304, 104305, 104306, // 「抗争」
|
||||
104313, 104314, 104315, // 「勤劳」
|
||||
104323, 104324, 104325, // 「风雅」
|
||||
104332, 104333, 104334, // 「巧思」
|
||||
114005, 114006, 114007, 114008, // 凛风奔狼
|
||||
114017, 114018, 114019, 114020, // 雾海云间
|
||||
114029, 114030, 114031, 114032, // 鸣神御灵
|
||||
114041, 114042, 114043, 114044, // 绿洲花园
|
||||
}.ToImmutableHashSet();
|
||||
|
||||
private static readonly ImmutableHashSet<MaterialId> WednesdaySaturdayItems = new HashSet<MaterialId>
|
||||
{
|
||||
104307, 104308, 104309, // 「诗文」
|
||||
104316, 104317, 104318, // 「黄金」
|
||||
104326, 104327, 104328, // 「天光」
|
||||
104335, 104336, 104337, // 「笃行」
|
||||
114009, 114010, 114011, 114012, // 狮牙斗士
|
||||
114021, 114022, 114023, 114024, // 漆黑陨铁
|
||||
114033, 114034, 114035, 114036, // 今昔剧画
|
||||
114045, 114046, 114047, 114048, // 谧林涓露
|
||||
}.ToImmutableHashSet();
|
||||
|
||||
/// <summary>
|
||||
/// 材料类型
|
||||
/// </summary>
|
||||
@@ -66,23 +30,24 @@ internal sealed class Material : Display
|
||||
public bool IsInventoryItem()
|
||||
{
|
||||
// 原质
|
||||
if (Id == 112001)
|
||||
if (Id == 112001U)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 摩拉
|
||||
if (Id == 202)
|
||||
if (Id == 202U)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: support non-CHS
|
||||
if (TypeDescription.EndsWith("区域特产"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Currently only support CN
|
||||
// TODO: support non-CHS
|
||||
return TypeDescription switch
|
||||
{
|
||||
"角色与武器培养素材" => true, // 怪物掉落
|
||||
@@ -106,9 +71,9 @@ internal sealed class Material : Display
|
||||
{
|
||||
return DateTimeOffset.UtcNow.AddHours(4).DayOfWeek switch
|
||||
{
|
||||
DayOfWeek.Monday or DayOfWeek.Thursday => MondayThursdayItems.Contains(Id),
|
||||
DayOfWeek.Tuesday or DayOfWeek.Friday => TuesdayFridayItems.Contains(Id),
|
||||
DayOfWeek.Wednesday or DayOfWeek.Saturday => WednesdaySaturdayItems.Contains(Id),
|
||||
DayOfWeek.Monday or DayOfWeek.Thursday => Materials.MondayThursdayItems.Contains(Id),
|
||||
DayOfWeek.Tuesday or DayOfWeek.Friday => Materials.TuesdayFridayItems.Contains(Id),
|
||||
DayOfWeek.Wednesday or DayOfWeek.Saturday => Materials.WednesdaySaturdayItems.Contains(Id),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
@@ -119,17 +84,17 @@ internal sealed class Material : Display
|
||||
/// <returns>DaysOfWeek</returns>
|
||||
public DaysOfWeek GetDaysOfWeek()
|
||||
{
|
||||
if (MondayThursdayItems.Contains(Id))
|
||||
if (Materials.MondayThursdayItems.Contains(Id))
|
||||
{
|
||||
return DaysOfWeek.MondayAndThursday;
|
||||
}
|
||||
|
||||
if (TuesdayFridayItems.Contains(Id))
|
||||
if (Materials.TuesdayFridayItems.Contains(Id))
|
||||
{
|
||||
return DaysOfWeek.TuesdayAndFriday;
|
||||
}
|
||||
|
||||
if (WednesdaySaturdayItems.Contains(Id))
|
||||
if (Materials.WednesdaySaturdayItems.Contains(Id))
|
||||
{
|
||||
return DaysOfWeek.WednesdayAndSaturday;
|
||||
}
|
||||
|
||||
66
src/Snap.Hutao/Snap.Hutao/Model/Metadata/Item/Materials.cs
Normal file
66
src/Snap.Hutao/Snap.Hutao/Model/Metadata/Item/Materials.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.ViewModel.Cultivation;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Item;
|
||||
|
||||
/// <summary>
|
||||
/// 材料表
|
||||
/// </summary>
|
||||
internal static class Materials
|
||||
{
|
||||
private static readonly ImmutableHashSet<MaterialId> MondayThursdayItemsInner = new HashSet<MaterialId>
|
||||
{
|
||||
104301, 104302, 104303, // 「自由」
|
||||
104310, 104311, 104312, // 「繁荣」
|
||||
104320, 104321, 104322, // 「浮世」
|
||||
104329, 104330, 104331, // 「诤言」
|
||||
114001, 114002, 114003, 114004, // 高塔孤王
|
||||
114013, 114014, 114015, 114016, // 孤云寒林
|
||||
114025, 114026, 114027, 114028, // 远海夷地
|
||||
114037, 114038, 114039, 114040, // 谧林涓露
|
||||
}.ToImmutableHashSet();
|
||||
|
||||
private static readonly ImmutableHashSet<MaterialId> TuesdayFridayItemsInner = new HashSet<MaterialId>
|
||||
{
|
||||
104304, 104305, 104306, // 「抗争」
|
||||
104313, 104314, 104315, // 「勤劳」
|
||||
104323, 104324, 104325, // 「风雅」
|
||||
104332, 104333, 104334, // 「巧思」
|
||||
114005, 114006, 114007, 114008, // 凛风奔狼
|
||||
114017, 114018, 114019, 114020, // 雾海云间
|
||||
114029, 114030, 114031, 114032, // 鸣神御灵
|
||||
114041, 114042, 114043, 114044, // 绿洲花园
|
||||
}.ToImmutableHashSet();
|
||||
|
||||
private static readonly ImmutableHashSet<MaterialId> WednesdaySaturdayItemsInner = new HashSet<MaterialId>
|
||||
{
|
||||
104307, 104308, 104309, // 「诗文」
|
||||
104316, 104317, 104318, // 「黄金」
|
||||
104326, 104327, 104328, // 「天光」
|
||||
104335, 104336, 104337, // 「笃行」
|
||||
114009, 114010, 114011, 114012, // 狮牙斗士
|
||||
114021, 114022, 114023, 114024, // 漆黑陨铁
|
||||
114033, 114034, 114035, 114036, // 今昔剧画
|
||||
114045, 114046, 114047, 114048, // 谧林涓露
|
||||
}.ToImmutableHashSet();
|
||||
|
||||
/// <summary>
|
||||
/// 周一/周四
|
||||
/// </summary>
|
||||
public static ImmutableHashSet<MaterialId> MondayThursdayItems { get => MondayThursdayItemsInner; }
|
||||
|
||||
/// <summary>
|
||||
/// 周二/周五
|
||||
/// </summary>
|
||||
public static ImmutableHashSet<MaterialId> TuesdayFridayItems { get => TuesdayFridayItemsInner; }
|
||||
|
||||
/// <summary>
|
||||
/// 周三/周六
|
||||
/// </summary>
|
||||
public static ImmutableHashSet<MaterialId> WednesdaySaturdayItems { get => WednesdaySaturdayItemsInner; }
|
||||
}
|
||||
@@ -17,6 +17,11 @@ internal sealed class Monster
|
||||
/// </summary>
|
||||
public MonsterId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述 Id
|
||||
/// </summary>
|
||||
public MonsterDescribeId DescribeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 内部代号
|
||||
/// </summary>
|
||||
@@ -60,10 +65,11 @@ internal sealed class Monster
|
||||
/// <summary>
|
||||
/// 生长曲线
|
||||
/// </summary>
|
||||
public Dictionary<FightProperty, GrowCurveType> GrowCurves { get; set; } = default!;
|
||||
public List<TypeValue<FightProperty, GrowCurveType>> GrowCurves { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 养成物品视图
|
||||
/// </summary>
|
||||
public List<Display>? DropsView { get; set; }
|
||||
[JsonIgnore]
|
||||
public List<DisplayItem>? DropsView { get; set; }
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace Snap.Hutao.Model.Metadata;
|
||||
[HighQuality]
|
||||
internal sealed class Promote
|
||||
{
|
||||
private Dictionary<FightProperty, float>? addPropertyMap;
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
@@ -20,10 +22,18 @@ internal sealed class Promote
|
||||
/// <summary>
|
||||
/// 突破等级
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
public PromoteLevel Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 增加的属性
|
||||
/// </summary>
|
||||
public Dictionary<FightProperty, float> AddProperties { get; set; } = default!;
|
||||
public List<TypeValue<FightProperty, float>> AddProperties { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 属性映射
|
||||
/// </summary>
|
||||
public Dictionary<FightProperty, float> AddPropertyMap
|
||||
{
|
||||
get => addPropertyMap ??= AddProperties.ToDictionary(a => a.Type, a => a.Value);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ internal sealed class Reliquary
|
||||
/// <summary>
|
||||
/// 允许出现的等级
|
||||
/// </summary>
|
||||
public ItemQuality RankLevel { get; set; } = default!;
|
||||
public QualityType RankLevel { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 套装Id
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Reliquary;
|
||||
|
||||
/// <summary>
|
||||
/// 圣遗物词条权重
|
||||
/// </summary>
|
||||
internal sealed class ReliquaryAffixWeight
|
||||
{
|
||||
/// <summary>
|
||||
/// 角色 Id
|
||||
/// </summary>
|
||||
public AvatarId AvatarId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生命值
|
||||
/// </summary>
|
||||
public float HpPercent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 攻击力
|
||||
/// </summary>
|
||||
public float AttackPercent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 防御力
|
||||
/// </summary>
|
||||
public float DefensePercent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 暴击率
|
||||
/// </summary>
|
||||
public float Critical { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 暴击伤害
|
||||
/// </summary>
|
||||
public float CriticalHurt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 元素精通
|
||||
/// </summary>
|
||||
public float ElementMastery { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 元素充能效率
|
||||
/// </summary>
|
||||
public float ChargeEfficiency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 治疗加成
|
||||
/// </summary>
|
||||
public float HealAdd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物理伤害加成
|
||||
/// </summary>
|
||||
public float PhysicalAddHurt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 伤害加成
|
||||
/// </summary>
|
||||
public float AddHurt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 元素类型
|
||||
/// </summary>
|
||||
public ElementType ElementType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取权重
|
||||
/// </summary>
|
||||
/// <param name="fightProperty">属性</param>
|
||||
/// <returns>权重</returns>
|
||||
public float this[FightProperty fightProperty]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((ElementType == ElementType.Fire && fightProperty == FightProperty.FIGHT_PROP_FIRE_ADD_HURT) ||
|
||||
(ElementType == ElementType.Water && fightProperty == FightProperty.FIGHT_PROP_WATER_ADD_HURT) ||
|
||||
(ElementType == ElementType.Grass && fightProperty == FightProperty.FIGHT_PROP_GRASS_ADD_HURT) ||
|
||||
(ElementType == ElementType.Electric && fightProperty == FightProperty.FIGHT_PROP_ELEC_ADD_HURT) ||
|
||||
(ElementType == ElementType.Ice && fightProperty == FightProperty.FIGHT_PROP_ICE_ADD_HURT) ||
|
||||
(ElementType == ElementType.Wind && fightProperty == FightProperty.FIGHT_PROP_WIND_ADD_HURT) ||
|
||||
(ElementType == ElementType.Rock && fightProperty == FightProperty.FIGHT_PROP_ROCK_ADD_HURT))
|
||||
{
|
||||
return AddHurt;
|
||||
}
|
||||
|
||||
return fightProperty switch
|
||||
{
|
||||
FightProperty.FIGHT_PROP_HP_PERCENT => HpPercent,
|
||||
FightProperty.FIGHT_PROP_ATTACK_PERCENT => AttackPercent,
|
||||
FightProperty.FIGHT_PROP_DEFENSE_PERCENT => DefensePercent,
|
||||
FightProperty.FIGHT_PROP_CRITICAL => Critical,
|
||||
FightProperty.FIGHT_PROP_CRITICAL_HURT => CriticalHurt,
|
||||
FightProperty.FIGHT_PROP_ELEMENT_MASTERY => ElementMastery,
|
||||
FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY => ChargeEfficiency,
|
||||
FightProperty.FIGHT_PROP_HEALED_ADD => HealAdd,
|
||||
FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT => PhysicalAddHurt,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Reliquary;
|
||||
|
||||
/// <summary>
|
||||
/// 属性映射
|
||||
/// </summary>
|
||||
internal sealed partial class ReliquaryMainAffixLevel
|
||||
{
|
||||
private Dictionary<FightProperty, float>? propertyMap;
|
||||
|
||||
/// <summary>
|
||||
/// 属性映射
|
||||
/// </summary>
|
||||
public Dictionary<FightProperty, float> PropertyMap
|
||||
{
|
||||
get => propertyMap ??= Properties.ToDictionary(t => t.Type, t => t.Value);
|
||||
}
|
||||
}
|
||||
@@ -9,20 +9,20 @@ namespace Snap.Hutao.Model.Metadata.Reliquary;
|
||||
/// 圣遗物等级
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class ReliquaryLevel
|
||||
internal sealed partial class ReliquaryMainAffixLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// 品质
|
||||
/// </summary>
|
||||
public ItemQuality Quality { get; set; }
|
||||
public QualityType Rank { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 等级 1-21
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
public uint Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 属性
|
||||
/// </summary>
|
||||
public Dictionary<FightProperty, float> Properties { get; set; } = default!;
|
||||
public List<TypeValue<FightProperty, float>> Properties { get; set; } = default!;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ internal sealed class ReliquarySet
|
||||
/// <summary>
|
||||
/// 套装Id
|
||||
/// </summary>
|
||||
public int SetId { get; set; }
|
||||
public ReliquarySetId SetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 装备被动Id
|
||||
|
||||
@@ -9,12 +9,12 @@ namespace Snap.Hutao.Model.Metadata.Reliquary;
|
||||
/// 圣遗物突破属性
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class ReliquaryAffix : ReliquaryMainAffix
|
||||
internal sealed class ReliquarySubAffix : ReliquaryMainAffix
|
||||
{
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public new ReliquaryAffixId Id { get; set; }
|
||||
public new ReliquarySubAffixId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 值
|
||||
32
src/Snap.Hutao/Snap.Hutao/Model/Metadata/Tower/TowerFloor.cs
Normal file
32
src/Snap.Hutao/Snap.Hutao/Model/Metadata/Tower/TowerFloor.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Tower;
|
||||
|
||||
/// <summary>
|
||||
/// 深渊 层
|
||||
/// </summary>
|
||||
internal sealed class TowerFloor
|
||||
{
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public TowerFloorId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编号 [1-12]
|
||||
/// </summary>
|
||||
public uint Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 深渊间分组编号
|
||||
/// </summary>
|
||||
public TowerLevelGroupId LevelGroupId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 背景图片
|
||||
/// </summary>
|
||||
public string Background { get; set; } = default!;
|
||||
}
|
||||
42
src/Snap.Hutao/Snap.Hutao/Model/Metadata/Tower/TowerLevel.cs
Normal file
42
src/Snap.Hutao/Snap.Hutao/Model/Metadata/Tower/TowerLevel.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Tower;
|
||||
|
||||
/// <summary>
|
||||
/// 深渊 间
|
||||
/// </summary>
|
||||
internal sealed class TowerLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public TowerLevelId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 深渊间分组编号
|
||||
/// </summary>
|
||||
public TowerLevelGroupId GroupId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编号
|
||||
/// </summary>
|
||||
public uint Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 怪物等级
|
||||
/// </summary>
|
||||
public uint MonsterLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上半怪物预览
|
||||
/// </summary>
|
||||
public List<MonsterId> FirstMonsters { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 下半怪物预览
|
||||
/// </summary>
|
||||
public List<MonsterId> SecondMonsters { get; set; } = default!;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Tower;
|
||||
|
||||
/// <summary>
|
||||
/// 深渊 计划
|
||||
/// </summary>
|
||||
internal sealed class TowerSchedule
|
||||
{
|
||||
/// <summary>
|
||||
/// 计划 Id
|
||||
/// </summary>
|
||||
public TowerScheduleId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 层 Id 表
|
||||
/// </summary>
|
||||
public List<TowerFloorId> FloorIds { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 开始时间
|
||||
/// </summary>
|
||||
public DateTimeOffset Open { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结束时间
|
||||
/// </summary>
|
||||
public DateTimeOffset Close { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 渊月祝福
|
||||
/// </summary>
|
||||
public string BuffName { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public List<string> Descriptions { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 图标
|
||||
/// </summary>
|
||||
public string Icon { get; set; } = default!;
|
||||
}
|
||||
33
src/Snap.Hutao/Snap.Hutao/Model/Metadata/TypeValue.cs
Normal file
33
src/Snap.Hutao/Snap.Hutao/Model/Metadata/TypeValue.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata;
|
||||
|
||||
/// <summary>
|
||||
/// 类型与值
|
||||
/// </summary>
|
||||
/// <typeparam name="TType">类型</typeparam>
|
||||
/// <typeparam name="TValue">值</typeparam>
|
||||
internal class TypeValue<TType, TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的类型与值
|
||||
/// </summary>
|
||||
/// <param name="type">类型</param>
|
||||
/// <param name="value">值</param>
|
||||
public TypeValue(TType type, TValue value)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// </summary>
|
||||
public TType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public TValue Value { get; set; }
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Weapon;
|
||||
|
||||
/// <summary>
|
||||
/// 生长曲线与值
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class GrowCurveTypeValue
|
||||
{
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// </summary>
|
||||
public GrowCurveType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public float Value { get; set; }
|
||||
}
|
||||
@@ -23,7 +23,7 @@ internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource
|
||||
|
||||
/// <inheritdoc/>
|
||||
[JsonIgnore]
|
||||
public ItemQuality Quality
|
||||
public QualityType Quality
|
||||
{
|
||||
get => RankLevel;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource
|
||||
/// <summary>
|
||||
/// 最大等级
|
||||
/// </summary>
|
||||
internal int MaxLevel { get => ((int)Quality) >= 3 ? 90 : 70; }
|
||||
internal uint MaxLevel { get => ((int)Quality) >= 3 ? 90U : 70U; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableWeapon ToCalculable()
|
||||
|
||||
@@ -30,7 +30,7 @@ internal sealed partial class Weapon
|
||||
/// <summary>
|
||||
/// 等级
|
||||
/// </summary>
|
||||
public ItemQuality RankLevel { get; set; }
|
||||
public QualityType RankLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
@@ -55,7 +55,7 @@ internal sealed partial class Weapon
|
||||
/// <summary>
|
||||
/// 生长曲线
|
||||
/// </summary>
|
||||
public Dictionary<FightProperty, GrowCurveTypeValue> GrowCurves { get; set; } = default!;
|
||||
public List<WeaponTypeValue> GrowCurves { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 被动信息, 无被动的武器为 <see langword="null"/>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Weapon;
|
||||
|
||||
/// <summary>
|
||||
/// 武器曲线值
|
||||
/// </summary>
|
||||
internal sealed class WeaponTypeValue : TypeValue<FightProperty, GrowCurveType>
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的武器曲线值
|
||||
/// </summary>
|
||||
/// <param name="type">属性类型</param>
|
||||
/// <param name="value">曲线类型</param>
|
||||
/// <param name="initValue">初始值</param>
|
||||
public WeaponTypeValue(FightProperty type, GrowCurveType value, float initValue)
|
||||
: base(type, value)
|
||||
{
|
||||
InitValue = initValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始值
|
||||
/// </summary>
|
||||
public float InitValue { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// Id 转换器
|
||||
/// </summary>
|
||||
/// <typeparam name="TWrapper">包装类型</typeparam>
|
||||
internal unsafe sealed class IdentityConverter<TWrapper> : JsonConverter<TWrapper>
|
||||
where TWrapper : unmanaged
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override TWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
uint value = JsonSerializer.Deserialize<uint>(ref reader, options);
|
||||
return *(TWrapper*)&value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override TWrapper ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.PropertyName)
|
||||
{
|
||||
string? value = reader.GetString();
|
||||
_ = uint.TryParse(value,out uint result);
|
||||
return *(TWrapper*)&result;
|
||||
}
|
||||
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, *(uint*)&value, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void WriteAsPropertyName(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WritePropertyName((*(uint*)&value).ToString());
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ internal sealed partial class AchievementService : IAchievementService
|
||||
/// <inheritdoc/>
|
||||
public List<AchievementView> GetAchievements(AchievementArchive archive, List<MetadataAchievement> metadata)
|
||||
{
|
||||
Dictionary<int, EntityAchievement> entityMap;
|
||||
Dictionary<AchievementId, EntityAchievement> entityMap;
|
||||
try
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
@@ -43,7 +43,7 @@ internal sealed partial class AchievementService : IAchievementService
|
||||
entityMap = appDbContext.Achievements
|
||||
.Where(a => a.ArchiveId == archive.InnerId)
|
||||
.AsEnumerable()
|
||||
.ToDictionary(a => a.Id);
|
||||
.ToDictionary(a => (AchievementId)a.Id);
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.AvatarInfo.Transformer;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
@@ -202,7 +203,7 @@ internal sealed partial class AvatarInfoDbBulkOperation
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void AddOrUpdateAvatarInfo<TSource>(ModelAvatarInfo? entity, int avatarId, string uid, AppDbContext appDbContext, IAvatarInfoTransformer<TSource> transformer, TSource source)
|
||||
private static void AddOrUpdateAvatarInfo<TSource>(ModelAvatarInfo? entity, AvatarId avatarId, string uid, AppDbContext appDbContext, IAvatarInfoTransformer<TSource> transformer, TSource source)
|
||||
{
|
||||
if (entity == null)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ internal sealed class AffixWeight : Dictionary<FightProperty, float>
|
||||
/// <param name="heal">治疗加成</param>
|
||||
/// <param name="name">名称</param>
|
||||
public AffixWeight(
|
||||
int avatarId,
|
||||
uint avatarId,
|
||||
float hp,
|
||||
float atk,
|
||||
float def,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.ViewModel.AvatarProperty;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
@@ -79,7 +80,7 @@ internal sealed class SummaryAvatarFactory
|
||||
Model.Metadata.Avatar.Costume costume = avatar.Costumes.Single(c => c.Id == avatarInfo.CostumeId.Value);
|
||||
|
||||
// Set to costume icon
|
||||
propertyAvatar.Icon = AvatarIconConverter.IconNameToUri(costume.Icon);
|
||||
propertyAvatar.Icon = AvatarIconConverter.IconNameToUri(costume.FrontIcon);
|
||||
propertyAvatar.SideIcon = AvatarIconConverter.IconNameToUri(costume.SideIcon);
|
||||
}
|
||||
else
|
||||
@@ -116,9 +117,9 @@ internal sealed class SummaryAvatarFactory
|
||||
{
|
||||
MetadataWeapon weapon = metadataContext.IdWeaponMap[equip.ItemId];
|
||||
|
||||
// AffixMap can be empty when it's a white weapon.
|
||||
KeyValuePair<string, int>? idLevel = equip.Weapon!.AffixMap?.Single();
|
||||
int affixLevel = idLevel?.Value ?? 0;
|
||||
// AffixMap can be null when it's a white weapon.
|
||||
KeyValuePair<EquipAffixId, WeaponAffixLevel>? idLevel = equip.Weapon!.AffixMap?.Single();
|
||||
uint affixLevel = idLevel?.Value ?? 0U;
|
||||
|
||||
WeaponStat? mainStat = equip.Flat.WeaponStats?.ElementAtOrDefault(0);
|
||||
WeaponStat? subStat = equip.Flat.WeaponStats?.ElementAtOrDefault(1);
|
||||
@@ -145,7 +146,7 @@ internal sealed class SummaryAvatarFactory
|
||||
Description = weapon.Description,
|
||||
|
||||
// EquipBase
|
||||
Level = $"Lv.{equip.Weapon!.Level}",
|
||||
Level = $"Lv.{equip.Weapon!.Level.Value}",
|
||||
Quality = weapon.Quality,
|
||||
MainProperty = mainStat != null ? FightPropertyFormat.ToNameValue(mainStat.AppendPropId, mainStat.StatValue) : default!,
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ internal sealed partial class SummaryFactory : ISummaryFactory
|
||||
IdAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false),
|
||||
IdWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false),
|
||||
IdRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false),
|
||||
IdReliquaryAffixMap = await metadataService.GetIdToReliquaryAffixMapAsync(token).ConfigureAwait(false),
|
||||
IdReliquarySubAffixMap = await metadataService.GetIdToReliquarySubAffixMapAsync(token).ConfigureAwait(false),
|
||||
ReliqueryLevels = await metadataService.GetReliquaryLevelsAsync(token).ConfigureAwait(false),
|
||||
Reliquaries = await metadataService.GetReliquariesAsync(token).ConfigureAwait(false),
|
||||
};
|
||||
|
||||
@@ -39,29 +39,26 @@ internal static class SummaryHelper
|
||||
/// <param name="proudSkillExtraLevelMap">额外提升等级映射</param>
|
||||
/// <param name="proudSkills">技能列表</param>
|
||||
/// <returns>技能</returns>
|
||||
public static List<SkillView> CreateSkills(Dictionary<string, int> skillLevelMap, Dictionary<string, int>? proudSkillExtraLevelMap, List<ProudableSkill> proudSkills)
|
||||
public static List<SkillView> CreateSkills(Dictionary<SkillId, SkillLevel> skillLevelMap, Dictionary<SkillGroupId, SkillLevel>? proudSkillExtraLevelMap, List<ProudableSkill> proudSkills)
|
||||
{
|
||||
if (skillLevelMap == null)
|
||||
{
|
||||
return new();
|
||||
}
|
||||
|
||||
Dictionary<string, int> skillExtraLeveledMap = new(skillLevelMap);
|
||||
Dictionary<SkillId, SkillLevel> skillExtraLeveledMap = new(skillLevelMap);
|
||||
|
||||
if (proudSkillExtraLevelMap != null)
|
||||
{
|
||||
foreach ((string skillGroupIdString, int extraLevel) in proudSkillExtraLevelMap)
|
||||
foreach ((SkillGroupId groupId, SkillLevel extraLevel) in proudSkillExtraLevelMap)
|
||||
{
|
||||
SkillGroupId skillGroupId = int.Parse(skillGroupIdString);
|
||||
SkillId skillId = proudSkills.Single(p => p.GroupId == skillGroupId).Id;
|
||||
|
||||
skillExtraLeveledMap.Increase($"{skillId.Value}", extraLevel);
|
||||
skillExtraLeveledMap.IncreaseValue(proudSkills.Single(p => p.GroupId == groupId).Id, extraLevel);
|
||||
}
|
||||
}
|
||||
|
||||
return proudSkills.SelectList(proudableSkill =>
|
||||
{
|
||||
string skillId = $"{proudableSkill.Id.Value}";
|
||||
SkillId skillId = proudableSkill.Id;
|
||||
|
||||
return new SkillView()
|
||||
{
|
||||
@@ -81,10 +78,10 @@ internal static class SummaryHelper
|
||||
/// </summary>
|
||||
/// <param name="appendId">属性Id</param>
|
||||
/// <returns>最大属性Id</returns>
|
||||
public static int GetAffixMaxId(int appendId)
|
||||
public static ReliquarySubAffixId GetAffixMaxId(in ReliquarySubAffixId appendId)
|
||||
{
|
||||
int value = appendId / 100000;
|
||||
int max = value switch
|
||||
uint value = (uint)appendId / 100000U;
|
||||
uint max = value switch
|
||||
{
|
||||
1 => 2,
|
||||
2 => 3,
|
||||
@@ -100,10 +97,10 @@ internal static class SummaryHelper
|
||||
/// </summary>
|
||||
/// <param name="appendId">id</param>
|
||||
/// <returns>分数</returns>
|
||||
public static float GetPercentSubAffixScore(int appendId)
|
||||
public static float GetPercentSubAffixScore(ReliquarySubAffixId appendId)
|
||||
{
|
||||
int maxId = GetAffixMaxId(appendId);
|
||||
int delta = maxId - appendId;
|
||||
uint maxId = GetAffixMaxId(appendId);
|
||||
uint delta = maxId - appendId;
|
||||
|
||||
return (maxId / 100000, delta) switch
|
||||
{
|
||||
|
||||
@@ -34,12 +34,12 @@ internal sealed class SummaryMetadataContext
|
||||
/// <summary>
|
||||
/// 圣遗物副属性映射
|
||||
/// </summary>
|
||||
public Dictionary<ReliquaryAffixId, ReliquaryAffix> IdReliquaryAffixMap { get; set; } = default!;
|
||||
public Dictionary<ReliquarySubAffixId, ReliquarySubAffix> IdReliquarySubAffixMap { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 圣遗物等级
|
||||
/// </summary>
|
||||
public List<ReliquaryLevel> ReliqueryLevels { get; set; } = default!;
|
||||
public List<ReliquaryMainAffixLevel> ReliqueryLevels { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 圣遗物
|
||||
|
||||
@@ -5,11 +5,12 @@ using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Intrinsic.Format;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.ViewModel.AvatarProperty;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using System.Runtime.InteropServices;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
using MetadataReliquaryAffix = Snap.Hutao.Model.Metadata.Reliquary.ReliquaryAffix;
|
||||
using MetadataReliquaryAffix = Snap.Hutao.Model.Metadata.Reliquary.ReliquarySubAffix;
|
||||
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
|
||||
using PropertyReliquary = Snap.Hutao.ViewModel.AvatarProperty.ReliquaryView;
|
||||
|
||||
@@ -67,11 +68,10 @@ internal sealed class SummaryReliquaryFactory
|
||||
result.PrimarySubProperties = new(span[..^affixCount].ToArray());
|
||||
result.SecondarySubProperties = new(span[^affixCount..].ToArray());
|
||||
result.ComposedSubProperties = equip.Flat.ReliquarySubstats!.SelectList(CreateComposedSubProperty);
|
||||
|
||||
ReliquaryLevel relicLevel = metadataContext.ReliqueryLevels.Single(r => r.Level == equip.Reliquary!.Level && r.Quality == reliquary.RankLevel);
|
||||
ReliquaryMainAffixLevel relicLevel = metadataContext.ReliqueryLevels.Single(r => r.Level == equip.Reliquary!.Level && r.Rank == reliquary.RankLevel);
|
||||
FightProperty property = metadataContext.IdRelicMainPropMap[equip.Reliquary.MainPropId];
|
||||
|
||||
result.MainProperty = FightPropertyFormat.ToNameValue(property, relicLevel.Properties[property]);
|
||||
result.MainProperty = FightPropertyFormat.ToNameValue(property, relicLevel.PropertyMap[property]);
|
||||
result.Score = ScoreReliquary(property, reliquary, relicLevel, subProperty);
|
||||
}
|
||||
|
||||
@@ -81,46 +81,46 @@ internal sealed class SummaryReliquaryFactory
|
||||
private int GetSecondaryAffixCount(MetadataReliquary reliquary)
|
||||
{
|
||||
// 强化词条个数
|
||||
return (reliquary.RankLevel, equip.Reliquary!.Level) switch
|
||||
return (reliquary.RankLevel, equip.Reliquary!.Level.Value) switch
|
||||
{
|
||||
(ItemQuality.QUALITY_ORANGE, > 20) => 5,
|
||||
(ItemQuality.QUALITY_ORANGE, > 16) => 4,
|
||||
(ItemQuality.QUALITY_ORANGE, > 12) => 3,
|
||||
(ItemQuality.QUALITY_ORANGE, > 8) => 2,
|
||||
(ItemQuality.QUALITY_ORANGE, > 4) => 1,
|
||||
(ItemQuality.QUALITY_ORANGE, _) => 0,
|
||||
(QualityType.QUALITY_ORANGE, > 20U) => 5,
|
||||
(QualityType.QUALITY_ORANGE, > 16U) => 4,
|
||||
(QualityType.QUALITY_ORANGE, > 12U) => 3,
|
||||
(QualityType.QUALITY_ORANGE, > 8U) => 2,
|
||||
(QualityType.QUALITY_ORANGE, > 4U) => 1,
|
||||
(QualityType.QUALITY_ORANGE, _) => 0,
|
||||
|
||||
(ItemQuality.QUALITY_PURPLE, > 16) => 4,
|
||||
(ItemQuality.QUALITY_PURPLE, > 12) => 3,
|
||||
(ItemQuality.QUALITY_PURPLE, > 8) => 2,
|
||||
(ItemQuality.QUALITY_PURPLE, > 4) => 1,
|
||||
(ItemQuality.QUALITY_PURPLE, _) => 0,
|
||||
(QualityType.QUALITY_PURPLE, > 16U) => 4,
|
||||
(QualityType.QUALITY_PURPLE, > 12U) => 3,
|
||||
(QualityType.QUALITY_PURPLE, > 8U) => 2,
|
||||
(QualityType.QUALITY_PURPLE, > 4U) => 1,
|
||||
(QualityType.QUALITY_PURPLE, _) => 0,
|
||||
|
||||
(ItemQuality.QUALITY_BLUE, > 12) => 3,
|
||||
(ItemQuality.QUALITY_BLUE, > 8) => 2,
|
||||
(ItemQuality.QUALITY_BLUE, > 4) => 1,
|
||||
(ItemQuality.QUALITY_BLUE, _) => 0,
|
||||
(QualityType.QUALITY_BLUE, > 12U) => 3,
|
||||
(QualityType.QUALITY_BLUE, > 8U) => 2,
|
||||
(QualityType.QUALITY_BLUE, > 4U) => 1,
|
||||
(QualityType.QUALITY_BLUE, _) => 0,
|
||||
|
||||
(ItemQuality.QUALITY_GREEN, > 4) => 1,
|
||||
(ItemQuality.QUALITY_GREEN, _) => 0,
|
||||
(QualityType.QUALITY_GREEN, > 4U) => 1,
|
||||
(QualityType.QUALITY_GREEN, _) => 0,
|
||||
|
||||
(ItemQuality.QUALITY_WHITE, > 4) => 1,
|
||||
(ItemQuality.QUALITY_WHITE, _) => 0,
|
||||
(QualityType.QUALITY_WHITE, > 4U) => 1,
|
||||
(QualityType.QUALITY_WHITE, _) => 0,
|
||||
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
private float ScoreReliquary(FightProperty property, MetadataReliquary reliquary, ReliquaryLevel relicLevel, List<ReliquarySubProperty> subProperties)
|
||||
private float ScoreReliquary(FightProperty property, MetadataReliquary reliquary, ReliquaryMainAffixLevel relicLevel, List<ReliquarySubProperty> subProperties)
|
||||
{
|
||||
// 沙 杯 头
|
||||
// equip.Flat.EquipType is EquipType.EQUIP_SHOES or EquipType.EQUIP_RING or EquipType.EQUIP_DRESS
|
||||
if ((int)equip.Flat.EquipType > 3)
|
||||
{
|
||||
AffixWeight weightConfig = GetAffixWeightForAvatarId();
|
||||
ReliquaryLevel maxRelicLevel = metadataContext.ReliqueryLevels.Where(r => r.Quality == reliquary.RankLevel).MaxBy(r => r.Level)!;
|
||||
ReliquaryMainAffixLevel maxRelicLevel = metadataContext.ReliqueryLevels.Where(r => r.Rank == reliquary.RankLevel).MaxBy(r => r.Level)!;
|
||||
|
||||
float percent = relicLevel.Properties[property] / maxRelicLevel.Properties[property];
|
||||
float percent = relicLevel.PropertyMap[property] / maxRelicLevel.PropertyMap[property];
|
||||
float baseScore = 8 * percent * weightConfig.GetValueOrDefault(property);
|
||||
|
||||
float score = subProperties.Sum(p => p.Score);
|
||||
@@ -151,9 +151,10 @@ internal sealed class SummaryReliquaryFactory
|
||||
return new(substat.AppendPropId.GetLocalizedDescription(), valueFormatted, 0);
|
||||
}
|
||||
|
||||
private ReliquarySubProperty CreateSubProperty(int appendPropId)
|
||||
[SuppressMessage("", "SH002")]
|
||||
private ReliquarySubProperty CreateSubProperty(ReliquarySubAffixId appendPropId)
|
||||
{
|
||||
MetadataReliquaryAffix affix = metadataContext.IdReliquaryAffixMap[appendPropId];
|
||||
MetadataReliquaryAffix affix = metadataContext.IdReliquarySubAffixMap[appendPropId];
|
||||
FightProperty property = affix.Type;
|
||||
|
||||
return new(
|
||||
@@ -162,9 +163,9 @@ internal sealed class SummaryReliquaryFactory
|
||||
ScoreSubAffix(appendPropId));
|
||||
}
|
||||
|
||||
private float ScoreSubAffix(int appendId)
|
||||
private float ScoreSubAffix(in ReliquarySubAffixId appendId)
|
||||
{
|
||||
MetadataReliquaryAffix affix = metadataContext.IdReliquaryAffixMap[appendId];
|
||||
MetadataReliquaryAffix affix = metadataContext.IdReliquarySubAffixMap[appendId];
|
||||
|
||||
AffixWeight weightConfig = GetAffixWeightForAvatarId();
|
||||
float weight = weightConfig.GetValueOrDefault(affix.Type) / 100F;
|
||||
@@ -179,7 +180,7 @@ internal sealed class SummaryReliquaryFactory
|
||||
weight = weightConfig.GetValueOrDefault(affix.Type + 1) / 100F;
|
||||
|
||||
// 最大同属性百分比数值 最大同属性百分比Id 第四五位是战斗属性位
|
||||
MetadataReliquaryAffix maxPercentAffix = metadataContext.IdReliquaryAffixMap[SummaryHelper.GetAffixMaxId(appendId + 10)];
|
||||
MetadataReliquaryAffix maxPercentAffix = metadataContext.IdReliquarySubAffixMap[SummaryHelper.GetAffixMaxId(appendId + 10U)];
|
||||
float equalScore = equalPercent / maxPercentAffix.Value;
|
||||
|
||||
return weight * equalScore * 100;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo.Transformer;
|
||||
@@ -16,6 +17,6 @@ internal sealed class CalculateAvatarDetailAvatarInfoTransformer : IAvatarInfoTr
|
||||
public void Transform(ref Web.Enka.Model.AvatarInfo avatarInfo, AvatarDetail source)
|
||||
{
|
||||
// update skills
|
||||
avatarInfo.SkillLevelMap = source.SkillList.ToDictionary(s => $"{s.Id}", s => s.LevelCurrent);
|
||||
avatarInfo.SkillLevelMap = source.SkillList.ToDictionary(s => (SkillId)s.Id, s => (SkillLevel)s.LevelCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,10 @@ internal sealed class GameRecordCharacterAvatarInfoTransformer : IAvatarInfoTran
|
||||
equip.Weapon = new()
|
||||
{
|
||||
Level = source.Weapon.Level,
|
||||
AffixMap = new() { { $"1{source.Weapon.Id}", source.Weapon.AffixLevel - 1 }, },
|
||||
AffixMap = new()
|
||||
{
|
||||
[100000 + source.Weapon.Id] = source.Weapon.AffixLevel - 1,
|
||||
},
|
||||
};
|
||||
|
||||
// Special case here, don't set EQUIP_WEAPON
|
||||
|
||||
@@ -186,7 +186,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> SaveConsumptionAsync(CultivateType type, int itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items)
|
||||
public async Task<bool> SaveConsumptionAsync(CultivateType type, uint itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@ internal interface ICultivationService
|
||||
/// <param name="itemId">主Id</param>
|
||||
/// <param name="items">待存物品</param>
|
||||
/// <returns>是否保存成功</returns>
|
||||
Task<bool> SaveConsumptionAsync(CultivateType type, int itemId, List<Item> items);
|
||||
Task<bool> SaveConsumptionAsync(CultivateType type, uint itemId, List<Item> items);
|
||||
|
||||
/// <summary>
|
||||
/// 保存养成物品状态
|
||||
|
||||
@@ -60,19 +60,19 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
// It's an avatar
|
||||
switch (item.ItemId.Place())
|
||||
{
|
||||
case 8:
|
||||
case 8U:
|
||||
{
|
||||
Avatar avatar = context.IdAvatarMap[item.ItemId];
|
||||
|
||||
bool isUp = false;
|
||||
switch (avatar.Quality)
|
||||
{
|
||||
case ItemQuality.QUALITY_ORANGE:
|
||||
orangeAvatarCounter.Increase(avatar);
|
||||
case QualityType.QUALITY_ORANGE:
|
||||
orangeAvatarCounter.IncreaseOne(avatar);
|
||||
isUp = targetHistoryWishBuilder?.IncreaseOrange(avatar) ?? false;
|
||||
break;
|
||||
case ItemQuality.QUALITY_PURPLE:
|
||||
purpleAvatarCounter.Increase(avatar);
|
||||
case QualityType.QUALITY_PURPLE:
|
||||
purpleAvatarCounter.IncreaseOne(avatar);
|
||||
targetHistoryWishBuilder?.IncreasePurple(avatar);
|
||||
break;
|
||||
}
|
||||
@@ -83,24 +83,24 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
break;
|
||||
}
|
||||
|
||||
case 5:
|
||||
case 5U:
|
||||
{
|
||||
Weapon weapon = context.IdWeaponMap[item.ItemId];
|
||||
|
||||
bool isUp = false;
|
||||
switch (weapon.RankLevel)
|
||||
{
|
||||
case ItemQuality.QUALITY_ORANGE:
|
||||
case QualityType.QUALITY_ORANGE:
|
||||
isUp = targetHistoryWishBuilder?.IncreaseOrange(weapon) ?? false;
|
||||
orangeWeaponCounter.Increase(weapon);
|
||||
orangeWeaponCounter.IncreaseOne(weapon);
|
||||
break;
|
||||
case ItemQuality.QUALITY_PURPLE:
|
||||
case QualityType.QUALITY_PURPLE:
|
||||
targetHistoryWishBuilder?.IncreasePurple(weapon);
|
||||
purpleWeaponCounter.Increase(weapon);
|
||||
purpleWeaponCounter.IncreaseOne(weapon);
|
||||
break;
|
||||
case ItemQuality.QUALITY_BLUE:
|
||||
case QualityType.QUALITY_BLUE:
|
||||
targetHistoryWishBuilder?.IncreaseBlue(weapon);
|
||||
blueWeaponCounter.Increase(weapon);
|
||||
blueWeaponCounter.IncreaseOne(weapon);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,15 +73,15 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
|
||||
{
|
||||
switch (nameQuality.Quality)
|
||||
{
|
||||
case ItemQuality.QUALITY_ORANGE:
|
||||
case QualityType.QUALITY_ORANGE:
|
||||
orangeTracker = 0;
|
||||
++purpleTracker;
|
||||
break;
|
||||
case ItemQuality.QUALITY_PURPLE:
|
||||
case QualityType.QUALITY_PURPLE:
|
||||
++orangeTracker;
|
||||
purpleTracker = 0;
|
||||
break;
|
||||
case ItemQuality.QUALITY_BLUE:
|
||||
case QualityType.QUALITY_BLUE:
|
||||
++orangeTracker;
|
||||
++purpleTracker;
|
||||
break;
|
||||
|
||||
@@ -71,10 +71,10 @@ internal sealed class HistoryWishBuilder
|
||||
/// <returns>是否为Up物品</returns>
|
||||
public bool IncreaseOrange(IStatisticsItemSource item)
|
||||
{
|
||||
orangeCounter.Increase(item);
|
||||
orangeCounter.IncreaseOne(item);
|
||||
++totalCountTracker;
|
||||
|
||||
return orangeUpCounter.TryIncrease(item);
|
||||
return orangeUpCounter.TryIncreaseOne(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,8 +83,8 @@ internal sealed class HistoryWishBuilder
|
||||
/// <param name="item">物品</param>
|
||||
public void IncreasePurple(IStatisticsItemSource item)
|
||||
{
|
||||
purpleUpCounter.TryIncrease(item);
|
||||
purpleCounter.Increase(item);
|
||||
purpleUpCounter.TryIncreaseOne(item);
|
||||
purpleCounter.IncreaseOne(item);
|
||||
++totalCountTracker;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ internal sealed class HistoryWishBuilder
|
||||
/// <param name="item">武器</param>
|
||||
public void IncreaseBlue(IStatisticsItemSource item)
|
||||
{
|
||||
blueCounter.Increase(item);
|
||||
blueCounter.IncreaseOne(item);
|
||||
++totalCountTracker;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ internal sealed class TypedWishSummaryBuilder
|
||||
|
||||
switch (source.Quality)
|
||||
{
|
||||
case ItemQuality.QUALITY_ORANGE:
|
||||
case QualityType.QUALITY_ORANGE:
|
||||
{
|
||||
TrackMinMaxOrangePull(lastOrangePullTracker);
|
||||
averageOrangePullTracker.Add(lastOrangePullTracker);
|
||||
@@ -105,14 +105,14 @@ internal sealed class TypedWishSummaryBuilder
|
||||
break;
|
||||
}
|
||||
|
||||
case ItemQuality.QUALITY_PURPLE:
|
||||
case QualityType.QUALITY_PURPLE:
|
||||
{
|
||||
lastPurplePullTracker = 0;
|
||||
++totalPurplePullTracker;
|
||||
break;
|
||||
}
|
||||
|
||||
case ItemQuality.QUALITY_BLUE:
|
||||
case QualityType.QUALITY_BLUE:
|
||||
{
|
||||
++totalBluePullTracker;
|
||||
break;
|
||||
|
||||
@@ -104,13 +104,13 @@ internal readonly struct GachaLogServiceContext
|
||||
/// </summary>
|
||||
/// <param name="id">Id</param>
|
||||
/// <returns>名称星级</returns>
|
||||
public INameQuality GetNameQualityByItemId(int id)
|
||||
public INameQuality GetNameQualityByItemId(uint id)
|
||||
{
|
||||
int place = id.Place();
|
||||
uint place = id.Place();
|
||||
return place switch
|
||||
{
|
||||
8 => IdAvatarMap![id],
|
||||
5 => IdWeaponMap![id],
|
||||
8U => IdAvatarMap![id],
|
||||
5U => IdWeaponMap![id],
|
||||
_ => throw Must.NeverHappen($"Id places: {place}"),
|
||||
};
|
||||
}
|
||||
@@ -121,13 +121,13 @@ internal readonly struct GachaLogServiceContext
|
||||
/// </summary>
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <returns>物品 Id</returns>
|
||||
public int GetItemId(GachaLogItem item)
|
||||
public uint GetItemId(GachaLogItem item)
|
||||
{
|
||||
return item.ItemType switch
|
||||
{
|
||||
"角色" => NameAvatarMap!.GetValueOrDefault(item.Name)?.Id ?? 0,
|
||||
"武器" => NameWeaponMap!.GetValueOrDefault(item.Name)?.Id ?? 0,
|
||||
_ => 0,
|
||||
_ => 0U,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Core.IO.Hashing;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
using System.IO;
|
||||
@@ -240,7 +241,7 @@ internal sealed partial class PackageConverter
|
||||
{
|
||||
if (File.Exists(cacheFilePath))
|
||||
{
|
||||
string remoteMd5 = await Digest.GetFileMD5Async(cacheFilePath).ConfigureAwait(false);
|
||||
string remoteMd5 = await MD5.HashFileAsync(cacheFilePath).ConfigureAwait(false);
|
||||
if (info.Md5 == remoteMd5.ToLowerInvariant() && new FileInfo(cacheFilePath).Length == info.TotalBytes)
|
||||
{
|
||||
// Valid, move it to target path
|
||||
@@ -270,7 +271,7 @@ internal sealed partial class PackageConverter
|
||||
StreamCopyWorker<PackageReplaceStatus> streamCopyWorker = new(webStream, fileStream, bytesRead => new(info.Target, bytesRead, totalBytes));
|
||||
await streamCopyWorker.CopyAsync(progress).ConfigureAwait(false);
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
string remoteMd5 = await Digest.GetStreamMD5Async(fileStream).ConfigureAwait(false);
|
||||
string remoteMd5 = await MD5.HashAsync(fileStream).ConfigureAwait(false);
|
||||
if (string.Equals(info.Md5, remoteMd5, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -58,7 +58,7 @@ internal static class ProcessInterop
|
||||
public static Task UnlockFpsAsync(IServiceProvider serviceProvider, Process game)
|
||||
{
|
||||
IGameFpsUnlocker unlocker = serviceProvider.CreateInstance<GameFpsUnlocker>(game);
|
||||
UnlockTimingOptions options = new(100, 10000, 3000);
|
||||
UnlockTimingOptions options = new(100, 20000, 3000);
|
||||
return unlocker.UnlockAsync(options);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,14 +90,14 @@ internal interface IMetadataService : ICastableService
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>圣遗物强化属性列表</returns>
|
||||
ValueTask<List<ReliquaryAffix>> GetReliquaryAffixesAsync(CancellationToken token = default);
|
||||
ValueTask<List<ReliquarySubAffix>> GetReliquarySubAffixesAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取圣遗物等级数据
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>圣遗物等级数据</returns>
|
||||
ValueTask<List<ReliquaryLevel>> GetReliquaryLevelsAsync(CancellationToken token = default);
|
||||
ValueTask<List<ReliquaryMainAffixLevel>> GetReliquaryLevelsAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取圣遗物主属性强化属性列表
|
||||
@@ -156,7 +156,7 @@ internal interface IMetadataService : ICastableService
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>显示与材料映射</returns>
|
||||
ValueTask<Dictionary<MaterialId, Display>> GetIdToDisplayAndMaterialMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<MaterialId, DisplayItem>> GetIdToDisplayAndMaterialMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取Id到材料的字典
|
||||
@@ -170,7 +170,7 @@ internal interface IMetadataService : ICastableService
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>字典</returns>
|
||||
ValueTask<Dictionary<ReliquaryAffixId, ReliquaryAffix>> GetIdToReliquaryAffixMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<ReliquarySubAffixId, ReliquarySubAffix>> GetIdToReliquarySubAffixMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取圣遗物主词条Id与属性的字典
|
||||
@@ -211,20 +211,20 @@ internal interface IMetadataService : ICastableService
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>等级角色曲线映射</returns>
|
||||
ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToAvatarCurveMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToAvatarCurveMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取等级怪物曲线映射
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>等级怪物曲线映射</returns>
|
||||
ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToMonsterCurveMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToMonsterCurveMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取等级武器曲线映射
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>等级武器曲线映射</returns>
|
||||
ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToWeaponCurveMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToWeaponCurveMapAsync(CancellationToken token = default);
|
||||
#endregion
|
||||
}
|
||||
@@ -84,7 +84,7 @@ internal sealed partial class MetadataOptions : IOptions<MetadataOptions>
|
||||
/// <returns>服务器上的本地化元数据文件</returns>
|
||||
public string GetLocalizedRemoteFile(string fileNameWithExtension)
|
||||
{
|
||||
return Web.HutaoEndpoints.HutaoMetadataFile(LocaleName, fileNameWithExtension);
|
||||
return Web.HutaoEndpoints.RawGithubUserContentMetadataFile(LocaleName, fileNameWithExtension);
|
||||
}
|
||||
|
||||
private string GetLocaleName()
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata;
|
||||
|
||||
/// <summary>
|
||||
/// 名称常量
|
||||
/// </summary>
|
||||
internal partial class MetadataService
|
||||
{
|
||||
private const string FileNameAchievement = "Achievement";
|
||||
private const string FileNameAchievementGoal = "AchievementGoal";
|
||||
private const string FileNameAvatar = "Avatar";
|
||||
private const string FileNameAvatarCurve = "AvatarCurve";
|
||||
private const string FileNameAvatarPromote = "AvatarPromote";
|
||||
private const string FileNameDisplayItem = "DisplayItem";
|
||||
private const string FileNameGachaEvent = "GachaEvent";
|
||||
private const string FileNameMaterial = "Material";
|
||||
private const string FileNameMonster = "Monster";
|
||||
private const string FileNameMonsterCurve = "MonsterCurve";
|
||||
private const string FileNameReliquary = "Reliquary";
|
||||
private const string FileNameReliquaryAffixWeight = "ReliquaryAffixWeight";
|
||||
private const string FileNameReliquaryMainAffix = "ReliquaryMainAffix";
|
||||
private const string FileNameReliquaryMainAffixLevel = "ReliquaryMainAffixLevel";
|
||||
private const string FileNameReliquarySet = "ReliquarySet";
|
||||
private const string FileNameReliquarySubAffix = "ReliquarySubAffix";
|
||||
private const string FileNameTowerFloor = "TowerFloor";
|
||||
private const string FileNameTowerLevel = "TowerLevel";
|
||||
private const string FileNameTowerSchedule = "TowerSchedule";
|
||||
private const string FileNameWeapon = "Weapon";
|
||||
private const string FileNameWeaponCurve = "WeaponCurve";
|
||||
private const string FileNameWeaponPromote = "WeaponPromote";
|
||||
}
|
||||
@@ -19,84 +19,84 @@ internal sealed partial class MetadataService
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<AchievementGoal>> GetAchievementGoalsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<AchievementGoal>>("AchievementGoal", token);
|
||||
return FromCacheOrFileAsync<List<AchievementGoal>>(FileNameAchievementGoal, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Model.Metadata.Achievement.Achievement>> GetAchievementsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Model.Metadata.Achievement.Achievement>>("Achievement", token);
|
||||
return FromCacheOrFileAsync<List<Model.Metadata.Achievement.Achievement>>(FileNameAchievement, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Avatar>> GetAvatarsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Avatar>>("Avatar", token);
|
||||
return FromCacheOrFileAsync<List<Avatar>>(FileNameAvatar, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Promote>> GetAvatarPromotesAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Promote>>("AvatarPromote", token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Promote>> GetWeaponPromotesAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Promote>>("WeaponPromote", token);
|
||||
return FromCacheOrFileAsync<List<Promote>>(FileNameAvatarPromote, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<GachaEvent>> GetGachaEventsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<GachaEvent>>("GachaEvent2", token);
|
||||
return FromCacheOrFileAsync<List<GachaEvent>>(FileNameGachaEvent, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Material>> GetMaterialsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Material>>("Material", token);
|
||||
return FromCacheOrFileAsync<List<Material>>(FileNameMaterial, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Monster>> GetMonstersAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Monster>>("Monster", token);
|
||||
return FromCacheOrFileAsync<List<Monster>>(FileNameMonster, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Reliquary>> GetReliquariesAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Reliquary>>("Reliquary", token);
|
||||
return FromCacheOrFileAsync<List<Reliquary>>(FileNameReliquary, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<ReliquaryAffix>> GetReliquaryAffixesAsync(CancellationToken token = default)
|
||||
public ValueTask<List<ReliquaryMainAffixLevel>> GetReliquaryLevelsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<ReliquaryAffix>>("ReliquaryAffix", token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<ReliquaryLevel>> GetReliquaryLevelsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<ReliquaryLevel>>("ReliquaryMainAffixLevel", token);
|
||||
return FromCacheOrFileAsync<List<ReliquaryMainAffixLevel>>(FileNameReliquaryMainAffixLevel, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<ReliquaryMainAffix>> GetReliquaryMainAffixesAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<ReliquaryMainAffix>>("ReliquaryMainAffix", token);
|
||||
return FromCacheOrFileAsync<List<ReliquaryMainAffix>>(FileNameReliquaryMainAffix, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<ReliquarySet>> GetReliquarySetsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<ReliquarySet>>("ReliquarySet", token);
|
||||
return FromCacheOrFileAsync<List<ReliquarySet>>(FileNameReliquarySet, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<ReliquarySubAffix>> GetReliquarySubAffixesAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<ReliquarySubAffix>>(FileNameReliquarySubAffix, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Weapon>> GetWeaponsAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Weapon>>("Weapon", token);
|
||||
return FromCacheOrFileAsync<List<Weapon>>(FileNameWeapon, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<Promote>> GetWeaponPromotesAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheOrFileAsync<List<Promote>>(FileNameWeaponPromote, token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata;
|
||||
|
||||
@@ -20,38 +21,38 @@ internal sealed partial class MetadataService
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<EquipAffixId, ReliquarySet>> GetEquipAffixIdToReliquarySetMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<EquipAffixId, ReliquarySet>("ReliquarySet", r => r.EquipAffixId, token);
|
||||
return FromCacheAsDictionaryAsync<EquipAffixId, ReliquarySet>(FileNameReliquarySet, r => r.EquipAffixId, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<AchievementId, Model.Metadata.Achievement.Achievement>> GetIdToAchievementMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<AchievementId, Model.Metadata.Achievement.Achievement>("Achievement", a => a.Id, token);
|
||||
return FromCacheAsDictionaryAsync<AchievementId, Model.Metadata.Achievement.Achievement>(FileNameAchievement, a => a.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<AvatarId, Avatar>> GetIdToAvatarMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<AvatarId, Avatar>("Avatar", a => a.Id, token);
|
||||
return FromCacheAsDictionaryAsync<AvatarId, Avatar>(FileNameAvatar, a => a.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<Dictionary<MaterialId, Display>> GetIdToDisplayAndMaterialMapAsync(CancellationToken token = default)
|
||||
public async ValueTask<Dictionary<MaterialId, DisplayItem>> GetIdToDisplayAndMaterialMapAsync(CancellationToken token = default)
|
||||
{
|
||||
string cacheKey = $"{nameof(MetadataService)}.Cache.DisplayAndMaterial.Map.{typeof(MaterialId).Name}";
|
||||
|
||||
if (memoryCache.TryGetValue(cacheKey, out object? value))
|
||||
{
|
||||
return Must.NotNull((Dictionary<MaterialId, Display>)value!);
|
||||
return Must.NotNull((Dictionary<MaterialId, DisplayItem>)value!);
|
||||
}
|
||||
|
||||
Dictionary<MaterialId, Display> displays = await FromCacheAsDictionaryAsync<MaterialId, Display>("Display", a => a.Id, token).ConfigureAwait(false);
|
||||
Dictionary<MaterialId, DisplayItem> displays = await FromCacheAsDictionaryAsync<MaterialId, DisplayItem>(FileNameDisplayItem, a => a.Id, token).ConfigureAwait(false);
|
||||
Dictionary<MaterialId, Material> materials = await GetIdToMaterialMapAsync(token).ConfigureAwait(false);
|
||||
|
||||
// TODO: Cache this
|
||||
Dictionary<MaterialId, Display> results = new(displays);
|
||||
Dictionary<MaterialId, DisplayItem> results = new(displays);
|
||||
|
||||
foreach ((MaterialId id, Display material) in materials)
|
||||
foreach ((MaterialId id, DisplayItem material) in materials)
|
||||
{
|
||||
results[id] = material;
|
||||
}
|
||||
@@ -62,54 +63,54 @@ internal sealed partial class MetadataService
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<MaterialId, Material>> GetIdToMaterialMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<MaterialId, Material>("Material", a => a.Id, token);
|
||||
return FromCacheAsDictionaryAsync<MaterialId, Material>(FileNameMaterial, a => a.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<ReliquaryAffixId, ReliquaryAffix>> GetIdToReliquaryAffixMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<ReliquarySubAffixId, ReliquarySubAffix>> GetIdToReliquarySubAffixMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<ReliquaryAffixId, ReliquaryAffix>("ReliquaryAffix", a => a.Id, token);
|
||||
return FromCacheAsDictionaryAsync<ReliquarySubAffixId, ReliquarySubAffix>(FileNameReliquarySubAffix, a => a.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<ReliquaryMainAffixId, FightProperty>> GetIdToReliquaryMainPropertyMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<ReliquaryMainAffixId, FightProperty, ReliquaryMainAffix>("ReliquaryMainAffix", r => r.Id, r => r.Type, token);
|
||||
return FromCacheAsDictionaryAsync<ReliquaryMainAffixId, FightProperty, ReliquaryMainAffix>(FileNameReliquaryMainAffix, r => r.Id, r => r.Type, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<WeaponId, Weapon>> GetIdToWeaponMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<WeaponId, Weapon>("Weapon", w => w.Id, token);
|
||||
return FromCacheAsDictionaryAsync<WeaponId, Weapon>(FileNameWeapon, w => w.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToAvatarCurveMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToAvatarCurveMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<int, Dictionary<GrowCurveType, float>, GrowCurve>("AvatarCurve", a => a.Level, a => a.Curves, token);
|
||||
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameAvatarCurve, a => a.Level, a => a.CurveMap, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToMonsterCurveMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToMonsterCurveMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<int, Dictionary<GrowCurveType, float>, GrowCurve>("MonsterCurve", m => m.Level, m => m.Curves, token);
|
||||
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameMonsterCurve, m => m.Level, m => m.CurveMap, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToWeaponCurveMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<Level, Dictionary<GrowCurveType, float>>> GetLevelToWeaponCurveMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<int, Dictionary<GrowCurveType, float>, GrowCurve>("WeaponCurve", w => w.Level, w => w.Curves, token);
|
||||
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameWeaponCurve, w => w.Level, w => w.CurveMap, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<string, Avatar>> GetNameToAvatarMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<string, Avatar>("Avatar", a => a.Name, token);
|
||||
return FromCacheAsDictionaryAsync<string, Avatar>(FileNameAvatar, a => a.Name, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<string, Weapon>> GetNameToWeaponMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<string, Weapon>("Weapon", w => w.Name, token);
|
||||
return FromCacheAsDictionaryAsync<string, Weapon>(FileNameWeapon, w => w.Name, token);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Core.Diagnostics;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Core.IO.Hashing;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
@@ -122,12 +122,12 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
|
||||
string fileFullPath = metadataOptions.GetLocalizedLocalFile(fileFullName);
|
||||
if (File.Exists(fileFullPath))
|
||||
{
|
||||
skip = md5 == await Digest.GetFileMD5Async(fileFullPath, token).ConfigureAwait(false);
|
||||
skip = md5 == await XXH64.HashFileAsync(fileFullPath, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
logger.LogInformation("MD5 of {file} not matched, begin downloading", fileFullName);
|
||||
logger.LogInformation("{hash} of {file} not matched, begin downloading", nameof(XXH64), fileFullName);
|
||||
|
||||
await DownloadMetadataAsync(fileFullName, token).ConfigureAwait(false);
|
||||
}
|
||||
@@ -182,6 +182,7 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
|
||||
}
|
||||
else
|
||||
{
|
||||
FileNotFoundException exception = new(SH.ServiceMetadataFileNotFound, fileName);
|
||||
throw ThrowHelper.UserdataCorrupted(SH.ServiceMetadataFileNotFound, null!);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.5.22">
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.6.40">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -268,6 +268,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.IO.Hashing" Version="7.0.0" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.10.1" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao.View.Control;
|
||||
[HighQuality]
|
||||
internal sealed partial class ItemIcon : UserControl
|
||||
{
|
||||
private static readonly DependencyProperty QualityProperty = Property<ItemIcon>.Depend(nameof(Quality), ItemQuality.QUALITY_NONE);
|
||||
private static readonly DependencyProperty QualityProperty = Property<ItemIcon>.Depend(nameof(Quality), QualityType.QUALITY_NONE);
|
||||
private static readonly DependencyProperty IconProperty = Property<ItemIcon>.Depend<Uri>(nameof(Icon));
|
||||
private static readonly DependencyProperty BadgeProperty = Property<ItemIcon>.Depend<Uri>(nameof(Badge));
|
||||
|
||||
@@ -29,9 +29,9 @@ internal sealed partial class ItemIcon : UserControl
|
||||
/// <summary>
|
||||
/// 等阶
|
||||
/// </summary>
|
||||
public ItemQuality Quality
|
||||
public QualityType Quality
|
||||
{
|
||||
get => (ItemQuality)GetValue(QualityProperty);
|
||||
get => (QualityType)GetValue(QualityProperty);
|
||||
set => SetValue(QualityProperty, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -521,7 +521,7 @@
|
||||
Width="80"
|
||||
Height="80"
|
||||
Margin="0,0,16,0"
|
||||
Source="{Binding Icon, Converter={StaticResource AvatarCardConverter}}"/>
|
||||
Source="{Binding FrontIcon, Converter={StaticResource AvatarCardConverter}}"/>
|
||||
<StackPanel Grid.Column="1" Margin="0,0,16,-4">
|
||||
<TextBlock Text="{Binding Name}"/>
|
||||
<TextBlock
|
||||
|
||||
@@ -38,7 +38,7 @@ internal sealed class AchievementGoalView : ObservableObject, INameIcon
|
||||
/// <summary>
|
||||
/// 排序顺序
|
||||
/// </summary>
|
||||
public int Order { get; }
|
||||
public uint Order { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
|
||||
@@ -36,7 +36,7 @@ internal sealed class AvatarView : INameIconSide, ICalculableSource<ICalculableA
|
||||
/// <summary>
|
||||
/// 星级
|
||||
/// </summary>
|
||||
public ItemQuality Quality { get; set; }
|
||||
public QualityType Quality { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 元素类型
|
||||
@@ -91,7 +91,7 @@ internal sealed class AvatarView : INameIconSide, ICalculableSource<ICalculableA
|
||||
/// <summary>
|
||||
/// 好感度等级
|
||||
/// </summary>
|
||||
public int FetterLevel { get; set; }
|
||||
public uint FetterLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
@@ -101,7 +101,7 @@ internal sealed class AvatarView : INameIconSide, ICalculableSource<ICalculableA
|
||||
/// <summary>
|
||||
/// 等级数字
|
||||
/// </summary>
|
||||
internal int LevelNumber { get; set; }
|
||||
internal uint LevelNumber { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableAvatar ToCalculable()
|
||||
|
||||
@@ -20,7 +20,7 @@ internal abstract class Equip : NameIconDescription
|
||||
/// <summary>
|
||||
/// 品质
|
||||
/// </summary>
|
||||
public ItemQuality Quality { get; set; }
|
||||
public QualityType Quality { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主属性
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Model.Calculable;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.AvatarProperty;
|
||||
|
||||
@@ -20,7 +21,7 @@ internal sealed class SkillView : NameIconDescription, ICalculableSource<ICalcul
|
||||
/// <summary>
|
||||
/// 不计算命座的技能等级
|
||||
/// </summary>
|
||||
public int LevelNumber { get; set; }
|
||||
public uint LevelNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 不计算命座的技能等级字符串
|
||||
@@ -30,7 +31,7 @@ internal sealed class SkillView : NameIconDescription, ICalculableSource<ICalcul
|
||||
/// <summary>
|
||||
/// 技能组Id
|
||||
/// </summary>
|
||||
internal int GroupId { get; set; }
|
||||
internal SkillGroupId GroupId { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableSkill ToCalculable()
|
||||
|
||||
@@ -20,7 +20,7 @@ internal sealed class WeaponView : Equip, ICalculableSource<ICalculableWeapon>
|
||||
/// <summary>
|
||||
/// 精炼等级
|
||||
/// </summary>
|
||||
public int AffixLevelNumber { get; set; }
|
||||
public uint AffixLevelNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 精炼属性
|
||||
@@ -45,12 +45,12 @@ internal sealed class WeaponView : Equip, ICalculableSource<ICalculableWeapon>
|
||||
/// <summary>
|
||||
/// 等级数字
|
||||
/// </summary>
|
||||
internal int LevelNumber { get; set; }
|
||||
internal uint LevelNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大等级
|
||||
/// </summary>
|
||||
internal int MaxLevel { get => ((int)Quality) >= 3 ? 90 : 70; }
|
||||
internal uint MaxLevel { get => ((int)Quality) >= 3 ? 90U : 70U; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableWeapon ToCalculable()
|
||||
|
||||
@@ -46,7 +46,7 @@ internal class AvatarView : INameIconSide
|
||||
/// <summary>
|
||||
/// 星级
|
||||
/// </summary>
|
||||
public ItemQuality Quality { get; }
|
||||
public QualityType Quality { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 比率
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user