Add InventoryItems localization support

This commit is contained in:
DismissedLight
2023-07-12 16:20:17 +08:00
parent 70ab81bb0f
commit 4c0d86fd13
46 changed files with 930 additions and 820 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@ desktop.ini
*.csproj.user
*.pubxml
*.DotSettings.user
.vs/
.idea/

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using Snap.Hutao.Service.Navigation;
@@ -25,7 +26,7 @@ internal class ScopedPage : Page
/// <summary>
/// 构造一个新的页面
/// </summary>
public ScopedPage()
protected ScopedPage()
{
Unloaded += OnScopedPageUnloaded;
currentScope = Ioc.Default.CreateScope();
@@ -51,7 +52,7 @@ internal class ScopedPage : Page
/// 应当在 InitializeComponent() 前调用
/// </summary>
/// <typeparam name="TViewModel">视图模型类型</typeparam>
public void InitializeWith<TViewModel>()
protected void InitializeWith<TViewModel>()
where TViewModel : class, IViewModel
{
IViewModel viewModel = currentScope.ServiceProvider.GetRequiredService<TViewModel>();

View File

@@ -23,6 +23,7 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
{
private const string CacheFolderName = nameof(ImageCache);
// TODO: use FrozenDictionary
private static readonly Dictionary<int, TimeSpan> RetryCountToDelay = new()
{
[0] = TimeSpan.FromSeconds(4),
@@ -173,14 +174,14 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
}
catch (Exception ex)
{
logger.LogWarning(ex, "Remove Cache Image Failed:{file}", filePath);
logger.LogWarning(ex, "Remove Cache Image Failed:{File}", filePath);
}
}
}
private async Task DownloadFileAsync(Uri uri, string baseFile)
{
logger.LogInformation("Begin downloading for {uri}", uri);
logger.LogInformation("Begin downloading for {Uri}", uri);
int retryCount = 0;
while (retryCount < 6)
@@ -209,7 +210,7 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
{
retryCount++;
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount];
logger.LogInformation("Retry {uri} after {delay}.", uri, delay);
logger.LogInformation("Retry {Uri} after {Delay}.", uri, delay);
await Task.Delay(delay).ConfigureAwait(false);
break;
}

View File

@@ -14,17 +14,17 @@ internal sealed class HttpClientAttribute : Attribute
/// <summary>
/// 构造一个新的特性
/// </summary>
/// <param name="configration">配置</param>
public HttpClientAttribute(HttpClientConfiguration configration)
/// <param name="configuration">配置</param>
public HttpClientAttribute(HttpClientConfiguration configuration)
{
}
/// <summary>
/// 构造一个新的特性
/// </summary>
/// <param name="configration">配置</param>
/// <param name="configuration">配置</param>
/// <param name="interfaceType">实现的接口类型</param>
public HttpClientAttribute(HttpClientConfiguration configration, Type interfaceType)
public HttpClientAttribute(HttpClientConfiguration configuration, Type interfaceType)
{
}
}

View File

@@ -4,7 +4,6 @@
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Json;
using Snap.Hutao.Model.Entity.Database;
using System.Diagnostics;
namespace Snap.Hutao.Core.DependencyInjection;

View File

@@ -17,6 +17,6 @@ internal readonly struct MeasureExecutionToken : IDisposable
public void Dispose()
{
logger.LogInformation("{caller} toke {time} ms.", callerName, stopwatch.GetElapsedTime().TotalMilliseconds);
logger.LogInformation("{Caller} toke {Time} ms", callerName, stopwatch.GetElapsedTime().TotalMilliseconds);
}
}

View File

@@ -41,16 +41,16 @@ internal sealed partial class ExceptionRecorder
#pragma warning restore VSTHRD002
#endif
logger.LogError("未经处理的全局异常:\r\n{detail}", ExceptionFormat.Format(e.Exception));
logger.LogError("未经处理的全局异常:\r\n{Detail}", ExceptionFormat.Format(e.Exception));
}
private void OnXamlBindingFailed(object? sender, BindingFailedEventArgs e)
{
logger.LogCritical("XAML 绑定失败:{message}", e.Message);
logger.LogCritical("XAML 绑定失败:{Message}", e.Message);
}
private void OnXamlResourceReferenceFailed(DebugSettings sender, XamlResourceReferenceFailedEventArgs e)
{
logger.LogCritical("XAML 资源引用失败:{message}", e.Message);
logger.LogCritical("XAML 资源引用失败:{Message}", e.Message);
}
}

View File

@@ -19,7 +19,7 @@ internal static class MD5
/// <returns>文件 Md5 摘要</returns>
public static async Task<string> HashFileAsync(string filePath, CancellationToken token = default)
{
using (FileStream stream = File.OpenRead(filePath))
await using (FileStream stream = File.OpenRead(filePath))
{
return await HashAsync(stream, token).ConfigureAwait(false);
}

View File

@@ -33,7 +33,7 @@ internal static class XXH64
/// <returns>摘要</returns>
public static async Task<string> HashFileAsync(string path, CancellationToken token = default)
{
using (FileStream stream = File.OpenRead(path))
await using (FileStream stream = File.OpenRead(path))
{
return await HashAsync(stream, token).ConfigureAwait(false);
}

View File

@@ -21,7 +21,7 @@ internal sealed class IniComment : IniElement
/// <summary>
/// 注释
/// </summary>
public string Comment { get; set; }
public string Comment { get; }
/// <inheritdoc/>
public override string ToString()

View File

@@ -23,12 +23,12 @@ internal sealed class IniParameter : IniElement
/// <summary>
/// 键
/// </summary>
public string Key { get; set; }
public string Key { get; }
/// <summary>
/// 值
/// </summary>
public string Value { get; set; }
public string Value { get; private set; }
/// <summary>
/// 设置值

View File

@@ -21,7 +21,7 @@ internal sealed class IniSection : IniElement
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
public string Name { get; }
/// <inheritdoc/>
public override string ToString()

View File

@@ -39,7 +39,7 @@ internal static class IniSerializer
if (line.IndexOf('=') > 0)
{
string[] parameters = line.Split('=', 2);
string[] parameters = line.Split('=', 2, StringSplitOptions.TrimEntries);
yield return new IniParameter(parameters[0], parameters[1]);
}
}

View File

@@ -16,7 +16,7 @@ internal sealed class TempFile : IDisposable
/// 构造一个新的临时文件
/// </summary>
/// <param name="delete">是否在创建时删除文件</param>
public TempFile(bool delete = false)
private TempFile(bool delete = false)
{
try
{

View File

@@ -10,23 +10,20 @@ namespace Snap.Hutao.Core.IO;
/// </summary>
internal readonly struct ValueFile
{
/// <summary>
/// 值
/// </summary>
public readonly string Value;
private readonly string value;
/// <summary>
/// Initializes a new instance of the <see cref="ValueFile"/> struct.
/// </summary>
/// <param name="value">value</param>
public ValueFile(string value)
private ValueFile(string value)
{
Value = value;
this.value = value;
}
public static implicit operator string(ValueFile value)
{
return value.Value;
return value.value;
}
public static implicit operator ValueFile(string value)
@@ -45,7 +42,7 @@ internal readonly struct ValueFile
{
try
{
using (FileStream stream = File.OpenRead(Value))
using (FileStream stream = File.OpenRead(value))
{
T? t = await JsonSerializer.DeserializeAsync<T>(stream, options).ConfigureAwait(false);
return new(true, t);
@@ -69,7 +66,7 @@ internal readonly struct ValueFile
{
try
{
using (FileStream stream = File.Create(Value))
using (FileStream stream = File.Create(value))
{
await JsonSerializer.SerializeAsync(stream, obj, options).ConfigureAwait(false);
}
@@ -85,6 +82,6 @@ internal readonly struct ValueFile
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
return value.GetHashCode();
}
}

View File

@@ -14,14 +14,17 @@ internal class JsonEnumAttribute : Attribute
{
private static readonly Type UnsafeEnumConverterType = typeof(UnsafeEnumConverter<>);
private readonly JsonSerializeType readAs;
private readonly JsonSerializeType writeAs;
/// <summary>
/// 构造一个新的Json枚举声明
/// </summary>
/// <param name="readAndWriteAs">读取与写入</param>
public JsonEnumAttribute(JsonSerializeType readAndWriteAs)
{
ReadAs = readAndWriteAs;
WriteAs = readAndWriteAs;
readAs = readAndWriteAs;
writeAs = readAndWriteAs;
}
/// <summary>
@@ -31,20 +34,10 @@ internal class JsonEnumAttribute : Attribute
/// <param name="writeAs">写入</param>
public JsonEnumAttribute(JsonSerializeType readAs, JsonSerializeType writeAs)
{
ReadAs = readAs;
WriteAs = writeAs;
this.readAs = readAs;
this.writeAs = writeAs;
}
/// <summary>
/// 读取形式
/// </summary>
public JsonSerializeType ReadAs { get; init; }
/// <summary>
/// 写入形式
/// </summary>
public JsonSerializeType WriteAs { get; init; }
/// <summary>
/// 创建一个新的转换器
/// </summary>
@@ -53,6 +46,6 @@ internal class JsonEnumAttribute : Attribute
internal JsonConverter CreateConverter(JsonPropertyInfo info)
{
Type converterType = UnsafeEnumConverterType.MakeGenericType(info.PropertyType);
return (JsonConverter)Activator.CreateInstance(converterType, ReadAs, WriteAs)!;
return (JsonConverter)Activator.CreateInstance(converterType, readAs, writeAs)!;
}
}

View File

@@ -28,6 +28,7 @@ internal sealed class FeatureOptions : IReadOnlyCollection<Feature>
{
// TODO: Use source generator
yield return IsDailyNoteSilentVerificationEnabled;
yield return IsMetadataUpdateCheckSuppressed;
}
/// <inheritdoc/>

View File

@@ -44,7 +44,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQue
/// <inheritdoc/>
public void OnCompleted(Action continuation)
{
dispatherQueue.TryEnqueue(continuation.Invoke);
dispatherQueue.TryEnqueue(new DispatcherQueueHandler(continuation));
}
/// <inheritdoc/>
@@ -52,7 +52,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQue
{
using (ExecutionContext.SuppressFlow())
{
dispatherQueue.TryEnqueue(continuation.Invoke);
dispatherQueue.TryEnqueue(new DispatcherQueueHandler(continuation));
}
}
}

View File

@@ -35,7 +35,7 @@ internal static class TaskExtension
}
}
#else
catch (Exception)
catch
{
}
#endif
@@ -46,7 +46,7 @@ internal static class TaskExtension
/// </summary>
/// <param name="task">任务</param>
/// <param name="logger">日志器</param>
public static async void SafeForget(this Task task, ILogger? logger = null)
public static async void SafeForget(this Task task, ILogger logger)
{
try
{
@@ -58,7 +58,7 @@ internal static class TaskExtension
}
catch (Exception e)
{
logger?.LogError(e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
logger?.LogError(e, "{Caller}:\r\n{Exception}", nameof(SafeForget), e.GetBaseException());
}
}
@@ -68,7 +68,7 @@ internal static class TaskExtension
/// <param name="task">任务</param>
/// <param name="logger">日志器</param>
/// <param name="onException">发生异常时调用</param>
public static async void SafeForget(this Task task, ILogger? logger = null, Action<Exception>? onException = null)
public static async void SafeForget(this Task task, ILogger logger, Action<Exception> onException)
{
try
{
@@ -80,7 +80,7 @@ internal static class TaskExtension
}
catch (Exception e)
{
logger?.LogError(e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
logger?.LogError(e, "{Caller}:\r\n{Exception}", nameof(SafeForget), e.GetBaseException());
onException?.Invoke(e);
}
}
@@ -92,7 +92,7 @@ internal static class TaskExtension
/// <param name="logger">日志器</param>
/// <param name="onCanceled">任务取消时调用</param>
/// <param name="onException">发生异常时调用</param>
public static async void SafeForget(this Task task, ILogger? logger = null, Action? onCanceled = null, Action<Exception>? onException = null)
public static async void SafeForget(this Task task, ILogger logger, Action onCanceled, Action<Exception>? onException = null)
{
try
{
@@ -104,7 +104,7 @@ internal static class TaskExtension
}
catch (Exception e)
{
logger?.LogError(e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
logger?.LogError(e, "{Caller}:\r\n{Exception}", nameof(SafeForget), e.GetBaseException());
onException?.Invoke(e);
}
}

View File

@@ -185,7 +185,7 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutOpenCloseMessag
double scale = Persistence.GetScaleForWindowHandle(options.Hwnd);
// 48 is the navigation button leftInset
RectInt32 dragRect = StructMarshal.RectInt32(new(48, 0), options.TitleBar.ActualSize).Scale(scale);
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
appTitleBar.SetDragRectangles(dragRect.ToArray());
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976

View File

@@ -85,13 +85,13 @@ internal static class Persistence
HWND fgHwnd = GetForegroundWindow();
uint threadIdHwnd = GetWindowThreadProcessId(hwnd);
uint threadIdfgHwnd = GetWindowThreadProcessId(fgHwnd);
uint threadIdFgHwnd = GetWindowThreadProcessId(fgHwnd);
if (threadIdHwnd != threadIdfgHwnd)
if (threadIdHwnd != threadIdFgHwnd)
{
AttachThreadInput(threadIdHwnd, threadIdfgHwnd, true);
AttachThreadInput(threadIdHwnd, threadIdFgHwnd, true);
SetForegroundWindow(hwnd);
AttachThreadInput(threadIdHwnd, threadIdfgHwnd, false);
AttachThreadInput(threadIdHwnd, threadIdFgHwnd, false);
}
else
{

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.InteropServices;
using Microsoft.UI.Xaml;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
@@ -24,7 +25,7 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
// We have to explicitly hold a reference to SUBCLASSPROC
private SUBCLASSPROC? windowProc;
private SUBCLASSPROC? dragBarProc;
private SUBCLASSPROC? legacyDragBarProc;
/// <summary>
/// 构造一个新的窗体子类管理器
@@ -62,8 +63,8 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
return windowHooked && titleBarHooked;
}
dragBarProc = OnDragBarProcedure;
titleBarHooked = SetWindowSubclass(hwndDragBar, dragBarProc, DragBarSubclassId, 0);
legacyDragBarProc = OnLegacyDragBarProcedure;
titleBarHooked = SetWindowSubclass(hwndDragBar, legacyDragBarProc, DragBarSubclassId, 0);
return windowHooked && titleBarHooked;
}
@@ -81,8 +82,8 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
return;
}
RemoveWindowSubclass(options.Hwnd, dragBarProc, DragBarSubclassId);
dragBarProc = null;
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
legacyDragBarProc = null;
}
[SuppressMessage("", "SH002")]
@@ -108,7 +109,7 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
}
[SuppressMessage("", "SH002")]
private LRESULT OnDragBarProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData)
private LRESULT OnLegacyDragBarProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData)
{
switch (uMsg)
{

View File

@@ -12,7 +12,7 @@ namespace Snap.Hutao.Extension;
internal static partial class EnumerableExtension
{
/// <inheritdoc cref="Enumerable.Average(IEnumerable{int})"/>
public static unsafe double UnsafeAverage(this List<int> source)
public static double SpanAverage(this List<int> source)
{
Span<int> span = CollectionsMarshal.AsSpan(source);
if (span.IsEmpty)

View File

@@ -8,6 +8,7 @@ namespace Snap.Hutao.Message;
/// </summary>
/// <typeparam name="TValue">值的类型</typeparam>
[HighQuality]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
internal abstract class ValueChangedMessage<TValue>
where TValue : class
{

View File

@@ -25,7 +25,7 @@ internal sealed class CalculableSkill : ObservableObject, ICalculableSkill
/// <param name="skill">技能</param>
public CalculableSkill(ProudableSkill skill)
{
GruopId = skill.GroupId;
GroupId = skill.GroupId;
LevelMin = 1;
LevelMax = 10; // hard coded 10 here
Name = skill.Name;
@@ -42,7 +42,7 @@ internal sealed class CalculableSkill : ObservableObject, ICalculableSkill
/// <param name="skill">技能</param>
public CalculableSkill(SkillView skill)
{
GruopId = skill.GroupId;
GroupId = skill.GroupId;
LevelMin = skill.LevelNumber;
LevelMax = 10; // hard coded 10 here
Name = skill.Name;
@@ -54,7 +54,7 @@ internal sealed class CalculableSkill : ObservableObject, ICalculableSkill
}
/// <inheritdoc/>
public SkillGroupId GruopId { get; }
public SkillGroupId GroupId { get; }
/// <inheritdoc/>
public uint LevelMin { get; }

View File

@@ -14,7 +14,7 @@ internal interface ICalculableSkill : ICalculable
/// <summary>
/// 技能组Id
/// </summary>
SkillGroupId GruopId { get; }
SkillGroupId GroupId { get; }
/// <summary>
/// 最小等级

View File

@@ -34,7 +34,7 @@ internal sealed class AppDbContext : DbContext
: this(options)
{
this.logger = logger;
logger.LogInformation("{name}[{id}] created.", nameof(AppDbContext), ContextId);
logger.LogInformation("{Name}[{Id}] created", nameof(AppDbContext), ContextId);
}
/// <summary>
@@ -136,7 +136,7 @@ internal sealed class AppDbContext : DbContext
public override void Dispose()
{
base.Dispose();
logger?.LogInformation("AppDbContext[{id}] disposed.", ContextId);
logger?.LogInformation("{Name}[{Id}] disposed", nameof(AppDbContext), ContextId);
}
/// <inheritdoc/>

View File

@@ -7,7 +7,7 @@ namespace Snap.Hutao.Model.Intrinsic;
/// 成就信息状态
/// </summary>
[HighQuality]
internal enum AchievementStatus
internal enum AchievementStatus /*: int*/
{
/// <summary>
/// 未识别

View File

@@ -18,13 +18,13 @@ internal enum AssociationType
/// <summary>
/// 蒙德
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeMondstadt")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeMondstadt))]
ASSOC_TYPE_MONDSTADT,
/// <summary>
/// 璃月
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeLiyue")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeLiyue))]
ASSOC_TYPE_LIYUE,
/// <summary>
@@ -35,42 +35,42 @@ internal enum AssociationType
/// <summary>
/// 愚人众
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeFatui")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeFatui))]
ASSOC_TYPE_FATUI,
/// <summary>
/// 稻妻
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeInazuma")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeInazuma))]
ASSOC_TYPE_INAZUMA,
/// <summary>
/// 游侠
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeRanger")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeRanger))]
ASSOC_TYPE_RANGER,
/// <summary>
/// 须弥
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeSumeru")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeSumeru))]
ASSOC_TYPE_SUMERU,
/// <summary>
/// 枫丹
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeFontaine")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeFontaine))]
ASSOC_TYPE_FONTAINE,
/// <summary>
/// 纳塔
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeNatlan")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeNatlan))]
ASSOC_TYPE_NATLAN,
/// <summary>
/// 至冬
/// </summary>
[LocalizationKey("ModelIntrinsicAssociationTypeSnezhnaya")]
[LocalizationKey(nameof(SH.ModelIntrinsicAssociationTypeSnezhnaya))]
ASSOC_TYPE_SNEZHNAYA,
}

View File

@@ -18,30 +18,30 @@ internal enum BodyType
/// <summary>
/// 男孩
/// </summary>
[LocalizationKey("ModelIntrinsicBodyTypeBoy")]
[LocalizationKey(nameof(SH.ModelIntrinsicBodyTypeBoy))]
BODY_BOY,
/// <summary>
/// 女孩
/// </summary>
[LocalizationKey("ModelIntrinsicBodyTypeGirl")]
[LocalizationKey(nameof(SH.ModelIntrinsicBodyTypeGirl))]
BODY_GIRL,
/// <summary>
/// 成女
/// </summary>
[LocalizationKey("ModelIntrinsicBodyTypeLady")]
[LocalizationKey(nameof(SH.ModelIntrinsicBodyTypeLady))]
BODY_LADY,
/// <summary>
/// 成男
/// </summary>
[LocalizationKey("ModelIntrinsicBodyTypeMale")]
[LocalizationKey(nameof(SH.ModelIntrinsicBodyTypeMale))]
BODY_MALE,
/// <summary>
/// 萝莉
/// </summary>
[LocalizationKey("ModelIntrinsicBodyTypeLoli")]
[LocalizationKey(nameof(SH.ModelIntrinsicBodyTypeLoli))]
BODY_LOLI,
}

View File

@@ -18,55 +18,55 @@ internal enum FightProperty
/// <summary>
/// 基础生命值
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyBaseHp")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyBaseHp))]
FIGHT_PROP_BASE_HP = 1,
/// <summary>
/// 小生命值加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyHp")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyHp))]
FIGHT_PROP_HP = 2,
/// <summary>
/// 生命值加成百分比
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyHp")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyHp))]
FIGHT_PROP_HP_PERCENT = 3,
/// <summary>
/// 基础攻击力
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyBaseAtk")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyBaseAtk))]
FIGHT_PROP_BASE_ATTACK = 4,
/// <summary>
/// 攻击力加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyAtk")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyAtk))]
FIGHT_PROP_ATTACK = 5,
/// <summary>
/// 攻击力百分比
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyAtk")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyAtk))]
FIGHT_PROP_ATTACK_PERCENT = 6,
/// <summary>
/// 基础防御力
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyBaseDef")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyBaseDef))]
FIGHT_PROP_BASE_DEFENSE = 7,
/// <summary>
/// 防御力加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDef")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDef))]
FIGHT_PROP_DEFENSE = 8,
/// <summary>
/// 防御力百分比
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDef")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDef))]
FIGHT_PROP_DEFENSE_PERCENT = 9,
/// <summary>
@@ -92,7 +92,7 @@ internal enum FightProperty
/// <summary>
/// 暴击率
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyCR")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyCR))]
FIGHT_PROP_CRITICAL = 20,
/// <summary>
@@ -103,13 +103,13 @@ internal enum FightProperty
/// <summary>
/// 暴击伤害
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyCDmg")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyCDmg))]
FIGHT_PROP_CRITICAL_HURT = 22,
/// <summary>
/// 元素充能效率
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyCE")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyCE))]
FIGHT_PROP_CHARGE_EFFICIENCY = 23,
/// <summary>
@@ -125,7 +125,7 @@ internal enum FightProperty
/// <summary>
/// 治疗提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyHB")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyHB))]
FIGHT_PROP_HEAL_ADD = 26,
/// <summary>
@@ -136,19 +136,19 @@ internal enum FightProperty
/// <summary>
/// 元素精通
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyEM")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyEM))]
FIGHT_PROP_ELEMENT_MASTERY = 28,
/// <summary>
/// 物理抗性提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyRESPhysical")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyRESPhysical))]
FIGHT_PROP_PHYSICAL_SUB_HURT = 29,
/// <summary>
/// 物理伤害加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDBPhyiscal")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDBPhyiscal))]
FIGHT_PROP_PHYSICAL_ADD_HURT = 30,
/// <summary>
@@ -164,43 +164,43 @@ internal enum FightProperty
/// <summary>
/// 火元素伤害加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDBFire")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDBFire))]
FIGHT_PROP_FIRE_ADD_HURT = 40,
/// <summary>
/// 雷元素伤害加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDBElec")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDBElec))]
FIGHT_PROP_ELEC_ADD_HURT = 41,
/// <summary>
/// 水元素伤害加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDBWater")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDBWater))]
FIGHT_PROP_WATER_ADD_HURT = 42,
/// <summary>
/// 草元素伤害加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDBGrass")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDBGrass))]
FIGHT_PROP_GRASS_ADD_HURT = 43,
/// <summary>
/// 风元素伤害加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDBWind")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDBWind))]
FIGHT_PROP_WIND_ADD_HURT = 44,
/// <summary>
/// 岩元素伤害加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDBRock")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDBRock))]
FIGHT_PROP_ROCK_ADD_HURT = 45,
/// <summary>
/// 冰元素伤害加成
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDBIce")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDBIce))]
FIGHT_PROP_ICE_ADD_HURT = 46,
/// <summary>
@@ -211,43 +211,43 @@ internal enum FightProperty
/// <summary>
/// 火元素抗性提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyRESFire")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyRESFire))]
FIGHT_PROP_FIRE_SUB_HURT = 50,
/// <summary>
/// 雷元素抗性提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyRESElec")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyRESElec))]
FIGHT_PROP_ELEC_SUB_HURT = 51,
/// <summary>
/// 雷元素抗性提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyRESWater")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyRESWater))]
FIGHT_PROP_WATER_SUB_HURT = 52,
/// <summary>
/// 草元素抗性提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyRESGrass")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyRESGrass))]
FIGHT_PROP_GRASS_SUB_HURT = 53,
/// <summary>
/// 风元素抗性提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyRESWind")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyRESWind))]
FIGHT_PROP_WIND_SUB_HURT = 54,
/// <summary>
/// 岩元素抗性提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyRESRock")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyRESRock))]
FIGHT_PROP_ROCK_SUB_HURT = 55,
/// <summary>
/// 冰元素抗性提升
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyRESIce")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyRESIce))]
FIGHT_PROP_ICE_SUB_HURT = 56,
/// <summary>
@@ -378,19 +378,19 @@ internal enum FightProperty
/// <summary>
/// 最大生命值
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyHp")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyHp))]
FIGHT_PROP_MAX_HP = 2000,
/// <summary>
/// 当前攻击力
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyAtk")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyAtk))]
FIGHT_PROP_CUR_ATTACK = 2001,
/// <summary>
/// 当前防御力
/// </summary>
[LocalizationKey("ServiceAvatarInfoPropertyDef")]
[LocalizationKey(nameof(SH.ServiceAvatarInfoPropertyDef))]
FIGHT_PROP_CUR_DEFENSE = 2002,
/// <summary>

View File

@@ -49,4 +49,15 @@ internal static class IntrinsicImmutable
SH.ModelIntrinsicElementNameIce,
SH.ModelIntrinsicElementNameRock,
}.ToImmutableHashSet();
public static readonly ImmutableHashSet<string> MaterialTypeDescriptions = new HashSet<string>(7)
{
SH.ModelMetadataMaterialCharacterAndWeaponEnhancementMaterial,
SH.ModelMetadataMaterialCharacterEXPMaterial,
SH.ModelMetadataMaterialCharacterAscensionMaterial,
SH.ModelMetadataMaterialCharacterTalentMaterial,
SH.ModelMetadataMaterialCharacterLevelUpMaterial,
SH.ModelMetadataMaterialWeaponEnhancementMaterial,
SH.ModelMetadataMaterialWeaponAscensionMaterial,
}.ToImmutableHashSet();
}

View File

@@ -18,36 +18,36 @@ internal enum QualityType
/// <summary>
/// 一星
/// </summary>
[LocalizationKey("ModelIntrinsicItemQualityWhite")]
[LocalizationKey(nameof(SH.ModelIntrinsicItemQualityWhite))]
QUALITY_WHITE = 1,
/// <summary>
/// 二星
/// </summary>
[LocalizationKey("ModelIntrinsicItemQualityGreen")]
[LocalizationKey(nameof(SH.ModelIntrinsicItemQualityGreen))]
QUALITY_GREEN = 2,
/// <summary>
/// 三星
/// </summary>
[LocalizationKey("ModelIntrinsicItemQualityBlue")]
[LocalizationKey(nameof(SH.ModelIntrinsicItemQualityBlue))]
QUALITY_BLUE = 3,
/// <summary>
/// 四星
/// </summary>
[LocalizationKey("ModelIntrinsicItemQualityPurple")]
[LocalizationKey(nameof(SH.ModelIntrinsicItemQualityPurple))]
QUALITY_PURPLE = 4,
/// <summary>
/// 五星
/// </summary>
[LocalizationKey("ModelIntrinsicItemQualityOrange")]
[LocalizationKey(nameof(SH.ModelIntrinsicItemQualityOrange))]
QUALITY_ORANGE = 5,
/// <summary>
/// 限定五星
/// </summary>
[LocalizationKey("ModelIntrinsicItemQualityRed")]
[LocalizationKey(nameof(SH.ModelIntrinsicItemQualityRed))]
QUALITY_ORANGE_SP = 105,
}

View File

@@ -19,7 +19,7 @@ internal enum WeaponType
/// <summary>
/// 单手剑
/// </summary>
[LocalizationKey("ModelIntrinsicWeaponTypeSwordOneHand")]
[LocalizationKey(nameof(SH.ModelIntrinsicWeaponTypeSwordOneHand))]
WEAPON_SWORD_ONE_HAND = 1,
#region Not Used
@@ -76,24 +76,24 @@ internal enum WeaponType
/// <summary>
/// 法器
/// </summary>
[LocalizationKey("ModelIntrinsicWeaponTypeCatalyst")]
[LocalizationKey(nameof(SH.ModelIntrinsicWeaponTypeCatalyst))]
WEAPON_CATALYST = 10,
/// <summary>
/// 双手剑
/// </summary>
[LocalizationKey("ModelIntrinsicWeaponTypeClaymore")]
[LocalizationKey(nameof(SH.ModelIntrinsicWeaponTypeClaymore))]
WEAPON_CLAYMORE = 11,
/// <summary>
/// 弓
/// </summary>
[LocalizationKey("ModelIntrinsicWeaponTypeBow")]
[LocalizationKey(nameof(SH.ModelIntrinsicWeaponTypeBow))]
WEAPON_BOW = 12,
/// <summary>
/// 长柄武器
/// </summary>
[LocalizationKey("ModelIntrinsicWeaponTypePole")]
[LocalizationKey(nameof(SH.ModelIntrinsicWeaponTypePole))]
WEAPON_POLE = 13,
}

View File

@@ -19,7 +19,7 @@ internal sealed class WeaponTypeIconConverter : ValueConverter<WeaponType, Uri>
/// <returns>图标链接</returns>
public static Uri WeaponTypeToIconUri(WeaponType type)
{
string element = type switch
string weapon = type switch
{
WeaponType.WEAPON_SWORD_ONE_HAND => "01",
WeaponType.WEAPON_BOW => "02",
@@ -29,7 +29,7 @@ internal sealed class WeaponTypeIconConverter : ValueConverter<WeaponType, Uri>
_ => throw Must.NeverHappen(),
};
return Web.HutaoEndpoints.StaticFile("Skill", $"Skill_A_{element}.png").ToUri();
return Web.HutaoEndpoints.StaticFile("Skill", $"Skill_A_{weapon}.png").ToUri();
}
/// <inheritdoc/>

View File

@@ -5,6 +5,8 @@ using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.Cultivation;
using System.Collections.Immutable;
using System.Text.RegularExpressions;
using Snap.Hutao.Model.Intrinsic.Immutable;
namespace Snap.Hutao.Model.Metadata.Item;
@@ -46,24 +48,19 @@ internal sealed class Material : DisplayItem
return true;
}
// TODO: support non-CHS
if (TypeDescription.EndsWith("区域特产"))
if (Regex.IsMatch(TypeDescription, SH.ModelMetadataMaterialLocalSpecialtyRegex))
{
return true;
}
// TODO: support non-CHS
return TypeDescription switch
{
"角色与武器培养素材" => true, // 怪物掉落
"角色经验素材" => true, // 经验书
"角色突破素材" => true, // 元素晶石
"角色天赋素材" => true, // 天赋
"角色培养素材" => true, // 40体BOSS/周本掉落
"武器强化素材" => true, // 魔矿
"武器突破素材" => true, // 武器本
_ => false,
};
// Character and Weapon Enhancement Material // 怪物掉落
// Character EXP Material // 经验书
// Character Ascension Material // 元素晶石
// Character Talent Material // 天赋本
// Character Level-Up Material // 40体BOSS/周本掉落
// Weapon Enhancement Material // 魔矿
// Weapon Ascension Material // 武器
return IntrinsicImmutable.MaterialTypeDescriptions.Contains(TypeDescription);
}
/// <summary>

File diff suppressed because it is too large Load Diff

View File

@@ -273,7 +273,7 @@
<data name="ModelIntrinsicBodyTypeLady" xml:space="preserve">
<value>Lady</value>
</data>
<data name="ModelIntrinsicBodyTypeloli" xml:space="preserve">
<data name="ModelIntrinsicBodyTypeLoli" xml:space="preserve">
<value>Loli</value>
</data>
<data name="ModelIntrinsicBodyTypeMale" xml:space="preserve">

View File

@@ -273,7 +273,7 @@
<data name="ModelIntrinsicBodyTypeLady" xml:space="preserve">
<value>女性</value>
</data>
<data name="ModelIntrinsicBodyTypeloli" xml:space="preserve">
<data name="ModelIntrinsicBodyTypeLoli" xml:space="preserve">
<value>幼女</value>
</data>
<data name="ModelIntrinsicBodyTypeMale" xml:space="preserve">

View File

@@ -273,7 +273,7 @@
<data name="ModelIntrinsicBodyTypeLady" xml:space="preserve">
<value>여성</value>
</data>
<data name="ModelIntrinsicBodyTypeloli" xml:space="preserve">
<data name="ModelIntrinsicBodyTypeLoli" xml:space="preserve">
<value>로리</value>
</data>
<data name="ModelIntrinsicBodyTypeMale" xml:space="preserve">

View File

@@ -273,7 +273,7 @@
<data name="ModelIntrinsicBodyTypeLady" xml:space="preserve">
<value>成女</value>
</data>
<data name="ModelIntrinsicBodyTypeloli" xml:space="preserve">
<data name="ModelIntrinsicBodyTypeLoli" xml:space="preserve">
<value>萝莉</value>
</data>
<data name="ModelIntrinsicBodyTypeMale" xml:space="preserve">
@@ -2046,4 +2046,28 @@
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
<value>统计</value>
</data>
<data name="ModelMetadataMaterialCharacterAndWeaponEnhancementMaterial" xml:space="preserve">
<value>角色与武器培养素材</value>
</data>
<data name="ModelMetadataMaterialCharacterEXPMaterial" xml:space="preserve">
<value>角色经验素材</value>
</data>
<data name="ModelMetadataMaterialCharacterAscensionMaterial" xml:space="preserve">
<value>角色突破素材</value>
</data>
<data name="ModelMetadataMaterialCharacterTalentMaterial" xml:space="preserve">
<value>角色天赋素材</value>
</data>
<data name="ModelMetadataMaterialCharacterLevelUpMaterial" xml:space="preserve">
<value>角色培养素材</value>
</data>
<data name="ModelMetadataMaterialWeaponEnhancementMaterial" xml:space="preserve">
<value>武器强化素材</value>
</data>
<data name="ModelMetadataMaterialWeaponAscensionMaterial" xml:space="preserve">
<value>武器突破素材</value>
</data>
<data name="ModelMetadataMaterialLocalSpecialtyRegex" xml:space="preserve">
<value>[\u4e00-\u9fa5]{2}区域特产$</value>
</data>
</root>

View File

@@ -273,7 +273,7 @@
<data name="ModelIntrinsicBodyTypeLady" xml:space="preserve">
<value>成女</value>
</data>
<data name="ModelIntrinsicBodyTypeloli" xml:space="preserve">
<data name="ModelIntrinsicBodyTypeLoli" xml:space="preserve">
<value>蘿莉</value>
</data>
<data name="ModelIntrinsicBodyTypeMale" xml:space="preserve">

View File

@@ -151,8 +151,8 @@ internal sealed class TypedWishSummaryBuilder
TotalOrangePercent = totalOrangePullTracker / totalCount,
TotalPurplePercent = totalPurplePullTracker / totalCount,
TotalBluePercent = totalBluePullTracker / totalCount,
AverageOrangePull = averageOrangePullTracker.UnsafeAverage(),
AverageUpOrangePull = averageUpOrangePullTracker.UnsafeAverage(),
AverageOrangePull = averageOrangePullTracker.SpanAverage(),
AverageUpOrangePull = averageUpOrangePullTracker.SpanAverage(),
OrangeList = summaryItems,
};
}

View File

@@ -73,7 +73,7 @@ internal sealed partial class CultivatePromotionDeltaDialog : ContentDialog
AvatarLevelTarget = Avatar?.LevelTarget ?? 0,
SkillList = Avatar?.Skills.Select(s => new PromotionDelta()
{
Id = s.GruopId,
Id = s.GroupId,
LevelCurrent = s.LevelCurrent,
LevelTarget = s.LevelTarget,
}),

View File

@@ -14,34 +14,34 @@ internal enum GachaConfigType
/// 新手池
/// </summary>
[Description("新手祈愿")]
[LocalizationKey("WebGachaConfigTypeNoviceWish")]
[LocalizationKey(nameof(SH.WebGachaConfigTypeNoviceWish))]
NoviceWish = 100,
/// <summary>
/// 常驻池
/// </summary>
[Description("常驻祈愿")]
[LocalizationKey("WebGachaConfigTypePermanentWish")]
[LocalizationKey(nameof(SH.WebGachaConfigTypePermanentWish))]
StandardWish = 200,
/// <summary>
/// 角色1池
/// </summary>
[Description("角色活动祈愿")]
[LocalizationKey("WebGachaConfigTypeAvatarEventWish")]
[LocalizationKey(nameof(SH.WebGachaConfigTypeAvatarEventWish))]
AvatarEventWish = 301,
/// <summary>
/// 武器池
/// </summary>
[Description("武器活动祈愿")]
[LocalizationKey("WebGachaConfigTypeWeaponEventWish")]
[LocalizationKey(nameof(SH.WebGachaConfigTypeWeaponEventWish))]
WeaponEventWish = 302,
/// <summary>
/// 角色2池
/// </summary>
[Description("角色活动祈愿-2")]
[LocalizationKey("WebGachaConfigTypeAvatarEventWish2")]
[LocalizationKey(nameof(SH.WebGachaConfigTypeAvatarEventWish2))]
AvatarEventWish2 = 400,
}

View File

@@ -66,7 +66,19 @@ internal static class StructMarshal
/// <returns>新的实例</returns>
public static RectInt32 RectInt32(PointInt32 point, Vector2 size)
{
return new(point.X, point.Y, (int)size.X, (int)size.Y);
return RectInt32(point.X, point.Y, size);
}
/// <summary>
/// 构造一个新的<see cref="Windows.Graphics.RectInt32"/>
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="size">尺寸</param>
/// <returns>新的实例</returns>
public static RectInt32 RectInt32(int x, int y, Vector2 size)
{
return new(x, y, (int)size.X, (int)size.Y);
}
/// <summary>