From ff146b4a2f7e8a192a2851aac0f0d411aa437d9b Mon Sep 17 00:00:00 2001
From: DismissedLight <1686188646@qq.com>
Date: Thu, 15 Dec 2022 16:14:16 +0800
Subject: [PATCH] cultivation
---
src/Snap.Hutao/Snap.Hutao/App.xaml | 8 +
src/Snap.Hutao/Snap.Hutao/App.xaml.cs | 39 +-
.../Context/Database/AppDbContext.cs | 18 +-
...dsContentIntoTitleBarWorkaroundBehavior.cs | 43 ++
.../InvokeCommandOnUnloadedBehavior.cs | 3 +-
.../Snap.Hutao/Core/Caching/ImageCache.cs | 4 +
.../Core/Database/DbSetExtension.cs | 27 -
.../Snap.Hutao/Core/JumpListHelper.cs | 2 +-
.../Snap.Hutao/Core/LifeCycle/Activation.cs | 3 +-
.../Core/LifeCycle/AppInstanceExtension.cs | 38 ++
.../Core/Windowing/ExtendedWindow.cs | 38 +-
.../Message/FlyoutOpenCloseMessage.cs | 26 +
.../20221210111128_Inventory.Designer.cs | 512 ++++++++++++++++++
.../Migrations/20221210111128_Inventory.cs | 117 ++++
.../Migrations/AppDbContextModelSnapshot.cs | 141 ++++-
.../Snap.Hutao/Model/Binding/Achievement.cs | 14 +-
.../Binding/Cultivation/CultivateEntry.cs | 27 +
.../Binding/Cultivation/CultivateItem.cs | 34 ++
.../Binding/Cultivation/CultivateType.cs | 9 +-
.../Model/Binding/Inventory/InventoryItem.cs | 52 ++
.../InventoryReliquaryConfiguration.cs | 23 +
.../Snap.Hutao/Model/Entity/CultivateEntry.cs | 15 +-
.../Snap.Hutao/Model/Entity/CultivateItem.cs | 17 +
.../Model/Entity/CultivateProject.cs | 17 +-
.../Snap.Hutao/Model/Entity/InventoryItem.cs | 57 ++
.../Model/Entity/InventoryReliquary.cs | 52 ++
.../Model/Entity/InventoryWeapon.cs | 47 ++
.../Model/InterChange/Achievement/UIAF.cs | 2 +-
.../Model/InterChange/GachaLog/UIGF.cs | 2 +-
.../Model/InterChange/Inventory/UIIF.cs | 90 +++
.../Model/InterChange/Inventory/UIIFInfo.cs | 80 +++
.../Metadata/Abstraction/INameQuality.cs | 3 +-
.../Metadata/Avatar/Avatar.Implementation.cs | 74 +++
.../Model/Metadata/Avatar/Avatar.cs | 70 +--
.../Model/Metadata/Avatar/CalculableAvatar.cs | 67 +++
.../Model/Metadata/Avatar/CalculableSkill.cs | 58 ++
.../Avatar/ProudableSkill.Implementation.cs | 18 +
.../Model/Metadata/Avatar/ProudableSkill.cs | 4 +-
.../Model/Metadata/Avatar/SkillDepot.cs | 3 +-
.../Snap.Hutao/Model/Metadata/GachaEvent.cs | 3 +-
.../Snap.Hutao/Model/Metadata/Material.cs | 48 ++
.../Model/Metadata/Weapon/CalculableWeapon.cs | 63 +++
.../Metadata/Weapon/Weapon.Implementation.cs | 90 +++
.../Model/Metadata/Weapon/Weapon.cs | 73 +--
src/Snap.Hutao/Snap.Hutao/NativeMethods.txt | 13 +-
.../Snap.Hutao/Package.appxmanifest | 2 +-
.../Service/Achievement/AchievementService.cs | 5 +-
.../Service/Achievement/ArchiveAddResult.cs | 2 +-
.../Service/Cultivation/CultivationService.cs | 237 +++++++-
.../Cultivation/ICultivationService.cs | 71 +++
.../Service/Cultivation/ProjectAddResult.cs | 25 +
.../Service/Metadata/IMetadataService.cs | 7 +
.../MetadataService.Implementation.cs | 6 +
src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 24 +-
.../View/Control/DescParamComboBox.xaml.cs | 14 +-
.../EmptyObjectToVisibilityConverter.cs | 22 +
.../View/Dialog/CultivateProjectDialog.xaml | 24 +
.../Dialog/CultivateProjectDialog.xaml.cs | 59 ++
.../Dialog/CultivatePromotionDeltaDialog.xaml | 172 ++++++
.../CultivatePromotionDeltaDialog.xaml.cs | 88 +++
.../View/Dialog/GameAccountNameDialog.xaml | 2 +-
.../Snap.Hutao/View/Page/AchievementPage.xaml | 11 +-
.../Snap.Hutao/View/Page/CultivationPage.xaml | 221 +++++++-
.../Snap.Hutao/View/Page/GachaLogPage.xaml | 11 +-
.../Snap.Hutao/View/Page/WikiAvatarPage.xaml | 12 +-
.../Snap.Hutao/View/Page/WikiWeaponPage.xaml | 5 +-
.../Snap.Hutao/View/TitleView.xaml.cs | 4 +
.../ViewModel/CultivationViewModel.cs | 197 ++++++-
.../ViewModel/LaunchGameViewModel.cs | 25 +-
.../ViewModel/WikiAvatarViewModel.cs | 61 ++-
.../Snap.Hutao/Web/Hoyolab/Cookie.cs | 4 +-
.../Hoyolab/HoyolabHttpClientExtensions.cs | 88 +++
.../Takumi/Event/Calculate/CalculateClient.cs | 2 +-
.../Event/Calculate/ElementAttributeId.cs | 5 +
.../Takumi/Event/Calculate/ICalculable.cs | 37 ++
.../Event/Calculate/ICalculableAvatar.cs | 32 ++
.../Event/Calculate/ICalculableSkill.cs | 25 +
.../Event/Calculate/ICalculableSource.cs | 18 +
.../Event/Calculate/ICalculableWeapon.cs | 27 +
.../Hoyolab/Takumi/Event/Calculate/Item.cs | 2 +-
.../Takumi/Event/Calculate/ItemHelper.cs | 51 ++
.../Web/{Hoyolab => }/HttpClientExtensions.cs | 97 +---
.../Snap.Hutao/Web/Hutao/HomaClient.cs | 12 +-
.../Snap.Hutao/Web/Hutao/HomaClient2.cs | 1 -
.../Web/Response/KnownReturnCode.cs | 5 +
85 files changed, 3489 insertions(+), 368 deletions(-)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Control/Behavior/ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Message/FlyoutOpenCloseMessage.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Migrations/20221210111128_Inventory.Designer.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Migrations/20221210111128_Inventory.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateEntry.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Binding/Inventory/InventoryItem.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/InventoryReliquaryConfiguration.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryReliquary.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryWeapon.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFInfo.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableAvatar.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableSkill.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.Implementation.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/CalculableWeapon.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ProjectAddResult.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Converter/EmptyObjectToVisibilityConverter.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivateProjectDialog.xaml
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivateProjectDialog.xaml.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyolabHttpClientExtensions.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculable.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableAvatar.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableSkill.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableSource.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableWeapon.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ItemHelper.cs
rename src/Snap.Hutao/Snap.Hutao/Web/{Hoyolab => }/HttpClientExtensions.cs (56%)
diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml b/src/Snap.Hutao/Snap.Hutao/App.xaml
index 16daa88f..0a40b759 100644
--- a/src/Snap.Hutao/Snap.Hutao/App.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/App.xaml
@@ -59,6 +59,7 @@
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
index 57ffc0b8..6ab7d94f 100644
--- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
@@ -36,28 +36,35 @@ public partial class App : Application
}
///
- [SuppressMessage("", "VSTHRD100")]
- protected override async void OnLaunched(LaunchActivatedEventArgs args)
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
{
- AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
- AppInstance firstInstance = AppInstance.FindOrRegisterForKey("main");
-
- if (firstInstance.IsCurrent)
+ try
{
- // manually invoke
- Activation.Activate(firstInstance, activatedEventArgs);
- firstInstance.Activated += Activation.Activate;
- ToastNotificationManagerCompat.OnActivated += Activation.NotificationActivate;
+ AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
+ AppInstance firstInstance = AppInstance.FindOrRegisterForKey("main");
- logger.LogInformation(EventIds.CommonLog, "Snap Hutao | {name} : {version}", CoreEnvironment.FamilyName, CoreEnvironment.Version);
- logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.TemporaryFolder.Path);
+ if (firstInstance.IsCurrent)
+ {
+ // manually invoke
+ Activation.Activate(firstInstance, activatedEventArgs);
+ firstInstance.Activated += Activation.Activate;
+ ToastNotificationManagerCompat.OnActivated += Activation.NotificationActivate;
- JumpListHelper.ConfigAsync().SafeForget(logger);
+ logger.LogInformation(EventIds.CommonLog, "Snap Hutao | {name} : {version}", CoreEnvironment.FamilyName, CoreEnvironment.Version);
+ logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.TemporaryFolder.Path);
+
+ JumpListHelper.ConfigureAsync().SafeForget(logger);
+ }
+ else
+ {
+ // Redirect the activation (and args) to the "main" instance, and exit.
+ firstInstance.RedirectActivationTo(activatedEventArgs);
+ Process.GetCurrentProcess().Kill();
+ }
}
- else
+ catch (Exception)
{
- // Redirect the activation (and args) to the "main" instance, and exit.
- await firstInstance.RedirectActivationToAsync(activatedEventArgs);
+ // AppInstance.GetCurrent() calls failed
Process.GetCurrentProcess().Kill();
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
index 794b107f..c93c649e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
@@ -102,6 +102,21 @@ public sealed class AppDbContext : DbContext
///
public DbSet CultivateItems { get; set; } = default!;
+ ///
+ /// 背包内物品
+ ///
+ public DbSet InventoryItems { get; set; } = default!;
+
+ ///
+ /// 背包内武器
+ ///
+ public DbSet InventoryWeapons { get; set; } = default!;
+
+ ///
+ /// 背包内圣遗物
+ ///
+ public DbSet InventoryReliquaries { get; set; } = default!;
+
///
/// 构造一个临时的应用程序数据库上下文
///
@@ -125,6 +140,7 @@ public sealed class AppDbContext : DbContext
modelBuilder
.ApplyConfiguration(new AvatarInfoConfiguration())
.ApplyConfiguration(new UserConfiguration())
- .ApplyConfiguration(new DailyNoteEntryConfiguration());
+ .ApplyConfiguration(new DailyNoteEntryConfiguration())
+ .ApplyConfiguration(new InventoryReliquaryConfiguration());
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Behavior/ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior.cs b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior.cs
new file mode 100644
index 00000000..bb75282d
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior.cs
@@ -0,0 +1,43 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.Messaging;
+using CommunityToolkit.WinUI.UI.Behaviors;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+
+namespace Snap.Hutao.Control.Behavior;
+
+///
+/// AppTitleBar Workaround
+/// https://github.com/microsoft/microsoft-ui-xaml/issues/7756
+///
+internal class ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior : BehaviorBase
+{
+ private readonly IMessenger messenger;
+
+ ///
+ /// AppTitleBar Workaround
+ ///
+ public ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior()
+ {
+ messenger = Ioc.Default.GetRequiredService();
+ }
+
+ ///
+ protected override void OnAssociatedObjectLoaded()
+ {
+ AssociatedObject.DropDownOpened += OnDropDownOpened;
+ AssociatedObject.DropDownClosed += OnDropDownClosed;
+ }
+
+ private void OnDropDownOpened(object? sender, object e)
+ {
+ messenger.Send(new Message.FlyoutOpenCloseMessage(true));
+ }
+
+ private void OnDropDownClosed(object? sender, object e)
+ {
+ messenger.Send(new Message.FlyoutOpenCloseMessage(false));
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InvokeCommandOnUnloadedBehavior.cs b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InvokeCommandOnUnloadedBehavior.cs
index 7976e75b..0603e64e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InvokeCommandOnUnloadedBehavior.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InvokeCommandOnUnloadedBehavior.cs
@@ -25,6 +25,7 @@ internal class InvokeCommandOnUnloadedBehavior : BehaviorBase
///
protected override void OnDetaching()
{
+ // 由于卸载顺序问题,必须重写此方法才能正确触发命令
if (Command != null && Command.CanExecute(null))
{
Command.Execute(null);
@@ -32,4 +33,4 @@ internal class InvokeCommandOnUnloadedBehavior : BehaviorBase
base.OnDetaching();
}
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
index 151205b5..dfda991c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
@@ -244,6 +244,10 @@ public class ImageCache : IImageCache
logger.LogInformation("Retry after {delay}.", delay);
await Task.Delay(delay).ConfigureAwait(false);
}
+ else
+ {
+ return;
+ }
}
if (retryCount == 3)
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs
index ce0590bd..e3eb44af 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Database/DbSetExtension.cs
@@ -23,33 +23,6 @@ public static class DbSetExtension
return dbSet.GetService().Context;
}
- ///
- /// 获取或添加一个对应的实体
- ///
- /// 实体类型
- /// 数据库集
- /// 谓词
- /// 实体工厂
- /// 是否添加
- /// 实体
- public static TEntity SingleOrAdd(this DbSet dbSet, Func predicate, Func entityFactory, out bool added)
- where TEntity : class
- {
- added = false;
- TEntity? entry = dbSet.SingleOrDefault(predicate);
-
- if (entry == null)
- {
- entry = entityFactory();
- dbSet.Add(entry);
- dbSet.Context().SaveChanges();
-
- added = true;
- }
-
- return entry;
- }
-
///
/// 添加并保存
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/JumpListHelper.cs b/src/Snap.Hutao/Snap.Hutao/Core/JumpListHelper.cs
index 612ac475..9dab2b4e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/JumpListHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/JumpListHelper.cs
@@ -15,7 +15,7 @@ public static class JumpListHelper
/// 异步配置跳转列表
///
/// 任务
- public static async Task ConfigAsync()
+ public static async Task ConfigureAsync()
{
if (JumpList.IsSupported())
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
index 89ce1c48..e3f1680e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Notifications;
+using Microsoft.Extensions.Caching.Memory;
using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Extension;
using Snap.Hutao.Service.Abstraction;
@@ -192,9 +193,9 @@ internal static class Activation
private static async Task HandleLaunchGameActionAsync(string? uid = null)
{
+ Ioc.Default.GetRequiredService().Set(ViewModel.LaunchGameViewModel.DesiredUid, uid);
await ThreadHelper.SwitchToMainThreadAsync();
- // TODO auto switch to account
if (!MainWindow.IsPresent)
{
_ = Ioc.Default.GetRequiredService();
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs
new file mode 100644
index 00000000..ea719c8e
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs
@@ -0,0 +1,38 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.Windows.AppLifecycle;
+using Windows.Win32.Foundation;
+using Windows.Win32.Security;
+using Windows.Win32.System.Com;
+using static Windows.Win32.PInvoke;
+
+namespace Snap.Hutao.Core.LifeCycle;
+
+///
+/// App 实例拓展
+///
+internal static class AppInstanceExtension
+{
+ ///
+ /// 同步非阻塞重定向
+ ///
+ /// app实例
+ /// 参数
+ [SuppressMessage("", "VSTHRD002")]
+ [SuppressMessage("", "VSTHRD110")]
+ public static unsafe void RedirectActivationTo(this AppInstance appInstance, AppActivationArguments args)
+ {
+ HANDLE redirectEventHandle = CreateEvent((SECURITY_ATTRIBUTES*)null, true, false, null);
+ Task.Run(() =>
+ {
+ appInstance.RedirectActivationToAsync(args).AsTask().Wait();
+ SetEvent(redirectEventHandle);
+ });
+
+ ReadOnlySpan handles = new(in redirectEventHandle);
+
+ // non-blocking
+ CoWaitForMultipleObjects((uint)CWMO_FLAGS.CWMO_DEFAULT, INFINITE, handles, out uint _);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
index 54391558..63b3cf11 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
@@ -23,7 +23,7 @@ namespace Snap.Hutao.Core.Windowing;
///
/// 窗体类型
[SuppressMessage("", "CA1001")]
-internal sealed class ExtendedWindow : IRecipient
+internal sealed class ExtendedWindow : IRecipient, IRecipient
where TWindow : Window, IExtendedWindowSource
{
private readonly HWND handle;
@@ -82,6 +82,12 @@ internal sealed class ExtendedWindow : IRecipient
+ public void Receive(FlyoutOpenCloseMessage message)
+ {
+ UpdateDragRectangles(appWindow.TitleBar, message.IsOpen);
+ }
+
private static void UpdateTitleButtonColor(AppWindowTitleBar appTitleBar)
{
appTitleBar.ButtonBackgroundColor = Colors.Transparent;
@@ -137,7 +143,9 @@ internal sealed class ExtendedWindow : IRecipient), subClassApplied ? "succeed" : "failed");
- Ioc.Default.GetRequiredService().Register(this);
+ IMessenger messenger = Ioc.Default.GetRequiredService();
+ messenger.Register(this);
+ messenger.Register(this);
window.Closed += OnWindowClosed;
}
@@ -172,17 +180,25 @@ internal sealed class ExtendedWindow : IRecipient
+/// Flyout开启关闭消息
+///
+internal class FlyoutOpenCloseMessage
+{
+ ///
+ /// 构造一个新的Flyout开启关闭消息
+ ///
+ /// 是否为开启状态
+ public FlyoutOpenCloseMessage(bool isOpen)
+ {
+ IsOpen = isOpen;
+ }
+
+ ///
+ /// 是否为开启状态
+ ///
+ public bool IsOpen { get; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221210111128_Inventory.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221210111128_Inventory.Designer.cs
new file mode 100644
index 00000000..cb43c6b6
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221210111128_Inventory.Designer.cs
@@ -0,0 +1,512 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Snap.Hutao.Context.Database;
+
+#nullable disable
+
+namespace Snap.Hutao.Migrations
+{
+ [DbContext(typeof(AppDbContext))]
+ [Migration("20221210111128_Inventory")]
+ partial class Inventory
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "7.0.0");
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("ArchiveId")
+ .HasColumnType("TEXT");
+
+ b.Property("Current")
+ .HasColumnType("INTEGER");
+
+ b.Property("Id")
+ .HasColumnType("INTEGER");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("Time")
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ArchiveId");
+
+ b.ToTable("achievements");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("IsSelected")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.ToTable("achievement_archives");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Info")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Uid")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.ToTable("avatar_infos");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Id")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProjectId")
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("cultivate_entries");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Count")
+ .HasColumnType("INTEGER");
+
+ b.Property("EntryId")
+ .HasColumnType("TEXT");
+
+ b.Property("ItemId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("EntryId");
+
+ b.ToTable("cultivate_items");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AttachedUid")
+ .HasColumnType("TEXT");
+
+ b.Property("IsSelected")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.ToTable("cultivate_projects");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("DailyNote")
+ .HasColumnType("TEXT");
+
+ b.Property("DailyTaskNotify")
+ .HasColumnType("INTEGER");
+
+ b.Property("DailyTaskNotifySuppressed")
+ .HasColumnType("INTEGER");
+
+ b.Property("ExpeditionNotify")
+ .HasColumnType("INTEGER");
+
+ b.Property("ExpeditionNotifySuppressed")
+ .HasColumnType("INTEGER");
+
+ b.Property("HomeCoinNotifySuppressed")
+ .HasColumnType("INTEGER");
+
+ b.Property("HomeCoinNotifyThreshold")
+ .HasColumnType("INTEGER");
+
+ b.Property("ResinNotifySuppressed")
+ .HasColumnType("INTEGER");
+
+ b.Property("ResinNotifyThreshold")
+ .HasColumnType("INTEGER");
+
+ b.Property("ShowInHomeWidget")
+ .HasColumnType("INTEGER");
+
+ b.Property("TransformerNotify")
+ .HasColumnType("INTEGER");
+
+ b.Property("TransformerNotifySuppressed")
+ .HasColumnType("INTEGER");
+
+ b.Property("Uid")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("daily_notes");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("IsSelected")
+ .HasColumnType("INTEGER");
+
+ b.Property("Uid")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.ToTable("gacha_archives");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("ArchiveId")
+ .HasColumnType("TEXT");
+
+ b.Property("GachaType")
+ .HasColumnType("INTEGER");
+
+ b.Property("Id")
+ .HasColumnType("INTEGER");
+
+ b.Property("ItemId")
+ .HasColumnType("INTEGER");
+
+ b.Property("QueryType")
+ .HasColumnType("INTEGER");
+
+ b.Property("Time")
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ArchiveId");
+
+ b.ToTable("gacha_items");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AttachUid")
+ .HasColumnType("TEXT");
+
+ b.Property("MihoyoSDK")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("InnerId");
+
+ b.ToTable("game_accounts");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Count")
+ .HasColumnType("INTEGER");
+
+ b.Property("ItemId")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProjectId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("inventory_items");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AppendPropIdList")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("ItemId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Level")
+ .HasColumnType("INTEGER");
+
+ b.Property("MainPropId")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProjectId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("inventory_reliquaries");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("ItemId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Level")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProjectId")
+ .HasColumnType("TEXT");
+
+ b.Property("PromoteLevel")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("inventory_weapons");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("TEXT");
+
+ b.Property("ExpireTime")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Key");
+
+ b.ToTable("object_cache");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
+ {
+ b.Property("Key")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Key");
+
+ b.ToTable("settings");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Aid")
+ .HasColumnType("TEXT");
+
+ b.Property("CookieToken")
+ .HasColumnType("TEXT");
+
+ b.Property("IsSelected")
+ .HasColumnType("INTEGER");
+
+ b.Property("Ltoken")
+ .HasColumnType("TEXT");
+
+ b.Property("Mid")
+ .HasColumnType("TEXT");
+
+ b.Property("Stoken")
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.ToTable("users");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
+ {
+ b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
+ .WithMany()
+ .HasForeignKey("ArchiveId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Archive");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
+ {
+ b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
+ .WithMany()
+ .HasForeignKey("ProjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Project");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
+ {
+ b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
+ .WithMany()
+ .HasForeignKey("EntryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Entry");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
+ {
+ b.HasOne("Snap.Hutao.Model.Entity.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
+ {
+ b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
+ .WithMany("Items")
+ .HasForeignKey("ArchiveId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Archive");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
+ {
+ b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
+ .WithMany()
+ .HasForeignKey("ProjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Project");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
+ {
+ b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
+ .WithMany()
+ .HasForeignKey("ProjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Project");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
+ {
+ b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
+ .WithMany()
+ .HasForeignKey("ProjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Project");
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
+ {
+ b.Navigation("Items");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221210111128_Inventory.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221210111128_Inventory.cs
new file mode 100644
index 00000000..c80c927c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221210111128_Inventory.cs
@@ -0,0 +1,117 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Snap.Hutao.Migrations
+{
+ ///
+ public partial class Inventory : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "AttachedUid",
+ table: "cultivate_projects",
+ type: "TEXT",
+ nullable: true);
+
+ migrationBuilder.CreateTable(
+ name: "inventory_items",
+ columns: table => new
+ {
+ InnerId = table.Column(type: "TEXT", nullable: false),
+ ProjectId = table.Column(type: "TEXT", nullable: false),
+ ItemId = table.Column(type: "INTEGER", nullable: false),
+ Count = table.Column(type: "INTEGER", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_inventory_items", x => x.InnerId);
+ table.ForeignKey(
+ name: "FK_inventory_items_cultivate_projects_ProjectId",
+ column: x => x.ProjectId,
+ principalTable: "cultivate_projects",
+ principalColumn: "InnerId",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "inventory_reliquaries",
+ columns: table => new
+ {
+ InnerId = table.Column(type: "TEXT", nullable: false),
+ ProjectId = table.Column(type: "TEXT", nullable: false),
+ ItemId = table.Column(type: "INTEGER", nullable: false),
+ Level = table.Column(type: "INTEGER", nullable: false),
+ MainPropId = table.Column(type: "INTEGER", nullable: false),
+ AppendPropIdList = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_inventory_reliquaries", x => x.InnerId);
+ table.ForeignKey(
+ name: "FK_inventory_reliquaries_cultivate_projects_ProjectId",
+ column: x => x.ProjectId,
+ principalTable: "cultivate_projects",
+ principalColumn: "InnerId",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "inventory_weapons",
+ columns: table => new
+ {
+ InnerId = table.Column(type: "TEXT", nullable: false),
+ ProjectId = table.Column(type: "TEXT", nullable: false),
+ ItemId = table.Column(type: "INTEGER", nullable: false),
+ Level = table.Column(type: "INTEGER", nullable: false),
+ PromoteLevel = table.Column(type: "INTEGER", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_inventory_weapons", x => x.InnerId);
+ table.ForeignKey(
+ name: "FK_inventory_weapons_cultivate_projects_ProjectId",
+ column: x => x.ProjectId,
+ principalTable: "cultivate_projects",
+ principalColumn: "InnerId",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_inventory_items_ProjectId",
+ table: "inventory_items",
+ column: "ProjectId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_inventory_reliquaries_ProjectId",
+ table: "inventory_reliquaries",
+ column: "ProjectId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_inventory_weapons_ProjectId",
+ table: "inventory_weapons",
+ column: "ProjectId");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "inventory_items");
+
+ migrationBuilder.DropTable(
+ name: "inventory_reliquaries");
+
+ migrationBuilder.DropTable(
+ name: "inventory_weapons");
+
+ migrationBuilder.DropColumn(
+ name: "AttachedUid",
+ table: "cultivate_projects");
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs
index bd0a41da..d2a50f77 100644
--- a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs
@@ -42,7 +42,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ArchiveId");
- b.ToTable("achievements");
+ b.ToTable("achievements", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
@@ -60,7 +60,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
- b.ToTable("achievement_archives");
+ b.ToTable("achievement_archives", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
@@ -79,7 +79,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
- b.ToTable("avatar_infos");
+ b.ToTable("avatar_infos", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
@@ -101,7 +101,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ProjectId");
- b.ToTable("cultivate_entries");
+ b.ToTable("cultivate_entries", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
@@ -123,7 +123,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("EntryId");
- b.ToTable("cultivate_items");
+ b.ToTable("cultivate_items", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
@@ -132,6 +132,9 @@ namespace Snap.Hutao.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
+ b.Property("AttachedUid")
+ .HasColumnType("TEXT");
+
b.Property("IsSelected")
.HasColumnType("INTEGER");
@@ -141,7 +144,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
- b.ToTable("cultivate_projects");
+ b.ToTable("cultivate_projects", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
@@ -197,7 +200,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("UserId");
- b.ToTable("daily_notes");
+ b.ToTable("daily_notes", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
@@ -215,7 +218,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
- b.ToTable("gacha_archives");
+ b.ToTable("gacha_archives", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
@@ -246,7 +249,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ArchiveId");
- b.ToTable("gacha_items");
+ b.ToTable("gacha_items", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
@@ -271,7 +274,83 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
- b.ToTable("game_accounts");
+ b.ToTable("game_accounts", (string)null);
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Count")
+ .HasColumnType("INTEGER");
+
+ b.Property("ItemId")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProjectId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("inventory_items", (string)null);
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AppendPropIdList")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("ItemId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Level")
+ .HasColumnType("INTEGER");
+
+ b.Property("MainPropId")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProjectId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("inventory_reliquaries", (string)null);
+ });
+
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
+ {
+ b.Property("InnerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("ItemId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Level")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProjectId")
+ .HasColumnType("TEXT");
+
+ b.Property("PromoteLevel")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("InnerId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("inventory_weapons", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
@@ -287,7 +366,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("Key");
- b.ToTable("object_cache");
+ b.ToTable("object_cache", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
@@ -300,7 +379,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("Key");
- b.ToTable("settings");
+ b.ToTable("settings", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
@@ -329,13 +408,13 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
- b.ToTable("users");
+ b.ToTable("users", (string)null);
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
- .WithMany("Achievements")
+ .WithMany()
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@@ -346,7 +425,7 @@ namespace Snap.Hutao.Migrations
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
- .WithMany("Entries")
+ .WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@@ -357,7 +436,7 @@ namespace Snap.Hutao.Migrations
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
- .WithMany("Items")
+ .WithMany()
.HasForeignKey("EntryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@@ -387,19 +466,37 @@ namespace Snap.Hutao.Migrations
b.Navigation("Archive");
});
- modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
{
- b.Navigation("Achievements");
+ b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
+ .WithMany()
+ .HasForeignKey("ProjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Project");
});
- modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
{
- b.Navigation("Items");
+ b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
+ .WithMany()
+ .HasForeignKey("ProjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Project");
});
- modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
+ modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
{
- b.Navigation("Entries");
+ b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
+ .WithMany()
+ .HasForeignKey("ProjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Project");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement.cs
index cd60b691..c88a37f7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Achievement.cs
@@ -15,9 +15,6 @@ public class Achievement : ObservableObject
///
public const int FullProgressPlaceholder = int.MaxValue;
- private readonly Metadata.Achievement.Achievement inner;
- private readonly Entity.Achievement entity;
-
private bool isChecked;
///
@@ -27,22 +24,21 @@ public class Achievement : ObservableObject
/// 实体部分
public Achievement(Metadata.Achievement.Achievement inner, Entity.Achievement entity)
{
- this.inner = inner;
- this.entity = entity;
+ Inner = inner;
+ Entity = entity;
- // Property should only be set when it's user checking.
isChecked = (int)entity.Status >= 2;
}
///
/// 实体
///
- public Entity.Achievement Entity { get => entity; }
+ public Entity.Achievement Entity { get; }
///
/// 元数据
///
- public Metadata.Achievement.Achievement Inner { get => inner; }
+ public Metadata.Achievement.Achievement Inner { get; }
///
/// 是否选中
@@ -69,6 +65,6 @@ public class Achievement : ObservableObject
///
public string Time
{
- get => entity.Time.ToString("yyyy.MM.dd HH:mm:ss");
+ get => Entity.Time.ToString("yyyy.MM.dd HH:mm:ss");
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateEntry.cs
new file mode 100644
index 00000000..b3d2fdcb
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateEntry.cs
@@ -0,0 +1,27 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Binding.Gacha.Abstraction;
+
+namespace Snap.Hutao.Model.Binding.Cultivation;
+
+///
+/// 养成物品
+///
+public class CultivateEntry : ItemBase
+{
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 入口Id
+ ///
+ public Guid EntryId { get; set; }
+
+ ///
+ /// 实体
+ ///
+ public List Items { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs
new file mode 100644
index 00000000..353a0d6c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs
@@ -0,0 +1,34 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using Snap.Hutao.Model.Metadata;
+
+namespace Snap.Hutao.Model.Binding.Cultivation;
+
+///
+/// 养成物品
+///
+public class CultivateItem : ObservableObject
+{
+ ///
+ /// 养成物品
+ ///
+ /// 元数据
+ /// 实体
+ public CultivateItem(Material inner, Entity.CultivateItem entity)
+ {
+ Inner = inner;
+ Entity = entity;
+ }
+
+ ///
+ /// 元数据
+ ///
+ public Material Inner { get; }
+
+ ///
+ /// 实体
+ ///
+ public Entity.CultivateItem Entity { get; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateType.cs
index e86901f8..d432a991 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateType.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateType.cs
@@ -14,14 +14,9 @@ public enum CultivateType
None,
///
- /// 角色
+ /// 角色与技能
///
- Avatar,
-
- ///
- /// 角色技能
- ///
- Skill,
+ AvatarAndSkill,
///
/// 武器
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Inventory/InventoryItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Inventory/InventoryItem.cs
new file mode 100644
index 00000000..0def3ec8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Inventory/InventoryItem.cs
@@ -0,0 +1,52 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using Snap.Hutao.Model.Metadata;
+
+namespace Snap.Hutao.Model.Binding.Inventory;
+
+///
+/// 背包物品
+///
+internal class InventoryItem : ObservableObject
+{
+ private uint count;
+
+ ///
+ /// 创建一个新的背包物品
+ ///
+ /// 元数据
+ /// 实体
+ public InventoryItem(Material inner, Entity.InventoryItem entity)
+ {
+ Entity = entity;
+ Inner = inner;
+ count = entity.Count;
+ }
+
+ ///
+ /// 实体
+ ///
+ public Entity.InventoryItem Entity { get; set; }
+
+ ///
+ /// 元数据
+ ///
+ public Material Inner { get; set; }
+
+ ///
+ /// 个数
+ ///
+ public uint Count
+ {
+ get => count; set
+ {
+ if (SetProperty(ref count, value))
+ {
+ Entity.Count = value;
+ Ioc.Default.GetRequiredService().SaveInventoryItem(this);
+ }
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/InventoryReliquaryConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/InventoryReliquaryConfiguration.cs
new file mode 100644
index 00000000..90027de5
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/InventoryReliquaryConfiguration.cs
@@ -0,0 +1,23 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Snap.Hutao.Model.Entity.Configuration;
+
+///
+/// 背包圣遗物配置
+///
+internal class InventoryReliquaryConfiguration : IEntityTypeConfiguration
+{
+ ///
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.Property(e => e.AppendPropIdList)
+ .HasColumnType("TEXT")
+ .HasConversion(
+ list => string.Join(',', list),
+ text => text.Split(',', StringSplitOptions.None).Select(x => int.Parse(x)).ToList());
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateEntry.cs
index 0ba5a4c2..5203ab3d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateEntry.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateEntry.cs
@@ -42,7 +42,18 @@ public class CultivateEntry
public int Id { get; set; }
///
- /// 物品
+ /// 创建一个新的养成入口点
///
- public virtual ICollection Items { get; set; } = default!;
+ /// 类型
+ /// 主Id
+ /// 养成入口点
+ public static CultivateEntry Create(Guid projectId,CultivateType type, int id)
+ {
+ return new()
+ {
+ ProjectId = projectId,
+ Type = type,
+ Id = id,
+ };
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateItem.cs
index 5d91fae4..879fdf7b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateItem.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateItem.cs
@@ -39,4 +39,21 @@ public class CultivateItem
/// 物品个数
///
public int Count { get; set; }
+
+ ///
+ /// 创建一个新的养成物品
+ ///
+ /// 入口点 Id
+ /// 物品 Id
+ /// 个数
+ /// 养成物品
+ public static CultivateItem Create(Guid entryId, int itemId, int count)
+ {
+ return new()
+ {
+ EntryId = entryId,
+ ItemId = itemId,
+ Count = count,
+ };
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateProject.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateProject.cs
index 6cd5bfaa..b24d6fe2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateProject.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/CultivateProject.cs
@@ -31,7 +31,18 @@ public class CultivateProject : ISelectable
public string Name { get; set; } = default!;
///
- /// 入口集合
+ /// 所属的Uid
///
- public virtual ICollection Entries { get; set; } = default!;
-}
+ public string? AttachedUid { get; set; }
+
+ ///
+ /// 创建新的养成计划
+ ///
+ /// 名称
+ /// 绑定的Uid
+ /// 新的养成计划
+ public static CultivateProject Create(string name, string? attachedUid = null)
+ {
+ return new() { Name = name, AttachedUid = attachedUid };
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs
new file mode 100644
index 00000000..db94d93a
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs
@@ -0,0 +1,57 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Snap.Hutao.Model.Entity;
+
+///
+/// 背包物品
+///
+[Table("inventory_items")]
+public class InventoryItem
+{
+ ///
+ /// 内部Id
+ ///
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public Guid InnerId { get; set; }
+
+ ///
+ /// 培养计划Id
+ ///
+ public Guid ProjectId { get; set; }
+
+ ///
+ /// 所属的计划
+ ///
+ [ForeignKey(nameof(ProjectId))]
+ public CultivateProject Project { get; set; } = default!;
+
+ ///
+ /// 物品Id
+ ///
+ public int ItemId { get; set; }
+
+ ///
+ /// 个数 4294967295
+ ///
+ public uint Count { get; set; }
+
+ ///
+ /// 构造一个新的个数为0的物品
+ ///
+ /// 项目Id
+ /// 物品Id
+ /// 新的个数为0的物品
+ public static InventoryItem Create(Guid projectId, int itemId)
+ {
+ return new()
+ {
+ ProjectId = projectId,
+ ItemId = itemId,
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryReliquary.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryReliquary.cs
new file mode 100644
index 00000000..e9d6aff3
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryReliquary.cs
@@ -0,0 +1,52 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Snap.Hutao.Model.Entity;
+
+///
+/// 背包圣遗物
+///
+[Table("inventory_reliquaries")]
+public class InventoryReliquary
+{
+ ///
+ /// 内部Id
+ ///
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public Guid InnerId { get; set; }
+
+ ///
+ /// 培养计划Id
+ ///
+ public Guid ProjectId { get; set; }
+
+ ///
+ /// 所属的计划
+ ///
+ [ForeignKey(nameof(ProjectId))]
+ public CultivateProject Project { get; set; } = default!;
+
+ ///
+ /// 物品Id
+ ///
+ public int ItemId { get; set; }
+
+ ///
+ /// 等级
+ ///
+ public int Level { get; set; }
+
+ ///
+ /// 主属性
+ ///
+ public int MainPropId { get; set; }
+
+ ///
+ /// 副词条Id
+ ///
+ public List AppendPropIdList { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryWeapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryWeapon.cs
new file mode 100644
index 00000000..e382a4ca
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryWeapon.cs
@@ -0,0 +1,47 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Snap.Hutao.Model.Entity;
+
+///
+/// 背包武器
+///
+[Table("inventory_weapons")]
+public class InventoryWeapon
+{
+ ///
+ /// 内部Id
+ ///
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public Guid InnerId { get; set; }
+
+ ///
+ /// 培养计划Id
+ ///
+ public Guid ProjectId { get; set; }
+
+ ///
+ /// 所属的计划
+ ///
+ [ForeignKey(nameof(ProjectId))]
+ public CultivateProject Project { get; set; } = default!;
+
+ ///
+ /// 物品Id
+ ///
+ public int ItemId { get; set; }
+
+ ///
+ /// 等级
+ ///
+ public int Level { get; set; }
+
+ ///
+ /// 精炼等级 0-4
+ ///
+ public int PromoteLevel { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs
index f1cfadac..58aa97d2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs
@@ -5,7 +5,7 @@ namespace Snap.Hutao.Model.InterChange.Achievement;
///
/// 统一可交换成就格式
-/// https://www.snapgenshin.com/development/UIAF.html
+/// https://uigf.org/standards/UIAF.html
///
public class UIAF
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGF.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGF.cs
index cd294e6f..25b19967 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGF.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGF.cs
@@ -7,7 +7,7 @@ namespace Snap.Hutao.Model.InterChange.GachaLog;
///
/// 统一可交换祈愿格式
-/// https://www.snapgenshin.com/development/UIGF.html
+/// https://uigf.org/standards/UIGF.html
///
public class UIGF
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs
new file mode 100644
index 00000000..e447e50d
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs
@@ -0,0 +1,90 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Immutable;
+
+namespace Snap.Hutao.Model.InterChange.Inventory;
+
+///
+/// 统一可交换物品格式
+///
+internal class UIIF
+{
+ ///
+ /// 当前发行的版本
+ ///
+ public const string CurrentVersion = "v1.0";
+
+ private static readonly ImmutableList SupportedVersion = new List()
+ {
+ CurrentVersion,
+ }.ToImmutableList();
+
+ ///
+ /// 信息
+ ///
+ [JsonPropertyName("info")]
+ public UIIFInfo Info { get; set; } = default!;
+
+ ///
+ /// 列表
+ ///
+ [JsonPropertyName("list")]
+ public List List { get; set; } = default!;
+}
+
+///
+/// UIIF物品
+///
+[JsonDerivedType(typeof(UIIFReliquary))]
+[JsonDerivedType(typeof(UIIFWeapon))]
+internal class UIIFItem
+{
+ ///
+ /// 物品Id
+ ///
+ [JsonPropertyName("itemId")]
+ public int ItemId { get; set; }
+
+ ///
+ /// 物品Id
+ ///
+ [JsonPropertyName("count")]
+ public int Count { get; set; }
+}
+
+///
+/// UIIF圣遗物
+///
+internal class UIIFReliquary : UIIFItem
+{
+ ///
+ /// 物品Id
+ ///
+ [JsonPropertyName("level")]
+ public int Level { get; set; }
+
+ ///
+ /// 副属性列表
+ ///
+ [JsonPropertyName("appendPropIdList")]
+ public List AppendPropIdList { get; set; }
+}
+
+///
+/// UIIF武器
+///
+internal class UIIFWeapon : UIIFItem
+{
+ ///
+ /// 物品Id
+ ///
+ [JsonPropertyName("level")]
+ public int Level { get; set; }
+
+ ///
+ /// 精炼等级 0-4
+ ///
+ [JsonPropertyName("promoteLevel")]
+ public int PromoteLevel { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFInfo.cs
new file mode 100644
index 00000000..a4ad940b
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFInfo.cs
@@ -0,0 +1,80 @@
+using Snap.Hutao.Core;
+using Snap.Hutao.Extension;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Snap.Hutao.Model.InterChange.Inventory;
+
+///
+/// UIIF格式的信息
+///
+public class UIIFInfo
+{
+ ///
+ /// 用户Uid
+ ///
+ [JsonPropertyName("uid")]
+ public string Uid { get; set; } = default!;
+
+ ///
+ /// 语言
+ ///
+ [JsonPropertyName("lang")]
+ public string Language { get; set; } = default!;
+
+ ///
+ /// 导出的时间戳
+ ///
+ [JsonPropertyName("export_timestamp")]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
+ public long? ExportTimestamp { get; set; }
+
+ ///
+ /// 导出时间
+ ///
+ [JsonIgnore]
+ public DateTimeOffset ExportDateTime
+ {
+ get => DateTimeOffsetExtension.FromUnixTime(ExportTimestamp, DateTimeOffset.MinValue);
+ }
+
+ ///
+ /// 导出的 App 名称
+ ///
+ [JsonPropertyName("export_app")]
+ public string ExportApp { get; set; } = default!;
+
+ ///
+ /// 导出的 App 版本
+ ///
+ [JsonPropertyName("export_app_version")]
+ public string ExportAppVersion { get; set; } = default!;
+
+ ///
+ /// 使用的UIGF版本
+ ///
+ [JsonPropertyName("uiif_version")]
+ public string UIIFVersion { get; set; } = default!;
+
+ ///
+ /// 构造一个新的专用 UIGF 信息
+ ///
+ /// uid
+ /// 专用 UIGF 信息
+ public static UIIFInfo Create(string uid)
+ {
+ return new()
+ {
+ Uid = uid,
+ Language = "zh-cn",
+ ExportTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds(),
+ ExportApp = "胡桃",
+ ExportAppVersion = CoreEnvironment.Version.ToString(),
+ UIIFVersion = UIIF.CurrentVersion,
+ };
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs
index eb4e1341..7199e3d7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs
@@ -19,4 +19,5 @@ public interface INameQuality
/// 星级
///
ItemQuality Quality { get; }
-}
\ 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
new file mode 100644
index 00000000..a950592c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
@@ -0,0 +1,74 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Binding.Gacha;
+using Snap.Hutao.Model.Binding.Gacha.Abstraction;
+using Snap.Hutao.Model.Binding.Hutao;
+using Snap.Hutao.Model.Metadata.Abstraction;
+using Snap.Hutao.Model.Metadata.Converter;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 角色的接口实现部分
+///
+public partial class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQuality, ICalculableSource
+{
+ ///
+ /// [非元数据] 搭配数据
+ ///
+ [JsonIgnore]
+ public ComplexAvatarCollocation? Collocation { get; set; }
+
+ ///
+ public ICalculableAvatar ToCalculable()
+ {
+ return new CalculableAvatar(this);
+ }
+
+ ///
+ /// 转换为基础物品
+ ///
+ /// 基础物品
+ public ItemBase ToItemBase()
+ {
+ return new()
+ {
+ Name = Name,
+ Icon = AvatarIconConverter.IconNameToUri(Icon),
+ Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore),
+ Quality = Quality,
+ };
+ }
+
+ ///
+ public StatisticsItem ToStatisticsItem(int count)
+ {
+ return new()
+ {
+ Name = Name,
+ Icon = AvatarIconConverter.IconNameToUri(Icon),
+ Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore),
+ Quality = Quality,
+
+ Count = count,
+ };
+ }
+
+ ///
+ public SummaryItem ToSummaryItem(int lastPull, DateTimeOffset time, bool isUp)
+ {
+ return new()
+ {
+ Name = Name,
+ Icon = AvatarIconConverter.IconNameToUri(Icon),
+ Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore),
+ Quality = Quality,
+
+ Time = time,
+ LastPull = lastPull,
+ IsUp = isUp,
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs
index e2809a43..65b5d653 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs
@@ -2,12 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.Json.Annotation;
-using Snap.Hutao.Model.Binding.Gacha;
-using Snap.Hutao.Model.Binding.Gacha.Abstraction;
-using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Model.Intrinsic;
-using Snap.Hutao.Model.Metadata.Abstraction;
-using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Avatar;
@@ -15,7 +10,7 @@ namespace Snap.Hutao.Model.Metadata.Avatar;
///
/// 角色
///
-public class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQuality
+public partial class Avatar
{
///
/// Id
@@ -87,65 +82,4 @@ public class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQuality
/// 皮肤
///
public IEnumerable Costumes { get; set; } = default!;
-
- ///
- /// [非元数据] 搭配数据
- ///
- [JsonIgnore]
- public ComplexAvatarCollocation? Collocation { get; set; }
-
- ///
- /// 转换为基础物品
- ///
- /// 基础物品
- public ItemBase ToItemBase()
- {
- return new()
- {
- Name = Name,
- Icon = AvatarIconConverter.IconNameToUri(Icon),
- Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore),
- Quality = Quality,
- };
- }
-
- ///
- /// 转换到统计物品
- ///
- /// 个数
- /// 统计物品
- public StatisticsItem ToStatisticsItem(int count)
- {
- return new()
- {
- Name = Name,
- Icon = AvatarIconConverter.IconNameToUri(Icon),
- Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore),
- Quality = Quality,
-
- Count = count,
- };
- }
-
- ///
- /// 转换到简述统计物品
- ///
- /// 距上个五星
- /// 时间
- /// 是否为Up物品
- /// 简述统计物品
- public SummaryItem ToSummaryItem(int lastPull, DateTimeOffset time, bool isUp)
- {
- return new()
- {
- Name = Name,
- Icon = AvatarIconConverter.IconNameToUri(Icon),
- Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore),
- Quality = Quality,
-
- Time = time,
- LastPull = lastPull,
- IsUp = isUp,
- };
- }
-}
+}
\ 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/Metadata/Avatar/CalculableAvatar.cs
new file mode 100644
index 00000000..e7a45d68
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableAvatar.cs
@@ -0,0 +1,67 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using Snap.Hutao.Model.Binding.Gacha;
+using Snap.Hutao.Model.Binding.Gacha.Abstraction;
+using Snap.Hutao.Model.Binding.Hutao;
+using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata.Abstraction;
+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;
+
+///
+/// 可计算角色
+///
+internal class CalculableAvatar : ObservableObject, ICalculableAvatar
+{
+ private int levelCurrent;
+ private int levelTarget;
+
+ ///
+ /// 构造一个新的可计算角色
+ ///
+ /// 角色
+ public CalculableAvatar(Avatar avatar)
+ {
+ AvatarId = avatar.Id;
+ LevelMin = 1;
+ LevelMax = int.Parse(avatar.Property.Parameters.Last().Level);
+ Skills = avatar.SkillDepot.GetCompositeSkillsNoInherents().Select(p => p.ToCalculable()).ToList();
+ Name = avatar.Name;
+ Icon = AvatarIconConverter.IconNameToUri(avatar.Icon);
+ Quality = avatar.Quality;
+ LevelCurrent = LevelMin;
+ LevelTarget = LevelMax;
+ }
+
+ ///
+ public AvatarId AvatarId { get; }
+
+ ///
+ public int LevelMin { get; }
+
+ ///
+ public int LevelMax { get; }
+
+ ///
+ public IList Skills { get; }
+
+ ///
+ public string Name { get; }
+
+ ///
+ public Uri Icon { get; }
+
+ ///
+ public ItemQuality Quality { get; }
+
+ ///
+ public int LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
+
+ ///
+ public int LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableSkill.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableSkill.cs
new file mode 100644
index 00000000..bba49bb4
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CalculableSkill.cs
@@ -0,0 +1,58 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata.Converter;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 可计算的技能
+///
+internal class CalculableSkill : ObservableObject, ICalculableSkill
+{
+ private int levelCurrent;
+ private int levelTarget;
+
+ ///
+ /// 构造一个新的可计算的技能
+ ///
+ /// 技能
+ public CalculableSkill(ProudableSkill skill)
+ {
+ GruopId = skill.GroupId;
+ LevelMin = 1;
+ LevelMax = 10; // hard coded 10 here
+ Name = skill.Name;
+ Icon = SkillIconConverter.IconNameToUri(skill.Icon);
+ Quality = ItemQuality.QUALITY_NONE;
+ LevelCurrent = LevelMin;
+ LevelTarget = LevelMax;
+ }
+
+ ///
+ public int GruopId { get; }
+
+ ///
+ public int LevelMin { get; }
+
+ ///
+ public int LevelMax { get; }
+
+ ///
+ public string Name { get; }
+
+ ///
+ public Uri Icon { get; }
+
+ ///
+ public ItemQuality Quality { get; }
+
+ ///
+ public int LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
+
+ ///
+ public int LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..3f00c30b
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.Implementation.cs
@@ -0,0 +1,18 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 技能信息的接口实现
+///
+public partial class ProudableSkill : ICalculableSource
+{
+ ///
+ public ICalculableSkill ToCalculable()
+ {
+ return new CalculableSkill(this);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs
index 37c4366f..321da60d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Model.Metadata.Avatar;
///
/// 技能信息
///
-public class ProudableSkill : SkillBase
+public partial class ProudableSkill : SkillBase
{
///
/// 组Id
@@ -17,4 +17,4 @@ public class ProudableSkill : SkillBase
/// 提升属性
///
public DescParam Proud { get; set; } = default!;
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs
index 391bf5be..6f54bfe3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs
@@ -24,7 +24,8 @@ public class SkillDepot
public IList Inherents { get; set; } = default!;
///
- /// 全部天赋,包括固有天赋
+ /// 全部天赋,包括固有天赋
+ /// 在 Wiki 中使用
///
public IList CompositeSkills
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/GachaEvent.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/GachaEvent.cs
index 3440e005..c1f76014 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/GachaEvent.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/GachaEvent.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
namespace Snap.Hutao.Model.Metadata;
@@ -49,4 +50,4 @@ public class GachaEvent
/// 四星列表
///
public List UpPurpleList { get; set; } = default!;
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs
new file mode 100644
index 00000000..3168901a
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs
@@ -0,0 +1,48 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
+
+namespace Snap.Hutao.Model.Metadata;
+
+///
+/// 材料
+///
+public class Material
+{
+ ///
+ /// 物品Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 等级
+ ///
+ public ItemQuality RankLevel { get; set; }
+
+ ///
+ /// 物品类型
+ ///
+ public ItemType ItemType { get; set; }
+
+ ///
+ /// 图标
+ ///
+ public string Icon { get; set; } = default!;
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; } = default!;
+
+ ///
+ /// 描述
+ ///
+ public string Description { get; set; } = default!;
+
+ ///
+ /// 类型描述
+ ///
+ public string TypeDescription { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/CalculableWeapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/CalculableWeapon.cs
new file mode 100644
index 00000000..a98ea482
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/CalculableWeapon.cs
@@ -0,0 +1,63 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using Snap.Hutao.Model.Binding.Gacha;
+using Snap.Hutao.Model.Binding.Gacha.Abstraction;
+using Snap.Hutao.Model.Binding.Hutao;
+using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata.Abstraction;
+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;
+
+///
+/// 可计算武器
+///
+public class CalculableWeapon : ObservableObject, ICalculableWeapon
+{
+ private int levelCurrent;
+ private int levelTarget;
+
+ ///
+ /// 构造一个新的可计算武器
+ ///
+ /// 武器
+ public CalculableWeapon(Weapon weapon)
+ {
+ WeaponId = weapon.Id;
+ LevelMin = 1;
+ LevelMax = int.Parse(weapon.Property.Parameters.Last().Level);
+ Name = weapon.Name;
+ Icon = EquipIconConverter.IconNameToUri(weapon.Icon);
+ Quality = weapon.RankLevel;
+ LevelCurrent = LevelMin;
+ LevelTarget = LevelMax;
+ }
+
+ ///
+ public WeaponId WeaponId { get; }
+
+ ///
+ public int LevelMin { get; }
+
+ ///
+ public int LevelMax { get; }
+
+ ///
+ public string Name { get; }
+
+ ///
+ public Uri Icon { get; }
+
+ ///
+ public ItemQuality Quality { get; }
+
+ ///
+ public int LevelCurrent { get => levelCurrent; set => SetProperty(ref levelCurrent, value); }
+
+ ///
+ public int LevelTarget { get => levelTarget; set => SetProperty(ref levelTarget, value); }
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..fe6f5ff6
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs
@@ -0,0 +1,90 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Binding.Gacha;
+using Snap.Hutao.Model.Binding.Gacha.Abstraction;
+using Snap.Hutao.Model.Binding.Hutao;
+using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata.Abstraction;
+using Snap.Hutao.Model.Metadata.Converter;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+namespace Snap.Hutao.Model.Metadata.Weapon;
+
+///
+/// 武器的接口实现
+///
+public partial class Weapon : IStatisticsItemSource, ISummaryItemSource, INameQuality, ICalculableSource
+{
+ ///
+ /// [非元数据] 搭配数据
+ ///
+ [JsonIgnore]
+ public ComplexWeaponCollocation? Collocation { get; set; }
+
+ ///
+ [JsonIgnore]
+ public ItemQuality Quality
+ {
+ get => RankLevel;
+ }
+
+ ///
+ public ICalculableWeapon ToCalculable()
+ {
+ return new CalculableWeapon(this);
+ }
+
+ ///
+ /// 转换为基础物品
+ ///
+ /// 基础物品
+ public ItemBase ToItemBase()
+ {
+ return new()
+ {
+ Name = Name,
+ Icon = EquipIconConverter.IconNameToUri(Icon),
+ Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType),
+ Quality = RankLevel,
+ };
+ }
+
+ ///
+ /// 转换到统计物品
+ ///
+ /// 个数
+ /// 统计物品
+ public StatisticsItem ToStatisticsItem(int count)
+ {
+ return new()
+ {
+ Name = Name,
+ Icon = EquipIconConverter.IconNameToUri(Icon),
+ Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType),
+ Quality = RankLevel,
+ Count = count,
+ };
+ }
+
+ ///
+ /// 转换到简述统计物品
+ ///
+ /// 距上个五星
+ /// 时间
+ /// 是否为Up物品
+ /// 简述统计物品
+ public SummaryItem ToSummaryItem(int lastPull, DateTimeOffset time, bool isUp)
+ {
+ return new()
+ {
+ Name = Name,
+ Icon = EquipIconConverter.IconNameToUri(Icon),
+ Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType),
+ Time = time,
+ Quality = RankLevel,
+ LastPull = lastPull,
+ IsUp = isUp,
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs
index 5fd2b8f4..ca96c6e9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs
@@ -1,12 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Snap.Hutao.Model.Binding.Gacha;
-using Snap.Hutao.Model.Binding.Gacha.Abstraction;
-using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Model.Intrinsic;
-using Snap.Hutao.Model.Metadata.Abstraction;
-using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Weapon;
@@ -14,7 +9,7 @@ namespace Snap.Hutao.Model.Metadata.Weapon;
///
/// 武器
///
-public class Weapon : IStatisticsItemSource, ISummaryItemSource, INameQuality
+public partial class Weapon
{
///
/// Id
@@ -60,70 +55,4 @@ public class Weapon : IStatisticsItemSource, ISummaryItemSource, INameQuality
/// 被动信息, 无被动的武器为
///
public AffixInfo? Affix { get; set; } = default!;
-
- ///
- /// [非元数据] 搭配数据
- ///
- [JsonIgnore]
- public ComplexWeaponCollocation? Collocation { get; set; }
-
- ///
- [JsonIgnore]
- public ItemQuality Quality
- {
- get => RankLevel;
- }
-
- ///
- /// 转换为基础物品
- ///
- /// 基础物品
- public ItemBase ToItemBase()
- {
- return new()
- {
- Name = Name,
- Icon = EquipIconConverter.IconNameToUri(Icon),
- Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType),
- Quality = RankLevel,
- };
- }
-
- ///
- /// 转换到统计物品
- ///
- /// 个数
- /// 统计物品
- public StatisticsItem ToStatisticsItem(int count)
- {
- return new()
- {
- Name = Name,
- Icon = EquipIconConverter.IconNameToUri(Icon),
- Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType),
- Quality = RankLevel,
- Count = count,
- };
- }
-
- ///
- /// 转换到简述统计物品
- ///
- /// 距上个五星
- /// 时间
- /// 是否为Up物品
- /// 简述统计物品
- public SummaryItem ToSummaryItem(int lastPull, DateTimeOffset time, bool isUp)
- {
- return new()
- {
- Name = Name,
- Icon = EquipIconConverter.IconNameToUri(Icon),
- Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType),
- Time = time,
- Quality = RankLevel,
- LastPull = lastPull,
- IsUp = isUp,
- };
- }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
index face9562..e8b1642b 100644
--- a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
+++ b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
@@ -1,24 +1,31 @@
// Const value
+INFINITE
WM_GETMINMAXINFO
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
// Type definition
+CWMO_FLAGS
HRESULT
MINMAXINFO
-// Comctl32
+// COMCTL32
DefSubclassProc
SetWindowSubclass
RemoveWindowSubclass
-// Kernel32
+// KERNEL32
+CreateEvent
CreateToolhelp32Snapshot
Module32First
Module32Next
ReadProcessMemory
+SetEvent
WriteProcessMemory
-// User32
+// OLE32
+CoWaitForMultipleObjects
+
+// USER32
FindWindowEx
GetDpiForWindow
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
index a8fe02c5..07daf684 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -12,7 +12,7 @@
+ Version="1.2.10.0" />
胡桃
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
index 8ccc1112..1d43218b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
+using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Context.Database;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Diagnostics;
@@ -53,7 +54,7 @@ internal class AchievementService : IAchievementService
///
public ObservableCollection GetArchiveCollection()
{
- return archiveCollection ??= new(appDbContext.AchievementArchives.ToList());
+ return archiveCollection ??= new(appDbContext.AchievementArchives.AsNoTracking().ToList());
}
///
@@ -78,7 +79,7 @@ internal class AchievementService : IAchievementService
}
// 查找是否有相同的名称
- if (archiveCollection!.SingleOrDefault(a => a.Name == newArchive.Name) is EntityArchive userWithSameUid)
+ if (archiveCollection!.SingleOrDefault(a => a.Name == newArchive.Name) != null)
{
return ArchiveAddResult.AlreadyExists;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/ArchiveAddResult.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/ArchiveAddResult.cs
index 82798eaa..d5910f9b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/ArchiveAddResult.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/ArchiveAddResult.cs
@@ -19,7 +19,7 @@ public enum ArchiveAddResult
InvalidName,
///
- /// 已经存在该用户
+ /// 已经存在该存档
///
AlreadyExists,
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
index f4260061..a0fc37b1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
@@ -2,9 +2,17 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Context.Database;
using Snap.Hutao.Core.Database;
+using Snap.Hutao.Model.Binding.Cultivation;
using Snap.Hutao.Model.Entity;
+using Snap.Hutao.Model.Primitive;
+using System.Collections.ObjectModel;
+using BindingCultivateEntry = Snap.Hutao.Model.Binding.Cultivation.CultivateEntry;
+using BindingCultivateItem = Snap.Hutao.Model.Binding.Cultivation.CultivateItem;
+using BindingInventoryItem = Snap.Hutao.Model.Binding.Inventory.InventoryItem;
namespace Snap.Hutao.Service.Cultivation;
@@ -14,24 +22,239 @@ namespace Snap.Hutao.Service.Cultivation;
[Injection(InjectAs.Singleton, typeof(ICultivationService))]
internal class CultivationService : ICultivationService
{
- private readonly DbCurrent dbCurrent;
+ private readonly IServiceScopeFactory scopeFactory;
+ private readonly ScopedDbCurrent dbCurrent;
+
+ private ObservableCollection? projects;
///
/// 构造一个新的养成计算服务
///
- /// 数据库上下文
+ /// 范围工厂
/// 消息器
- public CultivationService(AppDbContext appDbContext, IMessenger messenger)
+ public CultivationService(IServiceScopeFactory scopeFactory, IMessenger messenger)
{
- dbCurrent = new(appDbContext.CultivateProjects, messenger);
+ this.scopeFactory = scopeFactory;
+ dbCurrent = new(scopeFactory, provider => provider.GetRequiredService().CultivateProjects, messenger);
}
- ///
- /// 当前养成计划
- ///
+ ///
public CultivateProject? Current
{
get => dbCurrent.Current;
set => dbCurrent.Current = value;
}
+
+ ///
+ public ObservableCollection GetProjectCollection()
+ {
+ if (projects == null)
+ {
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ projects = new(appDbContext.CultivateProjects.AsNoTracking().ToList());
+ }
+
+ Current ??= projects.SingleOrDefault(proj => proj.IsSelected);
+ }
+
+ return projects;
+ }
+
+ ///
+ public async Task TryAddProjectAsync(CultivateProject project)
+ {
+ if (string.IsNullOrWhiteSpace(project.Name))
+ {
+ return ProjectAddResult.InvalidName;
+ }
+
+ if (projects!.SingleOrDefault(a => a.Name == project.Name) != null)
+ {
+ return ProjectAddResult.AlreadyExists;
+ }
+ else
+ {
+ // Sync cache
+ await ThreadHelper.SwitchToMainThreadAsync();
+ projects!.Add(project);
+
+ // Sync database
+ await ThreadHelper.SwitchToBackgroundAsync();
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ await scope.ServiceProvider.GetRequiredService().CultivateProjects.AddAndSaveAsync(project).ConfigureAwait(false);
+ }
+
+ return ProjectAddResult.Added;
+ }
+ }
+
+ ///
+ public async Task RemoveProjectAsync(CultivateProject project)
+ {
+ // Sync cache
+ // Keep this on main thread.
+ await ThreadHelper.SwitchToMainThreadAsync();
+ projects!.Remove(project);
+
+ // Sync database
+ await ThreadHelper.SwitchToBackgroundAsync();
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ await scope.ServiceProvider.GetRequiredService().CultivateProjects.RemoveAndSaveAsync(project).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ public List GetInventoryItems(CultivateProject cultivateProject, List metadata)
+ {
+ Guid projectId = cultivateProject.InnerId;
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ List entities = appDbContext.InventoryItems
+ .Where(a => a.ProjectId == projectId)
+ .ToList();
+
+ List results = new();
+ foreach (Model.Metadata.Material meta in metadata.Where(IsInventoryItem).OrderBy(m => m.Id))
+ {
+ InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.Create(projectId, meta.Id);
+ results.Add(new(meta, entity));
+ }
+
+ return results;
+ }
+ }
+
+ ///
+ public async Task> GetCultivateEntriesAsync(
+ CultivateProject cultivateProject,
+ List metadata,
+ Dictionary idAvatarMap,
+ Dictionary idWeaponMap)
+ {
+ // TODO: cache the collection
+ await ThreadHelper.SwitchToBackgroundAsync();
+ Guid projectId = cultivateProject.InnerId;
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ List bindingEntries = new();
+ foreach (Model.Entity.CultivateEntry? entry in await appDbContext.CultivateEntries.ToListAsync().ConfigureAwait(false))
+ {
+ Guid entryId = entry.InnerId;
+
+ List items = new();
+ foreach (Model.Entity.CultivateItem? item in await appDbContext.CultivateItems.Where(i => i.EntryId == entryId).OrderBy(i => i.ItemId).ToListAsync().ConfigureAwait(false))
+ {
+ items.Add(new(metadata.Single(m => m.Id == item.ItemId), item));
+ }
+
+ Model.Binding.Gacha.Abstraction.ItemBase itemBase = entry.Type switch
+ {
+ CultivateType.AvatarAndSkill => idAvatarMap[entry.Id].ToItemBase(),
+ CultivateType.Weapon => idWeaponMap[entry.Id].ToItemBase(),
+ _ => null!, // TODO: support furniture calc
+ };
+
+ bindingEntries.Add(new()
+ {
+ Id = entry.Id,
+ EntryId = entryId,
+ Name = itemBase.Name,
+ Icon = itemBase.Icon,
+ Badge = itemBase.Badge,
+ Quality = itemBase.Quality,
+ Items = items,
+ });
+ }
+
+ return new(bindingEntries);
+ }
+ }
+
+ ///
+ public async Task RemoveCultivateEntryAsync(Guid entryId)
+ {
+ await ThreadHelper.SwitchToBackgroundAsync();
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ await appDbContext.CultivateEntries.Where(i => i.InnerId == entryId).ExecuteDeleteAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ public void SaveInventoryItem(BindingInventoryItem item)
+ {
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ scope.ServiceProvider.GetRequiredService().InventoryItems.UpdateAndSave(item.Entity);
+ }
+ }
+
+ ///
+ public async Task SaveConsumptionAsync(CultivateType type, int itemId, List items)
+ {
+ using (IServiceScope scope = scopeFactory.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+
+ Current ??= appDbContext.CultivateProjects.AsNoTracking().SingleOrDefault(proj => proj.IsSelected);
+ if (Current == null)
+ {
+ return false;
+ }
+
+ Guid projectId = Current!.InnerId;
+ Model.Entity.CultivateEntry? entry = await appDbContext.CultivateEntries
+ .SingleOrDefaultAsync(e => e.ProjectId == projectId && e.Id == itemId)
+ .ConfigureAwait(false);
+
+ if (entry == null)
+ {
+ entry = Model.Entity.CultivateEntry.Create(projectId, type, itemId);
+ await appDbContext.CultivateEntries.AddAndSaveAsync(entry).ConfigureAwait(false);
+ }
+
+ Guid entryId = entry.InnerId;
+ await appDbContext.CultivateItems.Where(i => i.EntryId == entryId).ExecuteDeleteAsync().ConfigureAwait(false);
+ IEnumerable toAdd = items.Select(i => Model.Entity.CultivateItem.Create(entryId, i.Id, i.Num));
+ await appDbContext.CultivateItems.AddRangeAndSaveAsync(toAdd).ConfigureAwait(false);
+ }
+
+ return true;
+ }
+
+ private bool IsInventoryItem(Model.Metadata.Material material)
+ {
+ // 原质
+ if (material.Id == 112001)
+ {
+ return false;
+ }
+
+ // 摩拉
+ if (material.Id == 202)
+ {
+ return true;
+ }
+
+ if (material.TypeDescription.EndsWith("区域特产"))
+ {
+ return true;
+ }
+
+ return material.TypeDescription switch
+ {
+ "角色经验素材" => true,
+ "角色培养素材" => true,
+ "天赋培养素材" => true,
+ "武器强化素材" => true,
+ _ => false,
+ };
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
index 89b40e76..8dfb8cc8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs
@@ -1,6 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Binding.Cultivation;
+using Snap.Hutao.Model.Entity;
+using Snap.Hutao.Model.Metadata;
+using Snap.Hutao.Model.Primitive;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+using System.Collections.ObjectModel;
+
namespace Snap.Hutao.Service.Cultivation;
///
@@ -8,4 +15,68 @@ namespace Snap.Hutao.Service.Cultivation;
///
internal interface ICultivationService
{
+ ///
+ /// 当前养成计划
+ ///
+ CultivateProject? Current { get; set; }
+
+ ///
+ /// 获取绑定用的养成列表
+ ///
+ /// 养成计划
+ /// 材料
+ /// Id角色映射
+ /// Id武器映射
+ /// 绑定用的养成列表
+ Task> GetCultivateEntriesAsync(CultivateProject cultivateProject, List metadata, Dictionary idAvatarMap, Dictionary idWeaponMap);
+
+ ///
+ /// 获取物品列表
+ ///
+ /// 养成计划
+ /// 元数据
+ /// 物品列表
+ List GetInventoryItems(CultivateProject cultivateProject, List metadata);
+
+ ///
+ /// 获取用于绑定的项目集合
+ ///
+ /// 项目集合
+ ObservableCollection GetProjectCollection();
+
+ ///
+ /// 删除养成清单
+ ///
+ /// 入口Id
+ /// 任务
+ Task RemoveCultivateEntryAsync(Guid entryId);
+
+ ///
+ /// 异步移除项目
+ ///
+ /// 项目
+ /// 任务
+ Task RemoveProjectAsync(CultivateProject project);
+
+ ///
+ /// 异步保存养成物品
+ ///
+ /// 类型
+ /// 主Id
+ /// 待存物品
+ /// 是否保存成功
+ Task SaveConsumptionAsync(CultivateType type, int itemId, List- items);
+
+ ///
+ /// 保存单个物品
+ ///
+ /// 物品
+ void SaveInventoryItem(Model.Binding.Inventory.InventoryItem item);
+
+ ///
+ /// 异步尝试添加新的项目
+ ///
+ /// 项目
+ /// 添加操作的结果
+ Task TryAddProjectAsync(CultivateProject project);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ProjectAddResult.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ProjectAddResult.cs
new file mode 100644
index 00000000..7adadf8f
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ProjectAddResult.cs
@@ -0,0 +1,25 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.Cultivation;
+
+///
+/// 项目添加结果
+///
+public enum ProjectAddResult
+{
+ ///
+ /// 添加成功
+ ///
+ Added,
+
+ ///
+ /// 名称无效
+ ///
+ InvalidName,
+
+ ///
+ /// 已经存在该存档
+ ///
+ AlreadyExists,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs
index b19954a7..a3e2c2aa 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs
@@ -140,4 +140,11 @@ internal interface IMetadataService
/// 取消令牌
/// 圣遗物套装列表
ValueTask
> GetReliquarySetsAsync(CancellationToken token = default);
+
+ ///
+ /// 异步获取材料列表
+ ///
+ /// 取消令牌
+ /// 材料列表
+ ValueTask> GetMaterialsAsync(CancellationToken token = default(CancellationToken));
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Implementation.cs
index c6c6c203..4df0341a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Implementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Implementation.cs
@@ -38,6 +38,12 @@ internal partial class MetadataService
return FromCacheOrFileAsync>("GachaEvent", token);
}
+ ///
+ public ValueTask> GetMaterialsAsync(CancellationToken token = default)
+ {
+ return FromCacheOrFileAsync>("Material", token);
+ }
+
///
public ValueTask> GetReliquariesAsync(CancellationToken token = default)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index cfe85dee..7f9de3c3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -69,6 +69,8 @@
+
+
@@ -134,12 +136,12 @@
-
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -151,12 +153,12 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -179,6 +181,16 @@
+
+
+ MSBuild:Compile
+
+
+
+
+ MSBuild:Compile
+
+
MSBuild:Compile
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs
index 86a1237f..97c20a25 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs
@@ -16,6 +16,9 @@ public sealed partial class DescParamComboBox : UserControl
private static readonly DependencyProperty SourceProperty = Property
.Depend>>(nameof(Source), default!, OnSourceChanged);
+ private static readonly DependencyProperty PreferredSelectedIndexProperty = Property
+ .Depend(nameof(PreferredSelectedIndex), 0);
+
///
/// 构造一个新的描述参数组合框
///
@@ -33,6 +36,15 @@ public sealed partial class DescParamComboBox : UserControl
set => SetValue(SourceProperty, value);
}
+ ///
+ /// 期望的选中索引
+ ///
+ public int PreferredSelectedIndex
+ {
+ get { return (int)GetValue(PreferredSelectedIndexProperty); }
+ set { SetValue(PreferredSelectedIndexProperty, value); }
+ }
+
private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// Some of the {x:Bind} feature is not working properly,
@@ -42,7 +54,7 @@ public sealed partial class DescParamComboBox : UserControl
if (args.NewValue != args.OldValue && args.NewValue is IList> list)
{
descParamComboBox.ItemHost.ItemsSource = list;
- descParamComboBox.ItemHost.SelectedIndex = 0;
+ descParamComboBox.ItemHost.SelectedIndex = Math.Min(descParamComboBox.PreferredSelectedIndex, list.Count);
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Converter/EmptyObjectToVisibilityConverter.cs b/src/Snap.Hutao/Snap.Hutao/View/Converter/EmptyObjectToVisibilityConverter.cs
new file mode 100644
index 00000000..cbb9c1d6
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Converter/EmptyObjectToVisibilityConverter.cs
@@ -0,0 +1,22 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.WinUI.UI.Converters;
+using Microsoft.UI.Xaml;
+
+namespace Snap.Hutao.View.Converter;
+
+///
+/// This class converts a object? value into a Visibility enumeration.
+///
+public class EmptyObjectToVisibilityConverter : EmptyObjectToObjectConverter
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EmptyObjectToVisibilityConverter()
+ {
+ EmptyValue = Visibility.Collapsed;
+ NotEmptyValue = Visibility.Visible;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivateProjectDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivateProjectDialog.xaml
new file mode 100644
index 00000000..c6ce2502
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivateProjectDialog.xaml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivateProjectDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivateProjectDialog.xaml.cs
new file mode 100644
index 00000000..aacd5e91
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivateProjectDialog.xaml.cs
@@ -0,0 +1,59 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License.
+
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+using Snap.Hutao.Model.Entity;
+using Snap.Hutao.Service.User;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+
+namespace Snap.Hutao.View.Dialog;
+
+///
+/// ɼƻԻ
+///
+public sealed partial class CultivateProjectDialog : ContentDialog
+{
+ ///
+ /// һµɼƻԻ
+ ///
+ ///
+ public CultivateProjectDialog(Window window)
+ {
+ InitializeComponent();
+ XamlRoot = window.Content.XamlRoot;
+ }
+
+ ///
+ /// һµģûָļƻ
+ ///
+ /// ƻ
+ public async ValueTask> CreateProjectAsync()
+ {
+ await ThreadHelper.SwitchToMainThreadAsync();
+ ContentDialogResult result = await ShowAsync();
+ if (result == ContentDialogResult.Primary)
+ {
+ string text = InputText.Text;
+ string? uid = AttachUidBox.IsChecked == true
+ ? Ioc.Default.GetRequiredService().Current?.SelectedUserGameRole?.GameUid
+ : null;
+
+ CultivateProject project = CultivateProject.Create(text, uid);
+ return new(true, project);
+ }
+
+ return new(false, null!);
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml
new file mode 100644
index 00000000..9e9a2d70
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml.cs
new file mode 100644
index 00000000..cc6b50b8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml.cs
@@ -0,0 +1,88 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Snap.Hutao.Control;
+using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+namespace Snap.Hutao.View.Dialog;
+
+///
+/// ɼԻ
+///
+public sealed partial class CultivatePromotionDeltaDialog : ContentDialog
+{
+ private static readonly DependencyProperty AvatarProperty = Property.Depend(nameof(Avatar));
+ private static readonly DependencyProperty WeaponProperty = Property.Depend(nameof(Weapon));
+
+ ///
+ /// һµɼԻ
+ ///
+ ///
+ /// ɫ
+ ///
+ public CultivatePromotionDeltaDialog(Window window, ICalculableAvatar? avatar, ICalculableWeapon? weapon)
+ {
+ InitializeComponent();
+ XamlRoot = window.Content.XamlRoot;
+ DataContext = this;
+ Avatar = avatar;
+ Weapon = weapon;
+ }
+
+ ///
+ /// ɫ
+ ///
+ public ICalculableAvatar? Avatar
+ {
+ get { return (ICalculableAvatar?)GetValue(AvatarProperty); }
+ set { SetValue(AvatarProperty, value); }
+ }
+
+ ///
+ ///
+ ///
+ public ICalculableWeapon? Weapon
+ {
+ get { return (ICalculableWeapon?)GetValue(WeaponProperty); }
+ set { SetValue(WeaponProperty, value); }
+ }
+
+ ///
+ /// 첽ȡ
+ ///
+ ///
+ public async Task> GetPromotionDeltaAsync()
+ {
+ ContentDialogResult result = await ShowAsync();
+
+ if (result == ContentDialogResult.Primary)
+ {
+ AvatarPromotionDelta delta = new()
+ {
+ AvatarId = Avatar?.AvatarId ?? 0,
+ AvatarLevelCurrent = Avatar?.LevelCurrent ?? 0,
+ AvatarLevelTarget = Avatar?.LevelTarget ?? 0,
+ SkillList = Avatar?.Skills.Select(s => new PromotionDelta()
+ {
+ Id = s.GruopId,
+ LevelCurrent = s.LevelCurrent,
+ LevelTarget = s.LevelTarget,
+ }),
+ Weapon = Weapon == null ? null : new PromotionDelta()
+ {
+ Id = Weapon.WeaponId,
+ LevelCurrent = Weapon.LevelCurrent,
+ LevelTarget = Weapon.LevelTarget,
+ },
+ };
+
+ return new(true, delta);
+ }
+ else
+ {
+ return new(false, null!);
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GameAccountNameDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GameAccountNameDialog.xaml
index 86b43e08..d1144c2c 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GameAccountNameDialog.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GameAccountNameDialog.xaml
@@ -16,6 +16,6 @@
x:Name="InputText"
Margin="0,0,0,0"
VerticalAlignment="Top"
- PlaceholderText="在此处输入"/>
+ PlaceholderText="在此处输入名称"/>
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
index c8373362..db5ad0bf 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
@@ -24,10 +24,7 @@
-
+
@@ -61,7 +58,11 @@
Margin="2,6,3,6"
DisplayMemberPath="Name"
ItemsSource="{Binding Archives, Mode=OneWay}"
- SelectedItem="{Binding SelectedArchive, Mode=TwoWay}"/>
+ SelectedItem="{Binding SelectedArchive, Mode=TwoWay}">
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Visible
+
+
+
+
+
+
+
+
+
+ Collapsed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
index 7193cfb2..01d86a11 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml
@@ -23,8 +23,9 @@
-
+ Fill="{StaticResource CardBackgroundFillColorDefaultBrush}"
+ IsHitTestVisible="False"/>
+
+ SelectedItem="{Binding SelectedArchive, Mode=TwoWay}">
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
index 1a6c0fab..5fb97a35 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
@@ -33,6 +33,7 @@
@@ -40,7 +41,10 @@
-
+
@@ -68,7 +72,11 @@
-
+
-
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
index 2a35e584..3c0a5159 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
@@ -25,7 +25,11 @@ public sealed partial class TitleView : UserControl
[SuppressMessage("", "CA1822")]
public string Title
{
+#if DEBUG
+ get => $"胡桃 Dev Build";
+#else
get => $"胡桃 {Core.CoreEnvironment.Version}";
+#endif
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/CultivationViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/CultivationViewModel.cs
index f81eacf8..aae44fe2 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/CultivationViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/CultivationViewModel.cs
@@ -2,7 +2,17 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using CommunityToolkit.Mvvm.Messaging;
using Snap.Hutao.Control;
+using Snap.Hutao.Factory.Abstraction;
+using Snap.Hutao.Message;
+using Snap.Hutao.Model.Entity;
+using Snap.Hutao.Service.Abstraction;
+using Snap.Hutao.Service.Cultivation;
+using Snap.Hutao.Service.Metadata;
+using Snap.Hutao.View.Dialog;
+using System.Collections.ObjectModel;
namespace Snap.Hutao.ViewModel;
@@ -10,8 +20,193 @@ namespace Snap.Hutao.ViewModel;
/// 养成视图模型
///
[Injection(InjectAs.Scoped)]
-internal class CultivationViewModel : ObservableObject, ISupportCancellation
+internal class CultivationViewModel : ObservableObject, ISupportCancellation, IRecipient
{
+ private readonly ICultivationService cultivationService;
+ private readonly IInfoBarService infoBarService;
+ private readonly IMetadataService metadataService;
+ private readonly ILogger logger;
+
+ private ObservableCollection? projects;
+ private CultivateProject? selectedProject;
+ private List? inventoryItems;
+ private ObservableCollection? cultivateEntries;
+
+ ///
+ /// 构造一个新的养成视图模型
+ ///
+ /// 养成服务
+ /// 信息服务
+ /// 异步命令工厂
+ /// 元数据服务
+ /// 日志器
+ /// 消息器
+ public CultivationViewModel(
+ ICultivationService cultivationService,
+ IInfoBarService infoBarService,
+ IAsyncRelayCommandFactory asyncRelayCommandFactory,
+ IMetadataService metadataService,
+ ILogger logger,
+ IMessenger messenger)
+ {
+ this.cultivationService = cultivationService;
+ this.infoBarService = infoBarService;
+ this.metadataService = metadataService;
+ this.logger = logger;
+
+ OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
+ AddProjectCommand = asyncRelayCommandFactory.Create(AddProjectAsync);
+ RemoveProjectCommand = asyncRelayCommandFactory.Create(RemoveProjectAsync);
+ RemoveEntryCommand = asyncRelayCommandFactory.Create(RemoveEntryAsync);
+ SaveInventoryItemCommand = new RelayCommand(SaveInventoryItem);
+
+ messenger.Register(this);
+ }
+
///
public CancellationToken CancellationToken { get; set; }
+
+ ///
+ /// 项目
+ ///
+ public ObservableCollection? Projects { get => projects; set => SetProperty(ref projects, value); }
+
+ ///
+ /// 当前选中的计划
+ ///
+ public CultivateProject? SelectedProject
+ {
+ get => selectedProject; set
+ {
+ if (SetProperty(ref selectedProject, value))
+ {
+ cultivationService.Current = value;
+ }
+ }
+ }
+
+ ///
+ /// 物品列表
+ ///
+ public List? InventoryItems { get => inventoryItems; set => SetProperty(ref inventoryItems, value); }
+
+ ///
+ /// 养成列表
+ ///
+ public ObservableCollection? CultivateEntries { get => cultivateEntries; set => SetProperty(ref cultivateEntries, value); }
+
+ ///
+ /// 打开界面命令
+ ///
+ public ICommand OpenUICommand { get; }
+
+ ///
+ /// 添加项目命令
+ ///
+ public ICommand AddProjectCommand { get; }
+
+ ///
+ /// 删除项目命令
+ ///
+ public ICommand RemoveProjectCommand { get; }
+
+ ///
+ /// 移除
+ ///
+ public ICommand RemoveEntryCommand { get; }
+
+ ///
+ /// 保存物品命令
+ ///
+ public ICommand SaveInventoryItemCommand { get; }
+
+ ///
+ public void Receive(CultivateProjectChangedMessage message)
+ {
+ UpdateCultivateEntriesAndInventoryItemsAsync(message.NewValue).SafeForget(logger);
+ }
+
+ private async Task OpenUIAsync()
+ {
+ if (await metadataService.InitializeAsync().ConfigureAwait(true))
+ {
+ Projects = cultivationService.GetProjectCollection();
+ SelectedProject = cultivationService.Current;
+ await UpdateCultivateEntriesAndInventoryItemsAsync(SelectedProject).ConfigureAwait(false);
+ }
+ }
+
+ private async Task AddProjectAsync()
+ {
+ MainWindow mainWindow = Ioc.Default.GetRequiredService();
+ (bool isOk, CultivateProject project) = await new CultivateProjectDialog(mainWindow).CreateProjectAsync().ConfigureAwait(false);
+
+ if (isOk)
+ {
+ ProjectAddResult result = await cultivationService.TryAddProjectAsync(project).ConfigureAwait(false);
+
+ switch (result)
+ {
+ case ProjectAddResult.Added:
+ infoBarService.Success($"添加成功");
+ break;
+ case ProjectAddResult.InvalidName:
+ infoBarService.Information($"不能添加名称无效的计划");
+ break;
+ case ProjectAddResult.AlreadyExists:
+ infoBarService.Information($"不能添加名称重复的计划");
+ break;
+ default:
+ throw Must.NeverHappen();
+ }
+ }
+ }
+
+ private async Task RemoveProjectAsync(CultivateProject? project)
+ {
+ if (project != null)
+ {
+ await cultivationService.RemoveProjectAsync(project).ConfigureAwait(false);
+
+ await ThreadHelper.SwitchToMainThreadAsync();
+ SelectedProject = Projects!.FirstOrDefault();
+ }
+ }
+
+ private async Task UpdateCultivateEntriesAndInventoryItemsAsync(CultivateProject? project)
+ {
+ if (project != null)
+ {
+ List materials = await metadataService.GetMaterialsAsync().ConfigureAwait(false);
+ Dictionary idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
+ Dictionary idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false);
+
+ ObservableCollection entries = await cultivationService
+ .GetCultivateEntriesAsync(project, materials, idAvatarMap, idWeaponMap)
+ .ConfigureAwait(false);
+
+ await ThreadHelper.SwitchToMainThreadAsync();
+ CultivateEntries = entries;
+ InventoryItems = cultivationService.GetInventoryItems(project, materials);
+ }
+ }
+
+ private Task RemoveEntryAsync(Model.Binding.Cultivation.CultivateEntry? entry)
+ {
+ if (entry != null)
+ {
+ CultivateEntries!.Remove(entry);
+ return cultivationService.RemoveCultivateEntryAsync(entry.EntryId);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private void SaveInventoryItem(Model.Binding.Inventory.InventoryItem? inventoryItem)
+ {
+ if (inventoryItem != null)
+ {
+ cultivationService.SaveInventoryItem(inventoryItem);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs
index a1bc28a6..b0c1268d 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs
@@ -4,6 +4,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Context.Database;
using Snap.Hutao.Control;
using Snap.Hutao.Core.Database;
@@ -27,11 +28,17 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Scoped)]
internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
{
+ ///
+ /// 启动游戏目标 Uid
+ ///
+ public const string DesiredUid = nameof(DesiredUid);
+
private static readonly string TrueString = true.ToString();
private static readonly string FalseString = false.ToString();
private readonly IGameService gameService;
private readonly AppDbContext appDbContext;
+ private readonly IMemoryCache memoryCache;
private readonly List knownSchemes = new()
{
@@ -55,12 +62,18 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
/// 构造一个新的启动游戏视图模型
///
/// 游戏服务
+ /// 内存缓存
/// 数据库上下文
/// 异步命令工厂
- public LaunchGameViewModel(IGameService gameService, AppDbContext appDbContext, IAsyncRelayCommandFactory asyncRelayCommandFactory)
+ public LaunchGameViewModel(
+ IGameService gameService,
+ IMemoryCache memoryCache,
+ AppDbContext appDbContext,
+ IAsyncRelayCommandFactory asyncRelayCommandFactory)
{
this.gameService = gameService;
this.appDbContext = appDbContext;
+ this.memoryCache = memoryCache;
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
LaunchCommand = asyncRelayCommandFactory.Create(LaunchAsync);
@@ -169,13 +182,21 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
SelectedScheme = KnownSchemes.FirstOrDefault(s => s.Channel == multi.Channel && s.SubChannel == multi.SubChannel);
GameAccounts = gameService.GetGameAccountCollection();
+ // Sync uid
+ if (memoryCache.TryGetValue(DesiredUid, out object? value) && value is string uid)
+ {
+ SelectedGameAccount = GameAccounts.SingleOrDefault(g => g.AttachUid == uid);
+ }
+
// Sync from Settings
RetiveSetting();
}
else
{
Ioc.Default.GetRequiredService().Warning("游戏路径不正确,前往设置更改游戏路径。");
- await Ioc.Default.GetRequiredService().NavigateAsync(INavigationAwaiter.Default, true).ConfigureAwait(false);
+ await Ioc.Default.GetRequiredService()
+ .NavigateAsync(INavigationAwaiter.Default, true)
+ .ConfigureAwait(false);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
index a8c772c7..6d166c6a 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
@@ -5,12 +5,22 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.WinUI.UI;
using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model;
+using Snap.Hutao.Model.Binding.Cultivation;
using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Primitive;
+using Snap.Hutao.Service.Abstraction;
+using Snap.Hutao.Service.Cultivation;
using Snap.Hutao.Service.Hutao;
using Snap.Hutao.Service.Metadata;
+using Snap.Hutao.Service.User;
+using Snap.Hutao.View.Dialog;
+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;
@@ -44,6 +54,7 @@ internal class WikiAvatarViewModel : ObservableObject
this.metadataService = metadataService;
this.hutaoCache = hutaoCache;
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
+ CultivateCommand = asyncRelayCommandFactory.Create(CultivateAsync);
filterElementInfos = new()
{
@@ -147,6 +158,11 @@ internal class WikiAvatarViewModel : ObservableObject
///
public ICommand OpenUICommand { get; }
+ ///
+ /// 养成命令
+ ///
+ public ICommand CultivateCommand { get; }
+
private async Task OpenUIAsync()
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
@@ -160,8 +176,6 @@ internal class WikiAvatarViewModel : ObservableObject
await CombineWithAvatarCollocationsAsync(sorted).ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
-
- // RPC_E_WRONG_THREAD ?
Avatars = new AdvancedCollectionView(sorted, true);
Selected = Avatars.Cast().FirstOrDefault();
}
@@ -222,4 +236,47 @@ internal class WikiAvatarViewModel : ObservableObject
}
}
}
+
+ private async Task CultivateAsync(Avatar? avatar)
+ {
+ IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
+ if (avatar != null)
+ {
+ IUserService userService = Ioc.Default.GetRequiredService();
+ if (userService.Current != null)
+ {
+ MainWindow mainWindow = Ioc.Default.GetRequiredService();
+ (bool isOk, CalcAvatarPromotionDelta delta) = await new CultivatePromotionDeltaDialog(mainWindow, avatar.ToCalculable(), null)
+ .GetPromotionDeltaAsync()
+ .ConfigureAwait(false);
+
+ if (isOk)
+ {
+ CalcClient calculateClient = Ioc.Default.GetRequiredService();
+ CalcConsumption? consumption = await calculateClient.ComputeAsync(userService.Current.Entity, delta).ConfigureAwait(false);
+ if (consumption != null)
+ {
+ List items = CalcItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
+ bool saved = await Ioc.Default
+ .GetRequiredService()
+ .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
+ .ConfigureAwait(false);
+
+ if (saved)
+ {
+ infoBarService.Success("已成功添加至当前养成计划");
+ }
+ else
+ {
+ infoBarService.Warning("请先前往养成计划页面创建计划并选中");
+ }
+ }
+ }
+ }
+ else
+ {
+ infoBarService.Warning("必须先选择一个用户与角色");
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs
index c56fb805..d20a8ac3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs
@@ -51,7 +51,9 @@ public partial class Cookie
string name = parts[0].Trim();
string value = parts.Length == 1 ? string.Empty : parts[1].Trim();
- cookieMap.Add(name, value);
+ // System.ArgumentException: An item with the same key has already been added.
+ // cookieMap.Add(name, value);
+ cookieMap[name] = value;
}
return new(cookieMap);
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyolabHttpClientExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyolabHttpClientExtensions.cs
new file mode 100644
index 00000000..df0bc994
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyolabHttpClientExtensions.cs
@@ -0,0 +1,88 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Extension;
+using Snap.Hutao.Web.Request;
+using System.Net.Http;
+using System.Text;
+
+namespace Snap.Hutao.Web.Hoyolab;
+
+///
+/// Hoyolab HttpClient 拓展
+///
+internal static class HoyolabHttpClientExtensions
+{
+ ///
+ /// 设置用户的 Cookie
+ ///
+ /// http客户端
+ /// 实体用户
+ /// Cookie类型
+ /// 客户端
+ internal static HttpClient SetUser(this HttpClient httpClient, Model.Entity.User user, CookieType cookie)
+ {
+ httpClient.DefaultRequestHeaders.Remove("Cookie");
+ StringBuilder stringBuilder = new();
+
+ if ((cookie & CookieType.CookieToken) == CookieType.CookieToken)
+ {
+ stringBuilder.Append(user.CookieToken).AppendIf(user.CookieToken != null, ';');
+ }
+
+ if ((cookie & CookieType.Ltoken) == CookieType.Ltoken)
+ {
+ stringBuilder.Append(user.Ltoken).AppendIf(user.Ltoken != null, ';');
+ }
+
+ if ((cookie & CookieType.Stoken) == CookieType.Stoken)
+ {
+ stringBuilder.Append(user.Stoken).AppendIf(user.Stoken != null, ';');
+ }
+
+ if ((cookie & CookieType.Mid) == CookieType.Mid)
+ {
+ stringBuilder.Append("mid=").Append(user.Mid).Append(';');
+ }
+
+ httpClient.DefaultRequestHeaders.Set("Cookie", stringBuilder.ToString());
+ return httpClient;
+ }
+
+ ///
+ /// 设置Referer
+ ///
+ /// http客户端
+ /// 用户
+ /// 客户端
+ internal static HttpClient SetReferer(this HttpClient httpClient, string referer)
+ {
+ httpClient.DefaultRequestHeaders.Set("Referer", referer);
+ return httpClient;
+ }
+
+ ///
+ /// 设置验证流水号
+ ///
+ /// http客户端
+ /// 验证流水号
+ /// 客户端
+ internal static HttpClient SetXrpcChallenge(this HttpClient httpClient, string challenge)
+ {
+ httpClient.DefaultRequestHeaders.Set("x-rpc-challenge", challenge);
+ return httpClient;
+ }
+
+ ///
+ /// 设置头
+ ///
+ /// http客户端
+ /// 键
+ /// 值
+ /// 客户端
+ internal static HttpClient SetHeader(this HttpClient httpClient, string key, string value)
+ {
+ httpClient.DefaultRequestHeaders.Set(key, value);
+ return httpClient;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs
index 20e217dc..a4dc536b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs
@@ -39,7 +39,7 @@ internal class CalculateClient
/// 差异
/// 取消令牌
/// 消耗结果
- public async Task ComputeAsync(User user, AvatarPromotionDelta delta, CancellationToken token)
+ public async Task ComputeAsync(User user, AvatarPromotionDelta delta, CancellationToken token = default)
{
Response? resp = await httpClient
.SetUser(user, CookieType.CookieToken)
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ElementAttributeId.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ElementAttributeId.cs
index 4b117fa2..063c8685 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ElementAttributeId.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ElementAttributeId.cs
@@ -8,6 +8,11 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
///
public enum ElementAttributeId
{
+ ///
+ /// 无
+ ///
+ None = 0,
+
///
/// 火元素
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculable.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculable.cs
new file mode 100644
index 00000000..eecffd54
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculable.cs
@@ -0,0 +1,37 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+
+namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+///
+/// 可计算源
+///
+public interface ICalculable
+{
+ ///
+ /// 名称
+ ///
+ string Name { get; }
+
+ ///
+ /// 图标
+ ///
+ Uri Icon { get; }
+
+ ///
+ /// 星级
+ ///
+ ItemQuality Quality { get; }
+
+ ///
+ /// 当前等级
+ ///
+ int LevelCurrent { get; set; }
+
+ ///
+ /// 目标等级
+ ///
+ int LevelTarget { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableAvatar.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableAvatar.cs
new file mode 100644
index 00000000..1bcd9141
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableAvatar.cs
@@ -0,0 +1,32 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Primitive;
+
+namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+///
+/// 可计算的角色
+///
+public interface ICalculableAvatar : ICalculable
+{
+ ///
+ /// 角色Id
+ ///
+ AvatarId AvatarId { get; }
+
+ ///
+ /// 最小等级
+ ///
+ int LevelMin { get; }
+
+ ///
+ /// 最大等级
+ ///
+ int LevelMax { get; }
+
+ ///
+ /// 技能组
+ ///
+ IList Skills { get; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableSkill.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableSkill.cs
new file mode 100644
index 00000000..7c425ab4
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableSkill.cs
@@ -0,0 +1,25 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+///
+/// 可计算的技能
+///
+public interface ICalculableSkill : ICalculable
+{
+ ///
+ /// 技能组Id
+ ///
+ int GruopId { get; }
+
+ ///
+ /// 最小等级
+ ///
+ int LevelMin { get; }
+
+ ///
+ /// 最大等级
+ ///
+ int LevelMax { get; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableSource.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableSource.cs
new file mode 100644
index 00000000..356529a2
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableSource.cs
@@ -0,0 +1,18 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+///
+/// 可计算物品的源
+///
+/// 可计算类型
+public interface ICalculableSource
+ where T : ICalculable
+{
+ ///
+ /// 转换到可计算的对象
+ ///
+ /// 可计算物品
+ public T ToCalculable();
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableWeapon.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableWeapon.cs
new file mode 100644
index 00000000..e865e25c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ICalculableWeapon.cs
@@ -0,0 +1,27 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Primitive;
+
+namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+///
+/// 可计算的武器
+///
+public interface ICalculableWeapon : ICalculable
+{
+ ///
+ /// 武器Id
+ ///
+ WeaponId WeaponId { get; }
+
+ ///
+ /// 最小等级
+ ///
+ int LevelMin { get; }
+
+ ///
+ /// 最大等级
+ ///
+ int LevelMax { get; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs
index 302e30eb..a2abf2bb 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs
@@ -37,6 +37,6 @@ public class Item
///
/// 物品星级 仅有家具为有效值
///
- [JsonPropertyName("num")]
+ [JsonPropertyName("level")]
public ItemQuality Level { get; set; }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ItemHelper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ItemHelper.cs
new file mode 100644
index 00000000..931aaaf8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/ItemHelper.cs
@@ -0,0 +1,51 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
+
+///
+/// 物品帮助类
+///
+public static class ItemHelper
+{
+ ///
+ /// 合并两个物品列表
+ ///
+ /// 左列表
+ /// 右列表
+ /// 合并且排序好的列表
+ public static List- Merge(List
- ? left, List
- ? right)
+ {
+ if (left == null && right == null)
+ {
+ return new(0);
+ }
+
+ if (right == null)
+ {
+ return left!;
+ }
+
+ if (left == null)
+ {
+ return right!;
+ }
+
+ List
- result = new(left.Count + right.Count);
+ result.AddRange(left);
+
+ foreach (Item item in right)
+ {
+ if (result.SingleOrDefault(i => i.Id == item.Id) is Item existed)
+ {
+ existed.Num += item.Num;
+ }
+ else
+ {
+ result.Add(item);
+ }
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Web/HttpClientExtensions.cs
similarity index 56%
rename from src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs
rename to src/Snap.Hutao/Snap.Hutao/Web/HttpClientExtensions.cs
index c4d93f63..cb568140 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/HttpClientExtensions.cs
@@ -3,13 +3,15 @@
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Extension;
+using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Request;
+using System.IO;
using System.Net.Http;
using System.Net.Http.Json;
using System.Net.Sockets;
using System.Text;
-namespace Snap.Hutao.Web.Hoyolab;
+namespace Snap.Hutao.Web;
///
/// 扩展
@@ -34,6 +36,16 @@ internal static class HttpClientExtensions
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
}
+ catch (JsonException ex)
+ {
+ logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
+ return null;
+ }
+ catch (IOException ex)
+ {
+ logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
+ return null;
+ }
}
///
@@ -55,6 +67,16 @@ internal static class HttpClientExtensions
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
}
+ catch (JsonException ex)
+ {
+ logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
+ return null;
+ }
+ catch (IOException ex)
+ {
+ logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
+ return null;
+ }
}
///
@@ -74,78 +96,13 @@ internal static class HttpClientExtensions
{
return null;
}
- }
-
- ///
- /// 设置用户的 Cookie
- ///
- /// http客户端
- /// 实体用户
- /// Cookie类型
- /// 客户端
- internal static HttpClient SetUser(this HttpClient httpClient, Model.Entity.User user, CookieType cookie)
- {
- httpClient.DefaultRequestHeaders.Remove("Cookie");
- StringBuilder stringBuilder = new();
-
- if ((cookie & CookieType.CookieToken) == CookieType.CookieToken)
+ catch (JsonException)
{
- stringBuilder.Append(user.CookieToken).AppendIf(user.CookieToken != null, ';');
+ return null;
}
-
- if ((cookie & CookieType.Ltoken) == CookieType.Ltoken)
+ catch (IOException)
{
- stringBuilder.Append(user.Ltoken).AppendIf(user.Ltoken != null, ';');
+ return null;
}
-
- if ((cookie & CookieType.Stoken) == CookieType.Stoken)
- {
- stringBuilder.Append(user.Stoken).AppendIf(user.Stoken != null, ';');
- }
-
- if ((cookie & CookieType.Mid) == CookieType.Mid)
- {
- stringBuilder.Append("mid=").Append(user.Mid).Append(';');
- }
-
- httpClient.DefaultRequestHeaders.Set("Cookie", stringBuilder.ToString());
- return httpClient;
- }
-
- ///
- /// 设置Referer
- ///
- /// http客户端
- /// 用户
- /// 客户端
- internal static HttpClient SetReferer(this HttpClient httpClient, string referer)
- {
- httpClient.DefaultRequestHeaders.Set("Referer", referer);
- return httpClient;
- }
-
- ///
- /// 设置验证流水号
- ///
- /// http客户端
- /// 验证流水号
- /// 客户端
- internal static HttpClient SetXrpcChallenge(this HttpClient httpClient, string challenge)
- {
- httpClient.DefaultRequestHeaders.Set("x-rpc-challenge", challenge);
- return httpClient;
- }
-
- ///
- /// 设置头
- ///
- /// http客户端
- /// 键
- /// 值
- /// 客户端
- internal static HttpClient SetHeader(this HttpClient httpClient, string key, string value)
- {
- httpClient.DefaultRequestHeaders.Set(key, value);
- return httpClient;
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
index 033b856b..522ac6e3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
@@ -101,7 +101,7 @@ internal class HomaClient
public async Task
> GetAvatarAttendanceRatesAsync(CancellationToken token = default)
{
Response>? resp = await httpClient
- .GetFromJsonAsync>>($"{HutaoAPI}/Statistics/Avatar/AttendanceRate", token)
+ .TryCatchGetFromJsonAsync>>($"{HutaoAPI}/Statistics/Avatar/AttendanceRate", options, logger, token)
.ConfigureAwait(false);
return EnumerableExtension.EmptyIfNull(resp?.Data);
@@ -116,7 +116,7 @@ internal class HomaClient
public async Task> GetAvatarUtilizationRatesAsync(CancellationToken token = default)
{
Response>? resp = await httpClient
- .GetFromJsonAsync>>($"{HutaoAPI}/Statistics/Avatar/UtilizationRate", token)
+ .TryCatchGetFromJsonAsync>>($"{HutaoAPI}/Statistics/Avatar/UtilizationRate", options, logger, token)
.ConfigureAwait(false);
return EnumerableExtension.EmptyIfNull(resp?.Data);
@@ -131,7 +131,7 @@ internal class HomaClient
public async Task> GetAvatarCollocationsAsync(CancellationToken token = default)
{
Response>? resp = await httpClient
- .GetFromJsonAsync>>($"{HutaoAPI}/Statistics/Avatar/AvatarCollocation", token)
+ .TryCatchGetFromJsonAsync>>($"{HutaoAPI}/Statistics/Avatar/AvatarCollocation", options, logger, token)
.ConfigureAwait(false);
return EnumerableExtension.EmptyIfNull(resp?.Data);
@@ -146,7 +146,7 @@ internal class HomaClient
public async Task> GetWeaponCollocationsAsync(CancellationToken token = default)
{
Response>? resp = await httpClient
- .GetFromJsonAsync>>($"{HutaoAPI}/Statistics/Weapon/WeaponCollocation", token)
+ .TryCatchGetFromJsonAsync>>($"{HutaoAPI}/Statistics/Weapon/WeaponCollocation", options, logger, token)
.ConfigureAwait(false);
return EnumerableExtension.EmptyIfNull(resp?.Data);
@@ -161,7 +161,7 @@ internal class HomaClient
public async Task> GetAvatarHoldingRatesAsync(CancellationToken token = default)
{
Response>? resp = await httpClient
- .GetFromJsonAsync>>($"{HutaoAPI}/Statistics/Avatar/HoldingRate", token)
+ .TryCatchGetFromJsonAsync>>($"{HutaoAPI}/Statistics/Avatar/HoldingRate", options, logger, token)
.ConfigureAwait(false);
return EnumerableExtension.EmptyIfNull(resp?.Data);
@@ -176,7 +176,7 @@ internal class HomaClient
public async Task> GetTeamCombinationsAsync(CancellationToken token = default)
{
Response>? resp = await httpClient
- .GetFromJsonAsync>>($"{HutaoAPI}/Statistics/Team/Combination", token)
+ .TryCatchGetFromJsonAsync>>($"{HutaoAPI}/Statistics/Team/Combination", options, logger, token)
.ConfigureAwait(false);
return EnumerableExtension.EmptyIfNull(resp?.Data);
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs
index d6b3af74..731fc333 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
-using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hutao.Log;
using Snap.Hutao.Web.Response;
using System.Net.Http;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs b/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs
index a99707f4..6f9e7ff2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs
@@ -53,6 +53,11 @@ public enum KnownReturnCode : int
///
PleaseOpenInBbsApp = -1104,
+ ///
+ /// 天赋等级超出限制~
+ ///
+ SkillLevelLimitExcceed = -1009,
+
///
/// 登录信息已失效,请重新登录
///