From 2c0b32ab8b1b68a2c9bfec2e82c71d2579d971f8 Mon Sep 17 00:00:00 2001
From: DismissedLight <1686188646@qq.com>
Date: Tue, 20 Dec 2022 15:11:05 +0800
Subject: [PATCH] achievement progress
---
.../Control/{Image => Media}/Bgra8.cs | 2 +-
.../Snap.Hutao/Control/Media/Rgba8.cs | 175 ++++++++++++++++++
.../Control/Media/SoftwareBitmapExtension.cs | 42 +++++
.../Control/Text/DescriptionTextBlock.cs | 147 +--------------
.../Snap.Hutao/Core/CoreEnvironment.cs | 2 +
.../Core/Database/DbSetExtension.cs | 16 +-
...hedulerHelper.cs => ScheduleTaskHelper.cs} | 2 +-
.../DispatherQueueSwitchOperation.cs | 5 +-
.../Snap.Hutao/Core/WebView2Helper.cs | 6 +-
...dowExtensions.cs => AppWindowExtension.cs} | 2 +-
.../Core/Windowing/ExtendedWindow.cs | 3 +-
.../Binding/{ => Achievement}/Achievement.cs | 2 +-
.../Binding/Achievement/AchievementGoal.cs | 68 +++++++
.../Model/Binding/AvatarProperty/Avatar.cs | 31 +++-
.../Model/Binding/AvatarProperty/Skill.cs | 17 +-
.../Model/Binding/AvatarProperty/Weapon.cs | 22 ++-
.../Avatar => Calculable}/CalculableAvatar.cs | 23 ++-
.../Avatar => Calculable}/CalculableSkill.cs | 21 ++-
.../Weapon => Calculable}/CalculableWeapon.cs | 22 ++-
.../Model/Intrinsic/ImmutableIntrinsics.cs | 38 ++++
.../Snap.Hutao/Model/Intrinsic/WeaponType.cs | 2 +-
.../Metadata/Achievement/AchievementGoal.cs | 2 +-
.../Metadata/Avatar/Avatar.Implementation.cs | 1 +
.../Avatar/ProudableSkill.Implementation.cs | 1 +
.../Snap.Hutao/Model/Metadata/AvatarIds.cs | 2 +
.../Converter/AchievementIconConverter.cs | 12 +-
.../Converter/ElementNameIconConverter.cs | 2 +-
.../Metadata/Weapon/Weapon.Implementation.cs | 1 +
src/Snap.Hutao/Snap.Hutao/NativeMethods.txt | 2 +
.../Snap.Hutao/Package.appxmanifest | 2 +-
src/Snap.Hutao/Snap.Hutao/Program.cs | 4 +-
.../Service/Achievement/AchievementService.cs | 2 +-
.../Achievement/IAchievementService.cs | 2 +-
.../Service/AvatarInfo/Factory/AffixWeight.cs | 132 +++++++++++--
.../Factory/ReliquaryWeightConfiguration.cs | 127 +++++++------
.../Factory/SummaryAvatarFactory.cs | 44 ++---
.../AvatarInfo/Factory/SummaryFactory.cs | 24 +--
.../Factory/SummaryFactoryImplementation.cs | 44 +----
.../AvatarInfo/Factory/SummaryHelper.cs | 3 +-
.../Factory/SummaryMetadataContext.cs | 31 ++++
.../Factory/SummaryReliquaryFactory.cs | 42 ++---
.../Service/Cultivation/CultivationService.cs | 9 +-
.../Cultivation/ICultivationService.cs | 4 +-
.../Service/DailyNote/DailyNoteNotifier.cs | 48 ++---
.../Service/DailyNote/DailyNoteService.cs | 9 +-
.../View/Control/DescParamComboBox.xaml.cs | 2 +-
.../Snap.Hutao/View/Page/AchievementPage.xaml | 41 ++--
.../View/Page/AvatarPropertyPage.xaml | 31 +---
.../Snap.Hutao/View/Page/CultivationPage.xaml | 2 +-
.../Snap.Hutao/View/Page/GachaLogPage.xaml | 6 +-
.../Snap.Hutao/View/Page/SettingPage.xaml | 14 ++
.../Snap.Hutao/View/Page/WikiWeaponPage.xaml | 18 ++
.../ViewModel/AchievementViewModel.cs | 106 +++++++----
.../ViewModel/AvatarPropertyViewModel.cs | 94 +++++++---
.../ViewModel/DailyNoteViewModel.cs | 4 +-
.../ExperimentalFeaturesViewModel.cs | 24 ++-
.../Snap.Hutao/ViewModel/SettingViewModel.cs | 9 +
.../ViewModel/WikiAvatarViewModel.cs | 13 +-
.../ViewModel/WikiWeaponViewModel.cs | 79 ++++++++
.../Annotation/IgnoreSetCookieAttribute.cs | 12 --
.../Web/Hoyolab/Bbs/User/UserClient.cs | 1 -
61 files changed, 1118 insertions(+), 536 deletions(-)
rename src/Snap.Hutao/Snap.Hutao/Control/{Image => Media}/Bgra8.cs (97%)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Control/Media/Rgba8.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Control/Media/SoftwareBitmapExtension.cs
rename src/Snap.Hutao/Snap.Hutao/Core/{TaskSchedulerHelper.cs => ScheduleTaskHelper.cs} (98%)
rename src/Snap.Hutao/Snap.Hutao/Core/Windowing/{AppWindowExtensions.cs => AppWindowExtension.cs} (94%)
rename src/Snap.Hutao/Snap.Hutao/Model/Binding/{ => Achievement}/Achievement.cs (97%)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement/AchievementGoal.cs
rename src/Snap.Hutao/Snap.Hutao/Model/{Metadata/Avatar => Calculable}/CalculableAvatar.cs (72%)
rename src/Snap.Hutao/Snap.Hutao/Model/{Metadata/Avatar => Calculable}/CalculableSkill.cs (72%)
rename src/Snap.Hutao/Snap.Hutao/Model/{Metadata/Weapon => Calculable}/CalculableWeapon.cs (72%)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ImmutableIntrinsics.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryMetadataContext.cs
delete mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/IgnoreSetCookieAttribute.cs
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/Bgra8.cs b/src/Snap.Hutao/Snap.Hutao/Control/Media/Bgra8.cs
similarity index 97%
rename from src/Snap.Hutao/Snap.Hutao/Control/Image/Bgra8.cs
rename to src/Snap.Hutao/Snap.Hutao/Control/Media/Bgra8.cs
index d993ae39..da772d90 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/Bgra8.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Media/Bgra8.cs
@@ -4,7 +4,7 @@
using System.Runtime.InteropServices;
using Windows.UI;
-namespace Snap.Hutao.Control.Image;
+namespace Snap.Hutao.Control.Media;
///
/// BGRA8 结构
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Media/Rgba8.cs b/src/Snap.Hutao/Snap.Hutao/Control/Media/Rgba8.cs
new file mode 100644
index 00000000..3507c455
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Media/Rgba8.cs
@@ -0,0 +1,175 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+// Some part of this file came from:
+// https://github.com/xunkong/desktop/tree/main/src/Desktop/Desktop/Pages/CharacterInfoPage.xaml.cs
+
+using CommunityToolkit.WinUI;
+using System.Runtime.InteropServices;
+using Windows.UI;
+
+namespace Snap.Hutao.Control.Media;
+
+///
+/// RGBA 颜色
+///
+[StructLayout(LayoutKind.Explicit)]
+public struct Rgba8
+{
+ ///
+ /// R
+ ///
+ [FieldOffset(3)]
+ public byte R;
+
+ ///
+ /// G
+ ///
+ [FieldOffset(2)]
+ public byte G;
+
+ ///
+ /// B
+ ///
+ [FieldOffset(1)]
+ public byte B;
+
+ ///
+ /// A
+ ///
+ [FieldOffset(0)]
+ public byte A;
+
+ [FieldOffset(0)]
+ private readonly uint data;
+
+ ///
+ /// 构造一个新的 RGBA8 颜色
+ ///
+ /// 色值字符串
+ public Rgba8(ReadOnlySpan hex)
+ {
+ Must.Argument(hex.Length == 8, "色值长度不为8");
+ R = 0;
+ G = 0;
+ B = 0;
+ A = 0;
+ data = Convert.ToUInt32(hex.ToString(), 16);
+ }
+
+ private Rgba8(byte r, byte g, byte b, byte a)
+ {
+ data = 0;
+ R = r;
+ G = g;
+ B = b;
+ A = a;
+ }
+
+ public static implicit operator Color(Rgba8 hexColor)
+ {
+ return Color.FromArgb(hexColor.A, hexColor.R, hexColor.G, hexColor.B);
+ }
+
+ ///
+ /// 从 HSL 颜色转换
+ ///
+ /// HSL 颜色
+ /// RGBA8颜色
+ public static Rgba8 FromHsl(HslColor hsl)
+ {
+ double chroma = (1 - Math.Abs((2 * hsl.L) - 1)) * hsl.S;
+ double h1 = hsl.H / 60;
+ double x = chroma * (1 - Math.Abs((h1 % 2) - 1));
+ double m = hsl.L - (0.5 * chroma);
+ double r1, g1, b1;
+
+ if (h1 < 1)
+ {
+ r1 = chroma;
+ g1 = x;
+ b1 = 0;
+ }
+ else if (h1 < 2)
+ {
+ r1 = x;
+ g1 = chroma;
+ b1 = 0;
+ }
+ else if (h1 < 3)
+ {
+ r1 = 0;
+ g1 = chroma;
+ b1 = x;
+ }
+ else if (h1 < 4)
+ {
+ r1 = 0;
+ g1 = x;
+ b1 = chroma;
+ }
+ else if (h1 < 5)
+ {
+ r1 = x;
+ g1 = 0;
+ b1 = chroma;
+ }
+ else
+ {
+ r1 = chroma;
+ g1 = 0;
+ b1 = x;
+ }
+
+ byte r = (byte)(255 * (r1 + m));
+ byte g = (byte)(255 * (g1 + m));
+ byte b = (byte)(255 * (b1 + m));
+ byte a = (byte)(255 * hsl.A);
+
+ return new(r, g, b, a);
+ }
+
+ ///
+ /// 转换到 HSL 颜色
+ ///
+ /// HSL 颜色
+ public HslColor ToHsl()
+ {
+ const double toDouble = 1.0 / 255;
+ double r = toDouble * R;
+ double g = toDouble * G;
+ double b = toDouble * B;
+ double max = Math.Max(Math.Max(r, g), b);
+ double min = Math.Min(Math.Min(r, g), b);
+ double chroma = max - min;
+ double h1;
+
+ if (chroma == 0)
+ {
+ h1 = 0;
+ }
+ else if (max == r)
+ {
+ // The % operator doesn't do proper modulo on negative
+ // numbers, so we'll add 6 before using it
+ h1 = (((g - b) / chroma) + 6) % 6;
+ }
+ else if (max == g)
+ {
+ h1 = 2 + ((b - r) / chroma);
+ }
+ else
+ {
+ h1 = 4 + ((r - g) / chroma);
+ }
+
+ double lightness = 0.5 * (max + min);
+ double saturation = chroma == 0 ? 0 : chroma / (1 - Math.Abs((2 * lightness) - 1));
+
+ HslColor ret;
+ ret.H = 60 * h1;
+ ret.S = saturation;
+ ret.L = lightness;
+ ret.A = toDouble * A;
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Media/SoftwareBitmapExtension.cs b/src/Snap.Hutao/Snap.Hutao/Control/Media/SoftwareBitmapExtension.cs
new file mode 100644
index 00000000..3c5e02c8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Media/SoftwareBitmapExtension.cs
@@ -0,0 +1,42 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Windows.Foundation;
+using Windows.Graphics.Imaging;
+using Windows.Win32;
+using Windows.Win32.System.WinRT;
+using WinRT;
+
+namespace Snap.Hutao.Control.Media;
+
+///
+/// 软件位图拓展
+///
+public static class SoftwareBitmapExtension
+{
+ ///
+ /// 混合模式 正常
+ ///
+ /// 软件位图
+ /// 底色
+ public static unsafe void NormalBlend(this SoftwareBitmap softwareBitmap, Bgra8 tint)
+ {
+ using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.ReadWrite))
+ {
+ using (IMemoryBufferReference reference = buffer.CreateReference())
+ {
+ reference.As().GetBuffer(out byte* data, out uint length);
+
+ for (int i = 0; i < length; i += 4)
+ {
+ Bgra8* pixel = (Bgra8*)(data + i);
+ byte baseAlpha = pixel->A;
+ pixel->B = (byte)(((pixel->B * baseAlpha) + (tint.B * (0xFF - baseAlpha))) / 0xFF);
+ pixel->G = (byte)(((pixel->G * baseAlpha) + (tint.G * (0xFF - baseAlpha))) / 0xFF);
+ pixel->R = (byte)(((pixel->R * baseAlpha) + (tint.R * (0xFF - baseAlpha))) / 0xFF);
+ pixel->A = 0xFF;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs b/src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs
index 8cb68e12..ea1e0ffe 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs
@@ -8,8 +8,8 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents;
using Microsoft.UI.Xaml.Media;
+using Snap.Hutao.Control.Media;
using Snap.Hutao.Core;
-using System.Runtime.InteropServices;
using Windows.UI;
namespace Snap.Hutao.Control.Text;
@@ -19,8 +19,7 @@ namespace Snap.Hutao.Control.Text;
///
public class DescriptionTextBlock : ContentControl
{
- private static readonly DependencyProperty DescriptionProperty =
- Property.Depend(nameof(Description), string.Empty, OnDescriptionChanged);
+ private static readonly DependencyProperty DescriptionProperty = Property.Depend(nameof(Description), string.Empty, OnDescriptionChanged);
private static readonly int ColorTagFullLength = "".Length;
private static readonly int ColorTagLeftLength = "".Length;
@@ -79,7 +78,7 @@ public class DescriptionTextBlock : ContentControl
else if (description[i] == '<' && description[i + 1] == 'c')
{
AppendText(text, description[last..i]);
- HexColor color = new(description.Slice(i + 8, 8));
+ Rgba8 color = new(description.Slice(i + 8, 8));
int length = description[(i + ColorTagLeftLength)..].IndexOf('<');
AppendColorText(text, description.Slice(i + ColorTagLeftLength, length), color);
@@ -115,7 +114,7 @@ public class DescriptionTextBlock : ContentControl
text.Inlines.Add(new Run { Text = slice.ToString() });
}
- private static void AppendColorText(TextBlock text, ReadOnlySpan slice, HexColor color)
+ private static void AppendColorText(TextBlock text, ReadOnlySpan slice, Rgba8 color)
{
Color targetColor;
if (ThemeHelper.IsDarkMode(text.ActualTheme))
@@ -126,7 +125,7 @@ public class DescriptionTextBlock : ContentControl
{
HslColor hsl = color.ToHsl();
hsl.L *= 0.3;
- targetColor = HexColor.FromHsl(hsl);
+ targetColor = Rgba8.FromHsl(hsl);
}
text.Inlines.Add(new Run
@@ -154,138 +153,4 @@ public class DescriptionTextBlock : ContentControl
{
ApplyDescription((TextBlock)Content, Description);
}
-
- [StructLayout(LayoutKind.Explicit)]
- private struct HexColor
- {
- [FieldOffset(3)]
- public byte R;
- [FieldOffset(2)]
- public byte G;
- [FieldOffset(1)]
- public byte B;
- [FieldOffset(0)]
- public byte A;
-
- [FieldOffset(0)]
- private readonly uint data;
-
- public HexColor(ReadOnlySpan hex)
- {
- Must.Argument(hex.Length == 8, "色值长度不为8");
- R = 0;
- G = 0;
- B = 0;
- A = 0;
- data = Convert.ToUInt32(hex.ToString(), 16);
- }
-
- private HexColor(byte r, byte g, byte b, byte a)
- {
- data = 0;
- R = r;
- G = g;
- B = b;
- A = a;
- }
-
- public static implicit operator Color(HexColor hexColor)
- {
- return Color.FromArgb(hexColor.A, hexColor.R, hexColor.G, hexColor.B);
- }
-
- public static HexColor FromHsl(HslColor hsl)
- {
- double chroma = (1 - Math.Abs((2 * hsl.L) - 1)) * hsl.S;
- double h1 = hsl.H / 60;
- double x = chroma * (1 - Math.Abs((h1 % 2) - 1));
- double m = hsl.L - (0.5 * chroma);
- double r1, g1, b1;
-
- if (h1 < 1)
- {
- r1 = chroma;
- g1 = x;
- b1 = 0;
- }
- else if (h1 < 2)
- {
- r1 = x;
- g1 = chroma;
- b1 = 0;
- }
- else if (h1 < 3)
- {
- r1 = 0;
- g1 = chroma;
- b1 = x;
- }
- else if (h1 < 4)
- {
- r1 = 0;
- g1 = x;
- b1 = chroma;
- }
- else if (h1 < 5)
- {
- r1 = x;
- g1 = 0;
- b1 = chroma;
- }
- else
- {
- r1 = chroma;
- g1 = 0;
- b1 = x;
- }
-
- byte r = (byte)(255 * (r1 + m));
- byte g = (byte)(255 * (g1 + m));
- byte b = (byte)(255 * (b1 + m));
- byte a = (byte)(255 * hsl.A);
-
- return new(r, g, b, a);
- }
-
- public HslColor ToHsl()
- {
- const double toDouble = 1.0 / 255;
- double r = toDouble * R;
- double g = toDouble * G;
- double b = toDouble * B;
- double max = Math.Max(Math.Max(r, g), b);
- double min = Math.Min(Math.Min(r, g), b);
- double chroma = max - min;
- double h1;
-
- if (chroma == 0)
- {
- h1 = 0;
- }
- else if (max == r)
- {
- // The % operator doesn't do proper modulo on negative
- // numbers, so we'll add 6 before using it
- h1 = (((g - b) / chroma) + 6) % 6;
- }
- else if (max == g)
- {
- h1 = 2 + ((b - r) / chroma);
- }
- else
- {
- h1 = 4 + ((r - g) / chroma);
- }
-
- double lightness = 0.5 * (max + min);
- double saturation = chroma == 0 ? 0 : chroma / (1 - Math.Abs((2 * lightness) - 1));
-
- HslColor ret;
- ret.H = 60 * h1;
- ret.S = saturation;
- ret.L = lightness;
- ret.A = toDouble * A;
- return ret;
- }
- }
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs
index 5cb2a538..50552ce4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs
@@ -5,8 +5,10 @@ using Microsoft.Win32;
using Snap.Hutao.Core.Convert;
using Snap.Hutao.Core.Json;
using Snap.Hutao.Extension;
+using System.Runtime.InteropServices;
using System.Text.Json.Serialization.Metadata;
using Windows.ApplicationModel;
+using Windows.Win32.Foundation;
namespace Snap.Hutao.Core;
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs
index e3eb44af..707c50dc 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs
@@ -44,11 +44,11 @@ public static class DbSetExtension
/// 数据库集
/// 实体
/// 影响条数
- public static Task AddAndSaveAsync(this DbSet dbSet, TEntity entity)
+ public static async ValueTask AddAndSaveAsync(this DbSet dbSet, TEntity entity)
where TEntity : class
{
dbSet.Add(entity);
- return dbSet.Context().SaveChangesAsync();
+ return await dbSet.Context().SaveChangesAsync().ConfigureAwait(false);
}
///
@@ -72,11 +72,11 @@ public static class DbSetExtension
/// 数据库集
/// 实体
/// 影响条数
- public static Task AddRangeAndSaveAsync(this DbSet dbSet, IEnumerable entities)
+ public static async ValueTask AddRangeAndSaveAsync(this DbSet dbSet, IEnumerable entities)
where TEntity : class
{
dbSet.AddRange(entities);
- return dbSet.Context().SaveChangesAsync();
+ return await dbSet.Context().SaveChangesAsync().ConfigureAwait(false);
}
///
@@ -100,11 +100,11 @@ public static class DbSetExtension
/// 数据库集
/// 实体
/// 影响条数
- public static Task RemoveAndSaveAsync(this DbSet dbSet, TEntity entity)
+ public static async ValueTask RemoveAndSaveAsync(this DbSet dbSet, TEntity entity)
where TEntity : class
{
dbSet.Remove(entity);
- return dbSet.Context().SaveChangesAsync();
+ return await dbSet.Context().SaveChangesAsync().ConfigureAwait(false);
}
///
@@ -128,10 +128,10 @@ public static class DbSetExtension
/// 数据库集
/// 实体
/// 影响条数
- public static Task UpdateAndSaveAsync(this DbSet dbSet, TEntity entity)
+ public static async ValueTask UpdateAndSaveAsync(this DbSet dbSet, TEntity entity)
where TEntity : class
{
dbSet.Update(entity);
- return dbSet.Context().SaveChangesAsync();
+ return await dbSet.Context().SaveChangesAsync().ConfigureAwait(false);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs b/src/Snap.Hutao/Snap.Hutao/Core/ScheduleTaskHelper.cs
similarity index 98%
rename from src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs
rename to src/Snap.Hutao/Snap.Hutao/Core/ScheduleTaskHelper.cs
index e7526ac1..7c460e34 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/ScheduleTaskHelper.cs
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Core;
///
/// 任务计划器服务
///
-internal static class TaskSchedulerHelper
+internal static class ScheduleTaskHelper
{
private const string DailyNoteRefreshTaskName = "SnapHutaoDailyNoteRefreshTask";
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs
index 933c68fb..7fdc04ed 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs
@@ -43,6 +43,9 @@ public readonly struct DispatherQueueSwitchOperation : IAwaitable
public void OnCompleted(Action continuation)
{
- dispatherQueue.TryEnqueue(() => { continuation(); });
+ dispatherQueue.TryEnqueue(() =>
+ {
+ continuation();
+ });
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
index 48c5acfe..1b192f5f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
@@ -50,6 +50,10 @@ internal abstract class WebView2Helper
///
public static string Version
{
- get => version;
+ get
+ {
+ _ = IsSupported;
+ return version;
+ }
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/AppWindowExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/AppWindowExtension.cs
similarity index 94%
rename from src/Snap.Hutao/Snap.Hutao/Core/Windowing/AppWindowExtensions.cs
rename to src/Snap.Hutao/Snap.Hutao/Core/Windowing/AppWindowExtension.cs
index 027fee31..f540d473 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/AppWindowExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/AppWindowExtension.cs
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Core.Windowing;
///
/// 扩展
///
-public static class AppWindowExtensions
+public static class AppWindowExtension
{
///
/// 获取当前 的呈现矩形
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
index 63b3cf11..597cc831 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
@@ -134,7 +134,8 @@ internal sealed class ExtendedWindow : IRecipient
/// 用于视图绑定的成就
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement/AchievementGoal.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement/AchievementGoal.cs
new file mode 100644
index 00000000..06134b9b
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement/AchievementGoal.cs
@@ -0,0 +1,68 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Snap.Hutao.Model.Binding.Achievement;
+
+///
+/// 绑定成就分类
+///
+public class AchievementGoal : ObservableObject
+{
+ private double finishPercent;
+ private string? finishDescription;
+
+ ///
+ /// 构造一个新的成就分类
+ ///
+ /// 分类
+ public AchievementGoal(Metadata.Achievement.AchievementGoal goal)
+ {
+ Id = goal.Id;
+ Order = goal.Order;
+ Name = goal.Name;
+ Icon = Metadata.Converter.AchievementIconConverter.IconNameToUri(goal.Icon);
+ }
+
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 排序顺序
+ ///
+ public int Order { get; set; }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; } = default!;
+
+ ///
+ /// 图标
+ ///
+ public Uri? Icon { get; set; }
+
+ ///
+ /// 完成百分比
+ ///
+ public double FinishPercent { get => finishPercent; set => SetProperty(ref finishPercent, value); }
+
+ ///
+ /// 完成百分比
+ ///
+ public string? FinishDescription { get => finishDescription; set => SetProperty(ref finishDescription, value); }
+
+ ///
+ /// 更新进度
+ ///
+ /// 完成项
+ /// 总项
+ public void UpdateFinishPercent(int finished, int count)
+ {
+ FinishPercent = (double)finished / count;
+ FinishDescription = $"{finished}/{count} - {FinishPercent:P2}";
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Avatar.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Avatar.cs
index 7b4406cc..e4518053 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Avatar.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Avatar.cs
@@ -1,14 +1,17 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Primitive;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
namespace Snap.Hutao.Model.Binding.AvatarProperty;
///
/// 角色信息
///
-public class Avatar
+public class Avatar : ICalculableSource
{
///
/// 名称
@@ -45,11 +48,6 @@ public class Avatar
///
public string Level { get; set; } = default!;
- ///
- /// 好感度等级
- ///
- public int FetterLevel { get; set; }
-
///
/// 武器
///
@@ -84,4 +82,25 @@ public class Avatar
/// 双爆评分
///
public string CritScore { get; set; } = default!;
+
+ ///
+ /// 好感度等级
+ ///
+ public int FetterLevel { get; set; }
+
+ ///
+ /// Id
+ ///
+ internal AvatarId Id { get; set; }
+
+ ///
+ /// 等级数字
+ ///
+ internal int LevelNumber { get; set; }
+
+ ///
+ public ICalculableAvatar ToCalculable()
+ {
+ return new CalculableAvatar(this);
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Skill.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Skill.cs
index cd5e9510..8bf76de3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Skill.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Skill.cs
@@ -1,22 +1,35 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Metadata;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
namespace Snap.Hutao.Model.Binding.AvatarProperty;
///
/// 天赋
///
-public class Skill : NameIconDescription
+public class Skill : NameIconDescription, ICalculableSource
{
///
/// 技能属性
///
public LevelParam Info { get; set; } = default!;
+ ///
+ /// 技能组Id
+ ///
+ internal int GroupId { get; set; }
+
///
/// 技能等级,仅用于养成计算
///
- internal int Level { get; set; }
+ internal int LevelNumber { get; set; }
+
+ ///
+ public ICalculableSkill ToCalculable()
+ {
+ return new CalculableSkill(this);
+ }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Weapon.cs
index 8079dc66..b20d8758 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Weapon.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Weapon.cs
@@ -1,12 +1,16 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Calculable;
+using Snap.Hutao.Model.Primitive;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
namespace Snap.Hutao.Model.Binding.AvatarProperty;
///
/// 武器
///
-public class Weapon : EquipBase
+public class Weapon : EquipBase, ICalculableSource
{
///
/// 副属性
@@ -27,4 +31,20 @@ public class Weapon : EquipBase
/// 精炼被动
///
public string AffixDescription { get; set; } = default!;
+
+ ///
+ /// Id
+ ///
+ internal WeaponId Id { get; set; }
+
+ ///
+ /// 等级数字
+ ///
+ internal int LevelNumber { get; set; }
+
+ ///
+ public ICalculableWeapon ToCalculable()
+ {
+ return new CalculableWeapon(this);
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableAvatar.cs b/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableAvatar.cs
similarity index 72%
rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableAvatar.cs
rename to src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableAvatar.cs
index edc36cb2..362afd39 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableAvatar.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableAvatar.cs
@@ -7,7 +7,7 @@ using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
-namespace Snap.Hutao.Model.Metadata.Avatar;
+namespace Snap.Hutao.Model.Calculable;
///
/// 可计算角色
@@ -21,7 +21,7 @@ internal class CalculableAvatar : ObservableObject, ICalculableAvatar
/// 构造一个新的可计算角色
///
/// 角色
- public CalculableAvatar(Avatar avatar)
+ public CalculableAvatar(Metadata.Avatar.Avatar avatar)
{
AvatarId = avatar.Id;
LevelMin = 1;
@@ -30,6 +30,25 @@ internal class CalculableAvatar : ObservableObject, ICalculableAvatar
Name = avatar.Name;
Icon = AvatarIconConverter.IconNameToUri(avatar.Icon);
Quality = avatar.Quality;
+
+ LevelCurrent = LevelMin;
+ LevelTarget = LevelMax;
+ }
+
+ ///
+ /// 构造一个新的可计算角色
+ ///
+ /// 角色
+ public CalculableAvatar(Binding.AvatarProperty.Avatar avatar)
+ {
+ AvatarId = avatar.Id;
+ LevelMin = avatar.LevelNumber;
+ LevelMax = 90; // hard coded 90
+ Skills = avatar.Skills.Select(s => s.ToCalculable()).ToList();
+ Name = avatar.Name;
+ Icon = avatar.Icon;
+ Quality = avatar.Quality;
+
LevelCurrent = LevelMin;
LevelTarget = LevelMax;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableSkill.cs b/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableSkill.cs
similarity index 72%
rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableSkill.cs
rename to src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableSkill.cs
index bba49bb4..23bc7e1c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableSkill.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableSkill.cs
@@ -3,10 +3,11 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
-namespace Snap.Hutao.Model.Metadata.Avatar;
+namespace Snap.Hutao.Model.Calculable;
///
/// 可计算的技能
@@ -28,6 +29,24 @@ internal class CalculableSkill : ObservableObject, ICalculableSkill
Name = skill.Name;
Icon = SkillIconConverter.IconNameToUri(skill.Icon);
Quality = ItemQuality.QUALITY_NONE;
+
+ LevelCurrent = LevelMin;
+ LevelTarget = LevelMax;
+ }
+
+ ///
+ /// 构造一个新的可计算的技能
+ ///
+ /// 技能
+ public CalculableSkill(Binding.AvatarProperty.Skill skill)
+ {
+ GruopId = skill.GroupId;
+ LevelMin = skill.LevelNumber;
+ LevelMax = 10; // hard coded 10 here
+ Name = skill.Name;
+ Icon = skill.Icon;
+ Quality = ItemQuality.QUALITY_NONE;
+
LevelCurrent = LevelMin;
LevelTarget = LevelMax;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/CalculableWeapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableWeapon.cs
similarity index 72%
rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/CalculableWeapon.cs
rename to src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableWeapon.cs
index c0eaa0d3..67f05063 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/CalculableWeapon.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Calculable/CalculableWeapon.cs
@@ -7,7 +7,7 @@ using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
-namespace Snap.Hutao.Model.Metadata.Weapon;
+namespace Snap.Hutao.Model.Calculable;
///
/// 可计算武器
@@ -21,7 +21,7 @@ public class CalculableWeapon : ObservableObject, ICalculableWeapon
/// 构造一个新的可计算武器
///
/// 武器
- public CalculableWeapon(Weapon weapon)
+ public CalculableWeapon(Metadata.Weapon.Weapon weapon)
{
WeaponId = weapon.Id;
LevelMin = 1;
@@ -29,6 +29,24 @@ public class CalculableWeapon : ObservableObject, ICalculableWeapon
Name = weapon.Name;
Icon = EquipIconConverter.IconNameToUri(weapon.Icon);
Quality = weapon.RankLevel;
+
+ LevelCurrent = LevelMin;
+ LevelTarget = LevelMax;
+ }
+
+ ///
+ /// 构造一个新的可计算武器
+ ///
+ /// 武器
+ public CalculableWeapon(Binding.AvatarProperty.Weapon weapon)
+ {
+ WeaponId = weapon.Id;
+ LevelMin = weapon.LevelNumber;
+ LevelMax = (int)weapon.Quality >= 3 ? 90 : 70;
+ Name = weapon.Name;
+ Icon = weapon.Icon;
+ Quality = weapon.Quality;
+
LevelCurrent = LevelMin;
LevelTarget = LevelMax;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ImmutableIntrinsics.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ImmutableIntrinsics.cs
new file mode 100644
index 00000000..9c5b93b2
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ImmutableIntrinsics.cs
@@ -0,0 +1,38 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Extension;
+using System.Collections.Immutable;
+
+namespace Snap.Hutao.Model.Intrinsic;
+
+///
+/// 不可变的原生枚举
+///
+public static class ImmutableIntrinsics
+{
+ ///
+ /// 所属地区
+ ///
+ public static readonly ImmutableList AssociationTypes = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
+
+ ///
+ /// 武器类型
+ ///
+ public static readonly ImmutableList WeaponTypes = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
+
+ ///
+ /// 物品类型
+ ///
+ public static readonly ImmutableList ItemQualities = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
+
+ ///
+ /// 身材类型
+ ///
+ public static readonly ImmutableList BodyTypes = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
+
+ ///
+ /// 战斗属性
+ ///
+ public static readonly ImmutableList FightProperties = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs
index 9d533f91..a0e594d0 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs
@@ -94,4 +94,4 @@ public enum WeaponType
///
[Description("单手剑")]
WEAPON_POLE = 13,
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs
index 853b8ad4..2396d89b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs
@@ -31,5 +31,5 @@ public class AchievementGoal
///
/// 图标
///
- public string? Icon { get; set; }
+ public string Icon { get; set; } = default!;
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
index a950592c..d7997f5c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
@@ -4,6 +4,7 @@
using Snap.Hutao.Model.Binding.Gacha;
using Snap.Hutao.Model.Binding.Gacha.Abstraction;
using Snap.Hutao.Model.Binding.Hutao;
+using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.Implementation.cs
index 3f00c30b..0acfae5f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.Implementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.Implementation.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
namespace Snap.Hutao.Model.Metadata.Avatar;
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/AvatarIds.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/AvatarIds.cs
index afa049d5..70d2f339 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/AvatarIds.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/AvatarIds.cs
@@ -80,6 +80,8 @@ public static class AvatarIds
public static readonly AvatarId Layla = 10000074;
public static readonly AvatarId Wanderer = 10000075;
public static readonly AvatarId Faruzan = 10000076;
+ public static readonly AvatarId Yaoyao = 10000077;
+ public static readonly AvatarId Alhaitham = 10000078;
///
/// 检查该角色是否为主角
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AchievementIconConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AchievementIconConverter.cs
index 48b6b4db..a9e564a4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AchievementIconConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AchievementIconConverter.cs
@@ -12,9 +12,19 @@ internal class AchievementIconConverter : ValueConverterBase
{
private const string BaseUrl = "https://static.snapgenshin.com/AchievementIcon/{0}.png";
+ ///
+ /// 名称转Uri
+ ///
+ /// 名称
+ /// 链接
+ public static Uri IconNameToUri(string name)
+ {
+ return new Uri(string.Format(BaseUrl, name));
+ }
+
///
public override Uri Convert(string from)
{
- return new Uri(string.Format(BaseUrl, from));
+ return IconNameToUri(from);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/ElementNameIconConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/ElementNameIconConverter.cs
index f6f024ab..f4988703 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/ElementNameIconConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/ElementNameIconConverter.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Control;
-using Snap.Hutao.Control.Image;
+using Snap.Hutao.Control.Media;
using Snap.Hutao.Model.Intrinsic;
namespace Snap.Hutao.Model.Metadata.Converter;
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs
index fe6f5ff6..7d04b39f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs
@@ -4,6 +4,7 @@
using Snap.Hutao.Model.Binding.Gacha;
using Snap.Hutao.Model.Binding.Gacha.Abstraction;
using Snap.Hutao.Model.Binding.Hutao;
+using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Metadata.Converter;
diff --git a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
index 0c64a13b..1a601fd0 100644
--- a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
+++ b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
@@ -4,6 +4,8 @@ WM_GETMINMAXINFO
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
+STDAPI
+
// Type definition
CWMO_FLAGS
HRESULT
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
index 94f590ae..2e8b4b81 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -12,7 +12,7 @@
+ Version="1.2.14.0" />
胡桃
diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs
index b948b0ab..5d4047c2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Program.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Program.cs
@@ -66,9 +66,7 @@ public static partial class Program
ServiceProvider services = new ServiceCollection()
// Microsoft extension
- .AddLogging(builder => builder
- .AddDebug()
- .AddDatabase())
+ .AddLogging(builder => builder.AddDebug().AddDatabase())
.AddMemoryCache()
// Hutao extensions
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
index 1d43218b..98a2c640 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
@@ -9,7 +9,7 @@ using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Model.InterChange.Achievement;
using System.Collections.ObjectModel;
-using BindingAchievement = Snap.Hutao.Model.Binding.Achievement;
+using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
using EntityArchive = Snap.Hutao.Model.Entity.AchievementArchive;
using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementService.cs
index 8b69a8b5..a2bae36f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/IAchievementService.cs
@@ -4,7 +4,7 @@
using Snap.Hutao.Core.Threading.CodeAnalysis;
using Snap.Hutao.Model.InterChange.Achievement;
using System.Collections.ObjectModel;
-using BindingAchievement = Snap.Hutao.Model.Binding.Achievement;
+using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
using EntityArchive = Snap.Hutao.Model.Entity.AchievementArchive;
using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/AffixWeight.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/AffixWeight.cs
index 76cc700f..c760775a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/AffixWeight.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/AffixWeight.cs
@@ -15,28 +15,38 @@ internal class AffixWeight : Dictionary
/// 构造一个新的词条权重
///
/// 角色Id
- /// 大生命
- /// 大攻击
- /// 大防御
- /// 暴击率
- /// 暴击伤害
- /// 元素精通
- /// 充能效率
- /// 治疗加成
+ /// 大生命
+ /// 大攻击
+ /// 大防御
+ /// 暴击率
+ /// 暴击伤害
+ /// 元素精通
+ /// 充能效率
+ /// 治疗加成
/// 名称
- public AffixWeight(int avatarId, double hp, double atk, double def, double cr, double ch, double em, double ce, double ha, string name = "通用")
+ public AffixWeight(
+ int avatarId,
+ double hpPercent,
+ double attackPercenr,
+ double defensePercent,
+ double critical,
+ double criticalHurt,
+ double elementMastery,
+ double chargeEfficiency,
+ double healAdd,
+ string name = "通用")
{
AvatarId = avatarId;
Name = name;
- this[FightProperty.FIGHT_PROP_HP_PERCENT] = hp;
- this[FightProperty.FIGHT_PROP_ATTACK_PERCENT] = atk;
- this[FightProperty.FIGHT_PROP_DEFENSE_PERCENT] = def;
- this[FightProperty.FIGHT_PROP_CRITICAL] = cr;
- this[FightProperty.FIGHT_PROP_CRITICAL_HURT] = ch;
- this[FightProperty.FIGHT_PROP_ELEMENT_MASTERY] = em;
- this[FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY] = ce;
- this[FightProperty.FIGHT_PROP_HEAL_ADD] = ha;
+ this[FightProperty.FIGHT_PROP_HP_PERCENT] = hpPercent;
+ this[FightProperty.FIGHT_PROP_ATTACK_PERCENT] = attackPercenr;
+ this[FightProperty.FIGHT_PROP_DEFENSE_PERCENT] = defensePercent;
+ this[FightProperty.FIGHT_PROP_CRITICAL] = critical;
+ this[FightProperty.FIGHT_PROP_CRITICAL_HURT] = criticalHurt;
+ this[FightProperty.FIGHT_PROP_ELEMENT_MASTERY] = elementMastery;
+ this[FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY] = chargeEfficiency;
+ this[FightProperty.FIGHT_PROP_HEAL_ADD] = healAdd;
}
///
@@ -48,4 +58,92 @@ internal class AffixWeight : Dictionary
/// 名称
///
public string Name { get; }
+
+ ///
+ /// 风元素伤害加成
+ ///
+ /// 值
+ /// 链式调用对象
+ public AffixWeight Anemo(double value = 100)
+ {
+ this[FightProperty.FIGHT_PROP_WIND_ADD_HURT] = value;
+ return this;
+ }
+
+ ///
+ /// 冰元素伤害加成
+ ///
+ /// 值
+ /// 链式调用对象
+ public AffixWeight Cryo(double value = 100)
+ {
+ this[FightProperty.FIGHT_PROP_ICE_ADD_HURT] = value;
+ return this;
+ }
+
+ ///
+ /// 草元素伤害加成
+ ///
+ /// 值
+ /// 链式调用对象
+ public AffixWeight Dendro(double value = 100)
+ {
+ this[FightProperty.FIGHT_PROP_GRASS_ADD_HURT] = value;
+ return this;
+ }
+
+ ///
+ /// 雷元素伤害加成
+ ///
+ /// 值
+ /// 链式调用对象
+ public AffixWeight Electro(double value = 100)
+ {
+ this[FightProperty.FIGHT_PROP_ELEC_ADD_HURT] = value;
+ return this;
+ }
+
+ ///
+ /// 岩元素伤害加成
+ ///
+ /// 值
+ /// 链式调用对象
+ public AffixWeight Geo(double value = 100)
+ {
+ this[FightProperty.FIGHT_PROP_ROCK_ADD_HURT] = value;
+ return this;
+ }
+
+ ///
+ /// 水元素伤害加成
+ ///
+ /// 值
+ /// 链式调用对象
+ public AffixWeight Hydro(double value = 100)
+ {
+ this[FightProperty.FIGHT_PROP_WATER_ADD_HURT] = value;
+ return this;
+ }
+
+ ///
+ /// 火元素伤害加成
+ ///
+ /// 值
+ /// 链式调用对象
+ public AffixWeight Pyro(double value = 100)
+ {
+ this[FightProperty.FIGHT_PROP_FIRE_ADD_HURT] = value;
+ return this;
+ }
+
+ ///
+ /// 物理伤害伤害加成
+ ///
+ /// 值
+ /// 链式调用对象
+ public AffixWeight Phyiscal(double value = 100)
+ {
+ this[FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT] = value;
+ return this;
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ReliquaryWeightConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ReliquaryWeightConfiguration.cs
index de99489b..ce96c436 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ReliquaryWeightConfiguration.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ReliquaryWeightConfiguration.cs
@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata;
namespace Snap.Hutao.Service.AvatarInfo.Factory;
@@ -14,74 +13,74 @@ internal static partial class ReliquaryWeightConfiguration
///
/// 默认
///
- public static readonly AffixWeight Default = new(0, 100, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } };
+ public static readonly AffixWeight Default = new(0, 100, 75, 0, 100, 100, 0, 55, 0);
///
/// 词条权重
///
public static readonly List AffixWeights = new()
{
- new(AvatarIds.Ayaka, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
- new(AvatarIds.Qin, 0, 75, 0, 100, 100, 0, 55, 100) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
- new(AvatarIds.Lisa, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
- new(AvatarIds.Barbara, 100, 50, 0, 50, 50, 0, 55, 100) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 80 } },
- new(AvatarIds.Barbara, 50, 75, 0, 100, 100, 0, 55, 100, "暴力奶妈") { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
- new(AvatarIds.Kaeya, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
- new(AvatarIds.Diluc, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
- new(AvatarIds.Razor, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 50 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 100 } },
- new(AvatarIds.Ambor, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
- new(AvatarIds.Venti, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
- new(AvatarIds.Xiangling, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
- new(AvatarIds.Beidou, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
- new(AvatarIds.Xingqiu, 0, 75, 0, 100, 100, 0, 75, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Xiao, 0, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
- new(AvatarIds.Ningguang, 0, 75, 0, 100, 100, 0, 30, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 } },
- new(AvatarIds.Klee, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
- new(AvatarIds.Zhongli, 80, 75, 0, 100, 100, 0, 55, 0, "武神钟离") { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 50 } },
- new(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 55, 0, "血牛钟离") { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 75 } },
- new(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 75, 0, "血牛钟离(2命+)") { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 75 } },
- new(AvatarIds.Fischl, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
- new(AvatarIds.Bennett, 100, 50, 0, 100, 100, 0, 55, 100) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 70 } },
- new(AvatarIds.Tartaglia, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Noel, 0, 50, 90, 100, 100, 0, 70, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 } },
- new(AvatarIds.Qiqi, 0, 100, 0, 100, 100, 0, 55, 100) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 60 } },
- new(AvatarIds.Chongyun, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
- new(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 75, 0, 0, "融化流") { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
- new(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 0, 55, 0, "永冻流") { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
- new(AvatarIds.Albedo, 0, 0, 100, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 } },
- new(AvatarIds.Diona, 100, 50, 0, 50, 50, 0, 90, 100) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
- new(AvatarIds.Mona, 0, 75, 0, 100, 100, 75, 75, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Keqing, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 100 } },
- new(AvatarIds.Sucrose, 0, 75, 0, 100, 100, 100, 55, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 40 } },
- new(AvatarIds.Xinyan, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 50 } },
- new(AvatarIds.Rosaria, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 70 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 80 } },
- new(AvatarIds.Hutao, 80, 50, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
- new(AvatarIds.Kazuha, 0, 75, 0, 100, 100, 100, 55, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
- new(AvatarIds.Feiyan, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
- new(AvatarIds.Yoimiya, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
- new(AvatarIds.Tohma, 100, 50, 0, 50, 50, 0, 90, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 75 } },
- new(AvatarIds.Eula, 0, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 40 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 100 } },
- new(AvatarIds.Shougun, 0, 75, 0, 100, 100, 0, 90, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 75 } },
- new(AvatarIds.Sayu, 0, 50, 0, 50, 50, 100, 55, 100) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 80 } },
- new(AvatarIds.Kokomi, 100, 50, 0, 0, 0, 0, 55, 100) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Gorou, 0, 50, 100, 50, 50, 0, 90, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 25 } },
- new(AvatarIds.Sara, 0, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
- new(AvatarIds.Itto, 0, 50, 100, 100, 100, 0, 30, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 } },
- new(AvatarIds.Yae, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
- new(AvatarIds.Heizou, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
- new(AvatarIds.Yelan, 80, 0, 0, 100, 100, 0, 75, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Aloy, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
- new(AvatarIds.Shenhe, 0, 100, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
- new(AvatarIds.Yunjin, 0, 0, 100, 50, 50, 0, 90, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 25 } },
- new(AvatarIds.Shinobu, 100, 50, 0, 100, 100, 75, 55, 100) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
- new(AvatarIds.Ayato, 50, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Collei, 0, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_GRASS_ADD_HURT, 100 } },
- new(AvatarIds.Dori, 100, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
- new(AvatarIds.Tighnari, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_GRASS_ADD_HURT, 100 } },
- new(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "直伤流") { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "反应流") { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Cyno, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
- new(AvatarIds.Candace, 100, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
- new(AvatarIds.Nahida, 0, 55, 0, 100, 100, 100, 55, 0) { { FightProperty.FIGHT_PROP_GRASS_ADD_HURT, 100 } },
+ new AffixWeight(AvatarIds.Ayaka, 0, 75, 0, 100, 100, 0, 0, 0).Cryo(),
+ new AffixWeight(AvatarIds.Qin, 0, 75, 0, 100, 100, 0, 55, 100).Anemo(),
+ new AffixWeight(AvatarIds.Lisa, 0, 75, 0, 100, 100, 75, 0, 0).Electro(),
+ new AffixWeight(AvatarIds.Barbara, 100, 50, 0, 50, 50, 0, 55, 100).Hydro(80),
+ new AffixWeight(AvatarIds.Barbara, 50, 75, 0, 100, 100, 0, 55, 100, "暴力奶妈").Hydro(),
+ new AffixWeight(AvatarIds.Kaeya, 0, 75, 0, 100, 100, 0, 0, 0).Cryo(),
+ new AffixWeight(AvatarIds.Diluc, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
+ new AffixWeight(AvatarIds.Razor, 0, 75, 0, 100, 100, 0, 0, 0).Electro(50).Phyiscal(),
+ new AffixWeight(AvatarIds.Ambor, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
+ new AffixWeight(AvatarIds.Venti, 0, 75, 0, 100, 100, 75, 55, 0).Anemo(),
+ new AffixWeight(AvatarIds.Xiangling, 0, 75, 0, 100, 100, 75, 55, 0).Pyro(),
+ new AffixWeight(AvatarIds.Beidou, 0, 75, 0, 100, 100, 75, 55, 0).Electro(),
+ new AffixWeight(AvatarIds.Xingqiu, 0, 75, 0, 100, 100, 0, 75, 0).Hydro(),
+ new AffixWeight(AvatarIds.Xiao, 0, 75, 0, 100, 100, 0, 55, 0).Anemo(),
+ new AffixWeight(AvatarIds.Ningguang, 0, 75, 0, 100, 100, 0, 30, 0).Geo(),
+ new AffixWeight(AvatarIds.Klee, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
+ new AffixWeight(AvatarIds.Zhongli, 80, 75, 0, 100, 100, 0, 55, 0, "武神钟离").Geo().Phyiscal(50),
+ new AffixWeight(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 55, 0, "血牛钟离").Geo(75),
+ new AffixWeight(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 75, 0, "血牛钟离(2命+)").Geo(75),
+ new AffixWeight(AvatarIds.Fischl, 0, 75, 0, 100, 100, 0, 0, 0).Electro(),
+ new AffixWeight(AvatarIds.Bennett, 100, 50, 0, 100, 100, 0, 55, 100).Pyro(70),
+ new AffixWeight(AvatarIds.Tartaglia, 0, 75, 0, 100, 100, 75, 0, 0).Hydro(),
+ new AffixWeight(AvatarIds.Noel, 0, 50, 90, 100, 100, 0, 70, 0).Geo(),
+ new AffixWeight(AvatarIds.Qiqi, 0, 100, 0, 100, 100, 0, 55, 100).Cryo(60),
+ new AffixWeight(AvatarIds.Chongyun, 0, 75, 0, 100, 100, 75, 55, 0).Cryo(),
+ new AffixWeight(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 75, 0, 0, "融化流").Cryo(),
+ new AffixWeight(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 0, 55, 0, "永冻流").Cryo(),
+ new AffixWeight(AvatarIds.Albedo, 0, 0, 100, 100, 100, 0, 0, 0).Geo(),
+ new AffixWeight(AvatarIds.Diona, 100, 50, 0, 50, 50, 0, 90, 100).Cryo(),
+ new AffixWeight(AvatarIds.Mona, 0, 75, 0, 100, 100, 75, 75, 0).Hydro(),
+ new AffixWeight(AvatarIds.Keqing, 0, 75, 0, 100, 100, 0, 0, 0).Electro().Phyiscal(),
+ new AffixWeight(AvatarIds.Sucrose, 0, 75, 0, 100, 100, 100, 55, 0).Anemo(40),
+ new AffixWeight(AvatarIds.Xinyan, 0, 75, 0, 100, 100, 0, 0, 0).Pyro(50),
+ new AffixWeight(AvatarIds.Rosaria, 0, 75, 0, 100, 100, 0, 0, 0).Cryo(70).Phyiscal(80),
+ new AffixWeight(AvatarIds.Hutao, 80, 50, 0, 100, 100, 75, 0, 0).Pyro(),
+ new AffixWeight(AvatarIds.Kazuha, 0, 75, 0, 100, 100, 100, 55, 0).Anemo(),
+ new AffixWeight(AvatarIds.Feiyan, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
+ new AffixWeight(AvatarIds.Yoimiya, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
+ new AffixWeight(AvatarIds.Tohma, 100, 50, 0, 50, 50, 0, 90, 0).Pyro(75),
+ new AffixWeight(AvatarIds.Eula, 0, 75, 0, 100, 100, 0, 55, 0).Cryo(40).Phyiscal(100),
+ new AffixWeight(AvatarIds.Shougun, 0, 75, 0, 100, 100, 0, 90, 0).Electro(75),
+ new AffixWeight(AvatarIds.Sayu, 0, 50, 0, 50, 50, 100, 55, 100).Anemo(80),
+ new AffixWeight(AvatarIds.Kokomi, 100, 50, 0, 0, 0, 0, 55, 100).Hydro(),
+ new AffixWeight(AvatarIds.Gorou, 0, 50, 100, 50, 50, 0, 90, 0).Geo(25),
+ new AffixWeight(AvatarIds.Sara, 0, 75, 0, 100, 100, 0, 55, 0).Electro(),
+ new AffixWeight(AvatarIds.Itto, 0, 50, 100, 100, 100, 0, 30, 0).Geo(),
+ new AffixWeight(AvatarIds.Yae, 0, 75, 0, 100, 100, 75, 55, 0).Electro(),
+ new AffixWeight(AvatarIds.Heizou, 0, 75, 0, 100, 100, 75, 0, 0).Anemo(),
+ new AffixWeight(AvatarIds.Yelan, 80, 0, 0, 100, 100, 0, 75, 0).Hydro(),
+ new AffixWeight(AvatarIds.Aloy, 0, 75, 0, 100, 100, 0, 0, 0).Cryo(),
+ new AffixWeight(AvatarIds.Shenhe, 0, 100, 0, 100, 100, 0, 55, 0).Cryo(),
+ new AffixWeight(AvatarIds.Yunjin, 0, 0, 100, 50, 50, 0, 90, 0).Geo(25),
+ new AffixWeight(AvatarIds.Shinobu, 100, 50, 0, 100, 100, 75, 55, 100).Electro(),
+ new AffixWeight(AvatarIds.Ayato, 50, 75, 0, 100, 100, 0, 0, 0).Hydro(),
+ new AffixWeight(AvatarIds.Collei, 0, 75, 0, 100, 100, 0, 55, 0).Dendro(),
+ new AffixWeight(AvatarIds.Dori, 100, 75, 0, 100, 100, 0, 55, 0).Electro(),
+ new AffixWeight(AvatarIds.Tighnari, 0, 75, 0, 100, 100, 75, 55, 0).Dendro(),
+ new AffixWeight(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "直伤流").Hydro(),
+ new AffixWeight(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "反应流").Hydro(),
+ new AffixWeight(AvatarIds.Cyno, 0, 75, 0, 100, 100, 75, 55, 0).Electro(),
+ new AffixWeight(AvatarIds.Candace, 100, 75, 0, 100, 100, 0, 55, 0).Hydro(),
+ new AffixWeight(AvatarIds.Nahida, 0, 55, 0, 100, 100, 100, 55, 0).Dendro(),
};
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs
index 4481cfea..a01f343a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs
@@ -5,11 +5,8 @@ using Snap.Hutao.Extension;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Converter;
-using Snap.Hutao.Model.Metadata.Reliquary;
-using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Web.Enka.Model;
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
-using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using PropertyAvatar = Snap.Hutao.Model.Binding.AvatarProperty.Avatar;
@@ -23,40 +20,17 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
///
internal class SummaryAvatarFactory
{
- private readonly Dictionary idAvatarMap;
- private readonly Dictionary idWeaponMap;
- private readonly Dictionary idRelicMainPropMap;
- private readonly Dictionary idReliquaryAffixMap;
- private readonly List reliqueryLevels;
- private readonly List reliquaries;
-
private readonly ModelAvatarInfo avatarInfo;
+ private readonly SummaryMetadataContext metadataContext;
///
/// 构造一个新的角色工厂
///
- /// 角色映射
- /// 武器映射
- /// 圣遗物主属性映射
- /// 圣遗物副词条映射
- /// 圣遗物主属性等级
- /// 圣遗物
+ /// 元数据上下文
/// 角色信息
- public SummaryAvatarFactory(
- Dictionary idAvatarMap,
- Dictionary idWeaponMap,
- Dictionary idRelicMainPropMap,
- Dictionary idReliquaryAffixMap,
- List reliqueryLevels,
- List reliquaries,
- ModelAvatarInfo avatarInfo)
+ public SummaryAvatarFactory(SummaryMetadataContext metadataContext, ModelAvatarInfo avatarInfo)
{
- this.idAvatarMap = idAvatarMap;
- this.idWeaponMap = idWeaponMap;
- this.idRelicMainPropMap = idRelicMainPropMap;
- this.idReliquaryAffixMap = idReliquaryAffixMap;
- this.reliqueryLevels = reliqueryLevels;
- this.reliquaries = reliquaries;
+ this.metadataContext = metadataContext;
this.avatarInfo = avatarInfo;
}
@@ -67,10 +41,11 @@ internal class SummaryAvatarFactory
public PropertyAvatar CreateAvatar()
{
ReliquaryAndWeapon reliquaryAndWeapon = ProcessEquip(avatarInfo.EquipList);
- MetadataAvatar avatar = idAvatarMap[avatarInfo.AvatarId];
+ MetadataAvatar avatar = metadataContext.IdAvatarMap[avatarInfo.AvatarId];
return new()
{
+ Id = avatar.Id,
Name = avatar.Name,
Icon = AvatarIconConverter.IconNameToUri(avatar.Icon),
SideIcon = AvatarIconConverter.IconNameToUri(avatar.SideIcon),
@@ -78,6 +53,7 @@ internal class SummaryAvatarFactory
Quality = avatar.Quality,
Element = ElementNameIconConverter.ElementNameToElementType(avatar.FetterInfo.VisionBefore),
Level = $"Lv.{avatarInfo.PropMap[PlayerProperty.PROP_LEVEL].Value}",
+ LevelNumber = int.Parse(avatarInfo.PropMap[PlayerProperty.PROP_LEVEL].Value ?? string.Empty),
FetterLevel = avatarInfo.FetterInfo.ExpLevel,
Weapon = reliquaryAndWeapon.Weapon,
Reliquaries = reliquaryAndWeapon.Reliquaries,
@@ -99,7 +75,7 @@ internal class SummaryAvatarFactory
switch (equip.Flat.ItemType)
{
case ItemType.ITEM_RELIQUARY:
- SummaryReliquaryFactory summaryReliquaryFactory = new(idReliquaryAffixMap, idRelicMainPropMap, reliqueryLevels, reliquaries, avatarInfo, equip);
+ SummaryReliquaryFactory summaryReliquaryFactory = new(metadataContext, avatarInfo, equip);
reliquaryList.Add(summaryReliquaryFactory.CreateReliquary());
break;
case ItemType.ITEM_WEAPON:
@@ -113,7 +89,7 @@ internal class SummaryAvatarFactory
private PropertyWeapon CreateWeapon(Equip equip)
{
- MetadataWeapon weapon = idWeaponMap[equip.ItemId];
+ MetadataWeapon weapon = metadataContext.IdWeaponMap[equip.ItemId];
// AffixMap can be empty when it's a white weapon.
KeyValuePair? idLevel = equip.Weapon!.AffixMap?.Single();
@@ -146,6 +122,8 @@ internal class SummaryAvatarFactory
MainProperty = mainStat == null ? default! : new(mainStat.AppendPropId.GetDescription(), mainStat.StatValue.ToString()),
// Weapon
+ Id = weapon.Id,
+ LevelNumber = equip.Weapon!.Level,
SubProperty = subProperty,
AffixLevel = $"精炼{affixLevel + 1}",
AffixName = weapon.Affix?.Name ?? string.Empty,
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs
index 03177b79..8cfa7896 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs
@@ -2,13 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Binding.AvatarProperty;
-using Snap.Hutao.Model.Intrinsic;
-using Snap.Hutao.Model.Metadata.Reliquary;
-using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Metadata;
-using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
-using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
-using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using ModelPlayerInfo = Snap.Hutao.Web.Enka.Model.PlayerInfo;
@@ -34,15 +28,17 @@ internal class SummaryFactory : ISummaryFactory
///
public async Task CreateAsync(ModelPlayerInfo playerInfo, IEnumerable avatarInfos, CancellationToken token)
{
- Dictionary idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
- Dictionary idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
- Dictionary idRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false);
- Dictionary idReliquaryAffixMap = await metadataService.GetIdReliquaryAffixMapAsync(token).ConfigureAwait(false);
+ SummaryMetadataContext metadataContext = new()
+ {
+ IdAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false),
+ IdWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false),
+ IdRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false),
+ IdReliquaryAffixMap = await metadataService.GetIdReliquaryAffixMapAsync(token).ConfigureAwait(false),
+ ReliqueryLevels = await metadataService.GetReliquaryLevelsAsync(token).ConfigureAwait(false),
+ Reliquaries = await metadataService.GetReliquariesAsync(token).ConfigureAwait(false),
+ };
- List reliqueryLevels = await metadataService.GetReliquaryLevelsAsync(token).ConfigureAwait(false);
- List reliquaries = await metadataService.GetReliquariesAsync(token).ConfigureAwait(false);
-
- SummaryFactoryImplementation inner = new(idAvatarMap, idWeaponMap, idRelicMainPropMap, idReliquaryAffixMap, reliqueryLevels, reliquaries);
+ SummaryFactoryImplementation inner = new(metadataContext);
return inner.Create(playerInfo, avatarInfos);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs
index dc604b71..20428588 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs
@@ -2,13 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Binding.AvatarProperty;
-using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata;
-using Snap.Hutao.Model.Metadata.Reliquary;
-using Snap.Hutao.Model.Primitive;
-using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
-using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
-using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using ModelPlayerInfo = Snap.Hutao.Web.Enka.Model.PlayerInfo;
@@ -19,36 +13,15 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
///
internal class SummaryFactoryImplementation
{
- private readonly Dictionary idAvatarMap;
- private readonly Dictionary idWeaponMap;
- private readonly Dictionary idRelicMainPropMap;
- private readonly Dictionary idReliquaryAffixMap;
- private readonly List reliqueryLevels;
- private readonly List reliquaries;
+ private readonly SummaryMetadataContext metadataContext;
///
/// 装配一个工厂实现
///
- /// 角色映射
- /// 武器映射
- /// 圣遗物主属性映射
- /// 圣遗物副词条映射
- /// 圣遗物主属性等级
- /// 圣遗物
- public SummaryFactoryImplementation(
- Dictionary idAvatarMap,
- Dictionary idWeaponMap,
- Dictionary idRelicMainPropMap,
- Dictionary idReliquaryAffixMap,
- List reliqueryLevels,
- List reliquaries)
+ /// 元数据上下文
+ public SummaryFactoryImplementation(SummaryMetadataContext metadataContext)
{
- this.idAvatarMap = idAvatarMap;
- this.idRelicMainPropMap = idRelicMainPropMap;
- this.idWeaponMap = idWeaponMap;
- this.reliqueryLevels = reliqueryLevels;
- this.reliquaries = reliquaries;
- this.idReliquaryAffixMap = idReliquaryAffixMap;
+ this.metadataContext = metadataContext;
}
///
@@ -64,14 +37,7 @@ internal class SummaryFactoryImplementation
Player = SummaryHelper.CreatePlayer(playerInfo),
Avatars = avatarInfos.Where(a => !AvatarIds.IsPlayer(a.AvatarId)).Select(a =>
{
- SummaryAvatarFactory summaryAvatarFactory = new(
- idAvatarMap,
- idWeaponMap,
- idRelicMainPropMap,
- idReliquaryAffixMap,
- reliqueryLevels,
- reliquaries,
- a);
+ SummaryAvatarFactory summaryAvatarFactory = new(metadataContext, a);
return summaryAvatarFactory.CreateAvatar();
}).ToList(),
};
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs
index 2f26eb37..8d7ba8f2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs
@@ -88,7 +88,8 @@ internal static class SummaryHelper
Icon = SkillIconConverter.IconNameToUri(proudableSkill.Icon),
Description = proudableSkill.Description,
- Level = skillLevelMap[proudableSkill.Id.ToString()],
+ GroupId = proudableSkill.GroupId,
+ LevelNumber = skillLevelMap[proudableSkill.Id.ToString()],
Info = DescParamDescriptor.Convert(proudableSkill.Proud, skillExtraLeveledMap[proudableSkill.Id.ToString()]),
};
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryMetadataContext.cs
new file mode 100644
index 00000000..14f8e8d8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryMetadataContext.cs
@@ -0,0 +1,31 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Binding.AvatarProperty;
+using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata.Reliquary;
+using Snap.Hutao.Model.Primitive;
+using Snap.Hutao.Service.Metadata;
+using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
+using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
+using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
+using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
+using ModelPlayerInfo = Snap.Hutao.Web.Enka.Model.PlayerInfo;
+
+namespace Snap.Hutao.Service.AvatarInfo.Factory;
+
+[SuppressMessage("", "SA1600")]
+internal class SummaryMetadataContext
+{
+ public Dictionary IdAvatarMap { get; set; } = default!;
+
+ public Dictionary IdWeaponMap { get; set; } = default!;
+
+ public Dictionary IdRelicMainPropMap { get; set; } = default!;
+
+ public Dictionary IdReliquaryAffixMap { get; set; } = default!;
+
+ public List ReliqueryLevels { get; set; } = default!;
+
+ public List Reliquaries { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryReliquaryFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryReliquaryFactory.cs
index ad2810c7..6095da73 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryReliquaryFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryReliquaryFactory.cs
@@ -7,7 +7,6 @@ using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Annotation;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Metadata.Reliquary;
-using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Web.Enka.Model;
using System.Runtime.InteropServices;
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
@@ -22,36 +21,19 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
///
internal class SummaryReliquaryFactory
{
- private readonly Dictionary idReliquaryAffixMap;
- private readonly Dictionary idRelicMainPropMap;
- private readonly List reliqueryLevels;
- private readonly List reliquaries;
-
+ private readonly SummaryMetadataContext metadataContext;
private readonly ModelAvatarInfo avatarInfo;
private readonly Equip equip;
///
/// 构造一个新的圣遗物工厂
///
- /// 圣遗物副词条映射
- /// 圣遗物主属性映射
- /// 圣遗物主属性等级
- /// 圣遗物列表
+ /// 元数据上下文
/// 角色信息
/// 圣遗物
- public SummaryReliquaryFactory(
- Dictionary idReliquaryAffixMap,
- Dictionary idRelicMainPropMap,
- List reliqueryLevels,
- List reliquaries,
- ModelAvatarInfo avatarInfo,
- Equip equip)
+ public SummaryReliquaryFactory(SummaryMetadataContext metadataContext, ModelAvatarInfo avatarInfo, Equip equip)
{
- this.idReliquaryAffixMap = idReliquaryAffixMap;
- this.idRelicMainPropMap = idRelicMainPropMap;
- this.reliqueryLevels = reliqueryLevels;
- this.reliquaries = reliquaries;
-
+ this.metadataContext = metadataContext;
this.avatarInfo = avatarInfo;
this.equip = equip;
}
@@ -62,7 +44,7 @@ internal class SummaryReliquaryFactory
/// 圣遗物
public PropertyReliquary CreateReliquary()
{
- MetadataReliquary reliquary = reliquaries.Single(r => r.Ids.Contains(equip.ItemId));
+ MetadataReliquary reliquary = metadataContext.Reliquaries.Single(r => r.Ids.Contains(equip.ItemId));
List subProperty = equip.Reliquary!.AppendPropIdList.EmptyIfNull().Select(CreateSubProperty).ToList();
int affixCount = GetAffixCount(reliquary);
@@ -87,8 +69,8 @@ internal class SummaryReliquaryFactory
List composed = equip.Flat.ReliquarySubstats!.Select(CreateComposedSubProperty).ToList();
- ReliquaryLevel relicLevel = reliqueryLevels.Single(r => r.Level == equip.Reliquary!.Level && r.Quality == reliquary.RankLevel);
- FightProperty property = idRelicMainPropMap[equip.Reliquary.MainPropId];
+ ReliquaryLevel relicLevel = metadataContext.ReliqueryLevels.Single(r => r.Level == equip.Reliquary!.Level && r.Quality == reliquary.RankLevel);
+ FightProperty property = metadataContext.IdRelicMainPropMap[equip.Reliquary.MainPropId];
return new()
{
@@ -148,7 +130,7 @@ internal class SummaryReliquaryFactory
if (equip.Flat.EquipType is EquipType.EQUIP_SHOES or EquipType.EQUIP_RING or EquipType.EQUIP_DRESS)
{
AffixWeight weightConfig = GetAffixWeightForAvatarId();
- ReliquaryLevel maxRelicLevel = reliqueryLevels.Where(r => r.Quality == reliquary.RankLevel).MaxBy(r => r.Level)!;
+ ReliquaryLevel maxRelicLevel = metadataContext.ReliqueryLevels.Where(r => r.Quality == reliquary.RankLevel).MaxBy(r => r.Level)!;
double percent = relicLevel.Properties[property] / maxRelicLevel.Properties[property];
double baseScore = 8 * percent * weightConfig.GetValueOrDefault(property, 0);
@@ -183,7 +165,7 @@ internal class SummaryReliquaryFactory
private ReliquarySubProperty CreateSubProperty(int appendPropId)
{
- MetadataReliquaryAffix affix = idReliquaryAffixMap[appendPropId];
+ MetadataReliquaryAffix affix = metadataContext.IdReliquaryAffixMap[appendPropId];
FightProperty property = affix.Type;
double score = ScoreSubAffix(appendPropId);
@@ -192,7 +174,7 @@ internal class SummaryReliquaryFactory
private double ScoreSubAffix(int appendId)
{
- MetadataReliquaryAffix affix = idReliquaryAffixMap[appendId];
+ MetadataReliquaryAffix affix = metadataContext.IdReliquaryAffixMap[appendId];
AffixWeight weightConfig = GetAffixWeightForAvatarId();
double weight = weightConfig.GetValueOrDefault(affix.Type, 0) / 100D;
@@ -200,14 +182,14 @@ internal class SummaryReliquaryFactory
// 小字词条,转换到等效百分比计算
if (affix.Type is FightProperty.FIGHT_PROP_HP or FightProperty.FIGHT_PROP_ATTACK or FightProperty.FIGHT_PROP_DEFENSE)
{
- // 等效百分比 [ 当前小字词条 / 角色基本属性 ]
+ // 等效百分比 [ 当前小字词条 / 角色基本属性 ]
double equalPercent = affix.Value / avatarInfo.FightPropMap[affix.Type - 1];
// 获取对应百分比词条权重
weight = weightConfig.GetValueOrDefault(affix.Type + 1, 0) / 100D;
// 最大同属性百分比数值 最大同属性百分比Id 第四五位是战斗属性位
- MetadataReliquaryAffix maxPercentAffix = idReliquaryAffixMap[SummaryHelper.GetAffixMaxId(appendId + 10)];
+ MetadataReliquaryAffix maxPercentAffix = metadataContext.IdReliquaryAffixMap[SummaryHelper.GetAffixMaxId(appendId + 10)];
double equalScore = equalPercent / maxPercentAffix.Value;
return weight * equalScore * 100;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
index a4ce86c0..4f11e774 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
@@ -131,7 +131,7 @@ internal class CultivationService : ICultivationService
///
public async Task> GetCultivateEntriesAsync(
CultivateProject cultivateProject,
- List metadata,
+ List materials,
Dictionary idAvatarMap,
Dictionary idWeaponMap)
{
@@ -160,7 +160,7 @@ internal class CultivationService : ICultivationService
foreach (CultivateItem item in items)
{
- resultItems.Add(new(metadata.Single(m => m.Id == item.ItemId), item));
+ resultItems.Add(new(materials.Single(m => m.Id == item.ItemId), item));
}
Model.Binding.Gacha.Abstraction.ItemBase itemBase = entry.Type switch
@@ -209,6 +209,11 @@ internal class CultivationService : ICultivationService
///
public async Task SaveConsumptionAsync(Model.Binding.Cultivation.CultivateType type, int itemId, List items)
{
+ if (items.Count == 0)
+ {
+ return true;
+ }
+
using (IServiceScope scope = scopeFactory.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
index ed5df53c..f6e12b88 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
@@ -24,11 +24,11 @@ internal interface ICultivationService
/// 获取绑定用的养成列表
///
/// 养成计划
- /// 材料
+ /// 材料
/// Id角色映射
/// Id武器映射
/// 绑定用的养成列表
- Task> GetCultivateEntriesAsync(CultivateProject cultivateProject, List metadata, Dictionary idAvatarMap, Dictionary idWeaponMap);
+ Task> GetCultivateEntriesAsync(CultivateProject cultivateProject, List materials, Dictionary idAvatarMap, Dictionary idWeaponMap);
///
/// 获取物品列表
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
index 6c367dcc..130b29b1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
@@ -17,7 +17,6 @@ namespace Snap.Hutao.Service.DailyNote;
internal class DailyNoteNotifier
{
private readonly IServiceScopeFactory scopeFactory;
- private readonly BindingClient bindingClient;
private readonly DailyNoteEntry entry;
///
@@ -26,10 +25,9 @@ internal class DailyNoteNotifier
/// 范围工厂
/// 绑定客户端
/// 实时便笺入口
- public DailyNoteNotifier(IServiceScopeFactory scopeFactory, BindingClient bindingClient, DailyNoteEntry entry)
+ public DailyNoteNotifier(IServiceScopeFactory scopeFactory, DailyNoteEntry entry)
{
this.scopeFactory = scopeFactory;
- this.bindingClient = bindingClient;
this.entry = entry;
}
@@ -119,37 +117,39 @@ internal class DailyNoteNotifier
return;
}
- List roles = await bindingClient.GetUserGameRolesByCookieAsync(entry.User).ConfigureAwait(false);
- string attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? "未知角色";
-
- ToastContentBuilder builder = new ToastContentBuilder()
- .AddHeader("DAILYNOTE", "实时便笺提醒", "DAILYNOTE")
- .AddAttributionText(attribution)
- .AddButton(new ToastButton().SetContent("开始游戏").AddArgument("Action", "LaunchGame").AddArgument("Uid", entry.Uid))
- .AddButton(new ToastButtonDismiss("我知道了"));
-
using (IServiceScope scope = scopeFactory.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ BindingClient bindingClient = scope.ServiceProvider.GetRequiredService();
+
+ List roles = await bindingClient.GetUserGameRolesByCookieAsync(entry.User).ConfigureAwait(false);
+ string attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? "未知角色";
+
+ ToastContentBuilder builder = new ToastContentBuilder()
+ .AddHeader("DAILYNOTE", "实时便笺提醒", "DAILYNOTE")
+ .AddAttributionText(attribution)
+ .AddButton(new ToastButton().SetContent("开始游戏").AddArgument("Action", "LaunchGame").AddArgument("Uid", entry.Uid))
+ .AddButton(new ToastButtonDismiss("我知道了"));
+
if (appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, SettingEntryHelper.FalseString).GetBoolean())
{
builder.SetToastScenario(ToastScenario.Reminder);
}
- }
- if (hints.Count > 2)
- {
- builder.AddText("多个提醒项达到设定值");
- }
- else
- {
- foreach (string hint in hints)
+ if (hints.Count > 2)
{
- builder.AddText(hint);
+ builder.AddText("多个提醒项达到设定值");
+ }
+ else
+ {
+ foreach (string hint in hints)
+ {
+ builder.AddText(hint);
+ }
}
- }
- await ThreadHelper.SwitchToMainThreadAsync();
- builder.Show();
+ await ThreadHelper.SwitchToMainThreadAsync();
+ builder.Show();
+ }
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs
index aceeb2ee..79a3a8a4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs
@@ -61,9 +61,9 @@ internal class DailyNoteService : IDailyNoteService, IRecipient();
List entryList = appDbContext.DailyNotes.AsNoTracking().ToList();
entryList.ForEach(entry => { entry.UserGameRole = userService.GetUserGameRoleByUid(entry.Uid); });
- entries = new(appDbContext.DailyNotes);
+ entries = new(entryList);
}
}
@@ -96,7 +96,6 @@ internal class DailyNoteService : IDailyNoteService, IRecipient();
GameRecordClient gameRecordClient = scope.ServiceProvider.GetRequiredService();
- BindingClient bindingClient = scope.ServiceProvider.GetRequiredService();
foreach (DailyNoteEntry entry in appDbContext.DailyNotes.Include(n => n.User))
{
@@ -111,7 +110,7 @@ internal class DailyNoteService : IDailyNoteService, IRecipient> list)
{
descParamComboBox.ItemHost.ItemsSource = list;
- descParamComboBox.ItemHost.SelectedIndex = Math.Min(descParamComboBox.PreferredSelectedIndex, list.Count);
+ descParamComboBox.ItemHost.SelectedIndex = Math.Min(descParamComboBox.PreferredSelectedIndex, list.Count - 1);
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
index 73dc58f2..d9db0832 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
@@ -30,6 +30,12 @@
+
+
@@ -73,7 +79,10 @@
Label="删除当前存档"/>
-
+
@@ -114,21 +123,31 @@
SelectionMode="Single">
-
+
-
+
-
+ Width="36"
+ Height="36"
+ Source="{Binding Icon}"/>
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml
index ec4ef4f4..f404cced 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml
@@ -58,6 +58,11 @@
CommandParameter="{Binding ElementName=ImageExportPanel}"
Icon="{shcm:FontIcon Glyph=}"
Label="导出图片"/>
+
@@ -129,7 +134,7 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml
index 83a7bd29..1dddd568 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml
@@ -71,7 +71,7 @@
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
index 01d86a11..d62da498 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
@@ -249,7 +249,7 @@
Margin="0,16,0,8"
Style="{StaticResource BaseTextBlockStyle}"
Text="五星"/>
-
+
@@ -276,7 +276,7 @@
Margin="0,0,0,8"
Style="{StaticResource BaseTextBlockStyle}"
Text="四星"/>
-
+
@@ -303,7 +303,7 @@
Margin="0,0,0,8"
Style="{StaticResource BaseTextBlockStyle}"
Text="三星"/>
-
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
index 9e24f287..42681b42 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
@@ -65,6 +65,10 @@
Text="{Binding DeviceId}"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
- private static readonly SortDescription IncompletedItemsFirstSortDescription = new(nameof(Model.Binding.Achievement.IsChecked), SortDirection.Ascending);
- private static readonly SortDescription CompletionTimeSortDescription = new(nameof(Model.Binding.Achievement.Time), SortDirection.Descending);
+ private static readonly SortDescription IncompletedItemsFirstSortDescription = new(nameof(Model.Binding.Achievement.Achievement.IsChecked), SortDirection.Ascending);
+ private static readonly SortDescription CompletionTimeSortDescription = new(nameof(Model.Binding.Achievement.Achievement.Time), SortDirection.Descending);
private readonly IMetadataService metadataService;
private readonly IInfoBarService infoBarService;
@@ -52,13 +54,14 @@ internal class AchievementViewModel
private bool disposed;
private AdvancedCollectionView? achievements;
- private IList? achievementGoals;
- private AchievementGoal? selectedAchievementGoal;
+ private List? achievementGoals;
+ private Model.Binding.Achievement.AchievementGoal? selectedAchievementGoal;
private ObservableCollection? archives;
private Model.Entity.AchievementArchive? selectedArchive;
private bool isIncompletedItemsFirst = true;
private string searchText = string.Empty;
private bool isInitialized;
+ private string? finishDescription;
///
/// 构造一个新的成就视图模型
@@ -91,6 +94,7 @@ internal class AchievementViewModel
RemoveArchiveCommand = asyncRelayCommandFactory.Create(RemoveArchiveAsync);
SearchAchievementCommand = new RelayCommand(SearchAchievement);
SortIncompletedSwitchCommand = new RelayCommand(UpdateAchievementsSort);
+ RefreshFinishPercentCommand = new RelayCommand(UpdateAchievementFinishPercent);
messenger.Register(this);
}
@@ -139,7 +143,7 @@ internal class AchievementViewModel
///
/// 成就分类
///
- public IList? AchievementGoals
+ public List? AchievementGoals
{
get => achievementGoals;
set => SetProperty(ref achievementGoals, value);
@@ -148,7 +152,7 @@ internal class AchievementViewModel
///
/// 选中的成就分类
///
- public AchievementGoal? SelectedAchievementGoal
+ public Model.Binding.Achievement.AchievementGoal? SelectedAchievementGoal
{
get => selectedAchievementGoal;
set
@@ -177,6 +181,11 @@ internal class AchievementViewModel
set => SetProperty(ref isIncompletedItemsFirst, value);
}
+ ///
+ /// 完成进度描述
+ ///
+ public string? FinishDescription { get => finishDescription; set => SetProperty(ref finishDescription, value); }
+
///
/// 打开页面命令
///
@@ -217,6 +226,11 @@ internal class AchievementViewModel
///
public ICommand SortIncompletedSwitchCommand { get; }
+ ///
+ /// 刷新完成百分比命令
+ ///
+ public ICommand RefreshFinishPercentCommand { get; }
+
///
public void Receive(AchievementArchiveChangedMessage message)
{
@@ -230,7 +244,7 @@ internal class AchievementViewModel
{
if (Achievements != null && SelectedArchive != null)
{
- achievementService.SaveAchievements(SelectedArchive, (Achievements.Source as IList)!);
+ achievementService.SaveAchievements(SelectedArchive, (Achievements.Source as IList)!);
}
disposed = true;
@@ -252,7 +266,6 @@ internal class AchievementViewModel
return false;
}
- [ThreadAccess(ThreadAccessState.MainThread)]
private static Task ShowImportResultDialogAsync(string title, string message)
{
ContentDialog dialog = new()
@@ -267,7 +280,6 @@ internal class AchievementViewModel
return dialog.InitializeWithWindow(mainWindow).ShowAsync().AsTask();
}
- [ThreadAccess(ThreadAccessState.MainThread)]
private static Task ShowImportFailDialogAsync(string message)
{
ContentDialog dialog = new()
@@ -282,12 +294,11 @@ internal class AchievementViewModel
return dialog.InitializeWithWindow(mainWindow).ShowAsync().AsTask();
}
- [ThreadAccess(ThreadAccessState.MainThread)]
private async Task HandleArchiveChangeAsync(Model.Entity.AchievementArchive? oldArchieve, Model.Entity.AchievementArchive? newArchieve)
{
if (oldArchieve != null && Achievements != null)
{
- achievementService.SaveAchievements(oldArchieve, (Achievements.Source as IList)!);
+ achievementService.SaveAchievements(oldArchieve, (Achievements.Source as IList)!);
}
if (newArchieve != null)
@@ -296,7 +307,6 @@ internal class AchievementViewModel
}
}
- [ThreadAccess(ThreadAccessState.MainThread)]
private async Task OpenUIAsync()
{
bool metaInitialized = await metadataService.InitializeAsync().ConfigureAwait(false);
@@ -305,10 +315,10 @@ internal class AchievementViewModel
{
try
{
- List goals = await metadataService.GetAchievementGoalsAsync(CancellationToken).ConfigureAwait(false);
+ List goals = await metadataService.GetAchievementGoalsAsync(CancellationToken).ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
- AchievementGoals = goals.OrderBy(goal => goal.Order).ToList();
+ AchievementGoals = goals.OrderBy(goal => goal.Order).Select(goal => new Model.Binding.Achievement.AchievementGoal(goal)).ToList();
Archives = achievementService.GetArchiveCollection();
SelectedArchive = Archives.SingleOrDefault(a => a.IsSelected == true);
@@ -329,21 +339,20 @@ internal class AchievementViewModel
IsInitialized = true;
}
- [ThreadAccess(ThreadAccessState.AnyThread)]
private async Task UpdateAchievementsAsync(Model.Entity.AchievementArchive archive)
{
- List rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
- List combined = achievementService.GetAchievements(archive, rawAchievements);
+ List rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
+ List combined = achievementService.GetAchievements(archive, rawAchievements);
// Assemble achievements on the UI thread.
await ThreadHelper.SwitchToMainThreadAsync();
Achievements = new(combined, true);
+ UpdateAchievementFinishPercent();
UpdateAchievementFilter(SelectedAchievementGoal);
UpdateAchievementsSort();
}
- [ThreadAccess(ThreadAccessState.MainThread)]
private async Task AddArchiveAsync()
{
MainWindow mainWindow = Ioc.Default.GetRequiredService();
@@ -370,7 +379,6 @@ internal class AchievementViewModel
}
}
- [ThreadAccess(ThreadAccessState.MainThread)]
private async Task RemoveArchiveAsync()
{
if (Archives != null && SelectedArchive != null)
@@ -397,7 +405,6 @@ internal class AchievementViewModel
}
}
- [ThreadAccess(ThreadAccessState.MainThread)]
private async Task ExportAsUIAFToFileAsync()
{
if (SelectedArchive == null || Achievements == null)
@@ -406,7 +413,7 @@ internal class AchievementViewModel
return;
}
- achievementService.SaveAchievements(SelectedArchive, (Achievements.Source as IList)!);
+ achievementService.SaveAchievements(SelectedArchive, (Achievements.Source as IList)!);
await ThreadHelper.SwitchToMainThreadAsync();
IPickerFactory pickerFactory = Ioc.Default.GetRequiredService();
@@ -443,13 +450,13 @@ internal class AchievementViewModel
{
if (search.Length == 5 && int.TryParse(search, out int achiId))
{
- Achievements.Filter = (object o) => ((Model.Binding.Achievement)o).Inner.Id == achiId;
+ Achievements.Filter = (object o) => ((Model.Binding.Achievement.Achievement)o).Inner.Id == achiId;
}
else
{
Achievements.Filter = (object o) =>
{
- Model.Binding.Achievement achi = (Model.Binding.Achievement)o;
+ Model.Binding.Achievement.Achievement achi = (Model.Binding.Achievement.Achievement)o;
return achi.Inner.Title.Contains(search) || achi.Inner.Description.Contains(search);
};
}
@@ -457,19 +464,17 @@ internal class AchievementViewModel
}
}
- [ThreadAccess(ThreadAccessState.AnyThread)]
private async Task ImportUIAFFromClipboardAsync()
{
if (achievementService.CurrentArchive == null)
{
- // TODO: automatically create a archive.
- infoBarService.Information("必须创建一个用户才能导入成就");
+ infoBarService.Information("必须选择一个存档才能导入成就");
return;
}
if (await GetUIAFFromClipboardAsync().ConfigureAwait(false) is UIAF uiaf)
{
- await TryImportUIAFInternalAsync(achievementService.CurrentArchive, uiaf).ConfigureAwait(false);
+ await TryImportUIAFInternalAsync(achievementService.CurrentArchive!, uiaf).ConfigureAwait(false);
}
else
{
@@ -478,7 +483,6 @@ internal class AchievementViewModel
}
}
- [ThreadAccess(ThreadAccessState.MainThread)]
private async Task ImportUIAFFromFileAsync()
{
if (achievementService.CurrentArchive == null)
@@ -507,7 +511,6 @@ internal class AchievementViewModel
}
}
- [ThreadAccess(ThreadAccessState.AnyThread)]
private async Task GetUIAFFromClipboardAsync()
{
try
@@ -521,7 +524,6 @@ internal class AchievementViewModel
}
}
- [ThreadAccess(ThreadAccessState.AnyThread)]
private async Task TryImportUIAFInternalAsync(Model.Entity.AchievementArchive archive, UIAF uiaf)
{
if (uiaf.IsCurrentVersionSupported())
@@ -574,13 +576,53 @@ internal class AchievementViewModel
}
}
- private void UpdateAchievementFilter(AchievementGoal? goal)
+ private void UpdateAchievementFilter(Model.Binding.Achievement.AchievementGoal? goal)
{
if (Achievements != null)
{
Achievements.Filter = goal != null
- ? ((object o) => o is Model.Binding.Achievement achi && achi.Inner.Goal == goal.Id)
+ ? ((object o) => o is Snap.Hutao.Model.Binding.Achievement.Achievement achi && achi.Inner.Goal == goal.Id)
: null;
}
}
+
+ private void UpdateAchievementFinishPercent()
+ {
+ int finished = 0;
+ int count = 0;
+ if (Achievements != null && AchievementGoals != null)
+ {
+ Dictionary counter = AchievementGoals.ToDictionary(x => x.Id, x => new AchievementGoalAggregation(x));
+ foreach (Model.Binding.Achievement.Achievement achievement in Achievements.OfType())
+ {
+ ref AchievementGoalAggregation aggregation = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
+ aggregation.Count += 1;
+ count += 1;
+ if (achievement.IsChecked)
+ {
+ aggregation.Finished += 1;
+ finished += 1;
+ }
+ }
+
+ foreach (AchievementGoalAggregation aggregation1 in counter.Values)
+ {
+ aggregation1.AchievementGoal.UpdateFinishPercent(aggregation1.Finished, aggregation1.Count);
+ }
+
+ FinishDescription = $"{finished}/{count} - {(double)finished / count:P2}";
+ }
+ }
+
+ private struct AchievementGoalAggregation
+ {
+ public readonly Model.Binding.Achievement.AchievementGoal AchievementGoal;
+ public int Finished;
+ public int Count;
+
+ public AchievementGoalAggregation(Model.Binding.Achievement.AchievementGoal goal)
+ {
+ AchievementGoal = goal;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
index 4b4f41d0..87d52e09 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
@@ -5,22 +5,27 @@ using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging;
using Snap.Hutao.Control;
-using Snap.Hutao.Control.Image;
+using Snap.Hutao.Control.Media;
using Snap.Hutao.Core.IO.DataTransfer;
+using Snap.Hutao.Extension;
using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Binding.AvatarProperty;
+using Snap.Hutao.Model.Binding.Cultivation;
using Snap.Hutao.Model.Binding.User;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.AvatarInfo;
+using Snap.Hutao.Service.Cultivation;
using Snap.Hutao.Service.User;
+using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
-using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
using Windows.UI;
-using Windows.Win32;
-using Windows.Win32.System.WinRT;
-using WinRT;
+using CalcAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
+using CalcClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
+using CalcConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption;
+using CalcItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item;
+using CalcItemHelper = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.ItemHelper;
namespace Snap.Hutao.ViewModel;
@@ -58,6 +63,7 @@ internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
RefreshFromHoyolabGameRecordCommand = asyncRelayCommandFactory.Create(RefreshByHoyolabGameRecordAsync);
RefreshFromHoyolabCalculateCommand = asyncRelayCommandFactory.Create(RefreshByHoyolabCalculateAsync);
ExportAsImageCommand = asyncRelayCommandFactory.Create(ExportAsImageAsync);
+ CultivateCommand = asyncRelayCommandFactory.Create(CultivateAsync);
}
///
@@ -98,26 +104,10 @@ internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
///
public ICommand ExportAsImageCommand { get; }
- private static unsafe void NormalBlend(SoftwareBitmap softwareBitmap, Bgra8 tint)
- {
- using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.ReadWrite))
- {
- using (IMemoryBufferReference reference = buffer.CreateReference())
- {
- reference.As().GetBuffer(out byte* data, out uint length);
-
- for (int i = 0; i < length; i += 4)
- {
- Bgra8* pixel = (Bgra8*)(data + i);
- byte baseAlpha = pixel->A;
- pixel->B = (byte)(((pixel->B * baseAlpha) + (tint.B * (0xFF - baseAlpha))) / 0xFF);
- pixel->G = (byte)(((pixel->G * baseAlpha) + (tint.G * (0xFF - baseAlpha))) / 0xFF);
- pixel->R = (byte)(((pixel->R * baseAlpha) + (tint.R * (0xFF - baseAlpha))) / 0xFF);
- pixel->A = 0xFF;
- }
- }
- }
- }
+ ///
+ /// 养成命令
+ ///
+ public ICommand CultivateCommand { get; }
private Task OpenUIAsync()
{
@@ -205,6 +195,58 @@ internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
}
}
+ private async Task CultivateAsync(Avatar? avatar)
+ {
+ if (avatar != null)
+ {
+ IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
+ IUserService userService = Ioc.Default.GetRequiredService();
+
+ if (userService.Current != null)
+ {
+ MainWindow mainWindow = Ioc.Default.GetRequiredService();
+ (bool isOk, CalcAvatarPromotionDelta delta) = await new CultivatePromotionDeltaDialog(mainWindow, avatar.ToCalculable(), avatar.Weapon.ToCalculable())
+ .GetPromotionDeltaAsync()
+ .ConfigureAwait(false);
+
+ if (isOk)
+ {
+ CalcConsumption? consumption = await Ioc.Default
+ .GetRequiredService()
+ .ComputeAsync(userService.Current.Entity, delta)
+ .ConfigureAwait(false);
+
+ if (consumption != null)
+ {
+ List items = CalcItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
+ bool avatarSaved = await Ioc.Default
+ .GetRequiredService()
+ .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
+ .ConfigureAwait(false);
+
+ bool weaponSaved = await Ioc.Default
+ .GetRequiredService()
+ .SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull())
+ .ConfigureAwait(false);
+
+ if (avatarSaved && weaponSaved)
+ {
+ infoBarService.Success("已成功添加至当前养成计划");
+ }
+ else
+ {
+ infoBarService.Warning("请先前往养成计划页面创建计划并选中");
+ }
+ }
+ }
+ }
+ else
+ {
+ infoBarService.Warning("必须先选择一个用户与角色");
+ }
+ }
+ }
+
private async Task ExportAsImageAsync(UIElement? element)
{
if (element == null)
@@ -219,7 +261,7 @@ internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
SoftwareBitmap softwareBitmap = SoftwareBitmap.CreateCopyFromBuffer(buffer, BitmapPixelFormat.Bgra8, bitmap.PixelWidth, bitmap.PixelHeight, BitmapAlphaMode.Ignore);
Color tintColor = (Color)Ioc.Default.GetRequiredService().Resources["CompatBackgroundColor"];
Bgra8 tint = Bgra8.FromColor(tintColor);
- NormalBlend(softwareBitmap, tint);
+ softwareBitmap.NormalBlend(tint);
using (InMemoryRandomAccessStream memory = new())
{
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNoteViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNoteViewModel.cs
index 07ff3c9d..d5c965af 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNoteViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNoteViewModel.cs
@@ -93,7 +93,7 @@ internal class DailyNoteViewModel : ObservableObject, ISupportCancellation
{
refreshSecondsEntry!.SetInt32(value.Value);
appDbContext.Settings.UpdateAndSave(refreshSecondsEntry!);
- TaskSchedulerHelper.RegisterForDailyNoteRefresh(value.Value);
+ ScheduleTaskHelper.RegisterForDailyNoteRefresh(value.Value);
}
}
}
@@ -161,7 +161,7 @@ internal class DailyNoteViewModel : ObservableObject, ISupportCancellation
refreshSecondsEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteRefreshSeconds, "480");
selectedRefreshTime = refreshTimes.Single(t => t.Value == refreshSecondsEntry.GetInt32());
- TaskSchedulerHelper.RegisterForDailyNoteRefresh(480);
+ ScheduleTaskHelper.RegisterForDailyNoteRefresh(480);
OnPropertyChanged(nameof(SelectedRefreshTime));
reminderNotifyEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, false.ToString());
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs
index ab93755b..f70928dc 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Context.Database;
@@ -38,7 +39,8 @@ internal class ExperimentalFeaturesViewModel : ObservableObject
OpenCacheFolderCommand = asyncRelayCommandFactory.Create(OpenCacheFolderAsync);
OpenDataFolderCommand = asyncRelayCommandFactory.Create(OpenDataFolderAsync);
UploadSpiralAbyssRecordCommand = asyncRelayCommandFactory.Create(UploadSpiralAbyssRecordAsync);
- DeleteUsersCommand = asyncRelayCommandFactory.Create(DeleteUsersAsync);
+ DeleteUsersCommand = asyncRelayCommandFactory.Create(DangerousDeleteUsersAsync);
+ DeleteAllScheduleTasksCommand = new RelayCommand(DangerousDeleteAllScheduleTasks);
}
///
@@ -61,6 +63,11 @@ internal class ExperimentalFeaturesViewModel : ObservableObject
///
public ICommand DeleteUsersCommand { get; }
+ ///
+ /// 删除所有计划任务命令
+ ///
+ public ICommand DeleteAllScheduleTasksCommand { get; }
+
private Task OpenCacheFolderAsync()
{
return Launcher.LaunchFolderAsync(ApplicationData.Current.TemporaryFolder).AsTask();
@@ -94,7 +101,7 @@ internal class ExperimentalFeaturesViewModel : ObservableObject
}
}
- private async Task DeleteUsersAsync()
+ private async Task DangerousDeleteUsersAsync()
{
using (IServiceScope scope = Ioc.Default.CreateScope())
{
@@ -105,4 +112,17 @@ internal class ExperimentalFeaturesViewModel : ObservableObject
infoBarService.Success("清除用户数据成功,请重启胡桃");
}
}
+
+ private void DangerousDeleteAllScheduleTasks()
+ {
+ IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
+ if (Core.ScheduleTaskHelper.UnregisterAllTasks())
+ {
+ infoBarService.Success("清除任务计划成功");
+ }
+ else
+ {
+ infoBarService.Warning("清除任务计划失败");
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
index 77986802..aeb320ef 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
@@ -93,6 +93,15 @@ internal class SettingViewModel : ObservableObject
get => Core.CoreEnvironment.Version.ToString();
}
+ ///
+ /// Webview2 版本
+ ///
+ [SuppressMessage("", "CA1822")]
+ public string WebView2Version
+ {
+ get => Core.WebView2Helper.Version;
+ }
+
///
/// 设备Id
///
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
index 2e682eb3..b1c31087 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
@@ -185,11 +185,6 @@ internal class WikiAvatarViewModel : ObservableObject
private static class AvatarFilter
{
- private static readonly ImmutableList AssociationTypes = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
- private static readonly ImmutableList WeaponTypes = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
- private static readonly ImmutableList ItemQualities = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
- private static readonly ImmutableList BodyTypes = Enum.GetValues().Select(e => e.GetDescriptionOrNull()).OfType().ToImmutableList();
-
public static Predicate
public ICommand CultivateCommand { get; }
+ ///
+ /// 筛选命令
+ ///
+ public ICommand FilterCommand { get; }
+
private async Task OpenUIAsync()
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
@@ -152,4 +168,67 @@ internal class WikiWeaponViewModel : ObservableObject, ISupportCancellation
}
}
}
+
+ private void ApplyFilter(string? input)
+ {
+ if (Weapons != null)
+ {
+ if (!string.IsNullOrWhiteSpace(input))
+ {
+ Weapons.Filter = WeaponFilter.Compile(input);
+
+ if (!Weapons.Contains(Selected))
+ {
+ Weapons.MoveCurrentToFirst();
+ }
+ }
+ else
+ {
+ Weapons.Filter = null!;
+ }
+ }
+ }
+
+ private static class WeaponFilter
+ {
+ public static Predicate