launch game phase 1

This commit is contained in:
DismissedLight
2022-10-28 14:59:31 +08:00
parent 7a99c44b29
commit 62d0fb5d05
48 changed files with 1380 additions and 217 deletions

View File

@@ -2,7 +2,10 @@
x:Class="Snap.Hutao.App" x:Class="Snap.Hutao.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"> xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
xmlns:shvc="using:Snap.Hutao.View.Converter">
<Application.Resources> <Application.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
@@ -13,21 +16,36 @@
<!--Modify Window title bar color--> <!--Modify Window title bar color-->
<StaticResource x:Key="WindowCaptionBackground" ResourceKey="ControlFillColorTransparentBrush"/> <StaticResource x:Key="WindowCaptionBackground" ResourceKey="ControlFillColorTransparentBrush"/>
<StaticResource x:Key="WindowCaptionBackgroundDisabled" ResourceKey="ControlFillColorTransparentBrush"/> <StaticResource x:Key="WindowCaptionBackgroundDisabled" ResourceKey="ControlFillColorTransparentBrush"/>
<!--Page Transparent Background-->
<StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/> <StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/>
<!--IconFont-->
<FontFamily x:Key="SymbolThemeFontFamily">ms-appx:///Resource/Font/Segoe Fluent Icons.ttf#Segoe Fluent Icons</FontFamily> <FontFamily x:Key="SymbolThemeFontFamily">ms-appx:///Resource/Font/Segoe Fluent Icons.ttf#Segoe Fluent Icons</FontFamily>
<!--InfoBar Resource-->
<Thickness x:Key="InfoBarIconMargin">6,16,16,16</Thickness> <Thickness x:Key="InfoBarIconMargin">6,16,16,16</Thickness>
<Thickness x:Key="InfoBarContentRootPadding">16,0,0,0</Thickness> <Thickness x:Key="InfoBarContentRootPadding">16,0,0,0</Thickness>
<!--Pivot Resource-->
<x:Double x:Key="PivotHeaderItemFontSize">16</x:Double> <x:Double x:Key="PivotHeaderItemFontSize">16</x:Double>
<!--CornerRadius-->
<CornerRadius x:Key="CompatCornerRadius">6</CornerRadius> <CornerRadius x:Key="CompatCornerRadius">6</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusTop">6,6,0,0</CornerRadius> <CornerRadius x:Key="CompatCornerRadiusTop">6,6,0,0</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius> <CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius> <CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius> <CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
<!--Converters-->
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
<shmmc:DescParamDescriptor x:Key="DescParamDescriptor"/>
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
<shmmc:GachaAvatarImgConverter x:Key="GachaAvatarImgConverter"/>
<shmmc:GachaAvatarIconConverter x:Key="GachaAvatarIconConverter"/>
<shmmc:ItemIconConverter x:Key="ItemIconConverter"/>
<shmmc:PropertyInfoDescriptor x:Key="PropertyDescriptor"/>
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@@ -54,13 +54,17 @@ public partial class App : Application
logger.LogInformation(EventIds.CommonLog, "Snap Hutao : {version}", CoreEnvironment.Version); logger.LogInformation(EventIds.CommonLog, "Snap Hutao : {version}", CoreEnvironment.Version);
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.TemporaryFolder.Path); logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.TemporaryFolder.Path);
JumpListHelper.ConfigAsync().SafeForget(logger);
Ioc.Default Ioc.Default
.GetRequiredService<IMetadataService>() .GetRequiredService<IMetadataService>()
.ImplictAs<IMetadataInitializer>()? .ImplictAs<IMetadataInitializer>()?
.InitializeInternalAsync() .InitializeInternalAsync()
.SafeForget(logger); .SafeForget(logger);
Ioc.Default.GetRequiredService<AppCenter>().Initialize(); Ioc.Default
.GetRequiredService<AppCenter>()
.Initialize();
} }
else else
{ {

View File

@@ -0,0 +1,58 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.UI.Behaviors;
using Microsoft.UI.Xaml;
using Snap.Hutao.Core;
namespace Snap.Hutao.Control.Behavior;
/// <summary>
/// 按给定比例自动调整高度的行为
/// </summary>
internal class AutoWidthBehavior : BehaviorBase<FrameworkElement>
{
private static readonly DependencyProperty TargetWidthProperty = Property<AutoWidthBehavior>.Depend(nameof(TargetWidth), 320D);
private static readonly DependencyProperty TargetHeightProperty = Property<AutoWidthBehavior>.Depend(nameof(TargetHeight), 1024D);
/// <summary>
/// 目标宽度
/// </summary>
public double TargetWidth
{
get => (double)GetValue(TargetWidthProperty);
set => SetValue(TargetWidthProperty, value);
}
/// <summary>
/// 目标高度
/// </summary>
public double TargetHeight
{
get => (double)GetValue(TargetHeightProperty);
set => SetValue(TargetHeightProperty, value);
}
/// <inheritdoc/>
protected override void OnAssociatedObjectLoaded()
{
UpdateElementWidth();
AssociatedObject.SizeChanged += OnSizeChanged;
}
/// <inheritdoc/>
protected override void OnDetaching()
{
AssociatedObject.SizeChanged -= OnSizeChanged;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateElementWidth();
}
private void UpdateElementWidth()
{
AssociatedObject.Width = (double)AssociatedObject.Height * (TargetWidth / TargetHeight);
}
}

View File

@@ -0,0 +1,63 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Text;
namespace Snap.Hutao.Core;
/// <summary>
/// 命令行建造器
/// </summary>
public class CommandLineBuilder
{
private const char WhiteSpace = ' ';
private readonly Dictionary<string, string?> options = new();
/// <summary>
/// 当符合条件时添加参数
/// </summary>
/// <param name="name">参数名称</param>
/// <param name="condition">条件</param>
/// <param name="value">值</param>
/// <returns>命令行建造器</returns>
public CommandLineBuilder AppendIf(string name, bool condition, object? value = null)
{
return condition ? Append(name, value) : this;
}
/// <summary>
/// 添加参数
/// </summary>
/// <param name="name">参数名称</param>
/// <param name="value">值</param>
/// <returns>命令行建造器</returns>
public CommandLineBuilder Append(string name, object? value = null)
{
options.Add(name, value?.ToString());
return this;
}
/// <inheritdoc cref="ToString"/>
public string Build()
{
return ToString();
}
/// <inheritdoc/>
public override string ToString()
{
StringBuilder s = new();
foreach ((string key, string? value) in options)
{
s.Append(WhiteSpace);
s.Append(key);
if (!string.IsNullOrEmpty(value))
{
s.Append(WhiteSpace);
s.Append(value);
}
}
return s.ToString();
}
}

View File

@@ -0,0 +1,35 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.LifeCycle;
using Windows.UI.StartScreen;
namespace Snap.Hutao.Core;
/// <summary>
/// 跳转列表帮助类
/// </summary>
public static class JumpListHelper
{
/// <summary>
/// 异步配置跳转列表
/// </summary>
/// <returns>任务</returns>
public static async Task ConfigAsync()
{
if (JumpList.IsSupported())
{
JumpList list = await JumpList.LoadCurrentAsync();
list.Items.Clear();
JumpListItem launchGameItem = JumpListItem.CreateWithArguments(Activation.LaunchGame, "启动游戏");
launchGameItem.GroupName = "快捷操作";
launchGameItem.Logo = new("ms-appx:///Resource/Icon/UI_GuideIcon_PlayMethod.png");
list.Items.Add(launchGameItem);
await list.SaveAsync();
}
}
}

View File

@@ -13,6 +13,11 @@ namespace Snap.Hutao.Core.LifeCycle;
/// </summary> /// </summary>
internal static class Activation internal static class Activation
{ {
/// <summary>
/// 启动游戏启动参数
/// </summary>
public const string LaunchGame = "LaunchGame";
private static readonly SemaphoreSlim ActivateSemaphore = new(1); private static readonly SemaphoreSlim ActivateSemaphore = new(1);
/// <summary> /// <summary>
@@ -44,16 +49,36 @@ internal static class Activation
private static async Task HandleActivationCoreAsync(AppActivationArguments args) private static async Task HandleActivationCoreAsync(AppActivationArguments args)
{ {
_ = Ioc.Default.GetRequiredService<MainWindow>(); string argument = string.Empty;
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>(); if (args.Kind == ExtendedActivationKind.Launch)
await infoBarService.WaitInitializationAsync().ConfigureAwait(false); {
if (args.TryGetLaunchActivatedArgument(out string? arguments))
{
argument = arguments;
}
}
switch (argument)
{
case "":
{
_ = Ioc.Default.GetRequiredService<MainWindow>();
await Ioc.Default.GetRequiredService<IInfoBarService>().WaitInitializationAsync().ConfigureAwait(false);
break;
}
case LaunchGame:
{
break;
}
}
if (args.Kind == ExtendedActivationKind.Protocol) if (args.Kind == ExtendedActivationKind.Protocol)
{ {
if (args.TryGetProtocolActivatedUri(out Uri? uri)) if (args.TryGetProtocolActivatedUri(out Uri? uri))
{ {
infoBarService.Information(uri.ToString()); Ioc.Default.GetRequiredService<IInfoBarService>().Information(uri.ToString());
await HandleUrlActivationAsync(uri).ConfigureAwait(false); await HandleUrlActivationAsync(uri).ConfigureAwait(false);
} }
} }

View File

@@ -28,4 +28,22 @@ public static class AppActivationArgumentsExtensions
return false; return false;
} }
/// <summary>
/// 尝试获取启动的参数
/// </summary>
/// <param name="activatedEventArgs">应用程序激活参数</param>
/// <param name="arguments">参数</param>
/// <returns>是否存在参数</returns>
public static bool TryGetLaunchActivatedArgument(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments)
{
arguments = null;
if (activatedEventArgs.Data is ILaunchActivatedEventArgs launchArgs)
{
arguments = launchArgs.Arguments;
return true;
}
return false;
}
} }

View File

@@ -10,10 +10,6 @@ namespace Snap.Hutao.Core.Setting;
/// </summary> /// </summary>
internal static class LocalSetting internal static class LocalSetting
{ {
/// <summary>
/// 由于 <see cref="Windows.Foundation.Collections.IPropertySet"/> 没有 nullable context,
/// 在处理引用类型时需要格外小心
/// </summary>
private static readonly ApplicationDataContainer Container; private static readonly ApplicationDataContainer Container;
static LocalSetting() static LocalSetting()
@@ -21,6 +17,198 @@ internal static class LocalSetting
Container = ApplicationData.Current.LocalSettings; Container = ApplicationData.Current.LocalSettings;
} }
/// <inheritdoc cref="Get{T}(string, T)"/>
public static byte Get(string key, byte defaultValue)
{
return Get<byte>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static short Get(string key, short defaultValue)
{
return Get<short>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static ushort Get(string key, ushort defaultValue)
{
return Get<ushort>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static int Get(string key, int defaultValue)
{
return Get<int>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static uint Get(string key, uint defaultValue)
{
return Get<uint>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static ulong Get(string key, ulong defaultValue)
{
return Get<ulong>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static float Get(string key, float defaultValue)
{
return Get<float>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static double Get(string key, double defaultValue)
{
return Get<double>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static bool Get(string key, bool defaultValue)
{
return Get<bool>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static char Get(string key, char defaultValue)
{
return Get<char>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static DateTimeOffset Get(string key, DateTimeOffset defaultValue)
{
return Get<DateTimeOffset>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static TimeSpan Get(string key, TimeSpan defaultValue)
{
return Get<TimeSpan>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static Guid Get(string key, Guid defaultValue)
{
return Get<Guid>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static Windows.Foundation.Point Get(string key, Windows.Foundation.Point defaultValue)
{
return Get<Windows.Foundation.Point>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static Windows.Foundation.Size Get(string key, Windows.Foundation.Size defaultValue)
{
return Get<Windows.Foundation.Size>(key, defaultValue);
}
/// <inheritdoc cref="Get{T}(string, T)"/>
public static Windows.Foundation.Rect Get(string key, Windows.Foundation.Rect defaultValue)
{
return Get<Windows.Foundation.Rect>(key, defaultValue);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, byte value)
{
Set<byte>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, short value)
{
Set<short>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, ushort value)
{
Set<ushort>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, int value)
{
Set<int>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, uint value)
{
Set<uint>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, ulong value)
{
Set<ulong>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, float value)
{
Set<float>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, double value)
{
Set<double>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, bool value)
{
Set<bool>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, char value)
{
Set<char>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, DateTimeOffset value)
{
Set<DateTimeOffset>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, TimeSpan value)
{
Set<TimeSpan>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, Guid value)
{
Set<Guid>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, Windows.Foundation.Point value)
{
Set<Windows.Foundation.Point>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, Windows.Foundation.Size value)
{
Set<Windows.Foundation.Size>(key, value);
}
/// <inheritdoc cref="Set{T}(string, T)"/>
public static void Set(string key, Windows.Foundation.Rect value)
{
Set<Windows.Foundation.Rect>(key, value);
}
/// <summary> /// <summary>
/// 获取设置项的值 /// 获取设置项的值
/// </summary> /// </summary>
@@ -28,8 +216,8 @@ internal static class LocalSetting
/// <param name="key">键</param> /// <param name="key">键</param>
/// <param name="defaultValue">默认值</param> /// <param name="defaultValue">默认值</param>
/// <returns>获取的值</returns> /// <returns>获取的值</returns>
[return: MaybeNull] private static T Get<T>(string key, T defaultValue = default)
public static T Get<T>(string key, [AllowNull] T defaultValue = default) where T : struct
{ {
if (Container.Values.TryGetValue(key, out object? value)) if (Container.Values.TryGetValue(key, out object? value))
{ {
@@ -49,9 +237,9 @@ internal static class LocalSetting
/// <typeparam name="T">设置项的类型</typeparam> /// <typeparam name="T">设置项的类型</typeparam>
/// <param name="key">键</param> /// <param name="key">键</param>
/// <param name="value">值</param> /// <param name="value">值</param>
/// <returns>设置的值</returns> private static void Set<T>(string key, T value)
public static object? Set<T>(string key, T value) where T : struct
{ {
return Container.Values[key] = value; Container.Values[key] = value;
} }
} }

View File

@@ -44,7 +44,7 @@ internal static class Persistence
/// <param name="appWindow">应用窗体</param> /// <param name="appWindow">应用窗体</param>
public static void Save(AppWindow appWindow) public static void Save(AppWindow appWindow)
{ {
LocalSetting.Set(SettingKeys.WindowRect, (ulong)(CompactRect)appWindow.GetRect()); LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)appWindow.GetRect());
} }
/// <summary> /// <summary>
@@ -124,7 +124,7 @@ internal static class Persistence
return new(rect.X, rect.Y, rect.Width, rect.Height); return new(rect.X, rect.Y, rect.Width, rect.Height);
} }
public static explicit operator ulong(CompactRect rect) public static implicit operator ulong(CompactRect rect)
{ {
return rect.Value; return rect.Value;
} }

View File

@@ -0,0 +1,12 @@
<Window
x:Class="Snap.Hutao.LaunchGameWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Snap.Hutao"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,20 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
namespace Snap.Hutao;
/// <summary>
/// 启动游戏窗口
/// </summary>
public sealed partial class LaunchGameWindow : Window
{
/// <summary>
/// 构造一个新的启动游戏窗口
/// </summary>
public LaunchGameWindow()
{
InitializeComponent();
}
}

View File

@@ -10,6 +10,11 @@ namespace Snap.Hutao.Model.Binding.Gacha;
/// </summary> /// </summary>
public class HistoryWish : WishBase public class HistoryWish : WishBase
{ {
/// <summary>
/// 版本
/// </summary>
public string Version { get; set; }
/// <summary> /// <summary>
/// 五星Up /// 五星Up
/// </summary> /// </summary>

View File

@@ -1,76 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Metadata.Avatar;
/// <summary>
/// 角色ID
/// </summary>
[SuppressMessage("", "SA1600")]
public static class AvatarIds
{
public const int Ayaka = 10000002;
public const int Qin = 10000003;
public const int Lisa = 10000006;
public const int Barbara = 10000014;
public const int Kaeya = 10000015;
public const int Diluc = 10000016;
public const int Razor = 10000020;
public const int Ambor = 10000021;
public const int Venti = 10000022;
public const int Xiangling = 10000023;
public const int Beidou = 10000024;
public const int Xingqiu = 10000025;
public const int Xiao = 10000026;
public const int Ningguang = 10000027;
public const int Klee = 10000029;
public const int Zhongli = 10000030;
public const int Fischl = 10000031;
public const int Bennett = 10000032;
public const int Tartaglia = 10000033;
public const int Noel = 10000034;
public const int Qiqi = 10000035;
public const int Chongyun = 10000036;
public const int Ganyu = 10000037;
public const int Albedo = 10000038;
public const int Diona = 10000039;
public const int Mona = 10000041;
public const int Keqing = 10000042;
public const int Sucrose = 10000043;
public const int Xinyan = 10000044;
public const int Rosaria = 10000045;
public const int Hutao = 10000046;
public const int Kazuha = 10000047;
public const int Feiyan = 10000048;
public const int Yoimiya = 10000049;
public const int Tohma = 10000050;
public const int Eula = 10000051;
public const int Shougun = 10000052;
public const int Sayu = 10000053;
public const int Kokomi = 10000054;
public const int Gorou = 10000055;
public const int Sara = 10000056;
public const int Itto = 10000057;
public const int Yae = 10000058;
public const int Heizou = 10000059;
public const int Yelan = 10000060;
public const int Aloy = 10000062;
public const int Shenhe = 10000063;
public const int Yunjin = 10000064;
public const int Shinobu = 10000065;
public const int Ayato = 10000066;
public const int Collei = 10000067;
public const int Dori = 10000068;
public const int Tighnari = 10000069;
public const int Nilou = 10000070;
public const int Cyno = 10000071;
public const int Candace = 10000072;
public const int Nahida = 10000073;
public const int Layla = 10000074;
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
namespace Snap.Hutao.Model.Metadata.Avatar;
/// <summary>
/// 料理奖励
/// </summary>
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!;
/// <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; }
/// <summary>
/// 材料列表
/// </summary>
public List<ItemWithCount> InputList { get; set; } = default!;
}

View File

@@ -76,6 +76,11 @@ public class FetterInfo
/// </summary> /// </summary>
public string CvKorean { get; set; } = default!; public string CvKorean { get; set; } = default!;
/// <summary>
/// 料理
/// </summary>
public CookBonus? CookBonus { get; set; }
/// <summary> /// <summary>
/// 好感语音 /// 好感语音
/// </summary> /// </summary>

View File

@@ -0,0 +1,37 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
namespace Snap.Hutao.Model.Metadata.Avatar;
/// <summary>
/// 带有个数的物品
/// </summary>
public class ItemWithCount
{
/// <summary>
/// 物品Id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; } = default!;
/// <summary>
/// 图标
/// </summary>
public string Icon { get; set; } = default!;
/// <summary>
/// 物品等级
/// </summary>
public ItemQuality RankLevel { get; set; }
/// <summary>
/// 数量
/// </summary>
public int Count { get; set; }
}

View File

@@ -0,0 +1,78 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata;
/// <summary>
/// 角色ID
/// </summary>
[SuppressMessage("", "SA1600")]
public static class AvatarIds
{
public static readonly AvatarId Ayaka = 10000002;
public static readonly AvatarId Qin = 10000003;
public static readonly AvatarId Lisa = 10000006;
public static readonly AvatarId Barbara = 10000014;
public static readonly AvatarId Kaeya = 10000015;
public static readonly AvatarId Diluc = 10000016;
public static readonly AvatarId Razor = 10000020;
public static readonly AvatarId Ambor = 10000021;
public static readonly AvatarId Venti = 10000022;
public static readonly AvatarId Xiangling = 10000023;
public static readonly AvatarId Beidou = 10000024;
public static readonly AvatarId Xingqiu = 10000025;
public static readonly AvatarId Xiao = 10000026;
public static readonly AvatarId Ningguang = 10000027;
public static readonly AvatarId Klee = 10000029;
public static readonly AvatarId Zhongli = 10000030;
public static readonly AvatarId Fischl = 10000031;
public static readonly AvatarId Bennett = 10000032;
public static readonly AvatarId Tartaglia = 10000033;
public static readonly AvatarId Noel = 10000034;
public static readonly AvatarId Qiqi = 10000035;
public static readonly AvatarId Chongyun = 10000036;
public static readonly AvatarId Ganyu = 10000037;
public static readonly AvatarId Albedo = 10000038;
public static readonly AvatarId Diona = 10000039;
public static readonly AvatarId Mona = 10000041;
public static readonly AvatarId Keqing = 10000042;
public static readonly AvatarId Sucrose = 10000043;
public static readonly AvatarId Xinyan = 10000044;
public static readonly AvatarId Rosaria = 10000045;
public static readonly AvatarId Hutao = 10000046;
public static readonly AvatarId Kazuha = 10000047;
public static readonly AvatarId Feiyan = 10000048;
public static readonly AvatarId Yoimiya = 10000049;
public static readonly AvatarId Tohma = 10000050;
public static readonly AvatarId Eula = 10000051;
public static readonly AvatarId Shougun = 10000052;
public static readonly AvatarId Sayu = 10000053;
public static readonly AvatarId Kokomi = 10000054;
public static readonly AvatarId Gorou = 10000055;
public static readonly AvatarId Sara = 10000056;
public static readonly AvatarId Itto = 10000057;
public static readonly AvatarId Yae = 10000058;
public static readonly AvatarId Heizou = 10000059;
public static readonly AvatarId Yelan = 10000060;
public static readonly AvatarId Aloy = 10000062;
public static readonly AvatarId Shenhe = 10000063;
public static readonly AvatarId Yunjin = 10000064;
public static readonly AvatarId Shinobu = 10000065;
public static readonly AvatarId Ayato = 10000066;
public static readonly AvatarId Collei = 10000067;
public static readonly AvatarId Dori = 10000068;
public static readonly AvatarId Tighnari = 10000069;
public static readonly AvatarId Nilou = 10000070;
public static readonly AvatarId Cyno = 10000071;
public static readonly AvatarId Candace = 10000072;
public static readonly AvatarId Nahida = 10000073;
public static readonly AvatarId Layla = 10000074;
}

View File

@@ -0,0 +1,31 @@
// 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 GachaAvatarIconConverter : ValueConverterBase<string, Uri>
{
private const string BaseUrl = "https://static.snapgenshin.com/GachaAvatarIcon/UI_Gacha_AvatarIcon_{0}.png";
/// <summary>
/// 名称转Uri
/// </summary>
/// <param name="name">名称</param>
/// <returns>链接</returns>
public static Uri IconNameToUri(string name)
{
name = name["UI_AvatarIcon_".Length..];
return new Uri(string.Format(BaseUrl, name));
}
/// <inheritdoc/>
public override Uri Convert(string from)
{
return IconNameToUri(from);
}
}

View File

@@ -6,7 +6,7 @@ using Snap.Hutao.Control;
namespace Snap.Hutao.Model.Metadata.Converter; namespace Snap.Hutao.Model.Metadata.Converter;
/// <summary> /// <summary>
/// 角色头像转换器 /// 立绘转换器
/// </summary> /// </summary>
internal class GachaAvatarImgConverter : ValueConverterBase<string, Uri> internal class GachaAvatarImgConverter : ValueConverterBase<string, Uri>
{ {

View File

@@ -0,0 +1,30 @@
// 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 ItemIconConverter : ValueConverterBase<string, Uri>
{
private const string BaseUrl = "https://static.snapgenshin.com/ItemIcon/{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 IconNameToUri(from);
}
}

View File

@@ -15,6 +15,11 @@ public class GachaEvent
/// </summary> /// </summary>
public string Name { get; set; } = default!; public string Name { get; set; } = default!;
/// <summary>
/// 版本
/// </summary>
public string Version { get; set; } = default!;
/// <summary> /// <summary>
/// 开始时间 /// 开始时间
/// </summary> /// </summary>

View File

@@ -9,7 +9,7 @@
<Identity <Identity
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d" Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
Publisher="CN=DGP Studio" Publisher="CN=DGP Studio"
Version="1.1.13.0" /> Version="1.1.14.0" />
<Properties> <Properties>
<DisplayName>胡桃</DisplayName> <DisplayName>胡桃</DisplayName>

View File

@@ -7,7 +7,6 @@ using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Logging; using Snap.Hutao.Core.Logging;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Windows.UI.ViewManagement;
using WinRT; using WinRT;
namespace Snap.Hutao; namespace Snap.Hutao;
@@ -32,6 +31,7 @@ public static partial class Program
private static void Main(string[] args) private static void Main(string[] args)
{ {
_ = args; _ = args;
XamlCheckProcessRequirements(); XamlCheckProcessRequirements();
ComWrappersSupport.InitializeComWrappers(); ComWrappersSupport.InitializeComWrappers();
@@ -75,7 +75,6 @@ public static partial class Program
// Discrete services // Discrete services
.AddSingleton<IMessenger>(WeakReferenceMessenger.Default) .AddSingleton<IMessenger>(WeakReferenceMessenger.Default)
.AddSingleton(new UISettings())
.BuildServiceProvider(); .BuildServiceProvider();

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -2,7 +2,7 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Metadata;
namespace Snap.Hutao.Service.AvatarInfo.Factory; namespace Snap.Hutao.Service.AvatarInfo.Factory;

View File

@@ -147,6 +147,7 @@ internal class HistoryWishBuilder
TotalCount = totalCountTracker, TotalCount = totalCountTracker,
// fill // fill
Version = gachaEvent.Version,
OrangeUpList = orangeUpCounter.ToStatisticsList(), OrangeUpList = orangeUpCounter.ToStatisticsList(),
PurpleUpList = purpleUpCounter.ToStatisticsList(), PurpleUpList = purpleUpCounter.ToStatisticsList(),
OrangeList = orangeCounter.ToStatisticsList(), OrangeList = orangeCounter.ToStatisticsList(),

View File

@@ -1,82 +1,181 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Context.Database; using Snap.Hutao.Context.Database;
using Snap.Hutao.Core;
using Snap.Hutao.Core.Database; using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Threading; using Snap.Hutao.Core.Threading;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Game.Locator; using Snap.Hutao.Service.Game.Locator;
using Snap.Hutao.Service.Game.Unlocker;
using System.Diagnostics;
using System.IO;
namespace Snap.Hutao.Service.Game; namespace Snap.Hutao.Service.Game;
/// <summary> /// <summary>
/// 游戏服务 /// 游戏服务
/// </summary> /// </summary>
[Injection(InjectAs.Transient, typeof(IGameService))] [Injection(InjectAs.Singleton, typeof(IGameService))]
internal class GameService : IGameService internal class GameService : IGameService
{ {
private readonly AppDbContext appDbContext; private const string GamePathKey = $"{nameof(GameService)}.Cache.{SettingEntry.GamePath}";
private readonly IServiceScopeFactory scopeFactory;
private readonly IMemoryCache memoryCache; private readonly IMemoryCache memoryCache;
private readonly IEnumerable<IGameLocator> gameLocators; private readonly SemaphoreSlim gameSemaphore = new(1);
/// <summary> /// <summary>
/// 构造一个新的游戏服务 /// 构造一个新的游戏服务
/// </summary> /// </summary>
/// <param name="appDbContext">数据库上下文</param> /// <param name="scopeFactory">范围工厂</param>
/// <param name="memoryCache">内存缓存</param> /// <param name="memoryCache">内存缓存</param>
/// <param name="gameLocators">游戏定位器集合</param> /// <param name="gameLocators">游戏定位器集合</param>
public GameService(AppDbContext appDbContext, IMemoryCache memoryCache, IEnumerable<IGameLocator> gameLocators) public GameService(IServiceScopeFactory scopeFactory, IMemoryCache memoryCache)
{ {
this.appDbContext = appDbContext; this.scopeFactory = scopeFactory;
this.memoryCache = memoryCache; this.memoryCache = memoryCache;
this.gameLocators = gameLocators;
} }
/// <inheritdoc/> /// <inheritdoc/>
public async ValueTask<ValueResult<bool, string>> GetGamePathAsync() public async ValueTask<ValueResult<bool, string>> GetGamePathAsync()
{ {
string key = $"{nameof(GameService)}.Cache.{SettingEntry.GamePath}"; if (memoryCache.TryGetValue(GamePathKey, out object? value))
if (memoryCache.TryGetValue(key, out object? value))
{ {
return new(true, Must.NotNull((value as string)!)); return new(true, Must.NotNull((value as string)!));
} }
else else
{ {
SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, null), out bool added); using (IServiceScope scope = scopeFactory.CreateScope())
// Cannot find in setting
if (added)
{ {
// Try locate by registry AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
IGameLocator locator = gameLocators.Single(l => l.Name == nameof(RegistryLauncherLocator));
ValueResult<bool, string> result = await locator.LocateGamePathAsync().ConfigureAwait(false);
if (!result.IsOk) SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, null), out bool added);
// Cannot find in setting
if (added)
{ {
// Try locate manually IEnumerable<IGameLocator> gameLocators = scope.ServiceProvider.GetRequiredService<IEnumerable<IGameLocator>>();
locator = gameLocators.Single(l => l.Name == nameof(ManualGameLocator));
result = await locator.LocateGamePathAsync().ConfigureAwait(false); // Try locate by registry
IGameLocator locator = gameLocators.Single(l => l.Name == nameof(RegistryLauncherLocator));
ValueResult<bool, string> result = await locator.LocateGamePathAsync().ConfigureAwait(false);
if (!result.IsOk)
{
// Try locate manually
locator = gameLocators.Single(l => l.Name == nameof(ManualGameLocator));
result = await locator.LocateGamePathAsync().ConfigureAwait(false);
}
if (result.IsOk)
{
// Save result.
entry.Value = result.Value;
appDbContext.Settings.UpdateAndSave(entry);
}
else
{
return new(false, null!);
}
} }
if (result.IsOk) // Set cache and return.
string path = memoryCache.Set(GamePathKey, Must.NotNull(entry.Value!));
return new(true, path);
}
}
}
/// <inheritdoc/>
public string GetGamePathSkipLocator()
{
if (memoryCache.TryGetValue(GamePathKey, out object? value))
{
return (value as string)!;
}
else
{
using (IServiceScope scope = scopeFactory.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, null), out bool added);
entry.Value ??= string.Empty;
appDbContext.Settings.UpdateAndSave(entry);
// Set cache and return.
return memoryCache.Set(GamePathKey, entry.Value);
}
}
}
/// <inheritdoc/>
public void OverwriteGamePath(string path)
{
// sync cache
memoryCache.Set(GamePathKey, path);
using (IServiceScope scope = scopeFactory.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, null), out _);
entry.Value = path;
appDbContext.Settings.UpdateAndSave(entry);
}
}
/// <inheritdoc/>
public async ValueTask LaunchAsync(LaunchConfiguration configuration)
{
(bool isOk, string gamePath) = await GetGamePathAsync().ConfigureAwait(false);
if (isOk)
{
if (gameSemaphore.CurrentCount == 0)
{
return;
}
string commandLine = new CommandLineBuilder()
.Append("-window-mode", configuration.WindowMode)
.Append("-screen-fullscreen", configuration.IsFullScreen ? 1 : 0)
.Append("-screen-width", configuration.ScreenWidth)
.Append("-screen-height", configuration.ScreenHeight)
.Append("-monitor", configuration.Monitor)
.Build();
Process game = new()
{
StartInfo = new()
{ {
// Save result. Arguments = commandLine,
entry.Value = result.Value; FileName = gamePath,
appDbContext.Settings.Update(entry); UseShellExecute = true,
await appDbContext.SaveChangesAsync().ConfigureAwait(false); Verb = "runas",
WorkingDirectory = Path.GetDirectoryName(gamePath),
},
};
using (await gameSemaphore.EnterAsync().ConfigureAwait(false))
{
if (configuration.UnlockFPS)
{
IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, configuration.TargetFPS);
await unlocker.UnlockAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(10000), TimeSpan.FromMilliseconds(2000)).ConfigureAwait(false);
} }
else else
{ {
return new(false, null!); if (game.Start())
{
await game.WaitForExitAsync().ConfigureAwait(false);
}
} }
} }
// Set cache and return.
string path = memoryCache.Set(key, Must.NotNull(entry.Value!));
return new(true, path);
} }
} }
} }

View File

@@ -15,4 +15,23 @@ internal interface IGameService
/// </summary> /// </summary>
/// <returns>结果</returns> /// <returns>结果</returns>
ValueTask<ValueResult<bool, string>> GetGamePathAsync(); ValueTask<ValueResult<bool, string>> GetGamePathAsync();
/// <summary>
/// 获取游戏路径,跳过异步定位器
/// </summary>
/// <returns>游戏路径,当路径无效时会设置并返回 <see cref="string.Empty"/></returns>
string GetGamePathSkipLocator();
/// <summary>
/// 异步启动
/// </summary>
/// <param name="configuration">启动配置</param>
/// <returns>任务</returns>
ValueTask LaunchAsync(LaunchConfiguration configuration);
/// <summary>
/// 重写游戏路径
/// </summary>
/// <param name="path">路径</param>
void OverwriteGamePath(string path);
} }

View File

@@ -0,0 +1,45 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Game;
/// <summary>
/// 启动游戏配置
/// </summary>
internal struct LaunchConfiguration
{
/// <summary>
/// 是否全屏,全屏时无边框设置将被覆盖
/// </summary>
public bool IsFullScreen { get; set; }
/// <summary>
/// Override fullscreen windowed mode. Accepted values are exclusive or borderless.
/// </summary>
public string WindowMode { get; private set; }
/// <summary>
/// 是否启用解锁帧率
/// </summary>
public bool UnlockFPS { get; private set; }
/// <summary>
/// 目标帧率
/// </summary>
public int TargetFPS { get; private set; }
/// <summary>
/// 窗口宽度
/// </summary>
public int ScreenWidth { get; private set; }
/// <summary>
/// 窗口高度
/// </summary>
public int ScreenHeight { get; private set; }
/// <summary>
/// 显示器编号
/// </summary>
public int Monitor { get; private set; }
}

View File

@@ -34,6 +34,7 @@
<ItemGroup> <ItemGroup>
<None Remove="Control\Panel\PanelSelector.xaml" /> <None Remove="Control\Panel\PanelSelector.xaml" />
<None Remove="LaunchGameWindow.xaml" />
<None Remove="NativeMethods.json" /> <None Remove="NativeMethods.json" />
<None Remove="NativeMethods.txt" /> <None Remove="NativeMethods.txt" />
<None Remove="Resource\Icon\UI_BagTabIcon_Avatar.png" /> <None Remove="Resource\Icon\UI_BagTabIcon_Avatar.png" />
@@ -41,6 +42,7 @@
<None Remove="Resource\Icon\UI_BtnIcon_ActivityEntry.png" /> <None Remove="Resource\Icon\UI_BtnIcon_ActivityEntry.png" />
<None Remove="Resource\Icon\UI_BtnIcon_Gacha.png" /> <None Remove="Resource\Icon\UI_BtnIcon_Gacha.png" />
<None Remove="Resource\Icon\UI_ChapterIcon_Hutao.png" /> <None Remove="Resource\Icon\UI_ChapterIcon_Hutao.png" />
<None Remove="Resource\Icon\UI_GuideIcon_PlayMethod.png" />
<None Remove="Resource\Icon\UI_Icon_Achievement.png" /> <None Remove="Resource\Icon\UI_Icon_Achievement.png" />
<None Remove="Resource\Icon\UI_Icon_BoostUp.png" /> <None Remove="Resource\Icon\UI_Icon_BoostUp.png" />
<None Remove="Resource\Icon\UI_Icon_Locked.png" /> <None Remove="Resource\Icon\UI_Icon_Locked.png" />
@@ -67,6 +69,7 @@
<None Remove="View\Page\AvatarPropertyPage.xaml" /> <None Remove="View\Page\AvatarPropertyPage.xaml" />
<None Remove="View\Page\GachaLogPage.xaml" /> <None Remove="View\Page\GachaLogPage.xaml" />
<None Remove="View\Page\HutaoDatabasePage.xaml" /> <None Remove="View\Page\HutaoDatabasePage.xaml" />
<None Remove="View\Page\LaunchGamePage.xaml" />
<None Remove="View\Page\SettingPage.xaml" /> <None Remove="View\Page\SettingPage.xaml" />
<None Remove="View\Page\WikiAvatarPage.xaml" /> <None Remove="View\Page\WikiAvatarPage.xaml" />
<None Remove="View\TitleView.xaml" /> <None Remove="View\TitleView.xaml" />
@@ -93,6 +96,7 @@
<Content Include="Resource\Icon\UI_BtnIcon_ActivityEntry.png" /> <Content Include="Resource\Icon\UI_BtnIcon_ActivityEntry.png" />
<Content Include="Resource\Icon\UI_BtnIcon_Gacha.png" /> <Content Include="Resource\Icon\UI_BtnIcon_Gacha.png" />
<Content Include="Resource\Icon\UI_ChapterIcon_Hutao.png" /> <Content Include="Resource\Icon\UI_ChapterIcon_Hutao.png" />
<Content Include="Resource\Icon\UI_GuideIcon_PlayMethod.png" />
<Content Include="Resource\Icon\UI_Icon_Achievement.png" /> <Content Include="Resource\Icon\UI_Icon_Achievement.png" />
<Content Include="Resource\Icon\UI_Icon_BoostUp.png" /> <Content Include="Resource\Icon\UI_Icon_BoostUp.png" />
<Content Include="Resource\Icon\UI_Icon_Locked.png" /> <Content Include="Resource\Icon\UI_Icon_Locked.png" />
@@ -144,6 +148,16 @@
<ItemGroup> <ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" /> <None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="LaunchGameWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Page\LaunchGamePage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="View\Page\HutaoDatabasePage.xaml"> <Page Update="View\Page\HutaoDatabasePage.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View File

@@ -28,6 +28,11 @@
<NavigationViewItemHeader Content="工具"/> <NavigationViewItemHeader Content="工具"/>
<NavigationViewItem
Content="启动游戏"
shvh:NavHelper.NavigateTo="shvp:LaunchGamePage"
Icon="{shcm:BitmapIcon Source=ms-appx:///Resource/Icon/UI_GuideIcon_PlayMethod.png}"/>
<NavigationViewItem <NavigationViewItem
Content="祈愿记录" Content="祈愿记录"
shvh:NavHelper.NavigateTo="shvp:GachaLogPage" shvh:NavHelper.NavigateTo="shvp:GachaLogPage"

View File

@@ -2,9 +2,6 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Core.Threading;
using Snap.Hutao.Service.Abstraction; using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Navigation; using Snap.Hutao.Service.Navigation;
using Snap.Hutao.View.Page; using Snap.Hutao.View.Page;
@@ -34,22 +31,4 @@ public sealed partial class MainView : UserControl
navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true); navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
} }
private async Task UpdateThemeAsync()
{
// It seems that UISettings.ColorValuesChanged
// event can raise up on a background thread.
await ThreadHelper.SwitchToMainThreadAsync();
App current = Ioc.Default.GetRequiredService<App>();
if (!ThemeHelper.Equals(current.RequestedTheme, RequestedTheme))
{
ILogger<MainView> logger = Ioc.Default.GetRequiredService<ILogger<MainView>>();
logger.LogInformation(EventIds.CommonLog, "Element Theme [{element}] | App Theme [{app}]", RequestedTheme, current.RequestedTheme);
// Update controls' theme which presents in the PopupRoot
RequestedTheme = ThemeHelper.ApplicationToElement(current.RequestedTheme);
}
}
} }

View File

@@ -4,24 +4,17 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core" xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shc="using:Snap.Hutao.Control" xmlns:shc="using:Snap.Hutao.Control"
xmlns:shcb="using:Snap.Hutao.Control.Behavior" xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
xmlns:shv="using:Snap.Hutao.ViewModel" xmlns:shv="using:Snap.Hutao.ViewModel"
mc:Ignorable="d" mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
d:DataContext="{d:DesignInstance shv:AchievementViewModel}"> d:DataContext="{d:DesignInstance shv:AchievementViewModel}">
<shc:ScopedPage.Resources>
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</shc:ScopedPage.Resources>
<mxi:Interaction.Behaviors> <mxi:Interaction.Behaviors>
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/> <shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
</mxi:Interaction.Behaviors> </mxi:Interaction.Behaviors>

View File

@@ -8,7 +8,6 @@
xmlns:cwua="using:CommunityToolkit.WinUI.UI.Animations" xmlns:cwua="using:CommunityToolkit.WinUI.UI.Animations"
xmlns:cwub="using:CommunityToolkit.WinUI.UI.Behaviors" xmlns:cwub="using:CommunityToolkit.WinUI.UI.Behaviors"
xmlns:cwucont="using:CommunityToolkit.WinUI.UI.Controls" xmlns:cwucont="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:cwuconv="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core" xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shc="using:Snap.Hutao.Control" xmlns:shc="using:Snap.Hutao.Control"
@@ -21,7 +20,6 @@
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/> <shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
</mxi:Interaction.Behaviors> </mxi:Interaction.Behaviors>
<shc:ScopedPage.Resources> <shc:ScopedPage.Resources>
<cwuconv:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<shc:BindingProxy x:Key="BindingProxy" DataContext="{Binding}"/> <shc:BindingProxy x:Key="BindingProxy" DataContext="{Binding}"/>
</shc:ScopedPage.Resources> </shc:ScopedPage.Resources>
<Grid> <Grid>

View File

@@ -36,8 +36,6 @@
<x:Double>0.5</x:Double> <x:Double>0.5</x:Double>
</cwuconv:BoolToObjectConverter.FalseValue> </cwuconv:BoolToObjectConverter.FalseValue>
</cwuconv:BoolToObjectConverter> </cwuconv:BoolToObjectConverter>
<shvconv:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
</Page.Resources> </Page.Resources>
<Grid> <Grid>

View File

@@ -140,10 +140,17 @@
<RowDefinition/> <RowDefinition/>
<RowDefinition/> <RowDefinition/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock <StackPanel Orientation="Horizontal" Margin="0,8,0,0">
Margin="0,8,0,0" <TextBlock
Text="{Binding Name}" Width="32"
Style="{StaticResource BaseTextBlockStyle}"/> Text="{Binding Version}"
Style="{StaticResource BaseTextBlockStyle}"/>
<TextBlock
Margin="0,0,0,0"
Text="{Binding Name}"
Style="{StaticResource BaseTextBlockStyle}"/>
</StackPanel>
<TextBlock <TextBlock
Margin="0,8,0,0" Margin="0,8,0,0"
Text="{Binding TotalCountFormatted}" Text="{Binding TotalCountFormatted}"

View File

@@ -10,6 +10,8 @@
xmlns:shcb="using:Snap.Hutao.Control.Behavior" xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shvc="using:Snap.Hutao.View.Control" xmlns:shvc="using:Snap.Hutao.View.Control"
xmlns:shv="using:Snap.Hutao.ViewModel" xmlns:shv="using:Snap.Hutao.ViewModel"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:sc="using:SettingsUI.Controls"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance shv:HutaoDatabaseViewModel}" d:DataContext="{d:DesignInstance shv:HutaoDatabaseViewModel}"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
@@ -26,11 +28,15 @@
<Grid> <Grid>
<Pivot> <Pivot>
<Pivot.RightHeader> <Pivot.RightHeader>
<CommandBar> <CommandBar DefaultLabelPosition="Right">
<AppBarButton> <AppBarButton Label="详情" Icon="{shcm:FontIcon Glyph=&#xE946;}">
<AppBarButton.Flyout> <AppBarButton.Flyout>
<Flyout> <Flyout Placement="BottomEdgeAlignedRight">
<sc:SettingsGroup Header="数据来自「胡桃数据库」" Margin="0,-32,0,0" MinWidth="240">
<sc:Setting Header="上传记录总数" Content="{Binding Overview.RecordTotal}"/>
<sc:Setting Header="深渊记录总数" Content="{Binding Overview.SpiralAbyssTotal}"/>
<sc:Setting Header="深渊记录满星数" Content="{Binding Overview.SpiralAbyssFullStar}"/>
</sc:SettingsGroup>
</Flyout> </Flyout>
</AppBarButton.Flyout> </AppBarButton.Flyout>
</AppBarButton> </AppBarButton>

View File

@@ -0,0 +1,132 @@
<control:ScopedPage
x:Class="Snap.Hutao.View.Page.LaunchGamePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sc="using:SettingsUI.Controls"
xmlns:control="using:Snap.Hutao.Control"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<Style TargetType="Button" BasedOn="{StaticResource SettingButtonStyle}">
<Setter Property="MinWidth" Value="120"/>
</Style>
<Style TargetType="HyperlinkButton" BasedOn="{StaticResource HyperlinkButtonStyle}">
<Setter Property="MinWidth" Value="120"/>
</Style>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Column="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="800"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="32,0,24,24">
<sc:SettingsGroup Header="常规" Margin="0,0,0,0">
<sc:Setting
Icon="&#xE8AB;"
Header="服务器"
Description="切换游戏服务器B服用户需要自备额外的 PCGameSDK.dll 文件">
<sc:Setting.ActionContent>
<ComboBox Width="120"/>
</sc:Setting.ActionContent>
</sc:Setting>
</sc:SettingsGroup>
<sc:SettingsGroup Header="外观">
<sc:Setting
Icon="&#xE740;"
Header="全屏"
Description="覆盖默认的全屏状态">
<sc:Setting.ActionContent>
<ToggleSwitch
Style="{StaticResource ToggleSwitchSettingStyle}"
Width="120"/>
</sc:Setting.ActionContent>
</sc:Setting>
<sc:Setting
Icon="&#xE737;"
Header="无边框"
Description="将窗口创建为弹出窗口,不带框架">
<sc:Setting.ActionContent>
<ToggleSwitch
Style="{StaticResource ToggleSwitchSettingStyle}"
Width="120"/>
</sc:Setting.ActionContent>
</sc:Setting>
<sc:Setting
Margin="0,6,0,0"
Icon="&#xE76F;"
Header="宽度"
Description="覆盖默认屏幕宽度">
<sc:Setting.ActionContent>
<NumberBox
Width="120"/>
</sc:Setting.ActionContent>
</sc:Setting>
<sc:Setting
Icon="&#xE784;"
Header="高度"
Description="覆盖默认屏幕高度">
<sc:Setting.ActionContent>
<NumberBox
Width="120"/>
</sc:Setting.ActionContent>
</sc:Setting>
</sc:SettingsGroup>
<sc:SettingsGroup Header="Dangerous feature">
<sc:Setting
Icon="&#xE785;"
Header="Unlock frame rate limit"
Description="Requires administrator privilege.&#10;Otherwise the option does not take effect.">
<sc:Setting.ActionContent>
<ToggleSwitch
OnContent="Enable"
OffContent="Disable"
Style="{StaticResource ToggleSwitchSettingStyle}"
Width="120"/>
</sc:Setting.ActionContent>
</sc:Setting>
<sc:Setting
Header="Set frame rate"
Description="60">
<sc:Setting.ActionContent>
<Slider
Minimum="60"
Maximum="360"
Width="400"/>
</sc:Setting.ActionContent>
</sc:Setting>
</sc:SettingsGroup>
</StackPanel>
</Grid>
</ScrollViewer>
<Grid
Grid.Row="1"
VerticalAlignment="Bottom"
Background="{StaticResource CardBackgroundFillColorSecondary}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<ComboBox
Header="原神账号"
Grid.Column="0"
Margin="32,24,0,24"
Width="160"/>
<Button
Grid.Column="1"
Margin="24"
Width="138"
Content="启动游戏"/>
</Grid>
</Grid>
</control:ScopedPage>

View File

@@ -0,0 +1,22 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Control;
using Snap.Hutao.ViewModel;
namespace Snap.Hutao.View.Page;
/// <summary>
/// 启动游戏页面
/// </summary>
public sealed partial class LaunchGamePage : ScopedPage
{
/// <summary>
/// 构造一个新的启动游戏页面
/// </summary>
public LaunchGamePage()
{
InitializeWith<LaunchGameViewModel>();
InitializeComponent();
}
}

View File

@@ -95,6 +95,17 @@
</sc:SettingsGroup> </sc:SettingsGroup>
<sc:SettingsGroup Header="游戏">
<sc:Setting
Icon="&#xE7FC;"
Header="游戏路径"
Description="{Binding GamePath}">
<sc:Setting.ActionContent>
<Button Content="设置路径" Command="{Binding SetGamePathCommand}"/>
</sc:Setting.ActionContent>
</sc:Setting>
</sc:SettingsGroup>
<sc:SettingsGroup Header="测试功能"> <sc:SettingsGroup Header="测试功能">
<sc:Setting <sc:Setting
Icon="&#xEC25;" Icon="&#xEC25;"

View File

@@ -8,11 +8,11 @@
xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shcb="using:Snap.Hutao.Control.Behavior" xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shct="using:Snap.Hutao.Control.Text"
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
xmlns:shvc="using:Snap.Hutao.View.Control"
xmlns:shcp="using:Snap.Hutao.Control.Panel" xmlns:shcp="using:Snap.Hutao.Control.Panel"
xmlns:shct="using:Snap.Hutao.Control.Text"
xmlns:shvc="using:Snap.Hutao.View.Control"
xmlns:shv="using:Snap.Hutao.ViewModel" xmlns:shv="using:Snap.Hutao.ViewModel"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=shv:WikiAvatarViewModel}" d:DataContext="{d:DesignInstance Type=shv:WikiAvatarViewModel}"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
@@ -20,16 +20,6 @@
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/> <shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
</mxi:Interaction.Behaviors> </mxi:Interaction.Behaviors>
<Page.Resources> <Page.Resources>
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
<shmmc:GachaAvatarImgConverter x:Key="GachaAvatarImgConverter"/>
<shmmc:DescParamDescriptor x:Key="DescParamDescriptor"/>
<shmmc:PropertyInfoDescriptor x:Key="PropertyDescriptor"/>
<DataTemplate x:Key="SkillDataTemplate"> <DataTemplate x:Key="SkillDataTemplate">
<Grid Margin="0,12,0,0"> <Grid Margin="0,12,0,0">
<StackPanel Grid.Column="0"> <StackPanel Grid.Column="0">
@@ -92,10 +82,7 @@
<shcp:PanelSelector Margin="6,8,0,0" x:Name="ItemsPanelSelector"/> <shcp:PanelSelector Margin="6,8,0,0" x:Name="ItemsPanelSelector"/>
</CommandBar.Content> </CommandBar.Content>
<AppBarButton Label="筛选"> <AppBarButton Label="筛选" Icon="{shcm:FontIcon Glyph=&#xE71C;}">
<AppBarButton.Icon>
<FontIcon Glyph="&#xE71C;"/>
</AppBarButton.Icon>
<AppBarButton.Flyout> <AppBarButton.Flyout>
<Flyout Placement="RightEdgeAlignedTop" LightDismissOverlayMode="On"> <Flyout Placement="RightEdgeAlignedTop" LightDismissOverlayMode="On">
<cwuc:UniformGrid Columns="3" RowSpacing="16"> <cwuc:UniformGrid Columns="3" RowSpacing="16">
@@ -465,6 +452,176 @@
</GridView> </GridView>
<TextBlock Text="其他" Style="{StaticResource BaseTextBlockStyle}" Margin="16,32,0,0"/> <TextBlock Text="其他" Style="{StaticResource BaseTextBlockStyle}" Margin="16,32,0,0"/>
<Border
Margin="16,16,0,0"
Background="{StaticResource CardBackgroundFillColorDefault}"
BorderThickness="1"
BorderBrush="{StaticResource CardStrokeColorDefault}"
CornerRadius="{StaticResource CompatCornerRadius}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<mxi:Interaction.Behaviors>
<shcb:AutoHeightBehavior TargetWidth="2048" TargetHeight="1024"/>
</mxi:Interaction.Behaviors>
<Border
Background="{StaticResource CardBackgroundFillColorDefault}"
BorderThickness="1"
BorderBrush="{StaticResource CardStrokeColorDefault}"
CornerRadius="{StaticResource CompatCornerRadius}"
Margin="16"
Grid.Column="0">
<shci:CachedImage
HorizontalAlignment="Stretch"
Source="{Binding Selected.Icon,Converter={StaticResource GachaAvatarIconConverter}}"/>
</Border>
<shci:CachedImage
Grid.ColumnSpan="2"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"
Source="{Binding Selected.Icon,Converter={StaticResource GachaAvatarImgConverter}}"/>
</Grid>
</Border>
<!--料理-->
<Expander
Margin="16,16,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Header="料理">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock
Margin="16,16,0,0"
Text="特殊料理"
Grid.Column="0"
Style="{StaticResource BaseTextBlockStyle}"/>
<Border
Grid.Row="1"
Grid.Column="0"
Margin="16,16,0,16"
CornerRadius="{StaticResource CompatCornerRadius}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="{StaticResource CardBackgroundFillColorDefault}"
BorderThickness="1"
BorderBrush="{StaticResource CardStrokeColorDefault}">
<StackPanel>
<shvc:ItemIcon
Icon="{Binding Selected.FetterInfo.CookBonus.Icon,Converter={StaticResource ItemIconConverter}}"
Quality="{Binding Selected.FetterInfo.CookBonus.RankLevel}"/>
<TextBlock
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap"
MaxWidth="80"
Margin="0,0,0,2"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Selected.FetterInfo.CookBonus.Name}"/>
</StackPanel>
</Border>
<TextBlock
Margin="16,16,0,0"
Text="原料理"
Grid.Column="1"
Style="{StaticResource BaseTextBlockStyle}"/>
<Border
Grid.Row="1"
Grid.Column="1"
Margin="16,16,0,16"
CornerRadius="{StaticResource CompatCornerRadius}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="{StaticResource CardBackgroundFillColorDefault}"
BorderThickness="1"
BorderBrush="{StaticResource CardStrokeColorDefault}">
<StackPanel>
<shvc:ItemIcon
Icon="{Binding Selected.FetterInfo.CookBonus.OriginIcon,Converter={StaticResource ItemIconConverter}}"
Quality="{Binding Selected.FetterInfo.CookBonus.RankLevel}"/>
<TextBlock
TextWrapping="NoWrap"
TextTrimming="CharacterEllipsis"
MaxWidth="80"
Margin="0,0,0,2"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Selected.FetterInfo.CookBonus.OriginName}"/>
</StackPanel>
</Border>
<Rectangle
Grid.Column="2"
Grid.RowSpan="2"
Width="1"
Fill="{StaticResource SystemChromeHighColor}"
Margin="16,16,0,16"/>
<TextBlock
Margin="16,16,0,0"
Text="材料"
Grid.Column="3"
Style="{StaticResource BaseTextBlockStyle}"/>
<ItemsControl
Grid.Row="1"
Grid.Column="3"
ItemsSource="{Binding Selected.FetterInfo.CookBonus.InputList}"
ItemsPanel="{StaticResource HorizontalStackPanelTemplate}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
ToolTipService.ToolTip="{Binding Name}"
Grid.Row="1"
Grid.Column="1"
Margin="16,16,0,16"
CornerRadius="{StaticResource CompatCornerRadius}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="{StaticResource CardBackgroundFillColorDefault}"
BorderThickness="1"
BorderBrush="{StaticResource CardStrokeColorDefault}">
<StackPanel>
<shvc:ItemIcon
Icon="{Binding Icon,Converter={StaticResource ItemIconConverter}}"
Quality="{Binding RankLevel}"/>
<TextBlock
TextTrimming="CharacterEllipsis"
MaxWidth="80"
Margin="0,0,0,2"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Count}"/>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock
Grid.Row="2"
Grid.ColumnSpan="4"
Margin="16,0,16,16"
TextWrapping="Wrap"
Text="{Binding Selected.FetterInfo.CookBonus.Description}"/>
<TextBlock
Grid.Row="3"
Grid.ColumnSpan="4"
Margin="16,0,16,16"
TextWrapping="Wrap"
Text="{Binding Selected.FetterInfo.CookBonus.EffectDescription}"/>
</Grid>
</Expander>
<!--衣装--> <!--衣装-->
<Expander <Expander
Margin="16,16,0,0" Margin="16,16,0,0"
@@ -516,10 +673,10 @@
</Expander> </Expander>
<!--故事--> <!--故事-->
<Expander <Expander
Margin="16,16,0,0" Margin="16,16,0,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
Header="故事"> Header="故事">
<ItemsControl <ItemsControl
ItemsSource="{Binding Selected.FetterInfo.FetterStories}"> ItemsSource="{Binding Selected.FetterInfo.FetterStories}">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
@@ -538,17 +695,6 @@
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</Expander> </Expander>
<Border
Margin="16,16,0,0"
Background="{StaticResource CardBackgroundFillColorDefault}"
BorderThickness="1"
BorderBrush="{StaticResource CardStrokeColorDefault}"
CornerRadius="{StaticResource CompatCornerRadius}">
<shci:CachedImage
HorizontalAlignment="Stretch"
Source="{Binding Selected.Icon,Converter={StaticResource GachaAvatarImgConverter}}"/>
</Border>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>

View File

@@ -8,8 +8,8 @@
xmlns:mxim="using:Microsoft.Xaml.Interactions.Media" xmlns:mxim="using:Microsoft.Xaml.Interactions.Media"
xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shc="using:Snap.Hutao.Control" xmlns:shc="using:Snap.Hutao.Control"
xmlns:shvm="using:Snap.Hutao.ViewModel"
xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shvm="using:Snap.Hutao.ViewModel"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance shvm:UserViewModel}"> d:DataContext="{d:DesignInstance shvm:UserViewModel}">
<mxi:Interaction.Behaviors> <mxi:Interaction.Behaviors>
@@ -120,11 +120,19 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="0,0" Margin="0,0"
Height="32"/> Height="32"/>
<TextBlock <StackPanel
Margin="12,0,0,0"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" Margin="12,0,0,0"
Text="{Binding UserInfo.Nickname}"/> VerticalAlignment="Center">
<TextBlock Text="{Binding UserInfo.Nickname}"/>
<TextBlock
Text="Stoken"
Visibility="{Binding HasSToken,Converter={StaticResource BoolToVisibilityConverter}}"
Style="{StaticResource CaptionTextBlockStyle}"
Foreground="Green"
Opacity="0.6"/>
</StackPanel>
<StackPanel <StackPanel
x:Name="ButtonPanel" x:Name="ButtonPanel"
Orientation="Horizontal" Orientation="Horizontal"

View File

@@ -6,6 +6,7 @@ using Snap.Hutao.Control;
using Snap.Hutao.Factory.Abstraction; using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Binding.Hutao; using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Service.Hutao; using Snap.Hutao.Service.Hutao;
using Snap.Hutao.Web.Hutao.Model;
namespace Snap.Hutao.ViewModel; namespace Snap.Hutao.ViewModel;
@@ -21,6 +22,7 @@ internal class HutaoDatabaseViewModel : ObservableObject, ISupportCancellation
private List<ComplexAvatarRank>? avatarAppearanceRanks; private List<ComplexAvatarRank>? avatarAppearanceRanks;
private List<ComplexAvatarConstellationInfo>? avatarConstellationInfos; private List<ComplexAvatarConstellationInfo>? avatarConstellationInfos;
private List<ComplexTeamRank>? teamAppearances; private List<ComplexTeamRank>? teamAppearances;
private Overview? overview;
/// <summary> /// <summary>
/// 构造一个新的胡桃数据库视图模型 /// 构造一个新的胡桃数据库视图模型
@@ -58,6 +60,11 @@ internal class HutaoDatabaseViewModel : ObservableObject, ISupportCancellation
/// </summary> /// </summary>
public List<ComplexTeamRank>? TeamAppearances { get => teamAppearances; set => SetProperty(ref teamAppearances, value); } public List<ComplexTeamRank>? TeamAppearances { get => teamAppearances; set => SetProperty(ref teamAppearances, value); }
/// <summary>
/// 总览数据
/// </summary>
public Overview? Overview { get => overview; set => SetProperty(ref overview, value); }
/// <summary> /// <summary>
/// 打开界面命令 /// 打开界面命令
/// </summary> /// </summary>
@@ -71,6 +78,7 @@ internal class HutaoDatabaseViewModel : ObservableObject, ISupportCancellation
AvatarUsageRanks = hutaoCache.AvatarUsageRanks; AvatarUsageRanks = hutaoCache.AvatarUsageRanks;
AvatarConstellationInfos = hutaoCache.AvatarConstellationInfos; AvatarConstellationInfos = hutaoCache.AvatarConstellationInfos;
TeamAppearances = hutaoCache.TeamAppearances; TeamAppearances = hutaoCache.TeamAppearances;
Overview = hutaoCache.Overview;
} }
} }
} }

View File

@@ -0,0 +1,17 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Control;
namespace Snap.Hutao.ViewModel;
/// <summary>
/// 启动游戏视图模型
/// </summary>
[Injection(InjectAs.Scoped)]
internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
{
/// <inheritdoc/>
public CancellationToken CancellationToken { get; set; }
}

View File

@@ -4,7 +4,11 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Context.Database; using Snap.Hutao.Context.Database;
using Snap.Hutao.Core.Database; using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Threading;
using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Game;
using Snap.Hutao.Service.Game.Locator;
namespace Snap.Hutao.ViewModel; namespace Snap.Hutao.ViewModel;
@@ -15,23 +19,33 @@ namespace Snap.Hutao.ViewModel;
internal class SettingViewModel : ObservableObject internal class SettingViewModel : ObservableObject
{ {
private readonly AppDbContext appDbContext; private readonly AppDbContext appDbContext;
private readonly IGameService gameService;
private readonly SettingEntry isEmptyHistoryWishVisibleEntry; private readonly SettingEntry isEmptyHistoryWishVisibleEntry;
private bool isEmptyHistoryWishVisible; private bool isEmptyHistoryWishVisible;
private string gamePath;
/// <summary> /// <summary>
/// 构造一个新的测试视图模型 /// 构造一个新的测试视图模型
/// </summary> /// </summary>
/// <param name="appDbContext">数据库上下文</param> /// <param name="appDbContext">数据库上下文</param>
/// <param name="gameService">游戏服务</param>
/// <param name="asyncRelayCommandFactory">异步命令工厂</param>
/// <param name="experimental">实验性功能</param> /// <param name="experimental">实验性功能</param>
public SettingViewModel(AppDbContext appDbContext, ExperimentalFeaturesViewModel experimental) public SettingViewModel(AppDbContext appDbContext, IGameService gameService, IAsyncRelayCommandFactory asyncRelayCommandFactory, ExperimentalFeaturesViewModel experimental)
{ {
this.appDbContext = appDbContext; this.appDbContext = appDbContext;
this.gameService = gameService;
Experimental = experimental; Experimental = experimental;
isEmptyHistoryWishVisibleEntry = appDbContext.Settings isEmptyHistoryWishVisibleEntry = appDbContext.Settings
.SingleOrAdd(e => e.Key == SettingEntry.IsEmptyHistoryWishVisible, () => new(SettingEntry.IsEmptyHistoryWishVisible, true.ToString()), out _); .SingleOrAdd(e => e.Key == SettingEntry.IsEmptyHistoryWishVisible, () => new(SettingEntry.IsEmptyHistoryWishVisible, true.ToString()), out _);
IsEmptyHistoryWishVisible = bool.Parse(isEmptyHistoryWishVisibleEntry.Value!); IsEmptyHistoryWishVisible = bool.Parse(isEmptyHistoryWishVisibleEntry.Value!);
GamePath = gameService.GetGamePathSkipLocator();
SetGamePathCommand = asyncRelayCommandFactory.Create(SetGamePathAsync);
} }
/// <summary> /// <summary>
@@ -56,8 +70,37 @@ internal class SettingViewModel : ObservableObject
} }
} }
/// <summary>
/// 游戏路径
/// </summary>
public string GamePath
{
get => gamePath;
[MemberNotNull(nameof(gamePath))]
set => SetProperty(ref gamePath, value);
}
/// <summary> /// <summary>
/// 实验性功能 /// 实验性功能
/// </summary> /// </summary>
public ExperimentalFeaturesViewModel Experimental { get; } public ExperimentalFeaturesViewModel Experimental { get; }
/// <summary>
/// 设置游戏路径命令
/// </summary>
public ICommand SetGamePathCommand { get; }
private async Task SetGamePathAsync()
{
IGameLocator locator = Ioc.Default.GetRequiredService<IEnumerable<IGameLocator>>()
.Single(l => l.Name == nameof(ManualGameLocator));
(bool isOk, string path) = await locator.LocateGamePathAsync().ConfigureAwait(false);
if (isOk)
{
gameService.OverwriteGamePath(path);
await ThreadHelper.SwitchToMainThreadAsync();
GamePath = path;
}
}
} }