mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
423188c16a | ||
|
|
5fb935635b | ||
|
|
761049ec17 | ||
|
|
3f8c8874f3 | ||
|
|
b8f354bbc7 | ||
|
|
d45c40d4d7 | ||
|
|
6b309c4886 | ||
|
|
26e6d2008e | ||
|
|
fb77bd2f6b | ||
|
|
50459923f9 | ||
|
|
a97bab8a1c | ||
|
|
bbc8324f5d | ||
|
|
2c0b32ab8b | ||
|
|
0073636676 | ||
|
|
7457d72e1b |
1
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -68,6 +68,7 @@ body:
|
||||
- 游戏启动器
|
||||
- 实时便笺
|
||||
- 养成计算器
|
||||
- 用户面板
|
||||
- 文件缓存
|
||||
- 祈愿记录
|
||||
- 玩家查询
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
14
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -8,12 +8,20 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
请按下方的要求填写完整的问题表单,以便我们更快的定位问题。
|
||||
请按下方的要求填写完整的问题表单。
|
||||
|
||||
- type: textarea
|
||||
id: back
|
||||
attributes:
|
||||
label: 背景与动机
|
||||
description: 添加此功能的理由
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: req
|
||||
attributes:
|
||||
label: 你想要实现或优化的功能?
|
||||
description: 详细的描述一下你想要的功能
|
||||
label: 想要实现或优化的功能
|
||||
description: 详细的描述一下你想要的功能,描述的越具体,采纳的可能性越高
|
||||
validations:
|
||||
required: true
|
||||
25
README.md
25
README.md
@@ -1,35 +1,26 @@
|
||||
# Snap.Hutao
|
||||
# [Snap.Hutao](https://hut.ao)
|
||||
> 唷,找本堂主有何贵干呀?
|
||||
|
||||

|
||||
|
||||
## 项目首页(文档)
|
||||
# 特别感谢
|
||||
|
||||
[](https://github.com/DGP-Studio/Snap.Hutao.Docs/actions/workflows/deploy-docs.yml)
|
||||
### 原神组织与个人
|
||||
|
||||
[HUT.AO](https://hut.ao)
|
||||
* [HolographicHat](https://github.com/HolographicHat)
|
||||
* [UIGF organization](https://uigf.org)
|
||||
|
||||
## 安装
|
||||
|
||||
* 前往 [下载页面](https://go.hut.ao/down) 下载最新版本的 `胡桃` 安装包
|
||||
* (曾启用的可以跳过此步骤)在系统设置中打开 **开发者选项** 界面,勾选 `开发人员模式` 和 `允许 PowerShell 脚本`
|
||||
* 完全解压后,右键使用 powershell 运行 `install.ps1` 文件
|
||||
* 安装完成后可以关闭 `允许 PowerShell 脚本`
|
||||
|
||||
## 特别感谢
|
||||
|
||||
### 原神项目
|
||||
### 特定的原神项目
|
||||
|
||||
* [biuuu/genshin-wish-export](https://github.com/biuuu/genshin-wish-export)
|
||||
* [HolographicHat/YaeAchievement](https://github.com/HolographicHat/YaeAchievement)
|
||||
* [HolographicHat/MiHoYoWebBridge](https://github.com/HolographicHat/MiHoYoWebBridge)
|
||||
* [xunkong/xunkong](https://github.com/xunkong/xunkong)
|
||||
* [YuehaiTeam/cocogoat](https://github.com/YuehaiTeam/cocogoat)
|
||||
|
||||
### 技术栈
|
||||
### 使用的技术栈
|
||||
|
||||
* [CommunityToolkit/dotnet](https://github.com/CommunityToolkit/dotnet)
|
||||
* [CommunityToolkit/WindowsCommunityToolkit](https://github.com/CommunityToolkit/WindowsCommunityToolkit)
|
||||
* [dahall/taskscheduler](https://github.com/dahall/taskscheduler)
|
||||
* [dotnet/efcore](https://github.com/dotnet/efcore)
|
||||
* [dotnet/runtime](https://github.com/dotnet/runtime)
|
||||
* [DotNetAnalyzers/StyleCopAnalyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers)
|
||||
|
||||
156
azure-pipelines.yml
Normal file
156
azure-pipelines.yml
Normal file
@@ -0,0 +1,156 @@
|
||||
# CI process script for Snap.Hutao
|
||||
# Usage:
|
||||
# 1. Append the script in Pipelines
|
||||
# 2. Upload the pfx and cer certificates to Pipelines Library secrets
|
||||
# 3. Permit the pfx usage
|
||||
# 4. Add a `pw` variable in the script variables, which is pfx password
|
||||
# 5. Connect the GitHub in project settings
|
||||
# 6. Run
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
paths:
|
||||
exclude:
|
||||
- README.md
|
||||
- azure-pipelines.yml
|
||||
- .github/ISSUE_TEMPLATE/*.yml
|
||||
- .github/workflows/*.yml
|
||||
|
||||
|
||||
pool:
|
||||
vmImage: 'windows-2022'
|
||||
|
||||
variables:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
solution: '$(Build.SourcesDirectory)/src/Snap.Hutao/Snap.Hutao.sln'
|
||||
project: $(Build.SourcesDirectory)/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj'
|
||||
buildPlatform: 'x64'
|
||||
buildConfiguration: 'Release'
|
||||
build_date: $[ format('{0:yyyy}.{0:MM}.{0:dd}', pipeline.startTime) ]
|
||||
|
||||
|
||||
steps:
|
||||
- task: GetRevision@1
|
||||
displayName: get Pipelines revision number
|
||||
inputs:
|
||||
VariableName: 'rev_number'
|
||||
|
||||
- task: UseDotNet@2
|
||||
displayName: Install dotNet
|
||||
inputs:
|
||||
packageType: 'sdk'
|
||||
version: '7.x'
|
||||
includePreviewVersions: true
|
||||
|
||||
- task: NuGetToolInstaller@1
|
||||
name: 'NuGetToolInstaller'
|
||||
displayName: 'NuGet Installer'
|
||||
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet restore
|
||||
inputs:
|
||||
command: 'restore'
|
||||
restoreSolution: '$(solution)'
|
||||
feedsToUse: 'select'
|
||||
|
||||
- task: MsixPackaging@1
|
||||
displayName: Build binary package
|
||||
inputs:
|
||||
outputPath: '$(Build.ArtifactStagingDirectory)/'
|
||||
solution: '$(solution)'
|
||||
clean: false
|
||||
generateBundle: false
|
||||
buildConfiguration: 'Release'
|
||||
buildPlatform: 'x64'
|
||||
updateAppVersion: false
|
||||
appPackageDistributionMode: 'SideloadOnly'
|
||||
msbuildLocationMethod: 'location'
|
||||
msbuildLocation: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\MSBuild.exe'
|
||||
|
||||
- task: MagicChunks@2
|
||||
inputs:
|
||||
sourcePath: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\AppxManifest.xml'
|
||||
fileType: 'Xml'
|
||||
targetPathType: 'source'
|
||||
transformationType: 'json'
|
||||
transformations: |
|
||||
{
|
||||
"Package/Identity/@Name": "7f0db578-026f-4e0b-a75b-d5d06bb0a74c",
|
||||
"Package/Identity/@Publisher": "CN=DGP Studio CI",
|
||||
"Package/Identity/@Version": "$(build_date).$(rev_number)",
|
||||
"Package/Properties/DisplayName": "胡桃 Alpha",
|
||||
"Package/Properties/PublisherDisplayName":"DGP Studio CI"
|
||||
}
|
||||
|
||||
- task: CmdLine@2
|
||||
displayName: Create resources folder
|
||||
inputs:
|
||||
script: |
|
||||
mkdir Assets
|
||||
|
||||
mkdir Resource
|
||||
workingDirectory: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64'
|
||||
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy Assets Folder
|
||||
inputs:
|
||||
SourceFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\Assets'
|
||||
Contents: '**'
|
||||
TargetFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\Assets'
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy Resource Folder
|
||||
inputs:
|
||||
SourceFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\Resource'
|
||||
Contents: '**'
|
||||
TargetFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\Resource'
|
||||
|
||||
- task: CmdLine@2
|
||||
displayName: Build MSIX
|
||||
inputs:
|
||||
script: '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64\makeappx.exe" pack /d $(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64 /p $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix'
|
||||
|
||||
- task: MsixSigning@1
|
||||
displayName: Sign MSIX package
|
||||
inputs:
|
||||
package: '$(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix'
|
||||
certificate: 'DGP_Studio_CI.pfx'
|
||||
passwordVariable: 'pw'
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Upload Output'
|
||||
inputs:
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)/'
|
||||
artifact: 'Output'
|
||||
publishLocation: 'pipeline'
|
||||
|
||||
- task: DownloadSecureFile@1
|
||||
name: cerFile
|
||||
displayName: Download Root CA
|
||||
inputs:
|
||||
secureFile: 'Snap.Hutao.CI.cer'
|
||||
|
||||
- task: GitHubRelease@1
|
||||
inputs:
|
||||
gitHubConnection: 'github.com_Masterain'
|
||||
repositoryName: 'DGP-Studio/Snap.Hutao'
|
||||
action: 'create'
|
||||
target: '$(Build.SourceVersion)'
|
||||
tagSource: 'userSpecifiedTag'
|
||||
tag: '$(build_date).$(rev_number)'
|
||||
title: '$(build_date).$(rev_number)'
|
||||
releaseNotesSource: 'inline'
|
||||
releaseNotesInline: |
|
||||
## 提示 (Hint)
|
||||
该发布版本由 CI 程序自动打包生成,属于 `Alpha` 测试版,仅用于开发调试和内部测试用途。使用该版本可能存在意料之外的风险,请仅在有明确用途的情况下使用该版本。
|
||||
|
||||
This release is a Alpha Testing version generated by CI program automatically in a purpose of debugging and interal testing. Using this release may have unexpected risk, please only use it when you know what you are doing.
|
||||
assets: |
|
||||
$(Build.ArtifactStagingDirectory)/*
|
||||
$(cerFile.secureFilePath)
|
||||
isPreRelease: true
|
||||
changeLogCompareToRelease: 'lastFullRelease'
|
||||
changeLogType: 'commitBased'
|
||||
@@ -8,6 +8,7 @@
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -43,8 +43,10 @@
|
||||
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
|
||||
<!-- Converters -->
|
||||
<cwuc:BoolNegationConverter x:Key="BoolNegationConverter"/>
|
||||
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
|
||||
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
|
||||
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
|
||||
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
|
||||
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
||||
|
||||
@@ -46,7 +46,7 @@ public partial class App : Application
|
||||
if (firstInstance.IsCurrent)
|
||||
{
|
||||
// manually invoke
|
||||
Activation.Activate(firstInstance, activatedEventArgs);
|
||||
Activation.NonRedirectToActivate(firstInstance, activatedEventArgs);
|
||||
firstInstance.Activated += Activation.Activate;
|
||||
ToastNotificationManagerCompat.OnActivated += Activation.NotificationActivate;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.IO;
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
namespace Snap.Hutao.Context.FileSystem.Location;
|
||||
|
||||
@@ -19,7 +20,10 @@ internal class HutaoLocation : IFileSystemLocation
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
path = Path.GetFullPath(Path.Combine(myDocument, "Hutao"));
|
||||
|
||||
// 将测试版与正式版的文件目录分离
|
||||
string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao";
|
||||
path = Path.GetFullPath(Path.Combine(myDocument, folderName));
|
||||
}
|
||||
|
||||
return path;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI.UI.Behaviors;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Snap.Hutao.Control.Behavior;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.Image;
|
||||
namespace Snap.Hutao.Control.Media;
|
||||
|
||||
/// <summary>
|
||||
/// BGRA8 结构
|
||||
175
src/Snap.Hutao/Snap.Hutao/Control/Media/Rgba8.cs
Normal file
175
src/Snap.Hutao/Snap.Hutao/Control/Media/Rgba8.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// Some part of this file came from:
|
||||
// https://github.com/xunkong/desktop/tree/main/src/Desktop/Desktop/Pages/CharacterInfoPage.xaml.cs
|
||||
|
||||
using CommunityToolkit.WinUI;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.Media;
|
||||
|
||||
/// <summary>
|
||||
/// RGBA 颜色
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct Rgba8
|
||||
{
|
||||
/// <summary>
|
||||
/// R
|
||||
/// </summary>
|
||||
[FieldOffset(3)]
|
||||
public byte R;
|
||||
|
||||
/// <summary>
|
||||
/// G
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public byte G;
|
||||
|
||||
/// <summary>
|
||||
/// B
|
||||
/// </summary>
|
||||
[FieldOffset(1)]
|
||||
public byte B;
|
||||
|
||||
/// <summary>
|
||||
/// A
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public byte A;
|
||||
|
||||
[FieldOffset(0)]
|
||||
private readonly uint data;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的 RGBA8 颜色
|
||||
/// </summary>
|
||||
/// <param name="hex">色值字符串</param>
|
||||
public Rgba8(ReadOnlySpan<char> hex)
|
||||
{
|
||||
Must.Argument(hex.Length == 8, "色值长度不为8");
|
||||
R = 0;
|
||||
G = 0;
|
||||
B = 0;
|
||||
A = 0;
|
||||
data = Convert.ToUInt32(hex.ToString(), 16);
|
||||
}
|
||||
|
||||
private Rgba8(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
data = 0;
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
public static implicit operator Color(Rgba8 hexColor)
|
||||
{
|
||||
return Color.FromArgb(hexColor.A, hexColor.R, hexColor.G, hexColor.B);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 HSL 颜色转换
|
||||
/// </summary>
|
||||
/// <param name="hsl">HSL 颜色</param>
|
||||
/// <returns>RGBA8颜色</returns>
|
||||
public static Rgba8 FromHsl(HslColor hsl)
|
||||
{
|
||||
double chroma = (1 - Math.Abs((2 * hsl.L) - 1)) * hsl.S;
|
||||
double h1 = hsl.H / 60;
|
||||
double x = chroma * (1 - Math.Abs((h1 % 2) - 1));
|
||||
double m = hsl.L - (0.5 * chroma);
|
||||
double r1, g1, b1;
|
||||
|
||||
if (h1 < 1)
|
||||
{
|
||||
r1 = chroma;
|
||||
g1 = x;
|
||||
b1 = 0;
|
||||
}
|
||||
else if (h1 < 2)
|
||||
{
|
||||
r1 = x;
|
||||
g1 = chroma;
|
||||
b1 = 0;
|
||||
}
|
||||
else if (h1 < 3)
|
||||
{
|
||||
r1 = 0;
|
||||
g1 = chroma;
|
||||
b1 = x;
|
||||
}
|
||||
else if (h1 < 4)
|
||||
{
|
||||
r1 = 0;
|
||||
g1 = x;
|
||||
b1 = chroma;
|
||||
}
|
||||
else if (h1 < 5)
|
||||
{
|
||||
r1 = x;
|
||||
g1 = 0;
|
||||
b1 = chroma;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1 = chroma;
|
||||
g1 = 0;
|
||||
b1 = x;
|
||||
}
|
||||
|
||||
byte r = (byte)(255 * (r1 + m));
|
||||
byte g = (byte)(255 * (g1 + m));
|
||||
byte b = (byte)(255 * (b1 + m));
|
||||
byte a = (byte)(255 * hsl.A);
|
||||
|
||||
return new(r, g, b, a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换到 HSL 颜色
|
||||
/// </summary>
|
||||
/// <returns>HSL 颜色</returns>
|
||||
public HslColor ToHsl()
|
||||
{
|
||||
const double toDouble = 1.0 / 255;
|
||||
double r = toDouble * R;
|
||||
double g = toDouble * G;
|
||||
double b = toDouble * B;
|
||||
double max = Math.Max(Math.Max(r, g), b);
|
||||
double min = Math.Min(Math.Min(r, g), b);
|
||||
double chroma = max - min;
|
||||
double h1;
|
||||
|
||||
if (chroma == 0)
|
||||
{
|
||||
h1 = 0;
|
||||
}
|
||||
else if (max == r)
|
||||
{
|
||||
// The % operator doesn't do proper modulo on negative
|
||||
// numbers, so we'll add 6 before using it
|
||||
h1 = (((g - b) / chroma) + 6) % 6;
|
||||
}
|
||||
else if (max == g)
|
||||
{
|
||||
h1 = 2 + ((b - r) / chroma);
|
||||
}
|
||||
else
|
||||
{
|
||||
h1 = 4 + ((r - g) / chroma);
|
||||
}
|
||||
|
||||
double lightness = 0.5 * (max + min);
|
||||
double saturation = chroma == 0 ? 0 : chroma / (1 - Math.Abs((2 * lightness) - 1));
|
||||
|
||||
HslColor ret;
|
||||
ret.H = 60 * h1;
|
||||
ret.S = saturation;
|
||||
ret.L = lightness;
|
||||
ret.A = toDouble * A;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.System.WinRT;
|
||||
using WinRT;
|
||||
|
||||
namespace Snap.Hutao.Control.Media;
|
||||
|
||||
/// <summary>
|
||||
/// 软件位图拓展
|
||||
/// </summary>
|
||||
public static class SoftwareBitmapExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 混合模式 正常
|
||||
/// </summary>
|
||||
/// <param name="softwareBitmap">软件位图</param>
|
||||
/// <param name="tint">底色</param>
|
||||
public static unsafe void NormalBlend(this SoftwareBitmap softwareBitmap, Bgra8 tint)
|
||||
{
|
||||
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.ReadWrite))
|
||||
{
|
||||
using (IMemoryBufferReference reference = buffer.CreateReference())
|
||||
{
|
||||
reference.As<IMemoryBufferByteAccess>().GetBuffer(out byte* data, out uint length);
|
||||
|
||||
for (int i = 0; i < length; i += 4)
|
||||
{
|
||||
Bgra8* pixel = (Bgra8*)(data + i);
|
||||
byte baseAlpha = pixel->A;
|
||||
pixel->B = (byte)(((pixel->B * baseAlpha) + (tint.B * (0xFF - baseAlpha))) / 0xFF);
|
||||
pixel->G = (byte)(((pixel->G * baseAlpha) + (tint.G * (0xFF - baseAlpha))) / 0xFF);
|
||||
pixel->R = (byte)(((pixel->R * baseAlpha) + (tint.R * (0xFF - baseAlpha))) / 0xFF);
|
||||
pixel->A = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Documents;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Core;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.Text;
|
||||
@@ -19,8 +19,7 @@ namespace Snap.Hutao.Control.Text;
|
||||
/// </summary>
|
||||
public class DescriptionTextBlock : ContentControl
|
||||
{
|
||||
private static readonly DependencyProperty DescriptionProperty =
|
||||
Property<DescriptionTextBlock>.Depend(nameof(Description), string.Empty, OnDescriptionChanged);
|
||||
private static readonly DependencyProperty DescriptionProperty = Property<DescriptionTextBlock>.Depend(nameof(Description), string.Empty, OnDescriptionChanged);
|
||||
|
||||
private static readonly int ColorTagFullLength = "<color=#FFFFFFFF></color>".Length;
|
||||
private static readonly int ColorTagLeftLength = "<color=#FFFFFFFF>".Length;
|
||||
@@ -79,7 +78,7 @@ public class DescriptionTextBlock : ContentControl
|
||||
else if (description[i] == '<' && description[i + 1] == 'c')
|
||||
{
|
||||
AppendText(text, description[last..i]);
|
||||
HexColor color = new(description.Slice(i + 8, 8));
|
||||
Rgba8 color = new(description.Slice(i + 8, 8));
|
||||
int length = description[(i + ColorTagLeftLength)..].IndexOf('<');
|
||||
AppendColorText(text, description.Slice(i + ColorTagLeftLength, length), color);
|
||||
|
||||
@@ -115,7 +114,7 @@ public class DescriptionTextBlock : ContentControl
|
||||
text.Inlines.Add(new Run { Text = slice.ToString() });
|
||||
}
|
||||
|
||||
private static void AppendColorText(TextBlock text, ReadOnlySpan<char> slice, HexColor color)
|
||||
private static void AppendColorText(TextBlock text, ReadOnlySpan<char> slice, Rgba8 color)
|
||||
{
|
||||
Color targetColor;
|
||||
if (ThemeHelper.IsDarkMode(text.ActualTheme))
|
||||
@@ -126,7 +125,7 @@ public class DescriptionTextBlock : ContentControl
|
||||
{
|
||||
HslColor hsl = color.ToHsl();
|
||||
hsl.L *= 0.3;
|
||||
targetColor = HexColor.FromHsl(hsl);
|
||||
targetColor = Rgba8.FromHsl(hsl);
|
||||
}
|
||||
|
||||
text.Inlines.Add(new Run
|
||||
@@ -154,138 +153,4 @@ public class DescriptionTextBlock : ContentControl
|
||||
{
|
||||
ApplyDescription((TextBlock)Content, Description);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct HexColor
|
||||
{
|
||||
[FieldOffset(3)]
|
||||
public byte R;
|
||||
[FieldOffset(2)]
|
||||
public byte G;
|
||||
[FieldOffset(1)]
|
||||
public byte B;
|
||||
[FieldOffset(0)]
|
||||
public byte A;
|
||||
|
||||
[FieldOffset(0)]
|
||||
private readonly uint data;
|
||||
|
||||
public HexColor(ReadOnlySpan<char> hex)
|
||||
{
|
||||
Must.Argument(hex.Length == 8, "色值长度不为8");
|
||||
R = 0;
|
||||
G = 0;
|
||||
B = 0;
|
||||
A = 0;
|
||||
data = Convert.ToUInt32(hex.ToString(), 16);
|
||||
}
|
||||
|
||||
private HexColor(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
data = 0;
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
public static implicit operator Color(HexColor hexColor)
|
||||
{
|
||||
return Color.FromArgb(hexColor.A, hexColor.R, hexColor.G, hexColor.B);
|
||||
}
|
||||
|
||||
public static HexColor FromHsl(HslColor hsl)
|
||||
{
|
||||
double chroma = (1 - Math.Abs((2 * hsl.L) - 1)) * hsl.S;
|
||||
double h1 = hsl.H / 60;
|
||||
double x = chroma * (1 - Math.Abs((h1 % 2) - 1));
|
||||
double m = hsl.L - (0.5 * chroma);
|
||||
double r1, g1, b1;
|
||||
|
||||
if (h1 < 1)
|
||||
{
|
||||
r1 = chroma;
|
||||
g1 = x;
|
||||
b1 = 0;
|
||||
}
|
||||
else if (h1 < 2)
|
||||
{
|
||||
r1 = x;
|
||||
g1 = chroma;
|
||||
b1 = 0;
|
||||
}
|
||||
else if (h1 < 3)
|
||||
{
|
||||
r1 = 0;
|
||||
g1 = chroma;
|
||||
b1 = x;
|
||||
}
|
||||
else if (h1 < 4)
|
||||
{
|
||||
r1 = 0;
|
||||
g1 = x;
|
||||
b1 = chroma;
|
||||
}
|
||||
else if (h1 < 5)
|
||||
{
|
||||
r1 = x;
|
||||
g1 = 0;
|
||||
b1 = chroma;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1 = chroma;
|
||||
g1 = 0;
|
||||
b1 = x;
|
||||
}
|
||||
|
||||
byte r = (byte)(255 * (r1 + m));
|
||||
byte g = (byte)(255 * (g1 + m));
|
||||
byte b = (byte)(255 * (b1 + m));
|
||||
byte a = (byte)(255 * hsl.A);
|
||||
|
||||
return new(r, g, b, a);
|
||||
}
|
||||
|
||||
public HslColor ToHsl()
|
||||
{
|
||||
const double toDouble = 1.0 / 255;
|
||||
double r = toDouble * R;
|
||||
double g = toDouble * G;
|
||||
double b = toDouble * B;
|
||||
double max = Math.Max(Math.Max(r, g), b);
|
||||
double min = Math.Min(Math.Min(r, g), b);
|
||||
double chroma = max - min;
|
||||
double h1;
|
||||
|
||||
if (chroma == 0)
|
||||
{
|
||||
h1 = 0;
|
||||
}
|
||||
else if (max == r)
|
||||
{
|
||||
// The % operator doesn't do proper modulo on negative
|
||||
// numbers, so we'll add 6 before using it
|
||||
h1 = (((g - b) / chroma) + 6) % 6;
|
||||
}
|
||||
else if (max == g)
|
||||
{
|
||||
h1 = 2 + ((b - r) / chroma);
|
||||
}
|
||||
else
|
||||
{
|
||||
h1 = 4 + ((r - g) / chroma);
|
||||
}
|
||||
|
||||
double lightness = 0.5 * (max + min);
|
||||
double saturation = chroma == 0 ? 0 : chroma / (1 - Math.Abs((2 * lightness) - 1));
|
||||
|
||||
HslColor ret;
|
||||
ret.H = 60 * h1;
|
||||
ret.S = saturation;
|
||||
ret.L = lightness;
|
||||
ret.A = toDouble * A;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,17 @@ public class CommandLineBuilder
|
||||
return condition ? Append(name, value) : this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当参数不为 null 时添加参数
|
||||
/// </summary>
|
||||
/// <param name="name">参数名称</param>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>命令行建造器</returns>
|
||||
public CommandLineBuilder AppendIfNotNull(string name, object? value = null)
|
||||
{
|
||||
return AppendIf(name, value != null, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加参数
|
||||
/// </summary>
|
||||
@@ -37,12 +48,6 @@ public class CommandLineBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ToString"/>
|
||||
public string Build()
|
||||
{
|
||||
return ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ internal static class CoreEnvironment
|
||||
/// <summary>
|
||||
/// 米游社 Rpc 版本
|
||||
/// </summary>
|
||||
public const string HoyolabXrpcVersion = "2.41.0";
|
||||
public const string HoyolabXrpcVersion = "2.42.1";
|
||||
|
||||
/// <summary>
|
||||
/// 标准UA
|
||||
|
||||
@@ -44,11 +44,11 @@ public static class DbSetExtension
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static Task<int> AddAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
public static async ValueTask<int> AddAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.Add(entity);
|
||||
return dbSet.Context().SaveChangesAsync();
|
||||
return await dbSet.Context().SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -72,11 +72,11 @@ public static class DbSetExtension
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entities">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static Task<int> AddRangeAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, IEnumerable<TEntity> entities)
|
||||
public static async ValueTask<int> AddRangeAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, IEnumerable<TEntity> entities)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.AddRange(entities);
|
||||
return dbSet.Context().SaveChangesAsync();
|
||||
return await dbSet.Context().SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,11 +100,11 @@ public static class DbSetExtension
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static Task<int> RemoveAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
public static async ValueTask<int> RemoveAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.Remove(entity);
|
||||
return dbSet.Context().SaveChangesAsync();
|
||||
return await dbSet.Context().SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -128,10 +128,10 @@ public static class DbSetExtension
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static Task<int> UpdateAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
public static async ValueTask<int> UpdateAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.Update(entity);
|
||||
return dbSet.Context().SaveChangesAsync();
|
||||
return await dbSet.Context().SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Json.Annotation;
|
||||
using Snap.Hutao.Core.Json.Converter;
|
||||
using System.Reflection;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Core.Json;
|
||||
|
||||
@@ -23,6 +23,11 @@ internal static class Activation
|
||||
/// </summary>
|
||||
public const string LaunchGame = "LaunchGame";
|
||||
|
||||
/// <summary>
|
||||
/// 从剪贴板导入成就
|
||||
/// </summary>
|
||||
public const string ImportUIAFFromClipBoard = "ImportUIAFFromClipBoard";
|
||||
|
||||
private static readonly SemaphoreSlim ActivateSemaphore = new(1);
|
||||
|
||||
/// <summary>
|
||||
@@ -49,7 +54,21 @@ internal static class Activation
|
||||
_ = sender;
|
||||
if (!ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
|
||||
{
|
||||
HandleActivationAsync(args).SafeForget();
|
||||
HandleActivationAsync(args, true).SafeForget();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发激活事件
|
||||
/// </summary>
|
||||
/// <param name="sender">发送方</param>
|
||||
/// <param name="args">激活参数</param>
|
||||
public static void NonRedirectToActivate(object? sender, AppActivationArguments args)
|
||||
{
|
||||
_ = sender;
|
||||
if (!ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
|
||||
{
|
||||
HandleActivationAsync(args, false).SafeForget();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,24 +95,24 @@ internal static class Activation
|
||||
/// 异步响应激活事件
|
||||
/// </summary>
|
||||
/// <returns>任务</returns>
|
||||
private static async Task HandleActivationAsync(AppActivationArguments args)
|
||||
private static async Task HandleActivationAsync(AppActivationArguments args, bool isRedirected)
|
||||
{
|
||||
if (ActivateSemaphore.CurrentCount > 0)
|
||||
{
|
||||
using (await ActivateSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
await HandleActivationCoreAsync(args).ConfigureAwait(false);
|
||||
await HandleActivationCoreAsync(args, isRedirected).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleActivationCoreAsync(AppActivationArguments args)
|
||||
private static async Task HandleActivationCoreAsync(AppActivationArguments args, bool isRedirected)
|
||||
{
|
||||
if (args.Kind == ExtendedActivationKind.Protocol)
|
||||
{
|
||||
if (args.TryGetProtocolActivatedUri(out Uri? uri))
|
||||
{
|
||||
await HandleUrlActivationAsync(uri).ConfigureAwait(false);
|
||||
await HandleUrlActivationAsync(uri, isRedirected).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (args.Kind == ExtendedActivationKind.Launch)
|
||||
@@ -131,7 +150,7 @@ internal static class Activation
|
||||
.SafeForget();
|
||||
}
|
||||
|
||||
private static async Task HandleUrlActivationAsync(Uri uri)
|
||||
private static async Task HandleUrlActivationAsync(Uri uri, bool isRedirected)
|
||||
{
|
||||
UriBuilder builder = new(uri);
|
||||
|
||||
@@ -144,28 +163,29 @@ internal static class Activation
|
||||
case "achievement":
|
||||
{
|
||||
await WaitMainWindowAsync().ConfigureAwait(false);
|
||||
await HandleAchievementActionAsync(action, parameter).ConfigureAwait(false);
|
||||
await HandleAchievementActionAsync(action, parameter, isRedirected).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case "dailynote":
|
||||
{
|
||||
await HandleDailyNoteActionAsync(action, parameter).ConfigureAwait(false);
|
||||
await HandleDailyNoteActionAsync(action, parameter, isRedirected).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleAchievementActionAsync(string action, string parameter)
|
||||
private static async Task HandleAchievementActionAsync(string action, string parameter, bool isRedirected)
|
||||
{
|
||||
_ = parameter;
|
||||
_ = isRedirected;
|
||||
switch (action)
|
||||
{
|
||||
case "/import":
|
||||
{
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
|
||||
INavigationAwaiter navigationAwaiter = new NavigationExtra("InvokeByUri");
|
||||
INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipBoard);
|
||||
await Ioc.Default
|
||||
.GetRequiredService<INavigationService>()
|
||||
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
|
||||
@@ -175,7 +195,7 @@ internal static class Activation
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleDailyNoteActionAsync(string action, string parameter)
|
||||
private static async Task HandleDailyNoteActionAsync(string action, string parameter, bool isRedirected)
|
||||
{
|
||||
_ = parameter;
|
||||
switch (action)
|
||||
@@ -186,6 +206,14 @@ internal static class Activation
|
||||
.GetRequiredService<IDailyNoteService>()
|
||||
.RefreshDailyNotesAsync(true)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// Check if it's redirected.
|
||||
if (!isRedirected)
|
||||
{
|
||||
// It's a direct open process, should exit immediately.
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,22 @@ internal static class AppInstanceExtension
|
||||
/// </summary>
|
||||
/// <param name="appInstance">app实例</param>
|
||||
/// <param name="args">参数</param>
|
||||
[SuppressMessage("", "VSTHRD002")]
|
||||
[SuppressMessage("", "VSTHRD110")]
|
||||
public static unsafe void RedirectActivationTo(this AppInstance appInstance, AppActivationArguments args)
|
||||
public static void RedirectActivationTo(this AppInstance appInstance, AppActivationArguments args)
|
||||
{
|
||||
HANDLE redirectEventHandle = CreateEvent((SECURITY_ATTRIBUTES*)null, true, false, null);
|
||||
Task.Run(() =>
|
||||
HANDLE redirectEventHandle = UnsafeCreateEvent();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
appInstance.RedirectActivationToAsync(args).AsTask().Wait();
|
||||
await appInstance.RedirectActivationToAsync(args);
|
||||
SetEvent(redirectEventHandle);
|
||||
});
|
||||
|
||||
ReadOnlySpan<HANDLE> handles = new(in redirectEventHandle);
|
||||
|
||||
// non-blocking
|
||||
CoWaitForMultipleObjects((uint)CWMO_FLAGS.CWMO_DEFAULT, INFINITE, handles, out uint _);
|
||||
}
|
||||
|
||||
private static unsafe HANDLE UnsafeCreateEvent()
|
||||
{
|
||||
return CreateEvent(default(SECURITY_ATTRIBUTES*), true, false, null);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Core;
|
||||
/// <summary>
|
||||
/// 任务计划器服务
|
||||
/// </summary>
|
||||
internal static class TaskSchedulerHelper
|
||||
internal static class ScheduleTaskHelper
|
||||
{
|
||||
private const string DailyNoteRefreshTaskName = "SnapHutaoDailyNoteRefreshTask";
|
||||
|
||||
@@ -45,4 +45,30 @@ internal static class TaskSchedulerHelper
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载全部注册的任务
|
||||
/// </summary>
|
||||
/// <returns>是否卸载成功</returns>
|
||||
public static bool UnregisterAllTasks()
|
||||
{
|
||||
try
|
||||
{
|
||||
SchedulerTask? targetTask = TaskService.Instance.GetTask(DailyNoteRefreshTaskName);
|
||||
if (targetTask != null)
|
||||
{
|
||||
TaskService.Instance.RootFolder.DeleteTask(DailyNoteRefreshTaskName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (COMException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,9 @@ public readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueue
|
||||
/// <inheritdoc/>
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
dispatherQueue.TryEnqueue(() => { continuation(); });
|
||||
dispatherQueue.TryEnqueue(() =>
|
||||
{
|
||||
continuation();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@ internal abstract class WebView2Helper
|
||||
/// </summary>
|
||||
public static string Version
|
||||
{
|
||||
get => version;
|
||||
get
|
||||
{
|
||||
_ = IsSupported;
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Core.Windowing;
|
||||
/// <summary>
|
||||
/// <see cref="AppWindow"/> 扩展
|
||||
/// </summary>
|
||||
public static class AppWindowExtensions
|
||||
public static class AppWindowExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前 <see cref="AppWindow"/> 的呈现矩形
|
||||
@@ -134,7 +134,8 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<BackdropTypeChangedMe
|
||||
(string pos, string size) = GetPostionAndSize(appWindow);
|
||||
logger.LogInformation(EventIds.WindowState, "Postion: [{pos}], Size: [{size}]", pos, size);
|
||||
|
||||
appWindow.Show(true);
|
||||
// appWindow.Show(true);
|
||||
window.Activate();
|
||||
|
||||
systemBackdrop = new(window);
|
||||
bool micaApplied = systemBackdrop.TryApply();
|
||||
|
||||
@@ -37,6 +37,7 @@ internal class PickerFactory : IPickerFactory
|
||||
picker.FileTypeFilter.Add(type);
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/WindowsAppSDK/issues/2931
|
||||
picker.FileTypeFilter.Add(AnyType);
|
||||
|
||||
return picker;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding;
|
||||
namespace Snap.Hutao.Model.Binding.Achievement;
|
||||
|
||||
/// <summary>
|
||||
/// 用于视图绑定的成就
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.Achievement;
|
||||
|
||||
/// <summary>
|
||||
/// 绑定成就分类
|
||||
/// </summary>
|
||||
public class AchievementGoal : ObservableObject
|
||||
{
|
||||
private double finishPercent;
|
||||
private string? finishDescription;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的成就分类
|
||||
/// </summary>
|
||||
/// <param name="goal">分类</param>
|
||||
public AchievementGoal(Metadata.Achievement.AchievementGoal goal)
|
||||
{
|
||||
Id = goal.Id;
|
||||
Order = goal.Order;
|
||||
Name = goal.Name;
|
||||
Icon = Metadata.Converter.AchievementIconConverter.IconNameToUri(goal.Icon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序顺序
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 图标
|
||||
/// </summary>
|
||||
public Uri? Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 完成百分比
|
||||
/// </summary>
|
||||
public double FinishPercent { get => finishPercent; set => SetProperty(ref finishPercent, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 完成百分比
|
||||
/// </summary>
|
||||
public string? FinishDescription { get => finishDescription; set => SetProperty(ref finishDescription, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 更新进度
|
||||
/// </summary>
|
||||
/// <param name="finished">完成项</param>
|
||||
/// <param name="count">总项</param>
|
||||
public void UpdateFinishPercent(int finished, int count)
|
||||
{
|
||||
FinishPercent = (double)finished / count;
|
||||
FinishDescription = $"{finished}/{count} - {FinishPercent:P2}";
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Calculable;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
|
||||
/// <summary>
|
||||
/// 角色信息
|
||||
/// </summary>
|
||||
public class Avatar
|
||||
public class Avatar : ICalculableSource<ICalculableAvatar>
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
@@ -45,11 +48,6 @@ public class Avatar
|
||||
/// </summary>
|
||||
public string Level { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 好感度等级
|
||||
/// </summary>
|
||||
public int FetterLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 武器
|
||||
/// </summary>
|
||||
@@ -84,4 +82,25 @@ public class Avatar
|
||||
/// 双爆评分
|
||||
/// </summary>
|
||||
public string CritScore { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 好感度等级
|
||||
/// </summary>
|
||||
public int FetterLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
internal AvatarId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 等级数字
|
||||
/// </summary>
|
||||
internal int LevelNumber { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableAvatar ToCalculable()
|
||||
{
|
||||
return new CalculableAvatar(this);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,35 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Calculable;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
|
||||
/// <summary>
|
||||
/// 天赋
|
||||
/// </summary>
|
||||
public class Skill : NameIconDescription
|
||||
public class Skill : NameIconDescription, ICalculableSource<ICalculableSkill>
|
||||
{
|
||||
/// <summary>
|
||||
/// 技能属性
|
||||
/// </summary>
|
||||
public LevelParam<string, ParameterInfo> Info { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 技能组Id
|
||||
/// </summary>
|
||||
internal int GroupId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 技能等级,仅用于养成计算
|
||||
/// </summary>
|
||||
internal int Level { get; set; }
|
||||
internal int LevelNumber { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableSkill ToCalculable()
|
||||
{
|
||||
return new CalculableSkill(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Calculable;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
|
||||
/// <summary>
|
||||
/// 武器
|
||||
/// </summary>
|
||||
public class Weapon : EquipBase
|
||||
public class Weapon : EquipBase, ICalculableSource<ICalculableWeapon>
|
||||
{
|
||||
/// <summary>
|
||||
/// 副属性
|
||||
@@ -27,4 +31,20 @@ public class Weapon : EquipBase
|
||||
/// 精炼被动
|
||||
/// </summary>
|
||||
public string AffixDescription { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
internal WeaponId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 等级数字
|
||||
/// </summary>
|
||||
internal int LevelNumber { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableWeapon ToCalculable()
|
||||
{
|
||||
return new CalculableWeapon(this);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ public class CultivateItem : ObservableObject
|
||||
Inner = inner;
|
||||
Entity = entity;
|
||||
isFinished = Entity.IsFinished;
|
||||
IsToday = CultivateItemHelper.IsTodaysMaterial(inner.Id, DateTimeOffset.Now);
|
||||
|
||||
FinishStateCommand = new RelayCommand(FlipIsFinished);
|
||||
}
|
||||
@@ -59,6 +60,16 @@ public class CultivateItem : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为今日物品
|
||||
/// </summary>
|
||||
public bool IsToday { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 对应背包物品的个数
|
||||
/// </summary>
|
||||
public uint InventoryItemCount { get; set; }
|
||||
|
||||
private void FlipIsFinished()
|
||||
{
|
||||
IsFinished = !IsFinished;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.Cultivation;
|
||||
|
||||
/// <summary>
|
||||
/// 养成物品帮助类
|
||||
/// </summary>
|
||||
public static class CultivateItemHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 判断是否为当日物品
|
||||
/// </summary>
|
||||
/// <param name="itemId">材料Id</param>
|
||||
/// <param name="now">时间</param>
|
||||
/// <returns>是否为当日物品</returns>
|
||||
public static bool IsTodaysMaterial(int itemId, DateTimeOffset now)
|
||||
{
|
||||
DateTimeOffset utcNow = now.ToUniversalTime();
|
||||
utcNow = utcNow.AddHours(4);
|
||||
DayOfWeek dayOfWeek = utcNow.DayOfWeek;
|
||||
|
||||
return dayOfWeek switch
|
||||
{
|
||||
DayOfWeek.Monday or DayOfWeek.Thursday => itemId switch
|
||||
{
|
||||
104301 or 104302 or 104303 => true, // 「自由」
|
||||
104310 or 104311 or 104312 => true, // 「繁荣」
|
||||
104320 or 104321 or 104322 => true, // 「浮世」
|
||||
104329 or 104330 or 104331 => true, // 「诤言」
|
||||
114001 or 114002 or 114003 or 114004 => true, // 高塔孤王
|
||||
114013 or 114014 or 114015 or 114016 => true, // 孤云寒林
|
||||
114025 or 114026 or 114027 or 114028 => true, // 远海夷地
|
||||
114037 or 114038 or 114039 or 114040 => true, // 谧林涓露
|
||||
_ => false,
|
||||
},
|
||||
DayOfWeek.Tuesday or DayOfWeek.Friday => itemId switch
|
||||
{
|
||||
104304 or 104305 or 104306 => true, // 「抗争」
|
||||
104313 or 104314 or 104315 => true, // 「勤劳」
|
||||
104323 or 104324 or 104325 => true, // 「风雅」
|
||||
104332 or 104333 or 104334 => true, // 「巧思」
|
||||
114005 or 114006 or 114007 or 114008 => true, // 凛风奔狼
|
||||
114017 or 114018 or 114019 or 114020 => true, // 雾海云间
|
||||
114029 or 114030 or 114031 or 114032 => true, // 鸣神御灵
|
||||
114041 or 114042 or 114043 or 114044 => true, // 绿洲花园
|
||||
_ => false,
|
||||
},
|
||||
DayOfWeek.Wednesday or DayOfWeek.Saturday => itemId switch
|
||||
{
|
||||
104307 or 104308 or 104309 => true, // 「诗文」
|
||||
104316 or 104317 or 104318 => true, // 「黄金」
|
||||
104326 or 104327 or 104328 => true, // 「天光」
|
||||
104335 or 104336 or 104337 => true, // 「笃行」
|
||||
114009 or 114010 or 114011 or 114012 => true, // 狮牙斗士
|
||||
114021 or 114022 or 114023 or 114024 => true, // 漆黑陨铁
|
||||
114033 or 114034 or 114035 or 114036 => true, // 今昔剧画
|
||||
114045 or 114046 or 114047 or 114048 => true, // 谧林涓露
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.Cultivation;
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于统计总数的养成物品
|
||||
/// </summary>
|
||||
public class StatisticsCultivateItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的统计用养成物品
|
||||
/// </summary>
|
||||
/// <param name="inner">材料</param>
|
||||
/// <param name="entity">实体</param>
|
||||
public StatisticsCultivateItem(Material inner, Entity.CultivateItem entity)
|
||||
{
|
||||
Inner = inner;
|
||||
Count = entity.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 元数据
|
||||
/// </summary>
|
||||
public Material Inner { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 对应背包物品的个数
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 对应背包物品的个数
|
||||
/// </summary>
|
||||
public uint TotalCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否完成
|
||||
/// </summary>
|
||||
public bool IsFinished { get => Count >= TotalCount; }
|
||||
|
||||
/// <summary>
|
||||
/// 格式化总数
|
||||
/// </summary>
|
||||
public string CountFormatted { get => $"{Count}/{TotalCount}"; }
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.Hutao;
|
||||
|
||||
/// <summary>
|
||||
/// 料理奖励视图
|
||||
/// </summary>
|
||||
public class CookBonusView
|
||||
{
|
||||
/// <summary>
|
||||
/// 原型
|
||||
/// </summary>
|
||||
public Material OriginItem { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public Material Item { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的料理奖励视图
|
||||
/// </summary>
|
||||
/// <param name="cookBonus">料理奖励</param>
|
||||
/// <param name="idMaterialMap">材料映射</param>
|
||||
/// <returns>新的料理奖励视图</returns>
|
||||
public static CookBonusView? Create(CookBonus? cookBonus, Dictionary<MaterialId, Material> idMaterialMap)
|
||||
{
|
||||
if (cookBonus == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
CookBonusView view = new()
|
||||
{
|
||||
OriginItem = idMaterialMap[cookBonus.OriginItemId],
|
||||
Item = idMaterialMap[cookBonus.ItemId],
|
||||
};
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ internal class Team : List<ComplexAvatar>
|
||||
public Team(ItemRate<string, int> team, Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
: base(4)
|
||||
{
|
||||
IEnumerable<int> ids = team.Item.Split(',').Select(int.Parse);
|
||||
IOrderedEnumerable<int> ids = team.Item.Split(',').Select(int.Parse).OrderByDescending(x => x);
|
||||
|
||||
foreach (int id in ids)
|
||||
{
|
||||
@@ -28,8 +28,14 @@ internal class Team : List<ComplexAvatar>
|
||||
}
|
||||
|
||||
Rate = $"上场 {team.Rate} 次";
|
||||
Name = TeamPopularNameParser.GetName(ids.ToHashSet());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 队伍俗名
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上场次数
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.Hutao;
|
||||
|
||||
/// <summary>
|
||||
/// 队伍俗名解析器
|
||||
/// </summary>
|
||||
internal static class TeamPopularNameParser
|
||||
{
|
||||
/// <summary>
|
||||
/// 已知的队伍名称
|
||||
/// </summary>
|
||||
private static readonly ImmutableDictionary<string, ImmutableHashSet<int>> KnownTeamNames = new Dictionary<string, ImmutableHashSet<int>>()
|
||||
{
|
||||
["雷国|雷行香班"] = new HashSet<int>() { AvatarIds.Shougun, AvatarIds.Xingqiu, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||
["雷国|雷夜香班"] = new HashSet<int>() { AvatarIds.Shougun, AvatarIds.Yelan, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||
["雷国|雷万香班"] = new HashSet<int>() { AvatarIds.Shougun, AvatarIds.Kazuha, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||
["雷九|雷九万班"] = new HashSet<int>() { AvatarIds.Shougun, AvatarIds.Sara, AvatarIds.Kazuha, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||
["万达国际"] = new HashSet<int>() { AvatarIds.Kazuha, AvatarIds.Tartaglia, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||
["胡行钟夜"] = new HashSet<int>() { AvatarIds.Hutao, AvatarIds.Xingqiu, AvatarIds.Zhongli, AvatarIds.Yelan, }.ToImmutableHashSet(),
|
||||
["激晴|刻皇万妲"] = new HashSet<int>() { AvatarIds.Keqing, AvatarIds.Fischl, AvatarIds.Kazuha, AvatarIds.Nahida, }.ToImmutableHashSet(),
|
||||
["永冻|神鹤万心"] = new HashSet<int>() { AvatarIds.Ayaka, AvatarIds.Shenhe, AvatarIds.Kazuha, AvatarIds.Kokomi, }.ToImmutableHashSet(),
|
||||
["永冻|神钟万心"] = new HashSet<int>() { AvatarIds.Ayaka, AvatarIds.Zhongli, AvatarIds.Kazuha, AvatarIds.Kokomi, }.ToImmutableHashSet(),
|
||||
["融甘|融化甘雨"] = new HashSet<int>() { AvatarIds.Ganyu, AvatarIds.Zhongli, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||
["妮绽放|女主"] = new HashSet<int>() { AvatarIds.Nilou, AvatarIds.Kokomi, AvatarIds.Nahida, AvatarIds.PlayerGirl, }.ToImmutableHashSet(),
|
||||
["妮绽放|男主"] = new HashSet<int>() { AvatarIds.Nilou, AvatarIds.Kokomi, AvatarIds.Nahida, AvatarIds.PlayerBoy, }.ToImmutableHashSet(),
|
||||
["一斗岩队"] = new HashSet<int>() { AvatarIds.Itto, AvatarIds.Albedo, AvatarIds.Zhongli, AvatarIds.Gorou, }.ToImmutableHashSet(),
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// 获取队伍名称
|
||||
/// </summary>
|
||||
/// <param name="ids">队伍集</param>
|
||||
/// <returns>队伍名称</returns>
|
||||
public static string GetName(HashSet<int> ids)
|
||||
{
|
||||
foreach (KeyValuePair<string, ImmutableHashSet<int>> entry in KnownTeamNames)
|
||||
{
|
||||
if (entry.Value.SetEquals(ids))
|
||||
{
|
||||
return entry.Key;
|
||||
}
|
||||
}
|
||||
|
||||
return "尚未命名";
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,12 @@
|
||||
// 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;
|
||||
namespace Snap.Hutao.Model.Calculable;
|
||||
|
||||
/// <summary>
|
||||
/// 可计算角色
|
||||
@@ -25,7 +21,7 @@ internal class CalculableAvatar : ObservableObject, ICalculableAvatar
|
||||
/// 构造一个新的可计算角色
|
||||
/// </summary>
|
||||
/// <param name="avatar">角色</param>
|
||||
public CalculableAvatar(Avatar avatar)
|
||||
public CalculableAvatar(Metadata.Avatar.Avatar avatar)
|
||||
{
|
||||
AvatarId = avatar.Id;
|
||||
LevelMin = 1;
|
||||
@@ -34,6 +30,25 @@ internal class CalculableAvatar : ObservableObject, ICalculableAvatar
|
||||
Name = avatar.Name;
|
||||
Icon = AvatarIconConverter.IconNameToUri(avatar.Icon);
|
||||
Quality = avatar.Quality;
|
||||
|
||||
LevelCurrent = LevelMin;
|
||||
LevelTarget = LevelMax;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的可计算角色
|
||||
/// </summary>
|
||||
/// <param name="avatar">角色</param>
|
||||
public CalculableAvatar(Binding.AvatarProperty.Avatar avatar)
|
||||
{
|
||||
AvatarId = avatar.Id;
|
||||
LevelMin = avatar.LevelNumber;
|
||||
LevelMax = 90; // hard coded 90
|
||||
Skills = avatar.Skills.Select(s => s.ToCalculable()).ToList();
|
||||
Name = avatar.Name;
|
||||
Icon = avatar.Icon;
|
||||
Quality = avatar.Quality;
|
||||
|
||||
LevelCurrent = LevelMin;
|
||||
LevelTarget = LevelMax;
|
||||
}
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Avatar;
|
||||
namespace Snap.Hutao.Model.Calculable;
|
||||
|
||||
/// <summary>
|
||||
/// 可计算的技能
|
||||
@@ -28,6 +29,24 @@ internal class CalculableSkill : ObservableObject, ICalculableSkill
|
||||
Name = skill.Name;
|
||||
Icon = SkillIconConverter.IconNameToUri(skill.Icon);
|
||||
Quality = ItemQuality.QUALITY_NONE;
|
||||
|
||||
LevelCurrent = LevelMin;
|
||||
LevelTarget = LevelMax;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的可计算的技能
|
||||
/// </summary>
|
||||
/// <param name="skill">技能</param>
|
||||
public CalculableSkill(Binding.AvatarProperty.Skill skill)
|
||||
{
|
||||
GruopId = skill.GroupId;
|
||||
LevelMin = skill.LevelNumber;
|
||||
LevelMax = 10; // hard coded 10 here
|
||||
Name = skill.Name;
|
||||
Icon = skill.Icon;
|
||||
Quality = ItemQuality.QUALITY_NONE;
|
||||
|
||||
LevelCurrent = LevelMin;
|
||||
LevelTarget = LevelMax;
|
||||
}
|
||||
@@ -2,16 +2,12 @@
|
||||
// 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;
|
||||
namespace Snap.Hutao.Model.Calculable;
|
||||
|
||||
/// <summary>
|
||||
/// 可计算武器
|
||||
@@ -25,7 +21,7 @@ public class CalculableWeapon : ObservableObject, ICalculableWeapon
|
||||
/// 构造一个新的可计算武器
|
||||
/// </summary>
|
||||
/// <param name="weapon">武器</param>
|
||||
public CalculableWeapon(Weapon weapon)
|
||||
public CalculableWeapon(Metadata.Weapon.Weapon weapon)
|
||||
{
|
||||
WeaponId = weapon.Id;
|
||||
LevelMin = 1;
|
||||
@@ -33,6 +29,24 @@ public class CalculableWeapon : ObservableObject, ICalculableWeapon
|
||||
Name = weapon.Name;
|
||||
Icon = EquipIconConverter.IconNameToUri(weapon.Icon);
|
||||
Quality = weapon.RankLevel;
|
||||
|
||||
LevelCurrent = LevelMin;
|
||||
LevelTarget = LevelMax;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的可计算武器
|
||||
/// </summary>
|
||||
/// <param name="weapon">武器</param>
|
||||
public CalculableWeapon(Binding.AvatarProperty.Weapon weapon)
|
||||
{
|
||||
WeaponId = weapon.Id;
|
||||
LevelMin = weapon.LevelNumber;
|
||||
LevelMax = (int)weapon.Quality >= 3 ? 90 : 70;
|
||||
Name = weapon.Name;
|
||||
Icon = weapon.Icon;
|
||||
Quality = weapon.Quality;
|
||||
|
||||
LevelCurrent = LevelMin;
|
||||
LevelTarget = LevelMax;
|
||||
}
|
||||
@@ -32,59 +32,3 @@ internal class UIIF
|
||||
[JsonPropertyName("list")]
|
||||
public List<UIIFItem> List { get; set; } = default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UIIF物品
|
||||
/// </summary>
|
||||
[JsonDerivedType(typeof(UIIFReliquary))]
|
||||
[JsonDerivedType(typeof(UIIFWeapon))]
|
||||
internal class UIIFItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("itemId")]
|
||||
public int ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("count")]
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UIIF圣遗物
|
||||
/// </summary>
|
||||
internal class UIIFReliquary : UIIFItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("level")]
|
||||
public int Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 副属性列表
|
||||
/// </summary>
|
||||
[JsonPropertyName("appendPropIdList")]
|
||||
public List<int> AppendPropIdList { get; set; } = default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UIIF武器
|
||||
/// </summary>
|
||||
internal class UIIFWeapon : UIIFItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("level")]
|
||||
public int Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 精炼等级 0-4
|
||||
/// </summary>
|
||||
[JsonPropertyName("promoteLevel")]
|
||||
public int PromoteLevel { get; set; }
|
||||
}
|
||||
@@ -3,12 +3,6 @@
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.InterChange.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// UIIF物品
|
||||
/// </summary>
|
||||
internal class UIIFItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("itemId")]
|
||||
public int ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("count")]
|
||||
public int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 等级
|
||||
/// Reliquary/Weapon
|
||||
/// </summary>
|
||||
[JsonPropertyName("level")]
|
||||
public int? Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 副属性列表
|
||||
/// Reliquary
|
||||
/// </summary>
|
||||
[JsonPropertyName("appendPropIdList")]
|
||||
public List<int>? AppendPropIdList { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 精炼等级 0-4
|
||||
/// Weapon
|
||||
/// </summary>
|
||||
[JsonPropertyName("promoteLevel")]
|
||||
public int? PromoteLevel { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Extension;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
/// <summary>
|
||||
/// 不可变的原生枚举
|
||||
/// </summary>
|
||||
public static class ImmutableIntrinsics
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属地区
|
||||
/// </summary>
|
||||
public static readonly ImmutableList<string> AssociationTypes = Enum.GetValues<AssociationType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
|
||||
/// <summary>
|
||||
/// 武器类型
|
||||
/// </summary>
|
||||
public static readonly ImmutableList<string> WeaponTypes = Enum.GetValues<WeaponType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
|
||||
/// <summary>
|
||||
/// 物品类型
|
||||
/// </summary>
|
||||
public static readonly ImmutableList<string> ItemQualities = Enum.GetValues<ItemQuality>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
|
||||
/// <summary>
|
||||
/// 身材类型
|
||||
/// </summary>
|
||||
public static readonly ImmutableList<string> BodyTypes = Enum.GetValues<BodyType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
|
||||
/// <summary>
|
||||
/// 战斗属性
|
||||
/// </summary>
|
||||
public static readonly ImmutableList<string> FightProperties = Enum.GetValues<FightProperty>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
}
|
||||
59
src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs
Normal file
59
src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
/// <summary>
|
||||
/// 材料类型
|
||||
/// </summary>
|
||||
[SuppressMessage("", "SA1602")]
|
||||
public enum MaterialType
|
||||
{
|
||||
MATERIAL_NONE = 0,
|
||||
MATERIAL_FOOD = 1,
|
||||
MATERIAL_QUEST = 2,
|
||||
MATERIAL_EXCHANGE = 4,
|
||||
MATERIAL_CONSUME,
|
||||
MATERIAL_EXP_FRUIT,
|
||||
MATERIAL_AVATAR,
|
||||
MATERIAL_ADSORBATE,
|
||||
MATERIAL_CRICKET,
|
||||
MATERIAL_ELEM_CRYSTAL,
|
||||
MATERIAL_WEAPON_EXP_STONE,
|
||||
MATERIAL_CHEST,
|
||||
MATERIAL_RELIQUARY_MATERIAL,
|
||||
MATERIAL_AVATAR_MATERIAL,
|
||||
MATERIAL_NOTICE_ADD_HP,
|
||||
MATERIAL_SEA_LAMP,
|
||||
MATERIAL_SELECTABLE_CHEST,
|
||||
MATERIAL_FLYCLOAK,
|
||||
MATERIAL_NAMECARD,
|
||||
MATERIAL_TALENT,
|
||||
MATERIAL_WIDGET,
|
||||
MATERIAL_CHEST_BATCH_USE,
|
||||
MATERIAL_FAKE_ABSORBATE,
|
||||
MATERIAL_CONSUME_BATCH_USE,
|
||||
MATERIAL_WOOD,
|
||||
MATERIAL_FURNITURE_FORMULA = 27,
|
||||
MATERIAL_CHANNELLER_SLAB_BUFF,
|
||||
MATERIAL_FURNITURE_SUITE_FORMULA,
|
||||
MATERIAL_COSTUME,
|
||||
MATERIAL_HOME_SEED,
|
||||
MATERIAL_FISH_BAIT,
|
||||
MATERIAL_FISH_ROD,
|
||||
MATERIAL_SUMO_BUFF, // never appear
|
||||
MATERIAL_FIREWORKS,
|
||||
MATERIAL_BGM,
|
||||
MATERIAL_SPICE_FOOD,
|
||||
MATERIAL_ACTIVITY_ROBOT,
|
||||
MATERIAL_ACTIVITY_GEAR,
|
||||
MATERIAL_ACTIVITY_JIGSAW,
|
||||
MATERIAL_ARANARA,
|
||||
MATERIAL_GCG_CARD,
|
||||
MATERIAL_GCG_CARD_FACE, // 影幻卡面
|
||||
MATERIAL_GCG_CARD_BACK,
|
||||
MATERIAL_GCG_FIELD,
|
||||
MATERIAL_DESHRET_MANUAL,
|
||||
MATERIAL_RENAME_ITEM,
|
||||
MATERIAL_GCG_EXCHANGE_ITEM,
|
||||
}
|
||||
@@ -74,24 +74,24 @@ public enum WeaponType
|
||||
/// <summary>
|
||||
/// 法器
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
[Description("法器")]
|
||||
WEAPON_CATALYST = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 双手剑
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
[Description("双手剑")]
|
||||
WEAPON_CLAYMORE = 11,
|
||||
|
||||
/// <summary>
|
||||
/// 弓
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
[Description("弓")]
|
||||
WEAPON_BOW = 12,
|
||||
|
||||
/// <summary>
|
||||
/// 长柄武器
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
[Description("长柄武器")]
|
||||
WEAPON_POLE = 13,
|
||||
}
|
||||
@@ -31,5 +31,5 @@ public class AchievementGoal
|
||||
/// <summary>
|
||||
/// 图标
|
||||
/// </summary>
|
||||
public string? Icon { get; set; }
|
||||
public string Icon { get; set; } = default!;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
using Snap.Hutao.Model.Binding.Gacha;
|
||||
using Snap.Hutao.Model.Binding.Gacha.Abstraction;
|
||||
using Snap.Hutao.Model.Binding.Hutao;
|
||||
using Snap.Hutao.Model.Calculable;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
@@ -21,6 +22,17 @@ public partial class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQu
|
||||
[JsonIgnore]
|
||||
public ComplexAvatarCollocation? Collocation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// [非元数据] 烹饪奖励
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public CookBonusView? CookBonusView { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 养成物品视图
|
||||
/// </summary>
|
||||
public List<Material>? CultivationItemsView { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICalculableAvatar ToCalculable()
|
||||
{
|
||||
|
||||
@@ -81,5 +81,10 @@ public partial class Avatar
|
||||
/// <summary>
|
||||
/// 皮肤
|
||||
/// </summary>
|
||||
public IEnumerable<Costume> Costumes { get; set; } = default!;
|
||||
public List<Costume> Costumes { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 养成物品
|
||||
/// </summary>
|
||||
public List<MaterialId> CultivationItems { get; set; } = default!;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Avatar;
|
||||
|
||||
@@ -13,45 +13,15 @@ public class CookBonus
|
||||
/// <summary>
|
||||
/// 原型名称
|
||||
/// </summary>
|
||||
public string OriginName { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 原型描述
|
||||
/// </summary>
|
||||
public string OriginDescription { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 原型图标
|
||||
/// </summary>
|
||||
public string OriginIcon { get; set; } = default!;
|
||||
public MaterialId OriginItemId { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public string Description { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 效果描述
|
||||
/// </summary>
|
||||
public string EffectDescription { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 图标
|
||||
/// </summary>
|
||||
public string Icon { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 物品等级
|
||||
/// </summary>
|
||||
public ItemQuality RankLevel { get; set; }
|
||||
public MaterialId ItemId { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 材料列表
|
||||
/// </summary>
|
||||
public List<ItemWithCount> InputList { get; set; } = default!;
|
||||
public List<MaterialId> InputList { get; set; } = default!;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ public class FetterInfo
|
||||
/// <summary>
|
||||
/// 料理
|
||||
/// </summary>
|
||||
public CookBonus? CookBonus { get; set; }
|
||||
public CookBonus? CookBonus2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 好感语音
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Calculable;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Avatar;
|
||||
|
||||
@@ -80,6 +80,8 @@ public static class AvatarIds
|
||||
public static readonly AvatarId Layla = 10000074;
|
||||
public static readonly AvatarId Wanderer = 10000075;
|
||||
public static readonly AvatarId Faruzan = 10000076;
|
||||
public static readonly AvatarId Yaoyao = 10000077;
|
||||
public static readonly AvatarId Alhaitham = 10000078;
|
||||
|
||||
/// <summary>
|
||||
/// 检查该角色是否为主角
|
||||
|
||||
@@ -12,9 +12,19 @@ internal class AchievementIconConverter : ValueConverterBase<string, Uri>
|
||||
{
|
||||
private const string BaseUrl = "https://static.snapgenshin.com/AchievementIcon/{0}.png";
|
||||
|
||||
/// <summary>
|
||||
/// 名称转Uri
|
||||
/// </summary>
|
||||
/// <param name="name">名称</param>
|
||||
/// <returns>链接</returns>
|
||||
public static Uri IconNameToUri(string name)
|
||||
{
|
||||
return new Uri(string.Format(BaseUrl, name));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Uri Convert(string from)
|
||||
{
|
||||
return new Uri(string.Format(BaseUrl, from));
|
||||
return IconNameToUri(from);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 角色卡片转换器
|
||||
/// </summary>
|
||||
internal class AvatarCardConverter : ValueConverterBase<string, Uri>
|
||||
{
|
||||
private const string BaseUrl = "https://static.snapgenshin.com/AvatarCard/{0}_Card.png";
|
||||
|
||||
private static readonly Uri UIAvatarIconCostumeCard = new("https://static.snapgenshin.com/AvatarCard/UI_AvatarIcon_Costume_Card.png");
|
||||
|
||||
/// <summary>
|
||||
/// 名称转Uri
|
||||
/// </summary>
|
||||
/// <param name="name">名称</param>
|
||||
/// <returns>链接</returns>
|
||||
public static Uri IconNameToUri(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return UIAvatarIconCostumeCard;
|
||||
}
|
||||
|
||||
return new Uri(string.Format(BaseUrl, name));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Uri Convert(string from)
|
||||
{
|
||||
return IconNameToUri(from);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Control.Image;
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
@@ -12,6 +12,8 @@ internal class ItemIconConverter : ValueConverterBase<string, Uri>
|
||||
{
|
||||
private const string BaseUrl = "https://static.snapgenshin.com/ItemIcon/{0}.png";
|
||||
|
||||
private static readonly Uri UIItemIconNone = new("https://static.snapgenshin.com/Bg/UI_ItemIcon_None.png");
|
||||
|
||||
/// <summary>
|
||||
/// 名称转Uri
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// 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;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata;
|
||||
|
||||
@@ -14,7 +14,7 @@ public class Material
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
public MaterialId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 等级
|
||||
@@ -26,6 +26,11 @@ public class Material
|
||||
/// </summary>
|
||||
public ItemType ItemType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 材料类型
|
||||
/// </summary>
|
||||
public MaterialType MaterialType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标
|
||||
/// </summary>
|
||||
@@ -45,4 +50,9 @@ public class Material
|
||||
/// 类型描述
|
||||
/// </summary>
|
||||
public string TypeDescription { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 效果描述
|
||||
/// </summary>
|
||||
public string? EffectDescription { get; set; }
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
using Snap.Hutao.Model.Binding.Gacha;
|
||||
using Snap.Hutao.Model.Binding.Gacha.Abstraction;
|
||||
using Snap.Hutao.Model.Binding.Hutao;
|
||||
using Snap.Hutao.Model.Calculable;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
71
src/Snap.Hutao/Snap.Hutao/Model/Primitive/MaterialId.cs
Normal file
71
src/Snap.Hutao/Snap.Hutao/Model/Primitive/MaterialId.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 3-6位 材料Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(IdentityConverter<MaterialId>))]
|
||||
public readonly struct MaterialId : IEquatable<MaterialId>, IComparable<MaterialId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MaterialId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public MaterialId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(MaterialId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator MaterialId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(MaterialId left, MaterialId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(MaterialId left, MaterialId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int CompareTo(MaterialId other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(MaterialId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is MaterialId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
||||
Publisher="CN=DGP Studio"
|
||||
Version="1.2.12.0" />
|
||||
Version="1.2.19.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>胡桃</DisplayName>
|
||||
|
||||
@@ -66,9 +66,7 @@ public static partial class Program
|
||||
ServiceProvider services = new ServiceCollection()
|
||||
|
||||
// Microsoft extension
|
||||
.AddLogging(builder => builder
|
||||
.AddDebug()
|
||||
.AddDatabase())
|
||||
.AddLogging(builder => builder.AddDebug().AddDatabase())
|
||||
.AddMemoryCache()
|
||||
|
||||
// Hutao extensions
|
||||
|
||||
@@ -9,7 +9,7 @@ using Snap.Hutao.Core.Diagnostics;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Model.InterChange.Achievement;
|
||||
using System.Collections.ObjectModel;
|
||||
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement;
|
||||
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
|
||||
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
|
||||
using EntityArchive = Snap.Hutao.Model.Entity.AchievementArchive;
|
||||
using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
|
||||
@@ -22,6 +22,7 @@ namespace Snap.Hutao.Service.Achievement;
|
||||
[Injection(InjectAs.Scoped, typeof(IAchievementService))]
|
||||
internal class AchievementService : IAchievementService
|
||||
{
|
||||
private readonly object saveAchievementLocker = new();
|
||||
private readonly AppDbContext appDbContext;
|
||||
private readonly ILogger<AchievementService> logger;
|
||||
private readonly DbCurrent<EntityArchive, Message.AchievementArchiveChangedMessage> dbCurrent;
|
||||
@@ -187,4 +188,19 @@ internal class AchievementService : IAchievementService
|
||||
logger.LogInformation(EventIds.Achievement, "{add} added, {update} updated, {remove} removed", result.Add, result.Update, result.Remove);
|
||||
logger.LogInformation(EventIds.Achievement, "Save achievements for [{name}] completed in {time}ms", name, time);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SaveAchievement(BindingAchievement achievement)
|
||||
{
|
||||
if (achievement.IsChecked)
|
||||
{
|
||||
// set to default allow multiple time add
|
||||
achievement.Entity.InnerId = default;
|
||||
appDbContext.Achievements.UpdateAndSave(achievement.Entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
appDbContext.Achievements.RemoveAndSave(achievement.Entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Threading.CodeAnalysis;
|
||||
using Snap.Hutao.Model.InterChange.Achievement;
|
||||
using System.Collections.ObjectModel;
|
||||
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement;
|
||||
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
|
||||
using EntityArchive = Snap.Hutao.Model.Entity.AchievementArchive;
|
||||
using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
|
||||
|
||||
@@ -57,6 +56,12 @@ internal interface IAchievementService
|
||||
/// <returns>任务</returns>
|
||||
Task RemoveArchiveAsync(EntityArchive archive);
|
||||
|
||||
/// <summary>
|
||||
/// 保存单个成就
|
||||
/// </summary>
|
||||
/// <param name="achievement">成就</param>
|
||||
void SaveAchievement(BindingAchievement achievement);
|
||||
|
||||
/// <summary>
|
||||
/// 保存成就
|
||||
/// </summary>
|
||||
@@ -69,6 +74,5 @@ internal interface IAchievementService
|
||||
/// </summary>
|
||||
/// <param name="newArchive">新存档</param>
|
||||
/// <returns>存档添加结果</returns>
|
||||
[ThreadAccess(ThreadAccessState.AnyThread)]
|
||||
Task<ArchiveAddResult> TryAddArchiveAsync(EntityArchive newArchive);
|
||||
}
|
||||
@@ -15,28 +15,38 @@ internal class AffixWeight : Dictionary<FightProperty, double>
|
||||
/// 构造一个新的词条权重
|
||||
/// </summary>
|
||||
/// <param name="avatarId">角色Id</param>
|
||||
/// <param name="hp">大生命</param>
|
||||
/// <param name="atk">大攻击</param>
|
||||
/// <param name="def">大防御</param>
|
||||
/// <param name="cr">暴击率</param>
|
||||
/// <param name="ch">暴击伤害</param>
|
||||
/// <param name="em">元素精通</param>
|
||||
/// <param name="ce">充能效率</param>
|
||||
/// <param name="ha">治疗加成</param>
|
||||
/// <param name="hpPercent">大生命</param>
|
||||
/// <param name="attackPercenr">大攻击</param>
|
||||
/// <param name="defensePercent">大防御</param>
|
||||
/// <param name="critical">暴击率</param>
|
||||
/// <param name="criticalHurt">暴击伤害</param>
|
||||
/// <param name="elementMastery">元素精通</param>
|
||||
/// <param name="chargeEfficiency">充能效率</param>
|
||||
/// <param name="healAdd">治疗加成</param>
|
||||
/// <param name="name">名称</param>
|
||||
public AffixWeight(int avatarId, double hp, double atk, double def, double cr, double ch, double em, double ce, double ha, string name = "通用")
|
||||
public AffixWeight(
|
||||
int avatarId,
|
||||
double hpPercent,
|
||||
double attackPercenr,
|
||||
double defensePercent,
|
||||
double critical,
|
||||
double criticalHurt,
|
||||
double elementMastery,
|
||||
double chargeEfficiency,
|
||||
double healAdd,
|
||||
string name = "通用")
|
||||
{
|
||||
AvatarId = avatarId;
|
||||
Name = name;
|
||||
|
||||
this[FightProperty.FIGHT_PROP_HP_PERCENT] = hp;
|
||||
this[FightProperty.FIGHT_PROP_ATTACK_PERCENT] = atk;
|
||||
this[FightProperty.FIGHT_PROP_DEFENSE_PERCENT] = def;
|
||||
this[FightProperty.FIGHT_PROP_CRITICAL] = cr;
|
||||
this[FightProperty.FIGHT_PROP_CRITICAL_HURT] = ch;
|
||||
this[FightProperty.FIGHT_PROP_ELEMENT_MASTERY] = em;
|
||||
this[FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY] = ce;
|
||||
this[FightProperty.FIGHT_PROP_HEAL_ADD] = ha;
|
||||
this[FightProperty.FIGHT_PROP_HP_PERCENT] = hpPercent;
|
||||
this[FightProperty.FIGHT_PROP_ATTACK_PERCENT] = attackPercenr;
|
||||
this[FightProperty.FIGHT_PROP_DEFENSE_PERCENT] = defensePercent;
|
||||
this[FightProperty.FIGHT_PROP_CRITICAL] = critical;
|
||||
this[FightProperty.FIGHT_PROP_CRITICAL_HURT] = criticalHurt;
|
||||
this[FightProperty.FIGHT_PROP_ELEMENT_MASTERY] = elementMastery;
|
||||
this[FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY] = chargeEfficiency;
|
||||
this[FightProperty.FIGHT_PROP_HEAL_ADD] = healAdd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -48,4 +58,92 @@ internal class AffixWeight : Dictionary<FightProperty, double>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 风元素伤害加成
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>链式调用对象</returns>
|
||||
public AffixWeight Anemo(double value = 100)
|
||||
{
|
||||
this[FightProperty.FIGHT_PROP_WIND_ADD_HURT] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 冰元素伤害加成
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>链式调用对象</returns>
|
||||
public AffixWeight Cryo(double value = 100)
|
||||
{
|
||||
this[FightProperty.FIGHT_PROP_ICE_ADD_HURT] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 草元素伤害加成
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>链式调用对象</returns>
|
||||
public AffixWeight Dendro(double value = 100)
|
||||
{
|
||||
this[FightProperty.FIGHT_PROP_GRASS_ADD_HURT] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 雷元素伤害加成
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>链式调用对象</returns>
|
||||
public AffixWeight Electro(double value = 100)
|
||||
{
|
||||
this[FightProperty.FIGHT_PROP_ELEC_ADD_HURT] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 岩元素伤害加成
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>链式调用对象</returns>
|
||||
public AffixWeight Geo(double value = 100)
|
||||
{
|
||||
this[FightProperty.FIGHT_PROP_ROCK_ADD_HURT] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 水元素伤害加成
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>链式调用对象</returns>
|
||||
public AffixWeight Hydro(double value = 100)
|
||||
{
|
||||
this[FightProperty.FIGHT_PROP_WATER_ADD_HURT] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 火元素伤害加成
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>链式调用对象</returns>
|
||||
public AffixWeight Pyro(double value = 100)
|
||||
{
|
||||
this[FightProperty.FIGHT_PROP_FIRE_ADD_HURT] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 物理伤害伤害加成
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>链式调用对象</returns>
|
||||
public AffixWeight Phyiscal(double value = 100)
|
||||
{
|
||||
this[FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT] = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
@@ -14,74 +13,74 @@ internal static partial class ReliquaryWeightConfiguration
|
||||
/// <summary>
|
||||
/// 默认
|
||||
/// </summary>
|
||||
public static readonly AffixWeight Default = new(0, 100, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } };
|
||||
public static readonly AffixWeight Default = new(0, 100, 75, 0, 100, 100, 0, 55, 0);
|
||||
|
||||
/// <summary>
|
||||
/// 词条权重
|
||||
/// </summary>
|
||||
public static readonly List<AffixWeight> AffixWeights = new()
|
||||
{
|
||||
new(AvatarIds.Ayaka, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Qin, 0, 75, 0, 100, 100, 0, 55, 100) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Lisa, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Barbara, 100, 50, 0, 50, 50, 0, 55, 100) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 80 } },
|
||||
new(AvatarIds.Barbara, 50, 75, 0, 100, 100, 0, 55, 100, "暴力奶妈") { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Kaeya, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Diluc, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Razor, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 50 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Ambor, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Venti, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Xiangling, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Beidou, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Xingqiu, 0, 75, 0, 100, 100, 0, 75, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Xiao, 0, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Ningguang, 0, 75, 0, 100, 100, 0, 30, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Klee, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Zhongli, 80, 75, 0, 100, 100, 0, 55, 0, "武神钟离") { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 50 } },
|
||||
new(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 55, 0, "血牛钟离") { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 75 } },
|
||||
new(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 75, 0, "血牛钟离(2命+)") { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 75 } },
|
||||
new(AvatarIds.Fischl, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Bennett, 100, 50, 0, 100, 100, 0, 55, 100) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 70 } },
|
||||
new(AvatarIds.Tartaglia, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Noel, 0, 50, 90, 100, 100, 0, 70, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Qiqi, 0, 100, 0, 100, 100, 0, 55, 100) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 60 } },
|
||||
new(AvatarIds.Chongyun, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 75, 0, 0, "融化流") { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 0, 55, 0, "永冻流") { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Albedo, 0, 0, 100, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Diona, 100, 50, 0, 50, 50, 0, 90, 100) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Mona, 0, 75, 0, 100, 100, 75, 75, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Keqing, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Sucrose, 0, 75, 0, 100, 100, 100, 55, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 40 } },
|
||||
new(AvatarIds.Xinyan, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 50 } },
|
||||
new(AvatarIds.Rosaria, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 70 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 80 } },
|
||||
new(AvatarIds.Hutao, 80, 50, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Kazuha, 0, 75, 0, 100, 100, 100, 55, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Feiyan, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Yoimiya, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Tohma, 100, 50, 0, 50, 50, 0, 90, 0) { { FightProperty.FIGHT_PROP_FIRE_ADD_HURT, 75 } },
|
||||
new(AvatarIds.Eula, 0, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 40 }, { FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Shougun, 0, 75, 0, 100, 100, 0, 90, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 75 } },
|
||||
new(AvatarIds.Sayu, 0, 50, 0, 50, 50, 100, 55, 100) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 80 } },
|
||||
new(AvatarIds.Kokomi, 100, 50, 0, 0, 0, 0, 55, 100) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Gorou, 0, 50, 100, 50, 50, 0, 90, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 25 } },
|
||||
new(AvatarIds.Sara, 0, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Itto, 0, 50, 100, 100, 100, 0, 30, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Yae, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Heizou, 0, 75, 0, 100, 100, 75, 0, 0) { { FightProperty.FIGHT_PROP_WIND_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Yelan, 80, 0, 0, 100, 100, 0, 75, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Aloy, 0, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Shenhe, 0, 100, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_ICE_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Yunjin, 0, 0, 100, 50, 50, 0, 90, 0) { { FightProperty.FIGHT_PROP_ROCK_ADD_HURT, 25 } },
|
||||
new(AvatarIds.Shinobu, 100, 50, 0, 100, 100, 75, 55, 100) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Ayato, 50, 75, 0, 100, 100, 0, 0, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Collei, 0, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_GRASS_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Dori, 100, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Tighnari, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_GRASS_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "直伤流") { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "反应流") { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Cyno, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Candace, 100, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||
new(AvatarIds.Nahida, 0, 55, 0, 100, 100, 100, 55, 0) { { FightProperty.FIGHT_PROP_GRASS_ADD_HURT, 100 } },
|
||||
new AffixWeight(AvatarIds.Ayaka, 0, 75, 0, 100, 100, 0, 0, 0).Cryo(),
|
||||
new AffixWeight(AvatarIds.Qin, 0, 75, 0, 100, 100, 0, 55, 100).Anemo(),
|
||||
new AffixWeight(AvatarIds.Lisa, 0, 75, 0, 100, 100, 75, 0, 0).Electro(),
|
||||
new AffixWeight(AvatarIds.Barbara, 100, 50, 0, 50, 50, 0, 55, 100).Hydro(80),
|
||||
new AffixWeight(AvatarIds.Barbara, 50, 75, 0, 100, 100, 0, 55, 100, "暴力奶妈").Hydro(),
|
||||
new AffixWeight(AvatarIds.Kaeya, 0, 75, 0, 100, 100, 0, 0, 0).Cryo(),
|
||||
new AffixWeight(AvatarIds.Diluc, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
|
||||
new AffixWeight(AvatarIds.Razor, 0, 75, 0, 100, 100, 0, 0, 0).Electro(50).Phyiscal(),
|
||||
new AffixWeight(AvatarIds.Ambor, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
|
||||
new AffixWeight(AvatarIds.Venti, 0, 75, 0, 100, 100, 75, 55, 0).Anemo(),
|
||||
new AffixWeight(AvatarIds.Xiangling, 0, 75, 0, 100, 100, 75, 55, 0).Pyro(),
|
||||
new AffixWeight(AvatarIds.Beidou, 0, 75, 0, 100, 100, 75, 55, 0).Electro(),
|
||||
new AffixWeight(AvatarIds.Xingqiu, 0, 75, 0, 100, 100, 0, 75, 0).Hydro(),
|
||||
new AffixWeight(AvatarIds.Xiao, 0, 75, 0, 100, 100, 0, 55, 0).Anemo(),
|
||||
new AffixWeight(AvatarIds.Ningguang, 0, 75, 0, 100, 100, 0, 30, 0).Geo(),
|
||||
new AffixWeight(AvatarIds.Klee, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
|
||||
new AffixWeight(AvatarIds.Zhongli, 80, 75, 0, 100, 100, 0, 55, 0, "武神钟离").Geo().Phyiscal(50),
|
||||
new AffixWeight(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 55, 0, "血牛钟离").Geo(75),
|
||||
new AffixWeight(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 75, 0, "血牛钟离(2命+)").Geo(75),
|
||||
new AffixWeight(AvatarIds.Fischl, 0, 75, 0, 100, 100, 0, 0, 0).Electro(),
|
||||
new AffixWeight(AvatarIds.Bennett, 100, 50, 0, 100, 100, 0, 55, 100).Pyro(70),
|
||||
new AffixWeight(AvatarIds.Tartaglia, 0, 75, 0, 100, 100, 75, 0, 0).Hydro(),
|
||||
new AffixWeight(AvatarIds.Noel, 0, 50, 90, 100, 100, 0, 70, 0).Geo(),
|
||||
new AffixWeight(AvatarIds.Qiqi, 0, 100, 0, 100, 100, 0, 55, 100).Cryo(60),
|
||||
new AffixWeight(AvatarIds.Chongyun, 0, 75, 0, 100, 100, 75, 55, 0).Cryo(),
|
||||
new AffixWeight(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 75, 0, 0, "融化流").Cryo(),
|
||||
new AffixWeight(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 0, 55, 0, "永冻流").Cryo(),
|
||||
new AffixWeight(AvatarIds.Albedo, 0, 0, 100, 100, 100, 0, 0, 0).Geo(),
|
||||
new AffixWeight(AvatarIds.Diona, 100, 50, 0, 50, 50, 0, 90, 100).Cryo(),
|
||||
new AffixWeight(AvatarIds.Mona, 0, 75, 0, 100, 100, 75, 75, 0).Hydro(),
|
||||
new AffixWeight(AvatarIds.Keqing, 0, 75, 0, 100, 100, 0, 0, 0).Electro().Phyiscal(),
|
||||
new AffixWeight(AvatarIds.Sucrose, 0, 75, 0, 100, 100, 100, 55, 0).Anemo(40),
|
||||
new AffixWeight(AvatarIds.Xinyan, 0, 75, 0, 100, 100, 0, 0, 0).Pyro(50),
|
||||
new AffixWeight(AvatarIds.Rosaria, 0, 75, 0, 100, 100, 0, 0, 0).Cryo(70).Phyiscal(80),
|
||||
new AffixWeight(AvatarIds.Hutao, 80, 50, 0, 100, 100, 75, 0, 0).Pyro(),
|
||||
new AffixWeight(AvatarIds.Kazuha, 0, 75, 0, 100, 100, 100, 55, 0).Anemo(),
|
||||
new AffixWeight(AvatarIds.Feiyan, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
|
||||
new AffixWeight(AvatarIds.Yoimiya, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
|
||||
new AffixWeight(AvatarIds.Tohma, 100, 50, 0, 50, 50, 0, 90, 0).Pyro(75),
|
||||
new AffixWeight(AvatarIds.Eula, 0, 75, 0, 100, 100, 0, 55, 0).Cryo(40).Phyiscal(100),
|
||||
new AffixWeight(AvatarIds.Shougun, 0, 75, 0, 100, 100, 0, 90, 0).Electro(75),
|
||||
new AffixWeight(AvatarIds.Sayu, 0, 50, 0, 50, 50, 100, 55, 100).Anemo(80),
|
||||
new AffixWeight(AvatarIds.Kokomi, 100, 50, 0, 0, 0, 0, 55, 100).Hydro(),
|
||||
new AffixWeight(AvatarIds.Gorou, 0, 50, 100, 50, 50, 0, 90, 0).Geo(25),
|
||||
new AffixWeight(AvatarIds.Sara, 0, 75, 0, 100, 100, 0, 55, 0).Electro(),
|
||||
new AffixWeight(AvatarIds.Itto, 0, 50, 100, 100, 100, 0, 30, 0).Geo(),
|
||||
new AffixWeight(AvatarIds.Yae, 0, 75, 0, 100, 100, 75, 55, 0).Electro(),
|
||||
new AffixWeight(AvatarIds.Heizou, 0, 75, 0, 100, 100, 75, 0, 0).Anemo(),
|
||||
new AffixWeight(AvatarIds.Yelan, 80, 0, 0, 100, 100, 0, 75, 0).Hydro(),
|
||||
new AffixWeight(AvatarIds.Aloy, 0, 75, 0, 100, 100, 0, 0, 0).Cryo(),
|
||||
new AffixWeight(AvatarIds.Shenhe, 0, 100, 0, 100, 100, 0, 55, 0).Cryo(),
|
||||
new AffixWeight(AvatarIds.Yunjin, 0, 0, 100, 50, 50, 0, 90, 0).Geo(25),
|
||||
new AffixWeight(AvatarIds.Shinobu, 100, 50, 0, 100, 100, 75, 55, 100).Electro(),
|
||||
new AffixWeight(AvatarIds.Ayato, 50, 75, 0, 100, 100, 0, 0, 0).Hydro(),
|
||||
new AffixWeight(AvatarIds.Collei, 0, 75, 0, 100, 100, 0, 55, 0).Dendro(),
|
||||
new AffixWeight(AvatarIds.Dori, 100, 75, 0, 100, 100, 0, 55, 0).Electro(),
|
||||
new AffixWeight(AvatarIds.Tighnari, 0, 75, 0, 100, 100, 75, 55, 0).Dendro(),
|
||||
new AffixWeight(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "直伤流").Hydro(),
|
||||
new AffixWeight(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "反应流").Hydro(),
|
||||
new AffixWeight(AvatarIds.Cyno, 0, 75, 0, 100, 100, 75, 55, 0).Electro(),
|
||||
new AffixWeight(AvatarIds.Candace, 100, 75, 0, 100, 100, 0, 55, 0).Hydro(),
|
||||
new AffixWeight(AvatarIds.Nahida, 0, 55, 0, 100, 100, 100, 55, 0).Dendro(),
|
||||
};
|
||||
}
|
||||
@@ -5,11 +5,8 @@ using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
|
||||
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
|
||||
using PropertyAvatar = Snap.Hutao.Model.Binding.AvatarProperty.Avatar;
|
||||
@@ -23,40 +20,17 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
/// </summary>
|
||||
internal class SummaryAvatarFactory
|
||||
{
|
||||
private readonly Dictionary<AvatarId, MetadataAvatar> idAvatarMap;
|
||||
private readonly Dictionary<WeaponId, MetadataWeapon> idWeaponMap;
|
||||
private readonly Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap;
|
||||
private readonly Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly List<ReliquaryLevel> reliqueryLevels;
|
||||
private readonly List<MetadataReliquary> reliquaries;
|
||||
|
||||
private readonly ModelAvatarInfo avatarInfo;
|
||||
private readonly SummaryMetadataContext metadataContext;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的角色工厂
|
||||
/// </summary>
|
||||
/// <param name="idAvatarMap">角色映射</param>
|
||||
/// <param name="idWeaponMap">武器映射</param>
|
||||
/// <param name="idRelicMainPropMap">圣遗物主属性映射</param>
|
||||
/// <param name="idReliquaryAffixMap">圣遗物副词条映射</param>
|
||||
/// <param name="reliqueryLevels">圣遗物主属性等级</param>
|
||||
/// <param name="reliquaries">圣遗物</param>
|
||||
/// <param name="metadataContext">元数据上下文</param>
|
||||
/// <param name="avatarInfo">角色信息</param>
|
||||
public SummaryAvatarFactory(
|
||||
Dictionary<AvatarId, MetadataAvatar> idAvatarMap,
|
||||
Dictionary<WeaponId, MetadataWeapon> idWeaponMap,
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap,
|
||||
Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap,
|
||||
List<ReliquaryLevel> reliqueryLevels,
|
||||
List<MetadataReliquary> reliquaries,
|
||||
ModelAvatarInfo avatarInfo)
|
||||
public SummaryAvatarFactory(SummaryMetadataContext metadataContext, ModelAvatarInfo avatarInfo)
|
||||
{
|
||||
this.idAvatarMap = idAvatarMap;
|
||||
this.idWeaponMap = idWeaponMap;
|
||||
this.idRelicMainPropMap = idRelicMainPropMap;
|
||||
this.idReliquaryAffixMap = idReliquaryAffixMap;
|
||||
this.reliqueryLevels = reliqueryLevels;
|
||||
this.reliquaries = reliquaries;
|
||||
this.metadataContext = metadataContext;
|
||||
this.avatarInfo = avatarInfo;
|
||||
}
|
||||
|
||||
@@ -67,10 +41,11 @@ internal class SummaryAvatarFactory
|
||||
public PropertyAvatar CreateAvatar()
|
||||
{
|
||||
ReliquaryAndWeapon reliquaryAndWeapon = ProcessEquip(avatarInfo.EquipList);
|
||||
MetadataAvatar avatar = idAvatarMap[avatarInfo.AvatarId];
|
||||
MetadataAvatar avatar = metadataContext.IdAvatarMap[avatarInfo.AvatarId];
|
||||
|
||||
return new()
|
||||
{
|
||||
Id = avatar.Id,
|
||||
Name = avatar.Name,
|
||||
Icon = AvatarIconConverter.IconNameToUri(avatar.Icon),
|
||||
SideIcon = AvatarIconConverter.IconNameToUri(avatar.SideIcon),
|
||||
@@ -78,6 +53,7 @@ internal class SummaryAvatarFactory
|
||||
Quality = avatar.Quality,
|
||||
Element = ElementNameIconConverter.ElementNameToElementType(avatar.FetterInfo.VisionBefore),
|
||||
Level = $"Lv.{avatarInfo.PropMap[PlayerProperty.PROP_LEVEL].Value}",
|
||||
LevelNumber = int.Parse(avatarInfo.PropMap[PlayerProperty.PROP_LEVEL].Value ?? string.Empty),
|
||||
FetterLevel = avatarInfo.FetterInfo.ExpLevel,
|
||||
Weapon = reliquaryAndWeapon.Weapon,
|
||||
Reliquaries = reliquaryAndWeapon.Reliquaries,
|
||||
@@ -99,7 +75,7 @@ internal class SummaryAvatarFactory
|
||||
switch (equip.Flat.ItemType)
|
||||
{
|
||||
case ItemType.ITEM_RELIQUARY:
|
||||
SummaryReliquaryFactory summaryReliquaryFactory = new(idReliquaryAffixMap, idRelicMainPropMap, reliqueryLevels, reliquaries, avatarInfo, equip);
|
||||
SummaryReliquaryFactory summaryReliquaryFactory = new(metadataContext, avatarInfo, equip);
|
||||
reliquaryList.Add(summaryReliquaryFactory.CreateReliquary());
|
||||
break;
|
||||
case ItemType.ITEM_WEAPON:
|
||||
@@ -113,7 +89,7 @@ internal class SummaryAvatarFactory
|
||||
|
||||
private PropertyWeapon CreateWeapon(Equip equip)
|
||||
{
|
||||
MetadataWeapon weapon = idWeaponMap[equip.ItemId];
|
||||
MetadataWeapon weapon = metadataContext.IdWeaponMap[equip.ItemId];
|
||||
|
||||
// AffixMap can be empty when it's a white weapon.
|
||||
KeyValuePair<string, int>? idLevel = equip.Weapon!.AffixMap?.Single();
|
||||
@@ -146,6 +122,8 @@ internal class SummaryAvatarFactory
|
||||
MainProperty = mainStat == null ? default! : new(mainStat.AppendPropId.GetDescription(), mainStat.StatValue.ToString()),
|
||||
|
||||
// Weapon
|
||||
Id = weapon.Id,
|
||||
LevelNumber = equip.Weapon!.Level,
|
||||
SubProperty = subProperty,
|
||||
AffixLevel = $"精炼{affixLevel + 1}",
|
||||
AffixName = weapon.Affix?.Name ?? string.Empty,
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
|
||||
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
|
||||
using ModelPlayerInfo = Snap.Hutao.Web.Enka.Model.PlayerInfo;
|
||||
|
||||
@@ -34,15 +28,17 @@ internal class SummaryFactory : ISummaryFactory
|
||||
/// <inheritdoc/>
|
||||
public async Task<Summary> CreateAsync(ModelPlayerInfo playerInfo, IEnumerable<ModelAvatarInfo> avatarInfos, CancellationToken token)
|
||||
{
|
||||
Dictionary<AvatarId, MetadataAvatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<WeaponId, MetadataWeapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap = await metadataService.GetIdReliquaryAffixMapAsync(token).ConfigureAwait(false);
|
||||
SummaryMetadataContext metadataContext = new()
|
||||
{
|
||||
IdAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false),
|
||||
IdWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false),
|
||||
IdRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false),
|
||||
IdReliquaryAffixMap = await metadataService.GetIdReliquaryAffixMapAsync(token).ConfigureAwait(false),
|
||||
ReliqueryLevels = await metadataService.GetReliquaryLevelsAsync(token).ConfigureAwait(false),
|
||||
Reliquaries = await metadataService.GetReliquariesAsync(token).ConfigureAwait(false),
|
||||
};
|
||||
|
||||
List<ReliquaryLevel> reliqueryLevels = await metadataService.GetReliquaryLevelsAsync(token).ConfigureAwait(false);
|
||||
List<MetadataReliquary> reliquaries = await metadataService.GetReliquariesAsync(token).ConfigureAwait(false);
|
||||
|
||||
SummaryFactoryImplementation inner = new(idAvatarMap, idWeaponMap, idRelicMainPropMap, idReliquaryAffixMap, reliqueryLevels, reliquaries);
|
||||
SummaryFactoryImplementation inner = new(metadataContext);
|
||||
return inner.Create(playerInfo, avatarInfos);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
|
||||
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
|
||||
using ModelPlayerInfo = Snap.Hutao.Web.Enka.Model.PlayerInfo;
|
||||
|
||||
@@ -19,36 +13,15 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
/// </summary>
|
||||
internal class SummaryFactoryImplementation
|
||||
{
|
||||
private readonly Dictionary<AvatarId, MetadataAvatar> idAvatarMap;
|
||||
private readonly Dictionary<WeaponId, MetadataWeapon> idWeaponMap;
|
||||
private readonly Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap;
|
||||
private readonly Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly List<ReliquaryLevel> reliqueryLevels;
|
||||
private readonly List<MetadataReliquary> reliquaries;
|
||||
private readonly SummaryMetadataContext metadataContext;
|
||||
|
||||
/// <summary>
|
||||
/// 装配一个工厂实现
|
||||
/// </summary>
|
||||
/// <param name="idAvatarMap">角色映射</param>
|
||||
/// <param name="idWeaponMap">武器映射</param>
|
||||
/// <param name="idRelicMainPropMap">圣遗物主属性映射</param>
|
||||
/// <param name="idReliquaryAffixMap">圣遗物副词条映射</param>
|
||||
/// <param name="reliqueryLevels">圣遗物主属性等级</param>
|
||||
/// <param name="reliquaries">圣遗物</param>
|
||||
public SummaryFactoryImplementation(
|
||||
Dictionary<AvatarId, MetadataAvatar> idAvatarMap,
|
||||
Dictionary<WeaponId, MetadataWeapon> idWeaponMap,
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap,
|
||||
Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap,
|
||||
List<ReliquaryLevel> reliqueryLevels,
|
||||
List<MetadataReliquary> reliquaries)
|
||||
/// <param name="metadataContext">元数据上下文</param>
|
||||
public SummaryFactoryImplementation(SummaryMetadataContext metadataContext)
|
||||
{
|
||||
this.idAvatarMap = idAvatarMap;
|
||||
this.idRelicMainPropMap = idRelicMainPropMap;
|
||||
this.idWeaponMap = idWeaponMap;
|
||||
this.reliqueryLevels = reliqueryLevels;
|
||||
this.reliquaries = reliquaries;
|
||||
this.idReliquaryAffixMap = idReliquaryAffixMap;
|
||||
this.metadataContext = metadataContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,14 +37,7 @@ internal class SummaryFactoryImplementation
|
||||
Player = SummaryHelper.CreatePlayer(playerInfo),
|
||||
Avatars = avatarInfos.Where(a => !AvatarIds.IsPlayer(a.AvatarId)).Select(a =>
|
||||
{
|
||||
SummaryAvatarFactory summaryAvatarFactory = new(
|
||||
idAvatarMap,
|
||||
idWeaponMap,
|
||||
idRelicMainPropMap,
|
||||
idReliquaryAffixMap,
|
||||
reliqueryLevels,
|
||||
reliquaries,
|
||||
a);
|
||||
SummaryAvatarFactory summaryAvatarFactory = new(metadataContext, a);
|
||||
return summaryAvatarFactory.CreateAvatar();
|
||||
}).ToList(),
|
||||
};
|
||||
|
||||
@@ -88,7 +88,8 @@ internal static class SummaryHelper
|
||||
Icon = SkillIconConverter.IconNameToUri(proudableSkill.Icon),
|
||||
Description = proudableSkill.Description,
|
||||
|
||||
Level = skillLevelMap[proudableSkill.Id.ToString()],
|
||||
GroupId = proudableSkill.GroupId,
|
||||
LevelNumber = skillLevelMap[proudableSkill.Id.ToString()],
|
||||
Info = DescParamDescriptor.Convert(proudableSkill.Proud, skillExtraLeveledMap[proudableSkill.Id.ToString()]),
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
|
||||
[SuppressMessage("", "SA1600")]
|
||||
internal class SummaryMetadataContext
|
||||
{
|
||||
public Dictionary<AvatarId, MetadataAvatar> IdAvatarMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<WeaponId, MetadataWeapon> IdWeaponMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<ReliquaryMainAffixId, FightProperty> IdRelicMainPropMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<ReliquaryAffixId, ReliquaryAffix> IdReliquaryAffixMap { get; set; } = default!;
|
||||
|
||||
public List<ReliquaryLevel> ReliqueryLevels { get; set; } = default!;
|
||||
|
||||
public List<MetadataReliquary> Reliquaries { get; set; } = default!;
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Annotation;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using System.Runtime.InteropServices;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
@@ -22,36 +21,19 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
/// </summary>
|
||||
internal class SummaryReliquaryFactory
|
||||
{
|
||||
private readonly Dictionary<ReliquaryAffixId, MetadataReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap;
|
||||
private readonly List<ReliquaryLevel> reliqueryLevels;
|
||||
private readonly List<MetadataReliquary> reliquaries;
|
||||
|
||||
private readonly SummaryMetadataContext metadataContext;
|
||||
private readonly ModelAvatarInfo avatarInfo;
|
||||
private readonly Equip equip;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的圣遗物工厂
|
||||
/// </summary>
|
||||
/// <param name="idReliquaryAffixMap">圣遗物副词条映射</param>
|
||||
/// <param name="idRelicMainPropMap">圣遗物主属性映射</param>
|
||||
/// <param name="reliqueryLevels">圣遗物主属性等级</param>
|
||||
/// <param name="reliquaries">圣遗物列表</param>
|
||||
/// <param name="metadataContext">元数据上下文</param>
|
||||
/// <param name="avatarInfo">角色信息</param>
|
||||
/// <param name="equip">圣遗物</param>
|
||||
public SummaryReliquaryFactory(
|
||||
Dictionary<ReliquaryAffixId, MetadataReliquaryAffix> idReliquaryAffixMap,
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap,
|
||||
List<ReliquaryLevel> reliqueryLevels,
|
||||
List<MetadataReliquary> reliquaries,
|
||||
ModelAvatarInfo avatarInfo,
|
||||
Equip equip)
|
||||
public SummaryReliquaryFactory(SummaryMetadataContext metadataContext, ModelAvatarInfo avatarInfo, Equip equip)
|
||||
{
|
||||
this.idReliquaryAffixMap = idReliquaryAffixMap;
|
||||
this.idRelicMainPropMap = idRelicMainPropMap;
|
||||
this.reliqueryLevels = reliqueryLevels;
|
||||
this.reliquaries = reliquaries;
|
||||
|
||||
this.metadataContext = metadataContext;
|
||||
this.avatarInfo = avatarInfo;
|
||||
this.equip = equip;
|
||||
}
|
||||
@@ -62,7 +44,7 @@ internal class SummaryReliquaryFactory
|
||||
/// <returns>圣遗物</returns>
|
||||
public PropertyReliquary CreateReliquary()
|
||||
{
|
||||
MetadataReliquary reliquary = reliquaries.Single(r => r.Ids.Contains(equip.ItemId));
|
||||
MetadataReliquary reliquary = metadataContext.Reliquaries.Single(r => r.Ids.Contains(equip.ItemId));
|
||||
List<ReliquarySubProperty> subProperty = equip.Reliquary!.AppendPropIdList.EmptyIfNull().Select(CreateSubProperty).ToList();
|
||||
|
||||
int affixCount = GetAffixCount(reliquary);
|
||||
@@ -87,8 +69,8 @@ internal class SummaryReliquaryFactory
|
||||
|
||||
List<ReliquarySubProperty> composed = equip.Flat.ReliquarySubstats!.Select(CreateComposedSubProperty).ToList();
|
||||
|
||||
ReliquaryLevel relicLevel = reliqueryLevels.Single(r => r.Level == equip.Reliquary!.Level && r.Quality == reliquary.RankLevel);
|
||||
FightProperty property = idRelicMainPropMap[equip.Reliquary.MainPropId];
|
||||
ReliquaryLevel relicLevel = metadataContext.ReliqueryLevels.Single(r => r.Level == equip.Reliquary!.Level && r.Quality == reliquary.RankLevel);
|
||||
FightProperty property = metadataContext.IdRelicMainPropMap[equip.Reliquary.MainPropId];
|
||||
|
||||
return new()
|
||||
{
|
||||
@@ -148,7 +130,7 @@ internal class SummaryReliquaryFactory
|
||||
if (equip.Flat.EquipType is EquipType.EQUIP_SHOES or EquipType.EQUIP_RING or EquipType.EQUIP_DRESS)
|
||||
{
|
||||
AffixWeight weightConfig = GetAffixWeightForAvatarId();
|
||||
ReliquaryLevel maxRelicLevel = reliqueryLevels.Where(r => r.Quality == reliquary.RankLevel).MaxBy(r => r.Level)!;
|
||||
ReliquaryLevel maxRelicLevel = metadataContext.ReliqueryLevels.Where(r => r.Quality == reliquary.RankLevel).MaxBy(r => r.Level)!;
|
||||
|
||||
double percent = relicLevel.Properties[property] / maxRelicLevel.Properties[property];
|
||||
double baseScore = 8 * percent * weightConfig.GetValueOrDefault(property, 0);
|
||||
@@ -183,7 +165,7 @@ internal class SummaryReliquaryFactory
|
||||
|
||||
private ReliquarySubProperty CreateSubProperty(int appendPropId)
|
||||
{
|
||||
MetadataReliquaryAffix affix = idReliquaryAffixMap[appendPropId];
|
||||
MetadataReliquaryAffix affix = metadataContext.IdReliquaryAffixMap[appendPropId];
|
||||
FightProperty property = affix.Type;
|
||||
|
||||
double score = ScoreSubAffix(appendPropId);
|
||||
@@ -192,7 +174,7 @@ internal class SummaryReliquaryFactory
|
||||
|
||||
private double ScoreSubAffix(int appendId)
|
||||
{
|
||||
MetadataReliquaryAffix affix = idReliquaryAffixMap[appendId];
|
||||
MetadataReliquaryAffix affix = metadataContext.IdReliquaryAffixMap[appendId];
|
||||
|
||||
AffixWeight weightConfig = GetAffixWeightForAvatarId();
|
||||
double weight = weightConfig.GetValueOrDefault(affix.Type, 0) / 100D;
|
||||
@@ -207,7 +189,7 @@ internal class SummaryReliquaryFactory
|
||||
weight = weightConfig.GetValueOrDefault(affix.Type + 1, 0) / 100D;
|
||||
|
||||
// 最大同属性百分比数值 最大同属性百分比Id 第四五位是战斗属性位
|
||||
MetadataReliquaryAffix maxPercentAffix = idReliquaryAffixMap[SummaryHelper.GetAffixMaxId(appendId + 10)];
|
||||
MetadataReliquaryAffix maxPercentAffix = metadataContext.IdReliquaryAffixMap[SummaryHelper.GetAffixMaxId(appendId + 10)];
|
||||
double equalScore = equalPercent / maxPercentAffix.Value;
|
||||
|
||||
return weight * equalScore * 100;
|
||||
|
||||
@@ -131,7 +131,7 @@ internal class CultivationService : ICultivationService
|
||||
/// <inheritdoc/>
|
||||
public async Task<ObservableCollection<BindingCultivateEntry>> GetCultivateEntriesAsync(
|
||||
CultivateProject cultivateProject,
|
||||
List<Model.Metadata.Material> metadata,
|
||||
List<Model.Metadata.Material> materials,
|
||||
Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap,
|
||||
Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap)
|
||||
{
|
||||
@@ -148,19 +148,20 @@ internal class CultivationService : ICultivationService
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (CultivateEntry? entry in entries)
|
||||
foreach (CultivateEntry entry in entries)
|
||||
{
|
||||
Guid entryId = entry.InnerId;
|
||||
|
||||
List<BindingCultivateItem> resultItems = new();
|
||||
List<CultivateItem> items = await appDbContext.CultivateItems
|
||||
.Where(i => i.EntryId == entryId)
|
||||
.OrderBy(i => i.ItemId).ToListAsync()
|
||||
.OrderBy(i => i.ItemId)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (CultivateItem item in items)
|
||||
{
|
||||
resultItems.Add(new(metadata.Single(m => m.Id == item.ItemId), item));
|
||||
resultItems.Add(new(materials.Single(m => m.Id == item.ItemId), item));
|
||||
}
|
||||
|
||||
Model.Binding.Gacha.Abstraction.ItemBase itemBase = entry.Type switch
|
||||
@@ -173,7 +174,71 @@ internal class CultivationService : ICultivationService
|
||||
results.Add(new(entry, itemBase, resultItems));
|
||||
}
|
||||
|
||||
return new(results);
|
||||
return new(results.OrderByDescending(e => e.Items.Any(i => i.IsToday)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<Model.Binding.Cultivation.StatisticsCultivateItem>> GetStatisticsCultivateItemsAsync(CultivateProject cultivateProject, List<Model.Metadata.Material> materials)
|
||||
{
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
Guid projectId = cultivateProject.InnerId;
|
||||
|
||||
List<Model.Binding.Cultivation.StatisticsCultivateItem> resultItems = new();
|
||||
|
||||
List<CultivateEntry> entries = await appDbContext.CultivateEntries
|
||||
.AsNoTracking()
|
||||
.Where(e => e.ProjectId == projectId)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (CultivateEntry entry in entries)
|
||||
{
|
||||
Guid entryId = entry.InnerId;
|
||||
|
||||
List<CultivateItem> items = await appDbContext.CultivateItems
|
||||
.AsNoTracking()
|
||||
.Where(i => i.EntryId == entryId)
|
||||
.OrderBy(i => i.ItemId)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (CultivateItem item in items)
|
||||
{
|
||||
if (item.IsFinished)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resultItems.SingleOrDefault(i => i.Inner.Id == item.ItemId) is Model.Binding.Cultivation.StatisticsCultivateItem inPlaceItem)
|
||||
{
|
||||
inPlaceItem.Count += item.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultItems.Add(new(materials.Single(m => m.Id == item.ItemId), item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<InventoryItem> inventoryItems = await appDbContext.InventoryItems
|
||||
.AsNoTracking()
|
||||
.Where(e => e.ProjectId == projectId)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (InventoryItem inventoryItem in inventoryItems)
|
||||
{
|
||||
if (resultItems.SingleOrDefault(i => i.Inner.Id == inventoryItem.ItemId) is Model.Binding.Cultivation.StatisticsCultivateItem inPlaceItem)
|
||||
{
|
||||
inPlaceItem.TotalCount += inventoryItem.Count;
|
||||
}
|
||||
}
|
||||
|
||||
return resultItems.OrderByDescending(i => i.Count).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +274,11 @@ internal class CultivationService : ICultivationService
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> SaveConsumptionAsync(Model.Binding.Cultivation.CultivateType type, int itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
@@ -24,11 +24,11 @@ internal interface ICultivationService
|
||||
/// 获取绑定用的养成列表
|
||||
/// </summary>
|
||||
/// <param name="cultivateProject">养成计划</param>
|
||||
/// <param name="metadata">材料</param>
|
||||
/// <param name="materials">材料</param>
|
||||
/// <param name="idAvatarMap">Id角色映射</param>
|
||||
/// <param name="idWeaponMap">Id武器映射</param>
|
||||
/// <returns>绑定用的养成列表</returns>
|
||||
Task<ObservableCollection<Model.Binding.Cultivation.CultivateEntry>> GetCultivateEntriesAsync(CultivateProject cultivateProject, List<Material> metadata, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap, Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap);
|
||||
Task<ObservableCollection<Model.Binding.Cultivation.CultivateEntry>> GetCultivateEntriesAsync(CultivateProject cultivateProject, List<Material> materials, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap, Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap);
|
||||
|
||||
/// <summary>
|
||||
/// 获取物品列表
|
||||
@@ -44,6 +44,14 @@ internal interface ICultivationService
|
||||
/// <returns>项目集合</returns>
|
||||
ObservableCollection<CultivateProject> GetProjectCollection();
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取统计物品列表
|
||||
/// </summary>
|
||||
/// <param name="cultivateProject">养成计划</param>
|
||||
/// <param name="materials">元数据</param>
|
||||
/// <returns>统计物品列表</returns>
|
||||
Task<List<StatisticsCultivateItem>> GetStatisticsCultivateItemsAsync(CultivateProject cultivateProject, List<Material> materials);
|
||||
|
||||
/// <summary>
|
||||
/// 删除养成清单
|
||||
/// </summary>
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Message;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
|
||||
using System.Collections.ObjectModel;
|
||||
using WebDailyNote = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote;
|
||||
|
||||
namespace Snap.Hutao.Service.DailyNote;
|
||||
|
||||
@@ -26,19 +18,16 @@ namespace Snap.Hutao.Service.DailyNote;
|
||||
internal class DailyNoteNotifier
|
||||
{
|
||||
private readonly IServiceScopeFactory scopeFactory;
|
||||
private readonly BindingClient bindingClient;
|
||||
private readonly DailyNoteEntry entry;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的实时便笺通知器
|
||||
/// </summary>
|
||||
/// <param name="scopeFactory">范围工厂</param>
|
||||
/// <param name="bindingClient">绑定客户端</param>
|
||||
/// <param name="entry">实时便笺入口</param>
|
||||
public DailyNoteNotifier(IServiceScopeFactory scopeFactory, BindingClient bindingClient, DailyNoteEntry entry)
|
||||
public DailyNoteNotifier(IServiceScopeFactory scopeFactory, DailyNoteEntry entry)
|
||||
{
|
||||
this.scopeFactory = scopeFactory;
|
||||
this.bindingClient = bindingClient;
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
@@ -128,7 +117,21 @@ internal class DailyNoteNotifier
|
||||
return;
|
||||
}
|
||||
|
||||
List<UserGameRole> roles = await bindingClient.GetUserGameRolesByCookieAsync(entry.User).ConfigureAwait(false);
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
BindingClient bindingClient = scope.ServiceProvider.GetRequiredService<BindingClient>();
|
||||
AuthClient authClient = scope.ServiceProvider.GetRequiredService<AuthClient>();
|
||||
|
||||
string? actionTicket = await authClient
|
||||
.GetActionTicketByStokenAsync("game_role", entry.User)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
List<UserGameRole> roles = await scope.ServiceProvider
|
||||
.GetRequiredService<BindingClient>()
|
||||
.GetUserGameRolesByActionTicketAsync(actionTicket!, entry.User)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
string attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? "未知角色";
|
||||
|
||||
ToastContentBuilder builder = new ToastContentBuilder()
|
||||
@@ -137,14 +140,10 @@ internal class DailyNoteNotifier
|
||||
.AddButton(new ToastButton().SetContent("开始游戏").AddArgument("Action", "LaunchGame").AddArgument("Uid", entry.Uid))
|
||||
.AddButton(new ToastButtonDismiss("我知道了"));
|
||||
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
if (appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, SettingEntryHelper.FalseString).GetBoolean())
|
||||
{
|
||||
builder.SetToastScenario(ToastScenario.Reminder);
|
||||
}
|
||||
}
|
||||
|
||||
if (hints.Count > 2)
|
||||
{
|
||||
@@ -161,4 +160,5 @@ internal class DailyNoteNotifier
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
builder.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Snap.Hutao.Context.Database;
|
||||
@@ -12,9 +11,7 @@ using Snap.Hutao.Message;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
|
||||
using System.Collections.ObjectModel;
|
||||
using WebDailyNote = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote;
|
||||
|
||||
@@ -63,9 +60,9 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
||||
{
|
||||
DailyNoteEntry newEntry = DailyNoteEntry.Create(role);
|
||||
newEntry.DailyNote = await gameRecordClient.GetDailyNoteAsync(role.User, newEntry.Uid).ConfigureAwait(false);
|
||||
appDbContext.DailyNotes.AddAndSave(newEntry);
|
||||
|
||||
newEntry.UserGameRole = userService.GetUserGameRoleByUid(roleUid);
|
||||
await appDbContext.DailyNotes.AddAndSaveAsync(newEntry).ConfigureAwait(false);
|
||||
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
entries?.Add(newEntry);
|
||||
}
|
||||
@@ -84,7 +81,7 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
List<DailyNoteEntry> entryList = appDbContext.DailyNotes.AsNoTracking().ToList();
|
||||
entryList.ForEach(entry => { entry.UserGameRole = userService.GetUserGameRoleByUid(entry.Uid); });
|
||||
entries = new(appDbContext.DailyNotes);
|
||||
entries = new(entryList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +95,6 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
GameRecordClient gameRecordClient = scope.ServiceProvider.GetRequiredService<GameRecordClient>();
|
||||
BindingClient bindingClient = scope.ServiceProvider.GetRequiredService<BindingClient>();
|
||||
|
||||
foreach (DailyNoteEntry entry in appDbContext.DailyNotes.Include(n => n.User))
|
||||
{
|
||||
@@ -113,7 +109,7 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
||||
|
||||
if (notify)
|
||||
{
|
||||
await new DailyNoteNotifier(scopeFactory, bindingClient, entry).NotifyAsync().ConfigureAwait(false);
|
||||
await new DailyNoteNotifier(scopeFactory, entry).NotifyAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Extension;
|
||||
|
||||
@@ -244,9 +244,10 @@ internal class GameService : IGameService, IDisposable
|
||||
string commandLine = new CommandLineBuilder()
|
||||
.AppendIf("-popupwindow", configuration.IsBorderless)
|
||||
.Append("-screen-fullscreen", configuration.IsFullScreen ? 1 : 0)
|
||||
.AppendIf("-window-mode", configuration.IsExclusive, "exclusive")
|
||||
.Append("-screen-width", configuration.ScreenWidth)
|
||||
.Append("-screen-height", configuration.ScreenHeight)
|
||||
.Build();
|
||||
.ToString();
|
||||
|
||||
Process game = new()
|
||||
{
|
||||
@@ -310,10 +311,6 @@ internal class GameService : IGameService, IDisposable
|
||||
{
|
||||
account = GameAccount.Create(name, registrySdk);
|
||||
|
||||
// sync cache
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
gameAccounts.Add(GameAccount.Create(name, registrySdk));
|
||||
|
||||
// sync database
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
@@ -324,6 +321,10 @@ internal class GameService : IGameService, IDisposable
|
||||
.AddAndSaveAsync(account)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// sync cache
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
gameAccounts.Add(account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ namespace Snap.Hutao.Service.Game;
|
||||
/// </summary>
|
||||
internal readonly struct LaunchConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否为独占全屏
|
||||
/// </summary>
|
||||
public readonly bool IsExclusive;
|
||||
|
||||
/// <summary>
|
||||
/// 是否全屏,全屏时无边框设置将被覆盖
|
||||
/// </summary>
|
||||
@@ -41,14 +46,16 @@ internal readonly struct LaunchConfiguration
|
||||
/// <summary>
|
||||
/// 构造一个新的启动配置
|
||||
/// </summary>
|
||||
/// <param name="isExclusive">独占全屏</param>
|
||||
/// <param name="isFullScreen">全屏</param>
|
||||
/// <param name="isBorderless">无边框</param>
|
||||
/// <param name="screenWidth">宽度</param>
|
||||
/// <param name="screenHeight">高度</param>
|
||||
/// <param name="unlockFps">解锁帧率</param>
|
||||
/// <param name="targetFps">目标帧率</param>
|
||||
public LaunchConfiguration(bool isFullScreen, bool isBorderless, int screenWidth, int screenHeight, bool unlockFps, int targetFps)
|
||||
public LaunchConfiguration(bool isExclusive, bool isFullScreen, bool isBorderless, int screenWidth, int screenHeight, bool unlockFps, int targetFps)
|
||||
{
|
||||
IsExclusive = isExclusive;
|
||||
IsFullScreen = isFullScreen;
|
||||
IsBorderless = isBorderless;
|
||||
ScreenHeight = screenHeight;
|
||||
|
||||
@@ -23,7 +23,7 @@ internal partial class UnityLogGameLocator : IGameLocator
|
||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string logFilePath = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
|
||||
|
||||
using (var tempFile = TemporaryFile.CreateFromFileCopy(logFilePath))
|
||||
using (TemporaryFile? tempFile = TemporaryFile.CreateFromFileCopy(logFilePath))
|
||||
{
|
||||
if (tempFile == null)
|
||||
{
|
||||
@@ -32,7 +32,7 @@ internal partial class UnityLogGameLocator : IGameLocator
|
||||
|
||||
string content = File.ReadAllText(tempFile.Path);
|
||||
|
||||
var matchResult = WarmupFileLine().Match(content);
|
||||
Match matchResult = WarmupFileLine().Match(content);
|
||||
if (!matchResult.Success)
|
||||
{
|
||||
return new(false, $"在 Unity 日志文件中找不到游戏路径");
|
||||
|
||||
@@ -150,9 +150,12 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
|
||||
|
||||
Must.Range(adr >= 0, "未匹配到FPS字节");
|
||||
|
||||
fixed (byte* pSpan = image)
|
||||
{
|
||||
int rip = adr + 2;
|
||||
int rel = image.Fixed<int>(rip + 2); // Unsafe.ReadUnaligned<int>(ref image[rip + 2]);
|
||||
int rel = *(int*)(pSpan + rip + 2); // Unsafe.ReadUnaligned<int>(ref image[rip + 2]);
|
||||
int ofs = rip + rel + 6;
|
||||
fpsAddress = (nuint)((long)unityPlayer.modBaseAddr + ofs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,13 @@ internal interface IMetadataService
|
||||
/// <returns>Id到角色的字典</returns>
|
||||
ValueTask<Dictionary<AvatarId, Avatar>> GetIdToAvatarMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取Id到材料的字典
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>Id到材料的字典</returns>
|
||||
ValueTask<Dictionary<MaterialId, Material>> GetIdToMaterialMapAsync(CancellationToken token = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取ID到圣遗物副词条的字典
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
@@ -26,6 +27,12 @@ internal partial class MetadataService
|
||||
return FromCacheAsDictionaryAsync<AvatarId, Avatar>("Avatar", a => a.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<MaterialId, Material>> GetIdToMaterialMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<MaterialId, Material>("Material", a => a.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<ReliquaryAffixId, ReliquaryAffix>> GetIdReliquaryAffixMapAsync(CancellationToken token = default)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>Snap.Hutao</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Platform>x64</Platform>
|
||||
<Platforms>x64</Platforms>
|
||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
|
||||
<PublishProfile>win10-$(Platform).pubxml</PublishProfile>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
@@ -69,6 +71,7 @@
|
||||
<None Remove="View\Dialog\AchievementImportDialog.xaml" />
|
||||
<None Remove="View\Dialog\AdoptCalculatorDialog.xaml" />
|
||||
<None Remove="View\Dialog\AvatarInfoQueryDialog.xaml" />
|
||||
<None Remove="View\Dialog\CommunityGameRecordDialog.xaml" />
|
||||
<None Remove="View\Dialog\CultivateProjectDialog.xaml" />
|
||||
<None Remove="View\Dialog\CultivatePromotionDeltaDialog.xaml" />
|
||||
<None Remove="View\Dialog\DailyNoteNotificationDialog.xaml" />
|
||||
@@ -136,7 +139,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0-preview2" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0-preview3" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||
@@ -153,7 +156,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.0.64" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.164-beta">
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.138-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -181,6 +184,11 @@
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\CommunityGameRecordDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\CultivatePromotionDeltaDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -41,8 +41,8 @@ public sealed partial class DescParamComboBox : UserControl
|
||||
/// </summary>
|
||||
public int PreferredSelectedIndex
|
||||
{
|
||||
get { return (int)GetValue(PreferredSelectedIndexProperty); }
|
||||
set { SetValue(PreferredSelectedIndexProperty, value); }
|
||||
get => (int)GetValue(PreferredSelectedIndexProperty);
|
||||
set => SetValue(PreferredSelectedIndexProperty, value);
|
||||
}
|
||||
|
||||
private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
|
||||
@@ -54,7 +54,7 @@ public sealed partial class DescParamComboBox : UserControl
|
||||
if (args.NewValue != args.OldValue && args.NewValue is IList<LevelParam<string, ParameterInfo>> list)
|
||||
{
|
||||
descParamComboBox.ItemHost.ItemsSource = list;
|
||||
descParamComboBox.ItemHost.SelectedIndex = Math.Min(descParamComboBox.PreferredSelectedIndex, list.Count);
|
||||
descParamComboBox.ItemHost.SelectedIndex = Math.Min(descParamComboBox.PreferredSelectedIndex, list.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<ContentDialog
|
||||
x:Class="Snap.Hutao.View.Dialog.CommunityGameRecordDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Closed="OnContentDialogClosed"
|
||||
DefaultButton="Primary"
|
||||
PrimaryButtonText="完成"
|
||||
Style="{StaticResource DefaultContentDialogStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Loaded="OnGridLoaded">
|
||||
<WebView2
|
||||
Name="WebView"
|
||||
Width="360"
|
||||
Height="580"/>
|
||||
</Grid>
|
||||
</ContentDialog>
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) Microsoft Corporation and Contributors.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.Web.Bridge;
|
||||
|
||||
namespace Snap.Hutao.View.Dialog;
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><CFB7>¼<EFBFBD>Ի<EFBFBD><D4BB><EFBFBD>
|
||||
/// </summary>
|
||||
public sealed partial class CommunityGameRecordDialog : ContentDialog
|
||||
{
|
||||
private readonly IServiceScope scope;
|
||||
[SuppressMessage("", "IDE0052")]
|
||||
private MiHoYoJSInterface? jsInterface;
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µ<EFBFBD><C2B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><CFB7>¼<EFBFBD>Ի<EFBFBD><D4BB><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="window"><3E><><EFBFBD><EFBFBD></param>
|
||||
public CommunityGameRecordDialog(MainWindow window)
|
||||
{
|
||||
InitializeComponent();
|
||||
XamlRoot = window.Content.XamlRoot;
|
||||
scope = Ioc.Default.CreateScope();
|
||||
}
|
||||
|
||||
private void OnGridLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
InitializeAsync().SafeForget();
|
||||
}
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
await WebView.EnsureCoreWebView2Async();
|
||||
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
|
||||
User? user = scope.ServiceProvider.GetRequiredService<IUserService>().Current;
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent();
|
||||
jsInterface = new(coreWebView2, scope.ServiceProvider);
|
||||
coreWebView2.Navigate("https://webstatic.mihoyo.com/app/community-game-records/index.html");
|
||||
}
|
||||
|
||||
private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
|
||||
{
|
||||
jsInterface = null;
|
||||
scope.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -3,20 +3,8 @@
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ public sealed partial class CultivatePromotionDeltaDialog : ContentDialog
|
||||
/// </summary>
|
||||
public ICalculableAvatar? Avatar
|
||||
{
|
||||
get { return (ICalculableAvatar?)GetValue(AvatarProperty); }
|
||||
set { SetValue(AvatarProperty, value); }
|
||||
get => (ICalculableAvatar?)GetValue(AvatarProperty);
|
||||
set => SetValue(AvatarProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,8 +45,8 @@ public sealed partial class CultivatePromotionDeltaDialog : ContentDialog
|
||||
/// </summary>
|
||||
public ICalculableWeapon? Weapon
|
||||
{
|
||||
get { return (ICalculableWeapon?)GetValue(WeaponProperty); }
|
||||
set { SetValue(WeaponProperty, value); }
|
||||
get => (ICalculableWeapon?)GetValue(WeaponProperty);
|
||||
set => SetValue(WeaponProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -50,9 +50,6 @@ public sealed partial class DailyNoteVerificationDialog : ContentDialog
|
||||
coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent();
|
||||
dailyNoteJsInterface = new(coreWebView2, scope.ServiceProvider);
|
||||
|
||||
#if DEBUG
|
||||
coreWebView2.OpenDevToolsWindow();
|
||||
#endif
|
||||
string query = $"?role_id={uid.Value}&server={uid.Region}";
|
||||
coreWebView2.Navigate($"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/daily/{query}");
|
||||
}
|
||||
|
||||
@@ -39,8 +39,7 @@ public sealed partial class SignInWebViewDialog : ContentDialog
|
||||
{
|
||||
await WebView.EnsureCoreWebView2Async();
|
||||
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
|
||||
IUserService userService = scope.ServiceProvider.GetRequiredService<IUserService>();
|
||||
User? user = userService.Current;
|
||||
User? user = scope.ServiceProvider.GetRequiredService<IUserService>().Current;
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
@@ -49,10 +48,6 @@ public sealed partial class SignInWebViewDialog : ContentDialog
|
||||
|
||||
coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent();
|
||||
signInJsInterface = new(coreWebView2, scope.ServiceProvider);
|
||||
|
||||
#if DEBUG
|
||||
coreWebView2.OpenDevToolsWindow();
|
||||
#endif
|
||||
coreWebView2.Navigate("https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?act_id=e202009291139501");
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<shc:BindingProxy x:Key="BindingProxy" DataContext="{Binding}"/>
|
||||
</Page.Resources>
|
||||
|
||||
<mxi:Interaction.Behaviors>
|
||||
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
|
||||
</mxi:Interaction.Behaviors>
|
||||
@@ -30,6 +34,12 @@
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Margin="16,0,0,2"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{Binding FinishDescription}"/>
|
||||
|
||||
<CommandBar Grid.Column="1" DefaultLabelPosition="Right">
|
||||
|
||||
<CommandBar.Content>
|
||||
@@ -71,7 +81,6 @@
|
||||
Command="{Binding RemoveArchiveCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="删除当前存档"/>
|
||||
|
||||
<AppBarSeparator/>
|
||||
|
||||
<AppBarButton Icon="{shcm:FontIcon Glyph=}" Label="导入">
|
||||
@@ -93,6 +102,7 @@
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="导出"/>
|
||||
<AppBarSeparator/>
|
||||
|
||||
<AppBarToggleButton
|
||||
Command="{Binding SortIncompletedSwitchCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
@@ -114,21 +124,31 @@
|
||||
SelectionMode="Single">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid Margin="0,6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="24"/>
|
||||
<ColumnDefinition Width="36"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<shci:CachedImage
|
||||
Grid.Column="0"
|
||||
Width="24"
|
||||
Height="24"
|
||||
Source="{Binding Icon, Converter={StaticResource AchievementIconConverter}}"/>
|
||||
Width="36"
|
||||
Height="36"
|
||||
Source="{Binding Icon}"/>
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,2">
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding Name}"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,0,0,2"
|
||||
Margin="0,2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Name}"/>
|
||||
Opacity="0.7"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding FinishDescription}"/>
|
||||
<ProgressBar
|
||||
Height="1"
|
||||
MinHeight="1"
|
||||
Margin="0,4,0,0"
|
||||
Maximum="1"
|
||||
Value="{Binding FinishPercent}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
@@ -163,6 +183,8 @@
|
||||
Grid.Column="1"
|
||||
Margin="6,0,12,0"
|
||||
Padding="16,0,0,0"
|
||||
Command="{Binding Path=DataContext.SaveAchievementCommand, Source={StaticResource BindingProxy}}"
|
||||
CommandParameter="{Binding}"
|
||||
IsChecked="{Binding IsChecked, Mode=TwoWay}"
|
||||
Style="{StaticResource DefaultCheckBoxStyle}">
|
||||
<CheckBox.Content>
|
||||
|
||||
@@ -142,6 +142,13 @@
|
||||
<Grid>
|
||||
<ScrollViewer Padding="0,0,4,0">
|
||||
<StackPanel>
|
||||
<ItemsControl>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<InfoBar/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<Pivot>
|
||||
<PivotItem
|
||||
Content="{Binding Announcement.List[0]}"
|
||||
|
||||
@@ -58,6 +58,11 @@
|
||||
CommandParameter="{Binding ElementName=ImageExportPanel}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="导出图片"/>
|
||||
<AppBarButton
|
||||
Command="{Binding CultivateCommand}"
|
||||
CommandParameter="{Binding SelectedAvatar}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="养成计算"/>
|
||||
<AppBarSeparator/>
|
||||
<AppBarButton Icon="{shcm:FontIcon Glyph=}" Label="同步角色信息">
|
||||
<AppBarButton.Flyout>
|
||||
@@ -129,7 +134,7 @@
|
||||
</cwucont:SwitchPresenter>
|
||||
</SplitView.Pane>
|
||||
<SplitView.Content>
|
||||
<ScrollViewer Padding="0,0,0,-88">
|
||||
<ScrollViewer Padding="0,0,0,0">
|
||||
<StackPanel
|
||||
Name="ImageExportPanel"
|
||||
MaxWidth="800"
|
||||
@@ -551,30 +556,6 @@
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<Border
|
||||
Margin="16,0,16,16"
|
||||
Background="{StaticResource CardBackgroundFillColorDefault}"
|
||||
BorderBrush="{StaticResource CardStrokeColorDefault}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource CompatCornerRadius}">
|
||||
<Grid Margin="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Style="{StaticResource TitleTextBlockStyle}" Text="{Binding Summary.Player.Nickname}"/>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource BaseTextBlockStyle}"
|
||||
Text="Created by Snap Hutao"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="官网 hut.ao"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</SplitView.Content>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
x:Class="Snap.Hutao.View.Page.CultivationPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwua="using:CommunityToolkit.WinUI.UI.Animations"
|
||||
xmlns:cwucont="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:cwuconv="using:CommunityToolkit.WinUI.UI.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@@ -29,6 +30,11 @@
|
||||
<x:Double>1</x:Double>
|
||||
</cwuconv:BoolToObjectConverter.FalseValue>
|
||||
</cwuconv:BoolToObjectConverter>
|
||||
|
||||
<cwuconv:BoolToObjectConverter
|
||||
x:Key="BoolToStyleSelector"
|
||||
FalseValue="{StaticResource BodyTextBlockStyle}"
|
||||
TrueValue="{StaticResource BaseTextBlockStyle}"/>
|
||||
</Page.Resources>
|
||||
|
||||
<mxi:Interaction.Behaviors>
|
||||
@@ -43,6 +49,54 @@
|
||||
<Pivot>
|
||||
<Pivot.RightHeader>
|
||||
<CommandBar DefaultLabelPosition="Right">
|
||||
<AppBarButton
|
||||
Command="{Binding UpdateStatisticsItemsCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="材料统计">
|
||||
<AppBarButton.Flyout>
|
||||
<Flyout Placement="Bottom">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
|
||||
<Setter Property="MaxHeight" Value="480"/>
|
||||
<Setter Property="Padding" Value="0,2,0,2"/>
|
||||
<Setter Property="Background" Value="{ThemeResource FlyoutPresenterBackground}"/>
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<ItemsControl Margin="16,0,16,16" ItemsSource="{Binding StatisticsItems}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="0,16,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="160"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<shvc:ItemIcon
|
||||
Grid.Column="0"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Icon="{Binding Inner.Icon, Converter={StaticResource ItemIconConverter}}"
|
||||
Quality="{Binding Inner.RankLevel}"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Inner.Name}"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="16,0,4,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding CountFormatted}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Flyout>
|
||||
</AppBarButton.Flyout>
|
||||
</AppBarButton>
|
||||
<AppBarSeparator/>
|
||||
<AppBarElementContainer>
|
||||
<ComboBox
|
||||
Height="36"
|
||||
@@ -71,7 +125,8 @@
|
||||
<PivotItem Header="材料清单">
|
||||
<cwucont:AdaptiveGridView
|
||||
Padding="16,16,4,4"
|
||||
DesiredWidth="360"
|
||||
cwua:ItemsReorderAnimation.Duration="0:0:0.1"
|
||||
DesiredWidth="320"
|
||||
ItemContainerStyle="{StaticResource LargeGridViewItemStyle}"
|
||||
ItemsSource="{Binding CultivateEntries}"
|
||||
SelectionMode="None">
|
||||
@@ -81,7 +136,7 @@
|
||||
<Grid Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -115,10 +170,8 @@
|
||||
ToolTipService.ToolTip="删除清单"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ItemsControl
|
||||
Grid.Row="1"
|
||||
Margin="8,0,8,8"
|
||||
ItemsSource="{Binding Items}">
|
||||
<ScrollViewer Grid.Row="1" Height="240">
|
||||
<ItemsControl Margin="8,0,8,8" ItemsSource="{Binding Items}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="0,4,0,0">
|
||||
@@ -159,12 +212,14 @@
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{Binding IsToday, Converter={StaticResource BoolToStyleSelector}}"
|
||||
Text="{Binding Inner.Name}"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Style="{Binding IsToday, Converter={StaticResource BoolToStyleSelector}}"
|
||||
Text="{Binding Entity.Count}"/>
|
||||
</Grid>
|
||||
</Button>
|
||||
@@ -173,6 +228,7 @@
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid.Resources>
|
||||
<Storyboard x:Name="ButtonPanelVisibleStoryboard">
|
||||
|
||||
@@ -87,6 +87,10 @@
|
||||
</Flyout>
|
||||
</AppBarButton.Flyout>
|
||||
</AppBarButton>
|
||||
<AppBarButton
|
||||
Command="{Binding DailyNoteVerificationCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="验证当前账号角色"/>
|
||||
<AppBarButton Icon="{shcm:FontIcon Glyph=}" Label="通知设置">
|
||||
<AppBarButton.Flyout>
|
||||
<Flyout Placement="BottomEdgeAlignedRight">
|
||||
|
||||
@@ -249,7 +249,7 @@
|
||||
Margin="0,16,0,8"
|
||||
Style="{StaticResource BaseTextBlockStyle}"
|
||||
Text="五星"/>
|
||||
<GridView ItemsSource="{Binding SelectedHistoryWish.OrangeList}">
|
||||
<GridView ItemsSource="{Binding SelectedHistoryWish.OrangeList}" SelectionMode="None">
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -276,7 +276,7 @@
|
||||
Margin="0,0,0,8"
|
||||
Style="{StaticResource BaseTextBlockStyle}"
|
||||
Text="四星"/>
|
||||
<GridView ItemsSource="{Binding SelectedHistoryWish.PurpleList}">
|
||||
<GridView ItemsSource="{Binding SelectedHistoryWish.PurpleList}" SelectionMode="None">
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -303,7 +303,7 @@
|
||||
Margin="0,0,0,8"
|
||||
Style="{StaticResource BaseTextBlockStyle}"
|
||||
Text="三星"/>
|
||||
<GridView ItemsSource="{Binding SelectedHistoryWish.BlueList}">
|
||||
<GridView ItemsSource="{Binding SelectedHistoryWish.BlueList}" SelectionMode="None">
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
xmlns:shc="using:Snap.Hutao.Control"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
xmlns:shmbh="using:Snap.Hutao.Model.Binding.Hutao"
|
||||
xmlns:shv="using:Snap.Hutao.ViewModel"
|
||||
xmlns:shvc="using:Snap.Hutao.View.Control"
|
||||
d:DataContext="{d:DesignInstance shv:HutaoDatabaseViewModel}"
|
||||
@@ -20,6 +21,47 @@
|
||||
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
|
||||
</mxi:Interaction.Behaviors>
|
||||
|
||||
<Page.Resources>
|
||||
<DataTemplate x:Key="TeamItemTemplate" x:DataType="shmbh:Team">
|
||||
<Border Margin="12,0,12,12" Style="{StaticResource BorderCardStyle}">
|
||||
<Grid Margin="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<cwuc:UniformGrid ColumnSpacing="6" Columns="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<shvc:ItemIcon
|
||||
Width="48"
|
||||
Height="48"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="{Binding Quality}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Rate}"/>
|
||||
<TextBlock
|
||||
Margin="0,2,0,0"
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding Name}"/>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
<Pivot>
|
||||
<Pivot.RightHeader>
|
||||
@@ -97,6 +139,39 @@
|
||||
</Pivot.ItemTemplate>
|
||||
</Pivot>
|
||||
</PivotItem>
|
||||
<PivotItem Header="队伍出场">
|
||||
<Pivot ItemsSource="{Binding TeamAppearances}">
|
||||
<Pivot.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Floor}"/>
|
||||
</DataTemplate>
|
||||
</Pivot.HeaderTemplate>
|
||||
<Pivot.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollViewer Grid.Column="0" Margin="0,12,0,0">
|
||||
<ItemsControl
|
||||
ItemTemplate="{StaticResource TeamItemTemplate}"
|
||||
ItemsPanel="{StaticResource ItemsStackPanelTemplate}"
|
||||
ItemsSource="{Binding Up}"/>
|
||||
</ScrollViewer>
|
||||
|
||||
<ScrollViewer Grid.Column="1" Margin="0,12,0,0">
|
||||
<ItemsControl
|
||||
ItemTemplate="{StaticResource TeamItemTemplate}"
|
||||
ItemsPanel="{StaticResource ItemsStackPanelTemplate}"
|
||||
ItemsSource="{Binding Down}"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Pivot.ItemTemplate>
|
||||
</Pivot>
|
||||
</PivotItem>
|
||||
<PivotItem Header="角色持有">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -231,117 +306,6 @@
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</PivotItem>
|
||||
<PivotItem Header="队伍出场">
|
||||
<Pivot ItemsSource="{Binding TeamAppearances}">
|
||||
<Pivot.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Floor}"/>
|
||||
</DataTemplate>
|
||||
</Pivot.HeaderTemplate>
|
||||
<Pivot.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollViewer Grid.Column="0" Margin="0,12,0,0">
|
||||
<ItemsControl ItemsSource="{Binding Up}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="12,0,12,12"
|
||||
Background="{StaticResource CardBackgroundFillColorDefault}"
|
||||
CornerRadius="{StaticResource CompatCornerRadius}">
|
||||
<Grid Margin="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<cwuc:UniformGrid ColumnSpacing="6" Columns="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<shvc:ItemIcon
|
||||
Width="48"
|
||||
Height="48"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="{Binding Quality}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Rate}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<ScrollViewer Grid.Column="1" Margin="0,12,0,0">
|
||||
<ItemsControl ItemsSource="{Binding Down}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="12,0,12,12"
|
||||
Background="{StaticResource CardBackgroundFillColorDefault}"
|
||||
CornerRadius="{StaticResource CompatCornerRadius}">
|
||||
<Grid Margin="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<cwuc:UniformGrid ColumnSpacing="6" Columns="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<shvc:ItemIcon
|
||||
Width="48"
|
||||
Height="48"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="{Binding Quality}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Rate}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Pivot.ItemTemplate>
|
||||
</Pivot>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</Grid>
|
||||
</shc:ScopedPage>
|
||||
|
||||
@@ -158,6 +158,17 @@
|
||||
</sc:SettingExpander>
|
||||
</sc:SettingsGroup>
|
||||
<sc:SettingsGroup Header="外观">
|
||||
<sc:Setting
|
||||
Description="与游戏内浏览器不兼容,切屏等操作也能使游戏闪退"
|
||||
Header="独占全屏"
|
||||
Icon="">
|
||||
<sc:Setting.ActionContent>
|
||||
<ToggleSwitch
|
||||
Width="120"
|
||||
IsOn="{Binding IsExclusive, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchSettingStyle}"/>
|
||||
</sc:Setting.ActionContent>
|
||||
</sc:Setting>
|
||||
<sc:Setting
|
||||
Description="覆盖默认的全屏状态"
|
||||
Header="全屏"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user