mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Merge pull request #1473 from DGP-Studio/develop
This commit is contained in:
@@ -35,7 +35,8 @@ Install with Snap Hutao MSIX package, can be installed with Windows built-in App
|
||||
|
||||
* [向我们提交 PR / Make Pull Requests](https://github.com/DGP-Studio/Snap.Hutao/pulls)
|
||||
* [在 Crowdin 上进行本地化 / Translate Project on Crowdin](https://translate.hut.ao/)
|
||||
* [为我们更新文档 / Enhance our Document ](https://github.com/DGP-Studio/Snap.Hutao.Docs)
|
||||
* [为我们更新文档 / Enhance our Document](https://github.com/DGP-Studio/Snap.Hutao.Docs)
|
||||
* [帮助我们测试程序 / Test Binary Package](https://hut.ao/development/contribute.html)
|
||||
|
||||
## 特别感谢 / Special Thanks
|
||||
|
||||
|
||||
24
build.cake
24
build.cake
@@ -69,6 +69,15 @@ else if (AppVeyor.IsRunningOnAppVeyor)
|
||||
})[..^2];
|
||||
Information($"Version: {version}");
|
||||
}
|
||||
else // Local
|
||||
{
|
||||
repoDir = System.Environment.CurrentDirectory;
|
||||
outputPath = System.IO.Path.Combine(repoDir, "src", "output");
|
||||
|
||||
version = System.DateTime.Now.ToString("yyyy.M.d.") + ((int)((System.DateTime.Now - System.DateTime.Today).TotalSeconds / 86400 * 65535)).ToString();
|
||||
|
||||
Information($"Version: {version}");
|
||||
}
|
||||
|
||||
Task("Build")
|
||||
.IsDependentOn("Build binary package")
|
||||
@@ -112,6 +121,17 @@ Task("Generate AppxManifest")
|
||||
Information("Using Release configuration");
|
||||
content = System.Text.RegularExpressions.Regex.Replace(content, " Publisher=\"([^\"]*)\"", " Publisher=\"CN=SignPath Foundation, O=SignPath Foundation, L=Lewes, S=Delaware, C=US\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
Information("Using Local configuration.");
|
||||
content = content
|
||||
.Replace("Snap Hutao", "Snap Hutao Local")
|
||||
.Replace("胡桃", "胡桃 Local")
|
||||
.Replace("DGP Studio", "DGP Studio CI");
|
||||
content = System.Text.RegularExpressions.Regex.Replace(content, " Name=\"([^\"]*)\"", " Name=\"E8B6E2B3-D2A0-4435-A81D-2A16AAF405C7\"");
|
||||
content = System.Text.RegularExpressions.Regex.Replace(content, " Publisher=\"([^\"]*)\"", " Publisher=\"E=admin@dgp-studio.cn, CN=DGP Studio CI, OU=CI, O=DGP-Studio, L=San Jose, S=CA, C=US\"");
|
||||
content = System.Text.RegularExpressions.Regex.Replace(content, " Version=\"([0-9\\.]+)\"", $" Version=\"{version}\"");
|
||||
}
|
||||
|
||||
System.IO.File.WriteAllText(manifest, content);
|
||||
|
||||
@@ -173,6 +193,10 @@ Task("Build MSIX")
|
||||
{
|
||||
arguments = "pack /d " + binPath + " /p " + System.IO.Path.Combine(outputPath, $"Snap.Hutao-{version}.msix");
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments = "pack /d " + binPath + " /p " + System.IO.Path.Combine(outputPath, $"Snap.Hutao.Local-{version}.msix");
|
||||
}
|
||||
var p = StartProcess(
|
||||
"makeappx.exe",
|
||||
new ProcessSettings
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.2" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources/>
|
||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.TokenizingTextBox/TokenizingTextBox.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Loading.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Image/CachedImage.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Card.xaml"/>
|
||||
@@ -22,6 +23,7 @@
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/PageOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/PivotOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/ScrollViewer.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/SegmentedOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/SettingsStyle.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Thickness.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/TransitionCollection.xaml"/>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
|
||||
namespace Snap.Hutao.Control.AutoSuggestBox;
|
||||
|
||||
[DependencyProperty("FilterCommand", typeof(ICommand))]
|
||||
[DependencyProperty("FilterCommandParameter", typeof(object))]
|
||||
[DependencyProperty("AvailableTokens", typeof(IReadOnlyDictionary<string, SearchToken>))]
|
||||
internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
|
||||
{
|
||||
public AutoSuggestTokenBox()
|
||||
{
|
||||
DefaultStyleKey = typeof(TokenizingTextBox);
|
||||
TextChanged += OnFilterSuggestionRequested;
|
||||
QuerySubmitted += OnQuerySubmitted;
|
||||
TokenItemAdding += OnTokenItemAdding;
|
||||
TokenItemAdded += OnTokenItemModified;
|
||||
TokenItemRemoved += OnTokenItemModified;
|
||||
}
|
||||
|
||||
private void OnFilterSuggestionRequested(Microsoft.UI.Xaml.Controls.AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
||||
{
|
||||
sender.ItemsSource = AvailableTokens.Values.Where(q => q.Value.Contains(Text, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// TODO: CornerRadius
|
||||
// Popup? popup = this.FindDescendant("SuggestionsPopup") as Popup;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnQuerySubmitted(Microsoft.UI.Xaml.Controls.AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
|
||||
{
|
||||
if (args.ChosenSuggestion is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CommandExtension.TryExecute(FilterCommand, FilterCommandParameter);
|
||||
}
|
||||
|
||||
private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(args.TokenText))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
args.Item = AvailableTokens.GetValueOrDefault(args.TokenText) ?? new SearchToken(SearchTokenKind.None, args.TokenText);
|
||||
}
|
||||
|
||||
private void OnTokenItemModified(TokenizingTextBox sender, object args)
|
||||
{
|
||||
CommandExtension.TryExecute(FilterCommand, FilterCommandParameter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.AutoSuggestBox;
|
||||
|
||||
internal sealed class SearchToken
|
||||
{
|
||||
public SearchToken(SearchTokenKind kind, string value, Uri? iconUri = null, Uri? sideIconUri = null, Color? quality = null)
|
||||
{
|
||||
Value = value;
|
||||
Kind = kind;
|
||||
IconUri = iconUri;
|
||||
SideIconUri = sideIconUri;
|
||||
Quality = quality;
|
||||
}
|
||||
|
||||
public SearchTokenKind Kind { get; }
|
||||
|
||||
public string Value { get; set; } = default!;
|
||||
|
||||
public Uri? IconUri { get; }
|
||||
|
||||
public Uri? SideIconUri { get; }
|
||||
|
||||
public Color? Quality { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.AutoSuggestBox;
|
||||
|
||||
internal enum SearchTokenKind
|
||||
{
|
||||
None,
|
||||
AssociationType,
|
||||
Avatar,
|
||||
BodyType,
|
||||
ElementName,
|
||||
FightProperty,
|
||||
ItemQuality,
|
||||
Weapon,
|
||||
WeaponType,
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
|
||||
namespace Snap.Hutao.Control.Behavior;
|
||||
|
||||
@@ -45,10 +46,6 @@ internal sealed partial class InvokeCommandOnLoadedBehavior : BehaviorBase<UIEle
|
||||
return;
|
||||
}
|
||||
|
||||
if (Command is not null && Command.CanExecute(CommandParameter))
|
||||
{
|
||||
Command.Execute(CommandParameter);
|
||||
executed = true;
|
||||
}
|
||||
executed = Command.TryExecute(CommandParameter);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
|
||||
namespace Snap.Hutao.Control.Behavior;
|
||||
|
||||
@@ -49,10 +50,7 @@ internal sealed partial class PeriodicInvokeCommandOrOnActualThemeChangedBehavio
|
||||
return;
|
||||
}
|
||||
|
||||
if (Command is not null && Command.CanExecute(CommandParameter))
|
||||
{
|
||||
Command.Execute(CommandParameter);
|
||||
}
|
||||
Command.TryExecute(CommandParameter);
|
||||
}
|
||||
|
||||
private async ValueTask RunCoreAsync()
|
||||
|
||||
@@ -5,7 +5,6 @@ using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Shapes;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Control.Brush;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using CommunityToolkit.WinUI.Helpers;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
@@ -208,13 +207,11 @@ internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, IN
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Add(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Clear();
|
||||
}
|
||||
|
||||
@@ -230,7 +227,6 @@ internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, IN
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Remove(item);
|
||||
return true;
|
||||
}
|
||||
@@ -243,7 +239,6 @@ internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, IN
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Insert(index, item);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Extension;
|
||||
|
||||
internal static class CommandExtension
|
||||
{
|
||||
public static bool TryExecute(this ICommand? command, object? parameter = null)
|
||||
{
|
||||
if (command is not null && command.CanExecute(parameter))
|
||||
{
|
||||
command.Execute(parameter);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
24
src/Snap.Hutao/Snap.Hutao/Control/Helper/UIElementHelper.cs
Normal file
24
src/Snap.Hutao/Snap.Hutao/Control/Helper/UIElementHelper.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.Control.Helper;
|
||||
|
||||
[SuppressMessage("", "SH001")]
|
||||
[DependencyProperty("VisibilityObject", typeof(object), null, nameof(OnVisibilityObjectChanged), IsAttached = true, AttachedType = typeof(UIElement))]
|
||||
[DependencyProperty("OpacityObject", typeof(object), null, nameof(OnOpacityObjectChanged), IsAttached = true, AttachedType = typeof(UIElement))]
|
||||
public sealed partial class UIElementHelper
|
||||
{
|
||||
private static void OnVisibilityObjectChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
UIElement element = (UIElement)dp;
|
||||
element.Visibility = e.NewValue is null ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
private static void OnOpacityObjectChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
UIElement element = (UIElement)dp;
|
||||
element.Opacity = e.NewValue is null ? 0D : 1D;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control;
|
||||
|
||||
internal interface IXamlElementAccessor;
|
||||
@@ -168,6 +168,7 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
|
||||
if (surface.DecodedPhysicalSize.Size() <= 0D)
|
||||
{
|
||||
await Task.WhenAny(surfaceLoadTaskCompletionSource.Task, Task.Delay(5000, token)).ConfigureAwait(true);
|
||||
await Task.Delay(50, token).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
LoadImageSurfaceCompleted(surface);
|
||||
|
||||
89
src/Snap.Hutao/Snap.Hutao/Control/Panel/EqualPanel.cs
Normal file
89
src/Snap.Hutao/Snap.Hutao/Control/Panel/EqualPanel.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Data;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Control.Panel;
|
||||
|
||||
[DependencyProperty("Spacing", typeof(double), default(double), nameof(OnSpacingChanged))]
|
||||
internal partial class EqualPanel : Microsoft.UI.Xaml.Controls.Panel
|
||||
{
|
||||
private double maxItemWidth;
|
||||
private double maxItemHeight;
|
||||
private int visibleItemsCount;
|
||||
|
||||
public EqualPanel()
|
||||
{
|
||||
RegisterPropertyChangedCallback(HorizontalAlignmentProperty, OnHorizontalAlignmentChanged);
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
maxItemWidth = 0;
|
||||
maxItemHeight = 0;
|
||||
|
||||
List<UIElement> elements = [.. Children.Where(element => element.Visibility == Visibility.Visible)];
|
||||
visibleItemsCount = elements.Count;
|
||||
|
||||
foreach (ref readonly UIElement child in CollectionsMarshal.AsSpan(elements))
|
||||
{
|
||||
child.Measure(availableSize);
|
||||
maxItemWidth = Math.Max(maxItemWidth, child.DesiredSize.Width);
|
||||
maxItemHeight = Math.Max(maxItemHeight, child.DesiredSize.Height);
|
||||
}
|
||||
|
||||
if (visibleItemsCount > 0)
|
||||
{
|
||||
// Return equal widths based on the widest item
|
||||
// In very specific edge cases the AvailableWidth might be infinite resulting in a crash.
|
||||
if (HorizontalAlignment is not HorizontalAlignment.Stretch || double.IsInfinity(availableSize.Width))
|
||||
{
|
||||
return new Size((maxItemWidth * visibleItemsCount) + (Spacing * (visibleItemsCount - 1)), maxItemHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Equal columns based on the available width, adjust for spacing
|
||||
double totalWidth = availableSize.Width - (Spacing * (visibleItemsCount - 1));
|
||||
maxItemWidth = totalWidth / visibleItemsCount;
|
||||
return new Size(availableSize.Width, maxItemHeight);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Size(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
double x = 0;
|
||||
|
||||
// Check if there's more (little) width available - if so, set max item width to the maximum possible as we have an almost perfect height.
|
||||
if (finalSize.Width > (visibleItemsCount * maxItemWidth) + (Spacing * (visibleItemsCount - 1)))
|
||||
{
|
||||
maxItemWidth = (finalSize.Width - (Spacing * (visibleItemsCount - 1))) / visibleItemsCount;
|
||||
}
|
||||
|
||||
IEnumerable<UIElement> elements = Children.Where(static e => e.Visibility == Visibility.Visible);
|
||||
foreach (UIElement child in elements)
|
||||
{
|
||||
child.Arrange(new Rect(x, 0, maxItemWidth, maxItemHeight));
|
||||
x += maxItemWidth + Spacing;
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
(d as EqualPanel)?.InvalidateMeasure();
|
||||
}
|
||||
|
||||
private void OnHorizontalAlignmentChanged(DependencyObject sender, DependencyProperty dp)
|
||||
{
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Control.Panel;
|
||||
|
||||
[DependencyProperty("MinItemWidth", typeof(double))]
|
||||
[DependencyProperty("Spacing", typeof(double))]
|
||||
internal partial class HorizontalEqualPanel : Microsoft.UI.Xaml.Controls.Panel
|
||||
{
|
||||
public HorizontalEqualPanel()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
foreach (UIElement child in Children)
|
||||
{
|
||||
// ScrollViewer will always return an Infinity Size, we should use ActualWidth for this situation.
|
||||
double availableWidth = double.IsInfinity(availableSize.Width) ? ActualWidth : availableSize.Width;
|
||||
double childAvailableWidth = (availableWidth + Spacing) / Children.Count;
|
||||
double childMaxAvailableWidth = Math.Max(MinItemWidth, childAvailableWidth);
|
||||
child.Measure(new(childMaxAvailableWidth - Spacing, ActualHeight));
|
||||
}
|
||||
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
int itemCount = Children.Count;
|
||||
double availableWidthPerItem = (finalSize.Width - (Spacing * (itemCount - 1))) / itemCount;
|
||||
double actualItemWidth = Math.Max(MinItemWidth, availableWidthPerItem);
|
||||
|
||||
double offset = 0;
|
||||
foreach (UIElement child in Children)
|
||||
{
|
||||
child.Arrange(new Rect(offset, 0, actualItemWidth, finalSize.Height));
|
||||
offset += actualItemWidth + Spacing;
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MinWidth = (MinItemWidth * Children.Count) + (Spacing * (Children.Count - 1));
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
Style="{StaticResource DefaultSegmentedStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<cwc:SegmentedItem
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Snap.Hutao.Control.Panel;
|
||||
|
||||
@@ -19,11 +20,11 @@ internal sealed partial class PanelSelector : Segmented
|
||||
public const string List = nameof(List);
|
||||
public const string Grid = nameof(Grid);
|
||||
|
||||
private static readonly Dictionary<int, string> IndexTypeMap = new()
|
||||
{
|
||||
[0] = List,
|
||||
[1] = Grid,
|
||||
};
|
||||
private static readonly FrozenDictionary<int, string> IndexTypeMap = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(0, List),
|
||||
KeyValuePair.Create(1, Grid),
|
||||
]);
|
||||
|
||||
private readonly RoutedEventHandler loadedEventHandler;
|
||||
private readonly RoutedEventHandler unloadedEventHandler;
|
||||
|
||||
@@ -4,21 +4,19 @@
|
||||
xmlns:cwm="using:CommunityToolkit.WinUI.Media">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<cwm:AttachedCardShadow
|
||||
x:Key="CompatCardShadow"
|
||||
BlurRadius="8"
|
||||
Opacity="0.14"
|
||||
Offset="0,4,0"/>
|
||||
<x:Double x:Key="CompatShadowThemeOpacity">0.14</x:Double>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<cwm:AttachedCardShadow
|
||||
x:Key="CompatCardShadow"
|
||||
BlurRadius="8"
|
||||
Opacity="0.28"
|
||||
Offset="0,4,0"/>
|
||||
<x:Double x:Key="CompatShadowThemeOpacity">0.28</x:Double>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<cwm:AttachedCardShadow
|
||||
x:Key="CompatCardShadow"
|
||||
BlurRadius="8"
|
||||
Opacity="{ThemeResource CompatShadowThemeOpacity}"
|
||||
Offset="0,4,0"/>
|
||||
|
||||
<Style x:Key="BorderCardStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing0Template">
|
||||
<cwcont:WrapPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing2Template">
|
||||
<cwcont:WrapPanel HorizontalSpacing="2" VerticalSpacing="2"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing4Template">
|
||||
<cwcont:WrapPanel HorizontalSpacing="4" VerticalSpacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
@@ -17,6 +20,9 @@
|
||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing2Template">
|
||||
<StackPanel Orientation="Horizontal" Spacing="2"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing4Template">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="StackPanelSpacing4Template">
|
||||
<StackPanel Spacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
@@ -11,4 +11,6 @@ internal static class KnownColors
|
||||
public static readonly Color Orange = StructMarshal.Color(0xFFBC6932);
|
||||
public static readonly Color Purple = StructMarshal.Color(0xFFA156E0);
|
||||
public static readonly Color Blue = StructMarshal.Color(0xFF5180CB);
|
||||
public static readonly Color Green = StructMarshal.Color(0xFF2A8F72);
|
||||
public static readonly Color White = StructMarshal.Color(0xFF72778B);
|
||||
}
|
||||
115
src/Snap.Hutao/Snap.Hutao/Control/Theme/SegmentedOverride.xaml
Normal file
115
src/Snap.Hutao/Snap.Hutao/Control/Theme/SegmentedOverride.xaml
Normal file
@@ -0,0 +1,115 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cw="using:CommunityToolkit.WinUI"
|
||||
xmlns:cwc="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:shcp="using:Snap.Hutao.Control.Panel"
|
||||
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/SegmentedItem/SegmentedItem.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<StaticResource x:Key="SegmentedBackground" ResourceKey="ControlAltFillColorSecondaryBrush"/>
|
||||
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush"/>
|
||||
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="SegmentedBackground" ResourceKey="ControlAltFillColorSecondaryBrush"/>
|
||||
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush"/>
|
||||
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="SegmentedBackground" ResourceKey="SystemColorButtonFaceColor"/>
|
||||
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="SystemColorHighlightColorBrush"/>
|
||||
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<x:Double x:Key="SegmentedItemSpacing">1</x:Double>
|
||||
<x:Double x:Key="ButtonItemSpacing">2</x:Double>
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultSegmentedStyle}" TargetType="cwc:Segmented"/>
|
||||
|
||||
<Style x:Key="DefaultSegmentedStyle" TargetType="cwc:Segmented">
|
||||
<Style.Setters>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SegmentedBackground}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SegmentedBorderBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource SegmentedBorderThickness}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="SelectionMode" Value="Single"/>
|
||||
<Setter Property="IsItemClickEnabled" Value="False"/>
|
||||
<win:Setter Property="SingleSelectionFollowsFocus" Value="False"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="TabNavigation" Value="Once"/>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<shcp:EqualPanel
|
||||
HorizontalAlignment="{Binding (cw:FrameworkElementExtensions.Ancestor).HorizontalAlignment, RelativeSource={RelativeSource Self}}"
|
||||
cw:FrameworkElementExtensions.AncestorType="cwc:Segmented"
|
||||
Spacing="{ThemeResource SegmentedItemSpacing}"/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="cwc:Segmented">
|
||||
<Grid>
|
||||
<Border
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"/>
|
||||
<ItemsPresenter Margin="{TemplateBinding Padding}"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="PivotSegmentedStyle"
|
||||
BasedOn="{StaticResource DefaultSegmentedStyle}"
|
||||
TargetType="cwc:Segmented">
|
||||
<Style.Setters>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource PivotSegmentedItemStyle}"/>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="{ThemeResource SegmentedItemSpacing}"/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="ButtonSegmentedStyle"
|
||||
BasedOn="{StaticResource DefaultSegmentedStyle}"
|
||||
TargetType="cwc:Segmented">
|
||||
<Style.Setters>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource ButtonSegmentedItemStyle}"/>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="{ThemeResource ButtonItemSpacing}"/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
25
src/Snap.Hutao/Snap.Hutao/Control/Theme/SystemColors.cs
Normal file
25
src/Snap.Hutao/Snap.Hutao/Control/Theme/SystemColors.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.Theme;
|
||||
|
||||
internal static class SystemColors
|
||||
{
|
||||
public static Color BaseLowColor(bool isDarkMode)
|
||||
{
|
||||
return isDarkMode ? StructMarshal.Color(0x33FFFFFF) : StructMarshal.Color(0x33000000);
|
||||
}
|
||||
|
||||
public static Color BaseMediumLowColor(bool isDarkMode)
|
||||
{
|
||||
return isDarkMode ? StructMarshal.Color(0x66FFFFFF) : StructMarshal.Color(0x66000000);
|
||||
}
|
||||
|
||||
public static Color BaseHighColor(bool isDarkMode)
|
||||
{
|
||||
return isDarkMode ? StructMarshal.Color(0xFFFFFFFF) : StructMarshal.Color(0xFF000000);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
using Snap.Hutao.Core.IO;
|
||||
|
||||
namespace Snap.Hutao.Core.Caching;
|
||||
@@ -10,7 +9,7 @@ namespace Snap.Hutao.Core.Caching;
|
||||
/// 为图像缓存提供抽象
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal interface IImageCache : ICastService
|
||||
internal interface IImageCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the file path containing cached item for given Uri
|
||||
|
||||
@@ -26,12 +26,12 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
{
|
||||
private const string CacheFolderName = nameof(ImageCache);
|
||||
|
||||
private readonly FrozenDictionary<int, TimeSpan> retryCountToDelay = new Dictionary<int, TimeSpan>()
|
||||
{
|
||||
[0] = TimeSpan.FromSeconds(4),
|
||||
[1] = TimeSpan.FromSeconds(16),
|
||||
[2] = TimeSpan.FromSeconds(64),
|
||||
}.ToFrozenDictionary();
|
||||
private readonly FrozenDictionary<int, TimeSpan> retryCountToDelay = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(0, TimeSpan.FromSeconds(4)),
|
||||
KeyValuePair.Create(1, TimeSpan.FromSeconds(16)),
|
||||
KeyValuePair.Create(2, TimeSpan.FromSeconds(64)),
|
||||
]);
|
||||
|
||||
private readonly ConcurrentDictionary<string, Task> concurrentTasks = new();
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ internal static partial class IocHttpClientConfiguration
|
||||
.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
|
||||
{
|
||||
HttpClientHandler clientHandler = (HttpClientHandler)handler;
|
||||
clientHandler.AllowAutoRedirect = true;
|
||||
clientHandler.UseProxy = true;
|
||||
clientHandler.Proxy = provider.GetRequiredService<DynamicHttpProxy>();
|
||||
});
|
||||
|
||||
@@ -23,8 +23,16 @@ internal sealed partial class ExceptionRecorder
|
||||
public void Record(Application app)
|
||||
{
|
||||
app.UnhandledException += OnAppUnhandledException;
|
||||
app.DebugSettings.FailFastOnErrors = false;
|
||||
|
||||
app.DebugSettings.IsBindingTracingEnabled = true;
|
||||
app.DebugSettings.BindingFailed += OnXamlBindingFailed;
|
||||
|
||||
app.DebugSettings.IsXamlResourceReferenceTracingEnabled = true;
|
||||
app.DebugSettings.XamlResourceReferenceFailed += OnXamlResourceReferenceFailed;
|
||||
|
||||
app.DebugSettings.LayoutCycleTracingLevel = LayoutCycleTracingLevel.High;
|
||||
app.DebugSettings.LayoutCycleDebugBreakLevel = LayoutCycleDebugBreakLevel.High;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "CA2012")]
|
||||
|
||||
@@ -32,9 +32,23 @@ internal sealed class HutaoException : Exception
|
||||
}
|
||||
}
|
||||
|
||||
public static void ThrowIfNot(bool condition, HutaoExceptionKind kind, string message, Exception? innerException = default)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new HutaoException(kind, message, innerException);
|
||||
}
|
||||
}
|
||||
|
||||
public static HutaoException ServiceTypeCastFailed<TFrom, TTo>(string name, Exception? innerException = default)
|
||||
{
|
||||
string message = $"This instance of '{typeof(TFrom).FullName}' '{name}' doesn't implement '{typeof(TTo).FullName}'";
|
||||
throw new HutaoException(HutaoExceptionKind.ServiceTypeCastFailed, message, innerException);
|
||||
}
|
||||
|
||||
public static HutaoException GachaStatisticsInvalidItemId(uint id, Exception? innerException = default)
|
||||
{
|
||||
string message = SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(id);
|
||||
throw new HutaoException(HutaoExceptionKind.GachaStatisticsInvalidItemId, message, innerException);
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,15 @@ namespace Snap.Hutao.Core.ExceptionService;
|
||||
internal enum HutaoExceptionKind
|
||||
{
|
||||
None,
|
||||
|
||||
// Foundation
|
||||
ServiceTypeCastFailed,
|
||||
|
||||
// IO
|
||||
FileSystemCreateFileInsufficientPermissions,
|
||||
PrivateNamedPipeContentHashIncorrect,
|
||||
|
||||
// Service
|
||||
GachaStatisticsInvalidItemId,
|
||||
GameFpsUnlockingFailed,
|
||||
}
|
||||
@@ -29,7 +29,7 @@ internal readonly struct TempFile : IDisposable
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
ThrowHelper.RuntimeEnvironment(SH.CoreIOTempFileCreateFail, ex);
|
||||
HutaoException.Throw(HutaoExceptionKind.FileSystemCreateFileInsufficientPermissions, SH.CoreIOTempFileCreateFail, ex);
|
||||
}
|
||||
|
||||
if (delete)
|
||||
|
||||
@@ -148,17 +148,15 @@ internal sealed partial class Activation : IActivation
|
||||
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
|
||||
serviceProvider
|
||||
.GetRequiredService<IMetadataService>()
|
||||
.As<IMetadataServiceInitialization>()?
|
||||
.InitializeInternalAsync()
|
||||
.SafeForget();
|
||||
if (serviceProvider.GetRequiredService<IMetadataService>() is IMetadataServiceInitialization metadataServiceInitialization)
|
||||
{
|
||||
metadataServiceInitialization.InitializeInternalAsync().SafeForget();
|
||||
}
|
||||
|
||||
serviceProvider
|
||||
.GetRequiredService<IHutaoUserService>()
|
||||
.As<IHutaoUserServiceInitialization>()?
|
||||
.InitializeInternalAsync()
|
||||
.SafeForget();
|
||||
if (serviceProvider.GetRequiredService<IHutaoUserService>() is IHutaoUserServiceInitialization hutaoUserServiceInitialization)
|
||||
{
|
||||
hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget();
|
||||
}
|
||||
|
||||
serviceProvider
|
||||
.GetRequiredService<IDiscordService>()
|
||||
|
||||
@@ -49,7 +49,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
{
|
||||
byte[] content = new byte[header->ContentLength];
|
||||
serverStream.ReadAtLeast(content, header->ContentLength, false);
|
||||
ThrowHelper.InvalidDataIf(XxHash64.HashToUInt64(content) != header->Checksum, "PipePacket Content Hash incorrect");
|
||||
HutaoException.ThrowIf(XxHash64.HashToUInt64(content) != header->Checksum, HutaoExceptionKind.PrivateNamedPipeContentHashIncorrect, "PipePacket Content Hash incorrect");
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Collections.Frozen;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.Core;
|
||||
@@ -14,25 +15,25 @@ internal static class TypeNameHelper
|
||||
{
|
||||
private const char DefaultNestedTypeDelimiter = '+';
|
||||
|
||||
private static readonly Dictionary<Type, string> BuiltInTypeNames = new()
|
||||
{
|
||||
{ typeof(void), "void" },
|
||||
{ typeof(bool), "bool" },
|
||||
{ typeof(byte), "byte" },
|
||||
{ typeof(char), "char" },
|
||||
{ typeof(decimal), "decimal" },
|
||||
{ typeof(double), "double" },
|
||||
{ typeof(float), "float" },
|
||||
{ typeof(int), "int" },
|
||||
{ typeof(long), "long" },
|
||||
{ typeof(object), "object" },
|
||||
{ typeof(sbyte), "sbyte" },
|
||||
{ typeof(short), "short" },
|
||||
{ typeof(string), "string" },
|
||||
{ typeof(uint), "uint" },
|
||||
{ typeof(ulong), "ulong" },
|
||||
{ typeof(ushort), "ushort" },
|
||||
};
|
||||
private static readonly FrozenDictionary<Type, string> BuiltInTypeNames = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(typeof(void), "void"),
|
||||
KeyValuePair.Create(typeof(bool), "bool"),
|
||||
KeyValuePair.Create(typeof(byte), "byte"),
|
||||
KeyValuePair.Create(typeof(char), "char"),
|
||||
KeyValuePair.Create(typeof(decimal), "decimal"),
|
||||
KeyValuePair.Create(typeof(double), "double"),
|
||||
KeyValuePair.Create(typeof(float), "float"),
|
||||
KeyValuePair.Create(typeof(int), "int"),
|
||||
KeyValuePair.Create(typeof(long), "long"),
|
||||
KeyValuePair.Create(typeof(object), "object"),
|
||||
KeyValuePair.Create(typeof(sbyte), "sbyte"),
|
||||
KeyValuePair.Create(typeof(short), "short"),
|
||||
KeyValuePair.Create(typeof(string), "string"),
|
||||
KeyValuePair.Create(typeof(uint), "uint"),
|
||||
KeyValuePair.Create(typeof(ulong), "ulong"),
|
||||
KeyValuePair.Create(typeof(ushort), "ushort"),
|
||||
]);
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象类型的显示名称
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Snap.Hutao.Core.Validation;
|
||||
/// 封装验证方法,简化微软验证
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Obsolete("Use HutaoException instead")]
|
||||
internal static class Must
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse;
|
||||
using System.Text;
|
||||
@@ -17,6 +18,7 @@ namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
internal sealed class HotKeyCombination : ObservableObject
|
||||
{
|
||||
private readonly ICurrentWindowReference currentWindowReference;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
private readonly string settingKey;
|
||||
@@ -37,6 +39,7 @@ internal sealed class HotKeyCombination : ObservableObject
|
||||
public HotKeyCombination(IServiceProvider serviceProvider, string settingKey, int hotKeyId, HOT_KEY_MODIFIERS defaultModifiers, VirtualKey defaultKey)
|
||||
{
|
||||
currentWindowReference = serviceProvider.GetRequiredService<ICurrentWindowReference>();
|
||||
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
|
||||
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
|
||||
this.settingKey = settingKey;
|
||||
@@ -186,6 +189,12 @@ internal sealed class HotKeyCombination : ObservableObject
|
||||
HWND hwnd = currentWindowReference.GetWindowHandle();
|
||||
BOOL result = RegisterHotKey(hwnd, hotKeyId, Modifiers, (uint)Key);
|
||||
registered = result;
|
||||
|
||||
if (!result)
|
||||
{
|
||||
infoBarService.Warning(SH.FormatCoreWindowHotkeyCombinationRegisterFailed(SH.ViewPageSettingKeyShortcutAutoClickingHeader, DisplayName));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ using Snap.Hutao.Win32;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Graphics.Dwm;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.Collections.Frozen;
|
||||
using System.IO;
|
||||
using Windows.Graphics;
|
||||
using Windows.UI;
|
||||
@@ -56,25 +57,22 @@ internal sealed class WindowController
|
||||
private void InitializeCore()
|
||||
{
|
||||
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
|
||||
|
||||
window.AppWindow.Title = SH.FormatAppNameAndVersion(runtimeOptions.Version);
|
||||
window.AppWindow.SetIcon(Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico"));
|
||||
ExtendsContentIntoTitleBar();
|
||||
|
||||
RecoverOrInitWindowSize();
|
||||
UpdateElementTheme(appOptions.ElementTheme);
|
||||
UpdateImmersiveDarkMode(options.TitleBar, default!);
|
||||
|
||||
// appWindow.Show(true);
|
||||
// appWindow.Show can't bring window to top.
|
||||
window.Activate();
|
||||
options.BringToForeground();
|
||||
|
||||
if (options.UseSystemBackdrop)
|
||||
{
|
||||
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
|
||||
UpdateSystemBackdrop(appOptions.BackdropType);
|
||||
appOptions.PropertyChanged += OnOptionsPropertyChanged;
|
||||
}
|
||||
UpdateSystemBackdrop(appOptions.BackdropType);
|
||||
appOptions.PropertyChanged += OnOptionsPropertyChanged;
|
||||
|
||||
subclass.Initialize();
|
||||
|
||||
@@ -122,13 +120,17 @@ internal sealed class WindowController
|
||||
|
||||
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName is nameof(AppOptions.BackdropType))
|
||||
if (sender is not AppOptions options)
|
||||
{
|
||||
if (sender is AppOptions options)
|
||||
{
|
||||
UpdateSystemBackdrop(options.BackdropType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_ = e.PropertyName switch
|
||||
{
|
||||
nameof(AppOptions.BackdropType) => UpdateSystemBackdrop(options.BackdropType),
|
||||
nameof(AppOptions.ElementTheme) => UpdateElementTheme(options.ElementTheme),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||
@@ -158,7 +160,7 @@ internal sealed class WindowController
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSystemBackdrop(BackdropType backdropType)
|
||||
private bool UpdateSystemBackdrop(BackdropType backdropType)
|
||||
{
|
||||
window.SystemBackdrop = backdropType switch
|
||||
{
|
||||
@@ -168,6 +170,15 @@ internal sealed class WindowController
|
||||
BackdropType.Acrylic => new DesktopAcrylicBackdrop(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool UpdateElementTheme(ElementTheme theme)
|
||||
{
|
||||
((FrameworkElement)window.Content).RequestedTheme = theme;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateTitleButtonColor()
|
||||
@@ -177,12 +188,12 @@ internal sealed class WindowController
|
||||
appTitleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
appTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
|
||||
IAppResourceProvider resourceProvider = serviceProvider.GetRequiredService<IAppResourceProvider>();
|
||||
bool isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(options.TitleBar.ActualTheme);
|
||||
|
||||
Color systemBaseLowColor = resourceProvider.GetResource<Color>("SystemBaseLowColor");
|
||||
Color systemBaseLowColor = Control.Theme.SystemColors.BaseLowColor(isDarkMode);
|
||||
appTitleBar.ButtonHoverBackgroundColor = systemBaseLowColor;
|
||||
|
||||
Color systemBaseMediumLowColor = resourceProvider.GetResource<Color>("SystemBaseMediumLowColor");
|
||||
Color systemBaseMediumLowColor = Control.Theme.SystemColors.BaseMediumLowColor(isDarkMode);
|
||||
appTitleBar.ButtonPressedBackgroundColor = systemBaseMediumLowColor;
|
||||
|
||||
// The Foreground doesn't accept Alpha channel. So we translate it to gray.
|
||||
@@ -190,7 +201,7 @@ internal sealed class WindowController
|
||||
byte result = (byte)((systemBaseMediumLowColor.A / 255.0) * light);
|
||||
appTitleBar.ButtonInactiveForegroundColor = Color.FromArgb(0xFF, result, result, result);
|
||||
|
||||
Color systemBaseHighColor = resourceProvider.GetResource<Color>("SystemBaseHighColor");
|
||||
Color systemBaseHighColor = Control.Theme.SystemColors.BaseHighColor(isDarkMode);
|
||||
appTitleBar.ButtonForegroundColor = systemBaseHighColor;
|
||||
appTitleBar.ButtonHoverForegroundColor = systemBaseHighColor;
|
||||
appTitleBar.ButtonPressedForegroundColor = systemBaseHighColor;
|
||||
|
||||
@@ -41,21 +41,18 @@ internal readonly struct WindowOptions
|
||||
/// </summary>
|
||||
public readonly bool PersistSize;
|
||||
|
||||
public readonly bool UseSystemBackdrop;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用 Win UI 3 自带的拓展标题栏实现
|
||||
/// </summary>
|
||||
public readonly bool UseLegacyDragBarImplementation = !AppWindowTitleBar.IsCustomizationSupported();
|
||||
|
||||
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false, bool useSystemBackdrop = true)
|
||||
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false)
|
||||
{
|
||||
Hwnd = WindowNative.GetWindowHandle(window);
|
||||
InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
|
||||
TitleBar = titleBar;
|
||||
InitSize = initSize;
|
||||
PersistSize = persistSize;
|
||||
UseSystemBackdrop = useSystemBackdrop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,20 +18,11 @@ internal static class DateTimeOffsetExtension
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try
|
||||
return value switch
|
||||
{
|
||||
return DateTimeOffset.FromUnixTimeSeconds(value);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
try
|
||||
{
|
||||
return DateTimeOffset.FromUnixTimeMilliseconds(value);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
>= -62135596800 and <= 253402300799 => DateTimeOffset.FromUnixTimeSeconds(value),
|
||||
>= -62135596800000 and <= 253402300799999 => DateTimeOffset.FromUnixTimeMilliseconds(value),
|
||||
_ => defaultValue,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,12 @@ namespace Snap.Hutao.Extension;
|
||||
[HighQuality]
|
||||
internal static partial class EnumerableExtension
|
||||
{
|
||||
public static void Deconstruct<TKey, TElement>(this IGrouping<TKey, TElement> grouping, out TKey key, out IEnumerable<TElement> elements)
|
||||
{
|
||||
key = grouping.Key;
|
||||
elements = grouping;
|
||||
}
|
||||
|
||||
public static TElement? ElementAtOrLastOrDefault<TElement>(this IEnumerable<TElement> source, int index)
|
||||
{
|
||||
return source.ElementAtOrDefault(index) ?? source.LastOrDefault();
|
||||
@@ -46,34 +52,6 @@ internal static partial class EnumerableExtension
|
||||
return first;
|
||||
}
|
||||
|
||||
public static string JoinToString<T>(this IEnumerable<T> source, char separator, Action<StringBuilder, T> selector)
|
||||
{
|
||||
StringBuilder resultBuilder = new();
|
||||
|
||||
IEnumerator<T> enumerator = source.GetEnumerator();
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
T first = enumerator.Current;
|
||||
selector(resultBuilder, first);
|
||||
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
return resultBuilder.ToString();
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
resultBuilder.Append(separator);
|
||||
selector(resultBuilder, enumerator.Current);
|
||||
}
|
||||
while (enumerator.MoveNext());
|
||||
|
||||
return resultBuilder.ToString();
|
||||
}
|
||||
|
||||
public static string JoinToString<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source, char separator, Action<StringBuilder, TKey, TValue> selector)
|
||||
{
|
||||
StringBuilder resultBuilder = new();
|
||||
|
||||
@@ -41,6 +41,30 @@
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FurnitureId",
|
||||
"Documentation": "家具 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FurnitureMakeId",
|
||||
"Documentation": "家具配方 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FurnitureSuiteId",
|
||||
"Documentation": "家具套装 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FurnitureTypeId",
|
||||
"Documentation": "家具分类 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "Level",
|
||||
"Documentation": "等级 1 - 90",
|
||||
|
||||
@@ -27,7 +27,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMax
|
||||
public MainWindow(IServiceProvider serviceProvider)
|
||||
{
|
||||
InitializeComponent();
|
||||
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true, false);
|
||||
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
|
||||
this.InitializeController(serviceProvider);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
internal sealed class BackgroundImageTypeChangedMessage;
|
||||
@@ -42,14 +42,14 @@ internal sealed partial class GachaItem
|
||||
/// <summary>
|
||||
/// 祈愿记录分类
|
||||
/// </summary>
|
||||
public GachaConfigType GachaType { get; set; }
|
||||
public GachaType GachaType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿记录查询分类
|
||||
/// 合并保底的卡池使用此属性
|
||||
/// 仅4种(不含400)
|
||||
/// </summary>
|
||||
public GachaConfigType QueryType { get; set; }
|
||||
public GachaType QueryType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
|
||||
@@ -13,6 +13,7 @@ internal sealed partial class SettingEntry
|
||||
public const string Culture = "Culture";
|
||||
|
||||
public const string SystemBackdropType = "SystemBackdropType";
|
||||
public const string ElementTheme = "ElementTheme";
|
||||
public const string BackgroundImageType = "BackgroundImageType";
|
||||
|
||||
public const string AnnouncementRegion = "AnnouncementRegion";
|
||||
|
||||
@@ -17,7 +17,7 @@ internal sealed class UIGF : IJsonOnSerializing, IJsonOnDeserialized
|
||||
/// <summary>
|
||||
/// 当前版本
|
||||
/// </summary>
|
||||
public const string CurrentVersion = "v2.4";
|
||||
public const string CurrentVersion = "v3.0";
|
||||
|
||||
/// <summary>
|
||||
/// 信息
|
||||
@@ -61,6 +61,7 @@ internal sealed class UIGF : IJsonOnSerializing, IJsonOnDeserialized
|
||||
"v2.2" => UIGFVersion.Major2Minor2OrLower,
|
||||
"v2.3" => UIGFVersion.Major2Minor3OrHigher,
|
||||
"v2.4" => UIGFVersion.Major2Minor3OrHigher,
|
||||
"v3.0" => UIGFVersion.Major2Minor3OrHigher,
|
||||
_ => UIGFVersion.NotSupported,
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ internal sealed class UIGFItem : GachaLogItem, IMappingFrom<UIGFItem, GachaItem,
|
||||
/// </summary>
|
||||
[JsonPropertyName("uigf_gacha_type")]
|
||||
[JsonEnum(JsonSerializeType.NumberString)]
|
||||
public GachaConfigType UIGFGachaType { get; set; } = default!;
|
||||
public GachaType UIGFGachaType { get; set; } = default!;
|
||||
|
||||
public static UIGFItem From(GachaItem item, INameQuality nameQuality)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum FurnitureDeploySurfaceType
|
||||
{
|
||||
Ground = 0,
|
||||
Wall = 1,
|
||||
Ceil = 2,
|
||||
StackObjPlane = 3,
|
||||
Door = 4,
|
||||
Chandelier = 5,
|
||||
Floor = 6,
|
||||
WallBody = 7,
|
||||
Carpet = 8,
|
||||
LegoRockery = 9,
|
||||
Stair = 10,
|
||||
NPC = 11,
|
||||
Animal = 12,
|
||||
Apartment = 13,
|
||||
FurnitureSuite = 14,
|
||||
Road = 15,
|
||||
Terrain = 16,
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum FurnitureDeployType
|
||||
{
|
||||
Interior,
|
||||
Exterior,
|
||||
InteriorRoom,
|
||||
InteriorHall,
|
||||
Skybox,
|
||||
}
|
||||
14
src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/GroupRecordType.cs
Normal file
14
src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/GroupRecordType.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum GroupRecordType
|
||||
{
|
||||
None,
|
||||
Racing,
|
||||
Balloon,
|
||||
Stake,
|
||||
Seek,
|
||||
Explosion,
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum SpecialFurnitureType
|
||||
{
|
||||
NormalFurniture,
|
||||
BlockDependent,
|
||||
FarmField,
|
||||
TeleportPoint,
|
||||
Fishpond,
|
||||
Npc,
|
||||
Apartment,
|
||||
FurnitureSuite,
|
||||
Paimon,
|
||||
Fish,
|
||||
CustomBaseFurniture,
|
||||
CustomNodeFurniture,
|
||||
VirtualFurniture,
|
||||
GroupFurniture,
|
||||
CoopPictureFrame,
|
||||
ChangeBgmFurniture,
|
||||
ServerGadget,
|
||||
Fishtank,
|
||||
}
|
||||
@@ -1,22 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Achievement;
|
||||
|
||||
/// <summary>
|
||||
/// 奖励
|
||||
/// </summary>
|
||||
internal sealed class Reward
|
||||
{
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public MaterialId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数量
|
||||
/// </summary>
|
||||
public uint Count { get; set; }
|
||||
}
|
||||
internal sealed class Reward : IdCount;
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
internal sealed class AssociationTypeIconConverter : ValueConverter<AssociationType, Uri?>
|
||||
{
|
||||
private static readonly FrozenDictionary<string, AssociationType> LocalizedNameToAssociationType = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeMondstadt, AssociationType.ASSOC_TYPE_MONDSTADT),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeLiyue, AssociationType.ASSOC_TYPE_LIYUE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeFatui, AssociationType.ASSOC_TYPE_FATUI),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeInazuma, AssociationType.ASSOC_TYPE_INAZUMA),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeRanger, AssociationType.ASSOC_TYPE_RANGER),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeSumeru, AssociationType.ASSOC_TYPE_SUMERU),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeFontaine, AssociationType.ASSOC_TYPE_FONTAINE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeNatlan, AssociationType.ASSOC_TYPE_NATLAN),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicAssociationTypeSnezhnaya, AssociationType.ASSOC_TYPE_SNEZHNAYA),
|
||||
]);
|
||||
|
||||
public static Uri? AssociationTypeNameToIconUri(string associationTypeName)
|
||||
{
|
||||
return AssociationTypeToIconUri(LocalizedNameToAssociationType.GetValueOrDefault(associationTypeName));
|
||||
}
|
||||
|
||||
public static Uri? AssociationTypeToIconUri(AssociationType type)
|
||||
{
|
||||
string? association = type switch
|
||||
{
|
||||
AssociationType.ASSOC_TYPE_MONDSTADT => "Mengde",
|
||||
AssociationType.ASSOC_TYPE_LIYUE => "Liyue",
|
||||
AssociationType.ASSOC_TYPE_FATUI => default,
|
||||
AssociationType.ASSOC_TYPE_INAZUMA => "Inazuma",
|
||||
AssociationType.ASSOC_TYPE_RANGER => default,
|
||||
AssociationType.ASSOC_TYPE_SUMERU => "Sumeru",
|
||||
AssociationType.ASSOC_TYPE_FONTAINE => "Fontaine",
|
||||
AssociationType.ASSOC_TYPE_NATLAN => default,
|
||||
AssociationType.ASSOC_TYPE_SNEZHNAYA => default,
|
||||
_ => throw Must.NeverHappen(),
|
||||
};
|
||||
|
||||
return association is null
|
||||
? default
|
||||
: Web.HutaoEndpoints.StaticRaw("ChapterIcon", $"UI_ChapterIcon_{association}.png").ToUri();
|
||||
}
|
||||
|
||||
public override Uri? Convert(AssociationType from)
|
||||
{
|
||||
return AssociationTypeToIconUri(from);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
@@ -12,27 +13,27 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
[HighQuality]
|
||||
internal sealed class ElementNameIconConverter : ValueConverter<string, Uri>
|
||||
{
|
||||
private static readonly Dictionary<string, string> LocalizedNameToElementIconName = new()
|
||||
{
|
||||
[SH.ModelIntrinsicElementNameElec] = "Electric",
|
||||
[SH.ModelIntrinsicElementNameFire] = "Fire",
|
||||
[SH.ModelIntrinsicElementNameGrass] = "Grass",
|
||||
[SH.ModelIntrinsicElementNameIce] = "Ice",
|
||||
[SH.ModelIntrinsicElementNameRock] = "Rock",
|
||||
[SH.ModelIntrinsicElementNameWater] = "Water",
|
||||
[SH.ModelIntrinsicElementNameWind] = "Wind",
|
||||
};
|
||||
private static readonly FrozenDictionary<string, string> LocalizedNameToElementIconName = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameElec, "Electric"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameFire, "Fire"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameGrass, "Grass"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameIce, "Ice"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameRock, "Rock"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameWater, "Water"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameWind, "Wind"),
|
||||
]);
|
||||
|
||||
private static readonly Dictionary<string, ElementType> LocalizedNameToElementType = new()
|
||||
{
|
||||
[SH.ModelIntrinsicElementNameElec] = ElementType.Electric,
|
||||
[SH.ModelIntrinsicElementNameFire] = ElementType.Fire,
|
||||
[SH.ModelIntrinsicElementNameGrass] = ElementType.Grass,
|
||||
[SH.ModelIntrinsicElementNameIce] = ElementType.Ice,
|
||||
[SH.ModelIntrinsicElementNameRock] = ElementType.Rock,
|
||||
[SH.ModelIntrinsicElementNameWater] = ElementType.Water,
|
||||
[SH.ModelIntrinsicElementNameWind] = ElementType.Wind,
|
||||
};
|
||||
private static readonly FrozenDictionary<string, ElementType> LocalizedNameToElementType = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameElec, ElementType.Electric),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameFire, ElementType.Fire),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameGrass, ElementType.Grass),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameIce, ElementType.Ice),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameRock, ElementType.Rock),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameWater, ElementType.Water),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameWind, ElementType.Wind),
|
||||
]);
|
||||
|
||||
/// <summary>
|
||||
/// 将中文元素名称转换为图标链接
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
using Microsoft.UI;
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Win32;
|
||||
using System.Collections.Frozen;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
@@ -15,17 +16,39 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
[HighQuality]
|
||||
internal sealed class QualityColorConverter : ValueConverter<QualityType, Color>
|
||||
{
|
||||
private static readonly FrozenDictionary<string, QualityType> LocalizedNameToQualityType = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityWhite, QualityType.QUALITY_WHITE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityGreen, QualityType.QUALITY_GREEN),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityBlue, QualityType.QUALITY_BLUE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityPurple, QualityType.QUALITY_PURPLE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityOrange, QualityType.QUALITY_ORANGE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityRed, QualityType.QUALITY_ORANGE_SP),
|
||||
]);
|
||||
|
||||
private static readonly FrozenDictionary<QualityType, Color> QualityTypeToColor = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(QualityType.QUALITY_WHITE, KnownColors.White),
|
||||
KeyValuePair.Create(QualityType.QUALITY_GREEN, KnownColors.Green),
|
||||
KeyValuePair.Create(QualityType.QUALITY_BLUE, KnownColors.Blue),
|
||||
KeyValuePair.Create(QualityType.QUALITY_PURPLE, KnownColors.Purple),
|
||||
KeyValuePair.Create(QualityType.QUALITY_ORANGE, KnownColors.Orange),
|
||||
KeyValuePair.Create(QualityType.QUALITY_ORANGE_SP, KnownColors.Orange),
|
||||
]);
|
||||
|
||||
public static Color QualityNameToColor(string qualityName)
|
||||
{
|
||||
return QualityToColor(LocalizedNameToQualityType.GetValueOrDefault(qualityName));
|
||||
}
|
||||
|
||||
public static Color QualityToColor(QualityType quality)
|
||||
{
|
||||
return QualityTypeToColor.GetValueOrDefault(quality, Colors.Transparent);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Color Convert(QualityType from)
|
||||
{
|
||||
return from switch
|
||||
{
|
||||
QualityType.QUALITY_WHITE => StructMarshal.Color(0xFF72778B),
|
||||
QualityType.QUALITY_GREEN => StructMarshal.Color(0xFF2A8F72),
|
||||
QualityType.QUALITY_BLUE => StructMarshal.Color(0xFF5180CB),
|
||||
QualityType.QUALITY_PURPLE => StructMarshal.Color(0xFFA156E0),
|
||||
QualityType.QUALITY_ORANGE or QualityType.QUALITY_ORANGE_SP => StructMarshal.Color(0xFFBC6932),
|
||||
_ => Colors.Transparent,
|
||||
};
|
||||
return QualityToColor(from);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
@@ -12,6 +13,20 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
[HighQuality]
|
||||
internal sealed class WeaponTypeIconConverter : ValueConverter<WeaponType, Uri>
|
||||
{
|
||||
private static readonly FrozenDictionary<string, WeaponType> LocalizedNameToWeaponType = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeSwordOneHand, WeaponType.WEAPON_SWORD_ONE_HAND),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeBow, WeaponType.WEAPON_BOW),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypePole, WeaponType.WEAPON_POLE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeClaymore, WeaponType.WEAPON_CLAYMORE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeCatalyst, WeaponType.WEAPON_CATALYST),
|
||||
]);
|
||||
|
||||
public static Uri WeaponTypeNameToIconUri(string weaponTypeName)
|
||||
{
|
||||
return WeaponTypeToIconUri(LocalizedNameToWeaponType.GetValueOrDefault(weaponTypeName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将武器类型转换为图标链接
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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.Furniture;
|
||||
|
||||
internal sealed class Furniture
|
||||
{
|
||||
public List<FurnitureTypeId> Types { get; set; } = default!;
|
||||
|
||||
public FurnitureDeploySurfaceType SurfaceType { get; set; }
|
||||
|
||||
public bool IsSpecial { get; set; }
|
||||
|
||||
public SpecialFurnitureType SpecialType { get; set; }
|
||||
|
||||
public uint Comfort { get; set; }
|
||||
|
||||
public uint Cost { get; set; }
|
||||
|
||||
public uint DiscountCost { get; set; }
|
||||
|
||||
public bool CanFloat { get; set; }
|
||||
|
||||
public bool IsUnique { get; set; }
|
||||
|
||||
public string? ItemIcon { get; set; }
|
||||
|
||||
public string? EffectIcon { get; set; }
|
||||
|
||||
public QualityType RankLevel { get; set; }
|
||||
|
||||
public List<FurnitureId> GruopUnits { get; set; } = default!;
|
||||
|
||||
public GroupRecordType GroupRecordType { get; set; }
|
||||
|
||||
public List<string> SourceTexts { get; set; } = default!;
|
||||
|
||||
public FurnitureId Id { get; set; }
|
||||
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
public string Description { get; set; } = default!;
|
||||
|
||||
public string? Icon { get; set; }
|
||||
|
||||
public uint Rank { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Furniture;
|
||||
|
||||
internal sealed class FurnitureMake
|
||||
{
|
||||
public FurnitureMakeId Id { get; set; }
|
||||
|
||||
public FurnitureId ItemId { get; set; }
|
||||
|
||||
public uint Experience { get; set; }
|
||||
|
||||
public List<IdCount> Materials { get; set; } = default!;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Furniture;
|
||||
|
||||
internal sealed class FurnitureSuite
|
||||
{
|
||||
public FurnitureSuiteId Id { get; set; }
|
||||
|
||||
public List<FurnitureTypeId> Types { get; set; } = default!;
|
||||
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
public string Description { get; set; } = default!;
|
||||
|
||||
public string ItemIcon { get; set; } = default!;
|
||||
|
||||
public string? MapIcon { get; set; }
|
||||
|
||||
public List<AvatarId>? FavoriteNpcs { get; set; }
|
||||
|
||||
public List<FurnitureId> Units { get; set; } = default!;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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.Furniture;
|
||||
|
||||
internal sealed class FurnitureType
|
||||
{
|
||||
public FurnitureTypeId Id { get; set; }
|
||||
|
||||
public uint Category { get; set; }
|
||||
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
public string Name2 { get; set; } = default!;
|
||||
|
||||
public string TabIcon { get; set; } = default!;
|
||||
|
||||
public FurnitureDeployType SceneType { get; set; }
|
||||
|
||||
public bool BagPageOnly { get; set; }
|
||||
|
||||
public bool IsShowInBag { get; set; }
|
||||
|
||||
public uint Sort { get; set; }
|
||||
}
|
||||
@@ -49,7 +49,7 @@ internal sealed class GachaEvent
|
||||
/// <summary>
|
||||
/// 卡池类型
|
||||
/// </summary>
|
||||
public GachaConfigType Type { get; set; }
|
||||
public GachaType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 五星列表
|
||||
|
||||
19
src/Snap.Hutao/Snap.Hutao/Model/Metadata/IdCount.cs
Normal file
19
src/Snap.Hutao/Snap.Hutao/Model/Metadata/IdCount.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata;
|
||||
|
||||
internal class IdCount
|
||||
{
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public MaterialId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数量
|
||||
/// </summary>
|
||||
public uint Count { get; set; }
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutao"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.9.7.0" />
|
||||
Version="1.9.8.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao</DisplayName>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutaoDev"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.9.7.0" />
|
||||
Version="1.9.8.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao Dev</DisplayName>
|
||||
|
||||
@@ -186,6 +186,18 @@
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>No WebView2 Runtime detected</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value>Register [{0}] hotkey [{1}] failed</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>Dark</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeLight" xml:space="preserve">
|
||||
<value>Light</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeSystem" xml:space="preserve">
|
||||
<value>System</value>
|
||||
</data>
|
||||
<data name="FilePickerExportCommit" xml:space="preserve">
|
||||
<value>Export</value>
|
||||
</data>
|
||||
@@ -857,6 +869,9 @@
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>Character Event Wish</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
|
||||
<value>Chronicled Wish</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>Wanderlust Invocation</value>
|
||||
</data>
|
||||
@@ -2214,7 +2229,7 @@
|
||||
<value>Auto start Better GUI for automation tasks after game launched</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
<value>Automated Tasks</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>General</value>
|
||||
@@ -2232,7 +2247,7 @@
|
||||
<value>File</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||
<value>InterProcess</value>
|
||||
<value>Process Linkage</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>Run the software on the selected monitor</value>
|
||||
@@ -2253,7 +2268,7 @@
|
||||
<value>Try to start Starward after the game is started for game duration statistics</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGamePlayTimeHeader" xml:space="preserve">
|
||||
<value>Hours Played</value>
|
||||
<value>Game Hours Record</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameProcessHeader" xml:space="preserve">
|
||||
<value>Progress</value>
|
||||
@@ -2366,6 +2381,9 @@
|
||||
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
|
||||
<value>Wallpaper Image</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
|
||||
<value>Using local images as the background</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>Images cache are saved here</value>
|
||||
</data>
|
||||
@@ -2621,6 +2639,12 @@
|
||||
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
|
||||
<value>Rate Snap Hutao</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
|
||||
<value>Change window theme</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
|
||||
<value>Theme</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
|
||||
<value>Contribute Translations</value>
|
||||
</data>
|
||||
@@ -2630,6 +2654,12 @@
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>Webview2 Runtime</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
|
||||
<value>Second Half</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
|
||||
<value>First Half</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>Artifact Set Combination</value>
|
||||
</data>
|
||||
@@ -2736,7 +2766,7 @@
|
||||
<value>Character Appearance Rate = Character Appearance in this Floor (only count for 1 if repeated) / Total Number of Abyss Record of this Floor</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssAvatarUsageRankDescription" xml:space="preserve">
|
||||
<value>Character Usage Rate = Character Appearance in this Floor (only count for 1 is repeated) / Number of Player who Own this Character</value>
|
||||
<value>Character Usage Rate = Character Appearance in this Floor (only count for 1 if repeated) / Number of Player who Own this Character</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssBattleHeader" xml:space="preserve">
|
||||
<value>Battle Statistics</value>
|
||||
@@ -3044,6 +3074,9 @@
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>Character Event Wish-2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
|
||||
<value>Chronicled Wish</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>Beginners' Wish</value>
|
||||
</data>
|
||||
|
||||
@@ -186,6 +186,18 @@
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>WebView2 Runtime tidak terdeteksi.</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value>[{0}] Hotkey [{1}] gagal terdaftar</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>深色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeLight" xml:space="preserve">
|
||||
<value>浅色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeSystem" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
</data>
|
||||
<data name="FilePickerExportCommit" xml:space="preserve">
|
||||
<value>Ekspor</value>
|
||||
</data>
|
||||
@@ -199,19 +211,19 @@
|
||||
<value>Pilih akun untuk memulai</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>王子</color>/<color=#FFB6C1>公主</color></value>
|
||||
<value><color=#1E90FF>Prince</color>/<color=#FFB6C1>Princess</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>我</color>/<color=#FFB6C1>我</color></value>
|
||||
<value><color=#1E90FF>I</color>/<color=#FFB6C1>I</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>Pengembara</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>他</color>/<color=#FFB6C1>她</color></value>
|
||||
<value><color=#1E90FF>He</color>/<color=#FFB6C1>She</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>流浪者</value>
|
||||
<value>Pengembara</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>Perbaiki {0}</value>
|
||||
@@ -513,7 +525,7 @@
|
||||
<value>Gelombang 4: Gelombang akan muncul hanya setelah membunuh semua musuh di gelombang sebelumnya</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 个</value>
|
||||
<value>Tetapkan 4 Perampok Harta Karun di lapangan, langsung muncul kembali setelah mati. Total 12.</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>Silakan perbarui data tampilan.</value>
|
||||
@@ -768,19 +780,19 @@
|
||||
<value>Pameran Karakter: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeBing" xml:space="preserve">
|
||||
<value>必应每日一图</value>
|
||||
<value>Wallpaper Harian Bing</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeDaily" xml:space="preserve">
|
||||
<value>胡桃每日一图</value>
|
||||
<value>Wallpaper Harian Hutao</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeLauncher" xml:space="preserve">
|
||||
<value>官方启动器壁纸</value>
|
||||
<value>Wallpaper Resmi Peluncur Genshin</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeLocalFolder" xml:space="preserve">
|
||||
<value>本地随机图片</value>
|
||||
<value>Gambar Acak Lokal</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeNone" xml:space="preserve">
|
||||
<value>无背景图片</value>
|
||||
<value>Tanpa Gambar Latar</value>
|
||||
</data>
|
||||
<data name="ServiceCultivationProjectCurrentUserdataCourrpted" xml:space="preserve">
|
||||
<value>Gagal menyimpan status rencana pengembangan</value>
|
||||
@@ -840,7 +852,7 @@
|
||||
<value>Parametric Transformer telah siap</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordActivityElevationRequiredHint" xml:space="preserve">
|
||||
<value>权限不足,将无法为您设置 Discord Activity 状态</value>
|
||||
<value>Izin hilang, tidak dapat mengatur Aktivitas Discord Anda.</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordGameActivityDetails" xml:space="preserve">
|
||||
<value>Menjelajahi di Teyvat</value>
|
||||
@@ -857,6 +869,9 @@
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>Event Wish Karakter</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>Wanderlust Invocation</value>
|
||||
</data>
|
||||
@@ -1224,10 +1239,10 @@
|
||||
<value>Catatan Realtime Webhook URL</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
<value>Anda memerlukan alat lain untuk mengembalikan loopback setelah dikecualikan</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
<value>Konfirmasi untuk Mengonfigurasi Pengecualian Loopback</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>Mengimpor riwayat wish</value>
|
||||
@@ -1410,7 +1425,7 @@
|
||||
<value>Game Launcher</value>
|
||||
</data>
|
||||
<data name="ViewListViewDragElevatedHint" xml:space="preserve">
|
||||
<value>管理员模式下无法拖动排序</value>
|
||||
<value>Tidak dapat mengurutkan dalam Mode Administrator</value>
|
||||
</data>
|
||||
<data name="ViewModelAchievementArchiveAdded" xml:space="preserve">
|
||||
<value>Arsip [{0}] berhasil ditambahkan</value>
|
||||
@@ -1692,7 +1707,7 @@
|
||||
<value>Selesai</value>
|
||||
</data>
|
||||
<data name="ViewModelWelcomeDownloadSummaryContentTypeNotMatch" xml:space="preserve">
|
||||
<value>响应内容不是有效的文件字节流</value>
|
||||
<value>Stream respons tidak berisi tipe konten yang valid</value>
|
||||
</data>
|
||||
<data name="ViewModelWelcomeDownloadSummaryDefault" xml:space="preserve">
|
||||
<value>Mengantre</value>
|
||||
@@ -1911,16 +1926,16 @@
|
||||
<value>Tautan Berguna</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
<value>Proxy Saat ini</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
<value>Tidak ada Proxy</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
<value>Dikecualikan</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
<value>Konfigurasikan Pengecualian Loopback</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>Tetap berhubungan dengan kami</value>
|
||||
@@ -2211,10 +2226,10 @@
|
||||
<value>Argumen Awalan</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIDescription" xml:space="preserve">
|
||||
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
|
||||
<value>Mulai Otomatis Antarmuka Pengguna yang Lebih Baik untuk tugas otomatis setelah game diluncurkan</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
<value>自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>Umum</value>
|
||||
@@ -2232,7 +2247,7 @@
|
||||
<value>Berkas</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||
<value>InterProcess</value>
|
||||
<value>进程联动</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>Menjalankan perangkat lunak pada layar yang dipilih</value>
|
||||
@@ -2358,13 +2373,16 @@
|
||||
<value>Backdrop Material</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageCopyrightHeader" xml:space="preserve">
|
||||
<value>图片版权信息</value>
|
||||
<value>Informasi Hak Cipta Gambar</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageDescription" xml:space="preserve">
|
||||
<value>更改窗体的背景图片来源,重启胡桃以尽快生效</value>
|
||||
<value>Ubah sumber gambar latar, restart Snap Hutao untuk menerapkan perubahan</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
|
||||
<value>背景图片</value>
|
||||
<value>Gambar Latar</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
|
||||
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>Cache gambar disimpan di sini</value>
|
||||
@@ -2373,7 +2391,7 @@
|
||||
<value>Berkas Cache</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
<value>Hutao Passport</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>Salin</value>
|
||||
@@ -2568,10 +2586,10 @@
|
||||
<value>Website Resmi</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
<value>Latar Belakang Kustom, format didukung bmp/gif/ico/jpg/jpeg/png/tiff/webp</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
<value>Buka Berkas Latar Belakang</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>Reset</value>
|
||||
@@ -2583,10 +2601,10 @@
|
||||
<value>Setel ulang Sumber Daya Gambar</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
<value>Tambahkan Opsi Batasan FPS di Bagian Proses Peluncur Permainan</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
<value>Peluncur Permainan - Buka Limit FPS</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>Anda perlu memindahkan data di direktori secara manual, jika tidak, data pengguna baru akan dibuat.</value>
|
||||
@@ -2621,6 +2639,12 @@
|
||||
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
|
||||
<value>Beri Rating untuk Snap Hutao</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
|
||||
<value>更改窗体的颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
|
||||
<value>颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
|
||||
<value>Kontribusi penerjemahan</value>
|
||||
</data>
|
||||
@@ -2630,6 +2654,12 @@
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>Webview2 Runtime</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
|
||||
<value>下半</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
|
||||
<value>上半</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>Kombinasi Set Artefak</value>
|
||||
</data>
|
||||
@@ -2892,7 +2922,7 @@
|
||||
<value>〓Durasi Event〓.*?\d\.\d Tersedia selama versi ini</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||
<value>(?:〓Durasi Event〓|Durasi Event Wish|【Ketersediaan DUrasi】).*?(\d\.\dSetelah pembaruan versi).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>(?:〓Waktu Acara〓|Waktu Menginginkan|【Waktu Peluncuran】|〓Waktu Diskon〓).*?(\d\.\d Setelah Pembaruan Versi).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||
<value>〓Durasi Pemeliharaan Pembaruan.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
@@ -3044,6 +3074,9 @@
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>Event WIsh Karakter-2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>Wish Pemula</value>
|
||||
</data>
|
||||
|
||||
@@ -186,6 +186,18 @@
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>WebView2 ランタイムが検出されませんでした</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value>[{0}] 热键 [{1}] 注册失败</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>深色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeLight" xml:space="preserve">
|
||||
<value>浅色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeSystem" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
</data>
|
||||
<data name="FilePickerExportCommit" xml:space="preserve">
|
||||
<value>エクスポート</value>
|
||||
</data>
|
||||
@@ -857,6 +869,9 @@
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>イベント祈願・キャラクター</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>奔走世間</value>
|
||||
</data>
|
||||
@@ -2214,7 +2229,7 @@
|
||||
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
<value>自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>一般</value>
|
||||
@@ -2232,7 +2247,7 @@
|
||||
<value>ファイル</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||
<value>インタープロセス</value>
|
||||
<value>进程联动</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>指定したディスプレイで実行</value>
|
||||
@@ -2366,6 +2381,9 @@
|
||||
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
|
||||
<value>背景图片</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
|
||||
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>イメージキャッシュはここに格納されます</value>
|
||||
</data>
|
||||
@@ -2621,6 +2639,12 @@
|
||||
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
|
||||
<value>レビューや感想</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
|
||||
<value>更改窗体的颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
|
||||
<value>颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
|
||||
<value>和訳を提供</value>
|
||||
</data>
|
||||
@@ -2630,6 +2654,12 @@
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>Webview2 ランタイム</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
|
||||
<value>下半</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
|
||||
<value>上半</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>おすすめ聖遺物</value>
|
||||
</data>
|
||||
@@ -2892,13 +2922,13 @@
|
||||
<value>〓イベント期間〓.*?\d\.\d当バージョン期間オープン</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||
<value>(?:〓イベント期間〓|祈願期間|【開始日時】).*?(\d\.\dバージョンアップ完了後).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||
<value>〓更新日時〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>〓メンテナンス時間〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTitle" xml:space="preserve">
|
||||
<value>Ver.\d\.\d 更新内容</value>
|
||||
<value>Ver.\d\.\d.+正式リリース</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementTimeDaysBeginFormat" xml:space="preserve">
|
||||
<value>{0} 日後に開始</value>
|
||||
@@ -3044,6 +3074,9 @@
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>イベント祈願・キャラクター2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>初心者祈願</value>
|
||||
</data>
|
||||
|
||||
@@ -186,6 +186,18 @@
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>WebView2 런타임이 감지되지 않음</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value>[{0}] 热键 [{1}] 注册失败</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>深色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeLight" xml:space="preserve">
|
||||
<value>浅色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeSystem" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
</data>
|
||||
<data name="FilePickerExportCommit" xml:space="preserve">
|
||||
<value>내보내기</value>
|
||||
</data>
|
||||
@@ -857,6 +869,9 @@
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>캐릭터 이벤트</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>세상 여행</value>
|
||||
</data>
|
||||
@@ -2214,7 +2229,7 @@
|
||||
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
<value>自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>보통</value>
|
||||
@@ -2232,7 +2247,7 @@
|
||||
<value>文件</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||
<value>进程间</value>
|
||||
<value>进程联动</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>지정한 모니터에서 실행</value>
|
||||
@@ -2366,6 +2381,9 @@
|
||||
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
|
||||
<value>背景图片</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
|
||||
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>여기에 저장된 이미지 캐시</value>
|
||||
</data>
|
||||
@@ -2621,6 +2639,12 @@
|
||||
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
|
||||
<value>评价软件</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
|
||||
<value>更改窗体的颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
|
||||
<value>颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
|
||||
<value>번역에 기여하기</value>
|
||||
</data>
|
||||
@@ -2630,6 +2654,12 @@
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>Webview2 런타임</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
|
||||
<value>下半</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
|
||||
<value>上半</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>搭配圣遗物</value>
|
||||
</data>
|
||||
@@ -2892,7 +2922,7 @@
|
||||
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||
<value>〓更新时间〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
@@ -3044,6 +3074,9 @@
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>캐릭터 이벤트 기원-2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>초심자 기원</value>
|
||||
</data>
|
||||
|
||||
@@ -186,6 +186,18 @@
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>O WebView2 Runtime não foi detectado</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value>[{0}] 热键 [{1}] 注册失败</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>深色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeLight" xml:space="preserve">
|
||||
<value>浅色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeSystem" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
</data>
|
||||
<data name="FilePickerExportCommit" xml:space="preserve">
|
||||
<value>Exportar</value>
|
||||
</data>
|
||||
@@ -857,6 +869,9 @@
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>Evento de oração de personagem</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>Invocação do Mochileiro</value>
|
||||
</data>
|
||||
@@ -1122,7 +1137,7 @@
|
||||
<value>Sorte média</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardUpText" xml:space="preserve">
|
||||
<value>Acima</value>
|
||||
<value>Em destaque</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsSegmentedItemContentPrediction" xml:space="preserve">
|
||||
<value>Previsão</value>
|
||||
@@ -1140,7 +1155,7 @@
|
||||
<value>Planejamento</value>
|
||||
</data>
|
||||
<data name="ViewDailyNoteHeader" xml:space="preserve">
|
||||
<value>Lembrete em tempo real</value>
|
||||
<value>Lembretes</value>
|
||||
</data>
|
||||
<data name="ViewDataHeader" xml:space="preserve">
|
||||
<value>Dados</value>
|
||||
@@ -2034,7 +2049,7 @@
|
||||
<value>Visão geral</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>全球祈愿统计</value>
|
||||
<value>Estatísticas globais</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>Arma</value>
|
||||
@@ -2214,7 +2229,7 @@
|
||||
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
<value>自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>Geral</value>
|
||||
@@ -2232,7 +2247,7 @@
|
||||
<value>Arquivo</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||
<value>Comunicações entre processos</value>
|
||||
<value>进程联动</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>Executar o software na tela selecionada</value>
|
||||
@@ -2366,6 +2381,9 @@
|
||||
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
|
||||
<value>背景图片</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
|
||||
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>O cache de imagens é salvo aqui</value>
|
||||
</data>
|
||||
@@ -2621,6 +2639,12 @@
|
||||
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
|
||||
<value>Avalie o Snap Hutao</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
|
||||
<value>更改窗体的颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
|
||||
<value>颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
|
||||
<value>Contribuir com traduções</value>
|
||||
</data>
|
||||
@@ -2630,6 +2654,12 @@
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>Webview2 Runtime</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
|
||||
<value>下半</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
|
||||
<value>上半</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>Combinação de conjuntos de artefatos</value>
|
||||
</data>
|
||||
@@ -2892,7 +2922,7 @@
|
||||
<value>〓Duração do evento〓.*?\d\.\d Disponível em toda as versões</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||
<value>(?:〓Duração do evento〓|Duração da oração do evento|【Duração da disponibilidade】).*?(\d\.\dApós a atualização da versão).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||
<value>〓Duração da manutenção da atualização.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
@@ -2961,7 +2991,7 @@
|
||||
<value>Recompensa de comissão diária resgatada</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteHomeCoinRecoveryFormat" xml:space="preserve">
|
||||
<value>Estará cheio em {0} {1:HH:mm}</value>
|
||||
<value>Estará cheio {0} às {1:HH:mm}</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteHomeLocked" xml:space="preserve">
|
||||
<value>Bule de Relachá não desbloqueado</value>
|
||||
@@ -3044,6 +3074,9 @@
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>Evento de oração de personagem-2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>Oração de Novatos</value>
|
||||
</data>
|
||||
|
||||
@@ -186,6 +186,18 @@
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>未检测到 WebView2 运行时</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value>[{0}] 热键 [{1}] 注册失败</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>深色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeLight" xml:space="preserve">
|
||||
<value>浅色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeSystem" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
</data>
|
||||
<data name="FilePickerExportCommit" xml:space="preserve">
|
||||
<value>导出</value>
|
||||
</data>
|
||||
@@ -857,6 +869,9 @@
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>角色活动</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>奔行世间</value>
|
||||
</data>
|
||||
@@ -2214,7 +2229,7 @@
|
||||
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
<value>自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>常规</value>
|
||||
@@ -2232,7 +2247,7 @@
|
||||
<value>文件</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||
<value>进程间</value>
|
||||
<value>进程联动</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>在指定的显示器上运行</value>
|
||||
@@ -2366,6 +2381,9 @@
|
||||
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
|
||||
<value>背景图片</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
|
||||
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>图片缓存 在此处存放</value>
|
||||
</data>
|
||||
@@ -2621,6 +2639,12 @@
|
||||
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
|
||||
<value>评价软件</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
|
||||
<value>更改窗体的颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
|
||||
<value>颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
|
||||
<value>贡献翻译</value>
|
||||
</data>
|
||||
@@ -2630,6 +2654,12 @@
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>Webview2 运行时</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
|
||||
<value>下半</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
|
||||
<value>上半</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>搭配圣遗物</value>
|
||||
</data>
|
||||
@@ -2892,7 +2922,7 @@
|
||||
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||
<value>〓更新时间〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
@@ -3044,6 +3074,9 @@
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>角色活动祈愿-2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>新手祈愿</value>
|
||||
</data>
|
||||
|
||||
@@ -186,6 +186,18 @@
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>Среда выполнения WebView2 не обнаружена</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value>[{0}] 热键 [{1}] 注册失败</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>深色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeLight" xml:space="preserve">
|
||||
<value>浅色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeSystem" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
</data>
|
||||
<data name="FilePickerExportCommit" xml:space="preserve">
|
||||
<value>Экспорт</value>
|
||||
</data>
|
||||
@@ -857,6 +869,9 @@
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>角色活动</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>奔行世间</value>
|
||||
</data>
|
||||
@@ -2214,7 +2229,7 @@
|
||||
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
<value>自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>常规</value>
|
||||
@@ -2232,7 +2247,7 @@
|
||||
<value>文件</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||
<value>进程间</value>
|
||||
<value>进程联动</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>在指定的显示器上运行</value>
|
||||
@@ -2366,6 +2381,9 @@
|
||||
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
|
||||
<value>背景图片</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
|
||||
<value>当前背景为本地图片,如遇版权问题由用户个人负责</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>图片缓存 在此处存放</value>
|
||||
</data>
|
||||
@@ -2621,6 +2639,12 @@
|
||||
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
|
||||
<value>评价软件</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
|
||||
<value>更改窗体的颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
|
||||
<value>颜色主题</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
|
||||
<value>贡献翻译</value>
|
||||
</data>
|
||||
@@ -2630,6 +2654,12 @@
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>Webview2 Runtime</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
|
||||
<value>下半</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
|
||||
<value>上半</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>搭配圣遗物</value>
|
||||
</data>
|
||||
@@ -2892,7 +2922,7 @@
|
||||
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||
<value>〓更新时间〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
@@ -3044,6 +3074,9 @@
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>Молитва события персонажа - 2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
|
||||
<value>集录祈愿</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>Молитва новичка</value>
|
||||
</data>
|
||||
|
||||
@@ -186,6 +186,18 @@
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>未檢測到 WebView2 運行時</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value>[{0}] 鍵盤快速鍵 [{1}] 註冊失敗</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>深色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeLight" xml:space="preserve">
|
||||
<value>淺色</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeSystem" xml:space="preserve">
|
||||
<value>跟隨系統</value>
|
||||
</data>
|
||||
<data name="FilePickerExportCommit" xml:space="preserve">
|
||||
<value>匯出</value>
|
||||
</data>
|
||||
@@ -205,7 +217,7 @@
|
||||
<value><color=#1E90FF>我</color>/<color=#FFB6C1>我</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>旅人</value>
|
||||
<value>旅行者</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>他</color>/<color=#FFB6C1>她</color></value>
|
||||
@@ -513,7 +525,7 @@
|
||||
<value>第四波:擊敗所有怪物,下一波才會出現</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 个</value>
|
||||
<value>維持場上 4 個盜寶團怪物,擊殺後立即替換,總數 12 個</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>請更新角色櫥窗數據</value>
|
||||
@@ -768,19 +780,19 @@
|
||||
<value>角色櫥窗:{0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeBing" xml:space="preserve">
|
||||
<value>必应每日一图</value>
|
||||
<value>Bing每日一圖</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeDaily" xml:space="preserve">
|
||||
<value>胡桃每日一图</value>
|
||||
<value>胡桃每日一圖</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeLauncher" xml:space="preserve">
|
||||
<value>官方启动器壁纸</value>
|
||||
<value>官方啟動器壁紙</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeLocalFolder" xml:space="preserve">
|
||||
<value>本地随机图片</value>
|
||||
<value>本地隨機圖片</value>
|
||||
</data>
|
||||
<data name="ServiceBackgroundImageTypeNone" xml:space="preserve">
|
||||
<value>无背景图片</value>
|
||||
<value>無背景圖片</value>
|
||||
</data>
|
||||
<data name="ServiceCultivationProjectCurrentUserdataCourrpted" xml:space="preserve">
|
||||
<value>保存養成計劃狀態失敗</value>
|
||||
@@ -840,7 +852,7 @@
|
||||
<value>參量質變儀已準備完成</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordActivityElevationRequiredHint" xml:space="preserve">
|
||||
<value>权限不足,将无法为您设置 Discord Activity 状态</value>
|
||||
<value>權限不足,將無法為您設定 Discord Activity 狀態</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordGameActivityDetails" xml:space="preserve">
|
||||
<value>正在提瓦特大陸中探索</value>
|
||||
@@ -857,6 +869,9 @@
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>角色活動</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryChronicledWishName" xml:space="preserve">
|
||||
<value>集錄祈願</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>奔行世間</value>
|
||||
</data>
|
||||
@@ -1224,7 +1239,7 @@
|
||||
<value>即時便箋 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackContent" xml:space="preserve">
|
||||
<value>解除限制后需要使用其他工具恢复限制</value>
|
||||
<value>解除限制後需使用其他工具恢復限制</value>
|
||||
</data>
|
||||
<data name="ViewDialogFeedbackEnableLoopbackTitle" xml:space="preserve">
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
@@ -1410,7 +1425,7 @@
|
||||
<value>啟動遊戲</value>
|
||||
</data>
|
||||
<data name="ViewListViewDragElevatedHint" xml:space="preserve">
|
||||
<value>管理员模式下无法拖动排序</value>
|
||||
<value>管理員模式下無法拖動排序</value>
|
||||
</data>
|
||||
<data name="ViewModelAchievementArchiveAdded" xml:space="preserve">
|
||||
<value>存檔 [{0}] 添加成功</value>
|
||||
@@ -1692,7 +1707,7 @@
|
||||
<value>完成</value>
|
||||
</data>
|
||||
<data name="ViewModelWelcomeDownloadSummaryContentTypeNotMatch" xml:space="preserve">
|
||||
<value>响应内容不是有效的文件字节流</value>
|
||||
<value>回應內容不是有效的檔案位元組</value>
|
||||
</data>
|
||||
<data name="ViewModelWelcomeDownloadSummaryDefault" xml:space="preserve">
|
||||
<value>待處理</value>
|
||||
@@ -1911,16 +1926,16 @@
|
||||
<value>常用連結</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyHeader" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
<value>當前代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackCurrentProxyNoProxyDescription" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
<value>無代理</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackEnabledDescription" xml:space="preserve">
|
||||
<value>已解除</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEnableLoopbackHeader" xml:space="preserve">
|
||||
<value>解除 Loopback 限制</value>
|
||||
<value>是否解除 Loopback 限制</value>
|
||||
</data>
|
||||
<data name="ViewPageFeedbackEngageWithUsDescription" xml:space="preserve">
|
||||
<value>與我們密切聯繫</value>
|
||||
@@ -2211,10 +2226,10 @@
|
||||
<value>啟動參數</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIDescription" xml:space="preserve">
|
||||
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
|
||||
<value>在遊戲啟動後嘗試啟動並使用 Better GI 進行自動化任務</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
<value>自動化任務</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>一般</value>
|
||||
@@ -2232,7 +2247,7 @@
|
||||
<value>檔案</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||
<value>行程間</value>
|
||||
<value>進程聯動</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>在指定的屏幕上運行</value>
|
||||
@@ -2358,13 +2373,16 @@
|
||||
<value>背景材質</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageCopyrightHeader" xml:space="preserve">
|
||||
<value>图片版权信息</value>
|
||||
<value>圖片版權訊息</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageDescription" xml:space="preserve">
|
||||
<value>更改窗体的背景图片来源,重启胡桃以尽快生效</value>
|
||||
<value>更改視窗的背景圖片來源,重啟胡桃以盡快生效</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageHeader" xml:space="preserve">
|
||||
<value>背景图片</value>
|
||||
<value>背景圖片</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackgroundImageLocalFolderCopyrightHeader" xml:space="preserve">
|
||||
<value>當前背景為本地圖片,如遇版權問題由用戶個人負責</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCacheFolderDescription" xml:space="preserve">
|
||||
<value>圖片暫存存放在此</value>
|
||||
@@ -2373,7 +2391,7 @@
|
||||
<value>暫存檔案夾</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
<value>胡桃通行證</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>複製</value>
|
||||
@@ -2568,10 +2586,10 @@
|
||||
<value>前往官網</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
<value>自定義背景圖片,支援 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
<value>打開背景圖片資料夾</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>重設</value>
|
||||
@@ -2583,10 +2601,10 @@
|
||||
<value>重設圖片資源</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
<value>在啟動遊戲頁面的程序部分加入解鎖幀率限制選項</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
<value>啟動遊戲-解鎖幀率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>更改目錄后需要手動移動目錄内的數據,否則會重新創建用戶數據</value>
|
||||
@@ -2621,6 +2639,12 @@
|
||||
<data name="ViewPageSettingStoreReviewNavigate" xml:space="preserve">
|
||||
<value>評價軟體</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeDescription" xml:space="preserve">
|
||||
<value>更改窗體的顏色主題</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingThemeHeader" xml:space="preserve">
|
||||
<value>顏色主題</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingTranslateNavigate" xml:space="preserve">
|
||||
<value>貢獻翻譯</value>
|
||||
</data>
|
||||
@@ -2630,6 +2654,12 @@
|
||||
<data name="ViewPageSettingWebview2Header" xml:space="preserve">
|
||||
<value>WebView 2 運行時</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceDownHeader" xml:space="preserve">
|
||||
<value>下半</value>
|
||||
</data>
|
||||
<data name="ViewPageSpiralAbyssTeamAppearanceUpHeader" xml:space="preserve">
|
||||
<value>上半</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiAvatarArtifactSetCombinationHeader" xml:space="preserve">
|
||||
<value>搭配聖遺物</value>
|
||||
</data>
|
||||
@@ -2892,7 +2922,7 @@
|
||||
<value>〓活動時間〓.*?\d\.\d版本期間持續開放</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||
<value>(?:〓活動時間〓|祈願時間|【上架時間】).*?(\d\.\d版本更新後).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>(?:〓活動時間〓|祈願時間|【上架時間】|〓折扣時間〓).*?(\d\.\d版本更新後).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||
<value>〓更新時間〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
@@ -3044,6 +3074,9 @@
|
||||
<data name="WebGachaConfigTypeAvatarEventWish2" xml:space="preserve">
|
||||
<value>角色活動祈願-2</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeChronicledWish" xml:space="preserve">
|
||||
<value>集錄祈願</value>
|
||||
</data>
|
||||
<data name="WebGachaConfigTypeNoviceWish" xml:space="preserve">
|
||||
<value>新手祈願</value>
|
||||
</data>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 8.0 KiB |
@@ -85,8 +85,13 @@ internal sealed partial class AnnouncementService : IAnnouncementService
|
||||
{
|
||||
foreach (ref readonly Announcement item in CollectionsMarshal.AsSpan(listWrapper.List))
|
||||
{
|
||||
item.Subtitle = new StringBuilder(item.Subtitle).Replace("\r<br>", string.Empty).ToString();
|
||||
item.Content = AnnouncementRegex.XmlTimeTagRegex.Replace(item.Content, x => x.Groups[1].Value);
|
||||
item.Subtitle = new StringBuilder(item.Subtitle)
|
||||
.Replace("\r<br>", string.Empty)
|
||||
.Replace("<br />", string.Empty)
|
||||
.ToString();
|
||||
item.Content = AnnouncementRegex
|
||||
.XmlTimeTagRegex()
|
||||
.Replace(item.Content, x => x.Groups[1].Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +138,7 @@ internal sealed partial class AnnouncementService : IAnnouncementService
|
||||
continue;
|
||||
}
|
||||
|
||||
MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex.Matches(announcement.Content);
|
||||
MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex().Matches(announcement.Content);
|
||||
if (matches.Count < 2)
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
@@ -16,6 +17,7 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
{
|
||||
private bool? isEmptyHistoryWishVisible;
|
||||
private BackdropType? backdropType;
|
||||
private ElementTheme? elementTheme;
|
||||
private BackgroundImageType? backgroundImageType;
|
||||
private Region? region;
|
||||
private string? geetestCustomCompositeUrl;
|
||||
@@ -34,6 +36,19 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, EnumToStringOrEmpty);
|
||||
}
|
||||
|
||||
public Lazy<List<NameValue<ElementTheme>>> LazyElementThemes { get; } = new(() =>
|
||||
[
|
||||
new(SH.CoreWindowThemeLight, ElementTheme.Light),
|
||||
new(SH.CoreWindowThemeDark, ElementTheme.Dark),
|
||||
new(SH.CoreWindowThemeSystem, ElementTheme.Default),
|
||||
]);
|
||||
|
||||
public ElementTheme ElementTheme
|
||||
{
|
||||
get => GetOption(ref elementTheme, SettingEntry.ElementTheme, EnumParse<ElementTheme>, ElementTheme.Default).Value;
|
||||
set => SetOption(ref elementTheme, SettingEntry.ElementTheme, value, EnumToStringOrEmpty);
|
||||
}
|
||||
|
||||
public List<NameValue<BackgroundImageType>> BackgroundImageTypes { get; } = CollectionsNameValue.FromEnum<BackgroundImageType>(type => type.GetLocalizedDescription());
|
||||
|
||||
public BackgroundImageType BackgroundImageType
|
||||
|
||||
@@ -28,16 +28,16 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
|
||||
|
||||
private HashSet<string> currentBackgroundPathSet;
|
||||
|
||||
public async ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync(BackgroundImage? previous)
|
||||
public async ValueTask<ValueResult<bool, BackgroundImage?>> GetNextBackgroundImageAsync(BackgroundImage? previous)
|
||||
{
|
||||
HashSet<string> backgroundSet = await SkipOrInitBackgroundAsync().ConfigureAwait(false);
|
||||
|
||||
if (backgroundSet.Count <= 0)
|
||||
{
|
||||
return new(false, default!);
|
||||
return new(true, default!);
|
||||
}
|
||||
|
||||
string path = System.Random.Shared.GetItems([..backgroundSet], 1)[0];
|
||||
string path = System.Random.Shared.GetItems([.. backgroundSet], 1)[0];
|
||||
backgroundSet.Remove(path);
|
||||
|
||||
if (string.Equals(path, previous?.Path, StringComparison.OrdinalIgnoreCase))
|
||||
@@ -109,6 +109,9 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
|
||||
case BackgroundImageType.HutaoOfficialLauncher:
|
||||
await SetCurrentBackgroundPathSetAsync(client => client.GetLauncherWallpaperAsync()).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
currentBackgroundPathSet = [];
|
||||
break;
|
||||
}
|
||||
|
||||
currentBackgroundPathSet ??= [];
|
||||
|
||||
@@ -5,5 +5,5 @@ namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
internal interface IBackgroundImageService
|
||||
{
|
||||
ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync(BackgroundImage? previous);
|
||||
ValueTask<ValueResult<bool, BackgroundImage?>> GetNextBackgroundImageAsync(BackgroundImage? previous);
|
||||
}
|
||||
@@ -39,7 +39,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
List<InventoryItem> entities = cultivationDbService.GetInventoryItemListByProjectId(projectId);
|
||||
|
||||
List<InventoryItemView> results = [];
|
||||
foreach (Material meta in context.EnumerateInventroyMaterial())
|
||||
foreach (Material meta in context.EnumerateInventoryMaterial())
|
||||
{
|
||||
InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id);
|
||||
results.Add(new(entity, meta, saveCommand));
|
||||
|
||||
@@ -41,39 +41,33 @@ internal sealed partial class DiscordService : IDiscordService, IDisposable
|
||||
|
||||
private bool IsSupported()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Actually requires a discord client to be running on Windows platform.
|
||||
// If not, discord core creation code will throw.
|
||||
Process[] discordProcesses = Process.GetProcessesByName("Discord");
|
||||
// Actually requires a discord client to be running on Windows platform.
|
||||
// If not, discord core creation code will throw.
|
||||
Process[] discordProcesses = Process.GetProcessesByName("Discord");
|
||||
|
||||
if (discordProcesses.Length <= 0)
|
||||
if (discordProcesses.Length <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (Process process in discordProcesses)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = process.Handle;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
isInitialized = true;
|
||||
infoBarService.Warning(SH.ServiceDiscordActivityElevationRequiredHint);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (Process process in discordProcesses)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = process.Handle;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
infoBarService.Warning(SH.ServiceDiscordActivityElevationRequiredHint);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using System.Collections.Frozen;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿配置类型比较器
|
||||
/// </summary>
|
||||
internal sealed class GachaConfigTypeComparer : IComparer<GachaConfigType>
|
||||
{
|
||||
private static readonly Lazy<GachaConfigTypeComparer> LazyShared = new(() => new());
|
||||
private static readonly FrozenDictionary<GachaConfigType, int> OrderMap = new Dictionary<GachaConfigType, int>()
|
||||
{
|
||||
[GachaConfigType.AvatarEventWish] = 0,
|
||||
[GachaConfigType.AvatarEventWish2] = 1,
|
||||
[GachaConfigType.WeaponEventWish] = 2,
|
||||
[GachaConfigType.StandardWish] = 3,
|
||||
[GachaConfigType.NoviceWish] = 4,
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// 共享的比较器
|
||||
/// </summary>
|
||||
public static GachaConfigTypeComparer Shared { get => LazyShared.Value; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Compare(GachaConfigType x, GachaConfigType y)
|
||||
{
|
||||
return OrderOf(x) - OrderOf(y);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int OrderOf(GachaConfigType type)
|
||||
{
|
||||
return OrderMap.GetValueOrDefault(type, 0);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Windows.UI;
|
||||
@@ -25,7 +26,7 @@ internal static class GachaStatisticsExtension
|
||||
bool isPreviousUp = true;
|
||||
|
||||
// mark the IsGuarantee
|
||||
foreach (SummaryItem item in summaryItems)
|
||||
foreach (ref readonly SummaryItem item in CollectionsMarshal.AsSpan(summaryItems))
|
||||
{
|
||||
if (item.IsUp && (!isPreviousUp))
|
||||
{
|
||||
@@ -62,4 +63,4 @@ internal static class GachaStatisticsExtension
|
||||
ReadOnlySpan<byte> codes = MD5.HashData(Encoding.UTF8.GetBytes(name));
|
||||
return Color.FromArgb(255, codes.Slice(0, 5).Average(), codes.Slice(5, 5).Average(), codes.Slice(10, 5).Average());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using Snap.Hutao.Web.Hutao.GachaLog;
|
||||
@@ -31,9 +31,8 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
public async ValueTask<GachaStatistics> CreateAsync(List<Model.Entity.GachaItem> items, GachaLogServiceMetadataContext context)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
List<GachaEvent> gachaEvents = await metadataService.GetGachaEventListAsync().ConfigureAwait(false);
|
||||
List<HistoryWishBuilder> historyWishBuilders = gachaEvents.SelectList(gachaEvent => new HistoryWishBuilder(gachaEvent, context));
|
||||
|
||||
List<HistoryWishBuilder> historyWishBuilders = context.GachaEvents.SelectList(gachaEvent => new HistoryWishBuilder(gachaEvent, context));
|
||||
return CreateCore(taskContext, homaGachaLogClient, items, historyWishBuilders, context, options.IsEmptyHistoryWishVisible);
|
||||
}
|
||||
|
||||
@@ -54,6 +53,9 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
TypedWishSummaryBuilderContext weaponContext = TypedWishSummaryBuilderContext.WeaponEventWish(taskContext, gachaLogClient);
|
||||
TypedWishSummaryBuilder weaponWishBuilder = new(weaponContext);
|
||||
|
||||
TypedWishSummaryBuilderContext chronicledContext = TypedWishSummaryBuilderContext.ChronicledWish(taskContext, gachaLogClient);
|
||||
TypedWishSummaryBuilder chronicledWishBuilder = new(chronicledContext);
|
||||
|
||||
Dictionary<Avatar, int> orangeAvatarCounter = [];
|
||||
Dictionary<Avatar, int> purpleAvatarCounter = [];
|
||||
Dictionary<Weapon, int> orangeWeaponCounter = [];
|
||||
@@ -61,24 +63,25 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
Dictionary<Weapon, int> blueWeaponCounter = [];
|
||||
|
||||
// Pre group builders
|
||||
Dictionary<GachaConfigType, List<HistoryWishBuilder>> historyWishBuilderMap = historyWishBuilders
|
||||
Dictionary<GachaType, List<HistoryWishBuilder>> historyWishBuilderMap = historyWishBuilders
|
||||
.GroupBy(b => b.ConfigType)
|
||||
.ToDictionary(g => g.Key, g => g.ToList().SortBy(b => b.From));
|
||||
|
||||
// Items are ordered by precise time, first is oldest
|
||||
// 'ref' is not allowed here because we have lambda below
|
||||
foreach (Model.Entity.GachaItem item in CollectionsMarshal.AsSpan(items))
|
||||
foreach (ref readonly Model.Entity.GachaItem item in CollectionsMarshal.AsSpan(items))
|
||||
{
|
||||
// Find target history wish to operate. // w.From <= item.Time <= w.To
|
||||
HistoryWishBuilder? targetHistoryWishBuilder = item.GachaType is not (GachaConfigType.StandardWish or GachaConfigType.NoviceWish)
|
||||
? historyWishBuilderMap[item.GachaType].BinarySearch(w => item.Time < w.From ? -1 : item.Time > w.To ? 1 : 0)
|
||||
// Find target history wish to operate. // banner.From <= item.Time <= banner.To
|
||||
Model.Entity.GachaItem pinned = item;
|
||||
HistoryWishBuilder? targetHistoryWishBuilder = item.GachaType is not (GachaType.Standard or GachaType.NewBie)
|
||||
? historyWishBuilderMap[item.GachaType].BinarySearch(banner => pinned.Time < banner.From ? -1 : pinned.Time > banner.To ? 1 : 0)
|
||||
: default;
|
||||
|
||||
switch (item.ItemId.StringLength())
|
||||
{
|
||||
case 8U:
|
||||
{
|
||||
Avatar avatar = context.IdAvatarMap[item.ItemId];
|
||||
Avatar avatar = context.GetAvatar(item.ItemId);
|
||||
|
||||
bool isUp = false;
|
||||
switch (avatar.Quality)
|
||||
@@ -98,6 +101,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
standardWishBuilder.Track(item, avatar, isUp);
|
||||
avatarWishBuilder.Track(item, avatar, isUp);
|
||||
weaponWishBuilder.Track(item, avatar, isUp);
|
||||
chronicledWishBuilder.Track(item, avatar, isUp);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -127,17 +131,18 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
standardWishBuilder.Track(item, weapon, isUp);
|
||||
avatarWishBuilder.Track(item, weapon, isUp);
|
||||
weaponWishBuilder.Track(item, weapon, isUp);
|
||||
chronicledWishBuilder.Track(item, weapon, isUp);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// ItemId string length not correct.
|
||||
ThrowHelper.UserdataCorrupted(SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(item.ItemId), default!);
|
||||
HutaoException.GachaStatisticsInvalidItemId(item.ItemId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncBarrier barrier = new(3);
|
||||
AsyncBarrier barrier = new(4);
|
||||
|
||||
return new()
|
||||
{
|
||||
@@ -145,7 +150,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
HistoryWishes = historyWishBuilders
|
||||
.Where(b => isEmptyHistoryWishVisible || (!b.IsEmpty))
|
||||
.OrderByDescending(builder => builder.From)
|
||||
.ThenBy(builder => builder.ConfigType, GachaConfigTypeComparer.Shared)
|
||||
.ThenBy(builder => builder.ConfigType, GachaTypeComparer.Shared)
|
||||
.Select(builder => builder.ToHistoryWish())
|
||||
.ToList(),
|
||||
|
||||
@@ -162,6 +167,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
StandardWish = standardWishBuilder.ToTypedWishSummary(barrier),
|
||||
AvatarWish = avatarWishBuilder.ToTypedWishSummary(barrier),
|
||||
WeaponWish = weaponWishBuilder.ToTypedWishSummary(barrier),
|
||||
ChronicledWish = chronicledWishBuilder.ToTypedWishSummary(barrier),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -62,22 +62,29 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
|
||||
int weaponPurpleTracker = 0;
|
||||
TypedWishSummarySlim weaponWish = new(SH.ServiceGachaLogFactoryWeaponWishName, 80, 10);
|
||||
|
||||
int chronicledOrangeTracker = 0;
|
||||
int chronicledPurpleTracker = 0;
|
||||
TypedWishSummarySlim chronicledWish = new(SH.ServiceGachaLogFactoryChronicledWishName, 90, 10);
|
||||
|
||||
// O(n) operation
|
||||
foreach (ref readonly GachaItem item in CollectionsMarshal.AsSpan(items))
|
||||
{
|
||||
INameQuality nameQuality = context.GetNameQualityByItemId(item.ItemId);
|
||||
switch (item.QueryType)
|
||||
{
|
||||
case GachaConfigType.StandardWish:
|
||||
case GachaType.Standard:
|
||||
Track(nameQuality, ref standardOrangeTracker, ref standardPurpleTracker);
|
||||
break;
|
||||
case GachaConfigType.AvatarEventWish:
|
||||
case GachaConfigType.AvatarEventWish2:
|
||||
case GachaType.ActivityAvatar:
|
||||
case GachaType.SpecialActivityAvatar:
|
||||
Track(nameQuality, ref avatarOrangeTracker, ref avatarPurpleTracker);
|
||||
break;
|
||||
case GachaConfigType.WeaponEventWish:
|
||||
case GachaType.ActivityWeapon:
|
||||
Track(nameQuality, ref weaponOrangeTracker, ref weaponPurpleTracker);
|
||||
break;
|
||||
case GachaType.ActivityCity:
|
||||
Track(nameQuality, ref chronicledOrangeTracker, ref chronicledPurpleTracker);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -85,11 +92,16 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF
|
||||
|
||||
standardWish.LastOrangePull = standardOrangeTracker;
|
||||
standardWish.LastPurplePull = standardPurpleTracker;
|
||||
|
||||
avatarWish.LastOrangePull = avatarOrangeTracker;
|
||||
avatarWish.LastPurplePull = avatarPurpleTracker;
|
||||
|
||||
weaponWish.LastOrangePull = weaponOrangeTracker;
|
||||
weaponWish.LastPurplePull = weaponPurpleTracker;
|
||||
|
||||
chronicledWish.LastOrangePull = chronicledOrangeTracker;
|
||||
chronicledWish.LastPurplePull = chronicledPurpleTracker;
|
||||
|
||||
return new()
|
||||
{
|
||||
Uid = uid,
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using System.Collections.Frozen;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿配置类型比较器
|
||||
/// </summary>
|
||||
internal sealed class GachaTypeComparer : IComparer<GachaType>
|
||||
{
|
||||
private static readonly Lazy<GachaTypeComparer> LazyShared = new(() => new());
|
||||
private static readonly FrozenDictionary<GachaType, int> OrderMap = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(GachaType.ActivityAvatar, 0),
|
||||
KeyValuePair.Create(GachaType.SpecialActivityAvatar, 1),
|
||||
KeyValuePair.Create(GachaType.ActivityWeapon, 2),
|
||||
KeyValuePair.Create(GachaType.ActivityCity, 3),
|
||||
KeyValuePair.Create(GachaType.Standard, 4),
|
||||
KeyValuePair.Create(GachaType.NewBie, 5),
|
||||
]);
|
||||
|
||||
/// <summary>
|
||||
/// 共享的比较器
|
||||
/// </summary>
|
||||
public static GachaTypeComparer Shared { get => LazyShared.Value; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Compare(GachaType x, GachaType y)
|
||||
{
|
||||
return OrderOf(x) - OrderOf(y);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int OrderOf(GachaType type)
|
||||
{
|
||||
return OrderMap.GetValueOrDefault(type, 0);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ internal sealed class HistoryWishBuilder
|
||||
/// </summary>
|
||||
/// <param name="gachaEvent">卡池配置</param>
|
||||
/// <param name="context">祈愿记录上下文</param>
|
||||
[SuppressMessage("", "SH002")]
|
||||
public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceMetadataContext context)
|
||||
{
|
||||
this.gachaEvent = gachaEvent;
|
||||
@@ -37,21 +36,27 @@ internal sealed class HistoryWishBuilder
|
||||
|
||||
switch (ConfigType)
|
||||
{
|
||||
case GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2:
|
||||
case GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar:
|
||||
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0);
|
||||
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0);
|
||||
break;
|
||||
case GachaConfigType.WeaponEventWish:
|
||||
case GachaType.ActivityWeapon:
|
||||
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0);
|
||||
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0);
|
||||
break;
|
||||
case GachaType.ActivityCity:
|
||||
|
||||
// Avatars are less than weapons, so we try to get the value from avatar map first
|
||||
orangeUpCounter = gachaEvent.UpOrangeList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0);
|
||||
purpleUpCounter = gachaEvent.UpPurpleList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿配置类型
|
||||
/// </summary>
|
||||
public GachaConfigType ConfigType { get; }
|
||||
public GachaType ConfigType { get; }
|
||||
|
||||
/// <inheritdoc cref="GachaEvent.From"/>
|
||||
public DateTimeOffset From { get => gachaEvent.From; }
|
||||
@@ -106,13 +111,13 @@ internal sealed class HistoryWishBuilder
|
||||
{
|
||||
HistoryWish historyWish = new()
|
||||
{
|
||||
// base
|
||||
// Base
|
||||
Name = gachaEvent.Name,
|
||||
From = gachaEvent.From,
|
||||
To = gachaEvent.To,
|
||||
TotalCount = totalCountTracker,
|
||||
|
||||
// fill
|
||||
// Fill
|
||||
Version = gachaEvent.Version,
|
||||
BannerImage = gachaEvent.Banner,
|
||||
OrangeUpList = orangeUpCounter.ToStatisticsList(),
|
||||
|
||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using Snap.Hutao.Web.Hutao.GachaLog;
|
||||
@@ -18,18 +19,20 @@ internal sealed class HutaoStatisticsFactory
|
||||
private readonly GachaEvent avatarEvent;
|
||||
private readonly GachaEvent avatarEvent2;
|
||||
private readonly GachaEvent weaponEvent;
|
||||
private readonly GachaEvent chronicledEvent;
|
||||
|
||||
public HutaoStatisticsFactory(in HutaoStatisticsFactoryMetadataContext context)
|
||||
{
|
||||
this.context = context;
|
||||
|
||||
// TODO: when in new verion
|
||||
// when in new verion
|
||||
// due to lack of newer metadata
|
||||
// this can crash
|
||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||
avatarEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.AvatarEventWish);
|
||||
avatarEvent2 = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.AvatarEventWish2);
|
||||
weaponEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaConfigType.WeaponEventWish);
|
||||
avatarEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityAvatar);
|
||||
avatarEvent2 = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.SpecialActivityAvatar);
|
||||
weaponEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityWeapon);
|
||||
chronicledEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityCity);
|
||||
}
|
||||
|
||||
public HutaoStatistics Create(GachaEventStatistics raw)
|
||||
@@ -38,7 +41,8 @@ internal sealed class HutaoStatisticsFactory
|
||||
{
|
||||
AvatarEvent = CreateWishSummary(avatarEvent, raw.AvatarEvent),
|
||||
AvatarEvent2 = CreateWishSummary(avatarEvent2, raw.AvatarEvent2),
|
||||
WeaponWish = CreateWishSummary(weaponEvent, raw.WeaponEvent),
|
||||
WeaponEvent = CreateWishSummary(weaponEvent, raw.WeaponEvent),
|
||||
Chronicled = CreateWishSummary(chronicledEvent, raw.Chronicled),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,12 +57,13 @@ internal sealed class HutaoStatisticsFactory
|
||||
{
|
||||
IStatisticsItemSource source = item.Item.StringLength() switch
|
||||
{
|
||||
8U => context.IdAvatarMap[item.Item],
|
||||
5U => context.IdWeaponMap[item.Item],
|
||||
_ => throw ThrowHelper.UserdataCorrupted(SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(item.Item), default!),
|
||||
8U => context.GetAvatar(item.Item),
|
||||
5U => context.GetWeapon(item.Item),
|
||||
_ => throw HutaoException.GachaStatisticsInvalidItemId(item.Item),
|
||||
};
|
||||
StatisticsItem statisticsItem = source.ToStatisticsItem(unchecked((int)item.Count));
|
||||
|
||||
// Put UP items to a separate list
|
||||
if (gachaEvent.UpOrangeList.Contains(item.Item) || gachaEvent.UpPurpleList.Contains(item.Item))
|
||||
{
|
||||
upItems.Add(statisticsItem);
|
||||
|
||||
@@ -5,19 +5,18 @@ using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
|
||||
internal readonly struct HutaoStatisticsFactoryMetadataContext
|
||||
internal sealed class HutaoStatisticsFactoryMetadataContext : IMetadataContext,
|
||||
IMetadataDictionaryIdAvatarSource,
|
||||
IMetadataDictionaryIdWeaponSource,
|
||||
IMetadataListGachaEventSource
|
||||
{
|
||||
public readonly Dictionary<AvatarId, Avatar> IdAvatarMap;
|
||||
public readonly Dictionary<WeaponId, Weapon> IdWeaponMap;
|
||||
public readonly List<GachaEvent> GachaEvents;
|
||||
public Dictionary<AvatarId, Avatar> IdAvatarMap { get; set; } = default!;
|
||||
|
||||
public HutaoStatisticsFactoryMetadataContext(Dictionary<AvatarId, Avatar> idAvatarMap, Dictionary<WeaponId, Weapon> idWeaponMap, List<GachaEvent> gachaEvents)
|
||||
{
|
||||
IdAvatarMap = idAvatarMap;
|
||||
IdWeaponMap = idWeaponMap;
|
||||
GachaEvents = gachaEvents;
|
||||
}
|
||||
public Dictionary<WeaponId, Weapon> IdWeaponMap { get; set; } = default!;
|
||||
|
||||
public List<GachaEvent> GachaEvents { get; set; } = default!;
|
||||
}
|
||||
@@ -38,6 +38,10 @@ internal sealed class PullPrediction
|
||||
typedWishSummary.PredictedPullLeftToOrange = result.PredictedPullLeftToOrange;
|
||||
typedWishSummary.IsPredictPullAvailable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
await barrier.SignalAndWaitAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static PredictResult PredictCore(List<PullCount> distribution, TypedWishSummary typedWishSummary)
|
||||
|
||||
@@ -5,7 +5,6 @@ using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
|
||||
@@ -15,21 +14,6 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
[HighQuality]
|
||||
internal sealed class TypedWishSummaryBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// 常驻祈愿
|
||||
/// </summary>
|
||||
public static readonly Func<GachaConfigType, bool> IsStandardWish = type => type is GachaConfigType.StandardWish;
|
||||
|
||||
/// <summary>
|
||||
/// 角色活动
|
||||
/// </summary>
|
||||
public static readonly Func<GachaConfigType, bool> IsAvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
|
||||
|
||||
/// <summary>
|
||||
/// 武器活动
|
||||
/// </summary>
|
||||
public static readonly Func<GachaConfigType, bool> IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
|
||||
|
||||
private readonly TypedWishSummaryBuilderContext context;
|
||||
|
||||
private readonly List<int> averageOrangePullTracker = [];
|
||||
@@ -62,52 +46,54 @@ internal sealed class TypedWishSummaryBuilder
|
||||
/// <param name="isUp">是否为Up物品</param>
|
||||
public void Track(GachaItem item, ISummaryItemSource source, bool isUp)
|
||||
{
|
||||
if (context.TypeEvaluator(item.GachaType))
|
||||
if (!context.TypeEvaluator(item.GachaType))
|
||||
{
|
||||
++lastOrangePullTracker;
|
||||
++lastPurplePullTracker;
|
||||
++lastUpOrangePullTracker;
|
||||
return;
|
||||
}
|
||||
|
||||
// track total pulls
|
||||
++totalCountTracker;
|
||||
TrackFromToTime(item.Time);
|
||||
++lastOrangePullTracker;
|
||||
++lastPurplePullTracker;
|
||||
++lastUpOrangePullTracker;
|
||||
|
||||
switch (source.Quality)
|
||||
{
|
||||
case QualityType.QUALITY_ORANGE:
|
||||
// track total pulls
|
||||
++totalCountTracker;
|
||||
TrackFromToTime(item.Time);
|
||||
|
||||
switch (source.Quality)
|
||||
{
|
||||
case QualityType.QUALITY_ORANGE:
|
||||
{
|
||||
TrackMinMaxOrangePull(lastOrangePullTracker);
|
||||
averageOrangePullTracker.Add(lastOrangePullTracker);
|
||||
|
||||
if (isUp)
|
||||
{
|
||||
TrackMinMaxOrangePull(lastOrangePullTracker);
|
||||
averageOrangePullTracker.Add(lastOrangePullTracker);
|
||||
|
||||
if (isUp)
|
||||
{
|
||||
averageUpOrangePullTracker.Add(lastUpOrangePullTracker);
|
||||
lastUpOrangePullTracker = 0;
|
||||
}
|
||||
|
||||
summaryItems.Add(source.ToSummaryItem(lastOrangePullTracker, item.Time, isUp));
|
||||
|
||||
lastOrangePullTracker = 0;
|
||||
++totalOrangePullTracker;
|
||||
break;
|
||||
averageUpOrangePullTracker.Add(lastUpOrangePullTracker);
|
||||
lastUpOrangePullTracker = 0;
|
||||
}
|
||||
|
||||
case QualityType.QUALITY_PURPLE:
|
||||
{
|
||||
lastPurplePullTracker = 0;
|
||||
++totalPurplePullTracker;
|
||||
break;
|
||||
}
|
||||
summaryItems.Add(source.ToSummaryItem(lastOrangePullTracker, item.Time, isUp));
|
||||
|
||||
case QualityType.QUALITY_BLUE:
|
||||
{
|
||||
++totalBluePullTracker;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
lastOrangePullTracker = 0;
|
||||
++totalOrangePullTracker;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case QualityType.QUALITY_PURPLE:
|
||||
{
|
||||
lastPurplePullTracker = 0;
|
||||
++totalPurplePullTracker;
|
||||
break;
|
||||
}
|
||||
|
||||
case QualityType.QUALITY_BLUE:
|
||||
{
|
||||
++totalBluePullTracker;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ internal readonly struct TypedWishSummaryBuilderContext
|
||||
public readonly string Name;
|
||||
public readonly int GuaranteeOrangeThreshold;
|
||||
public readonly int GuaranteePurpleThreshold;
|
||||
public readonly Func<GachaConfigType, bool> TypeEvaluator;
|
||||
public readonly Func<GachaType, bool> TypeEvaluator;
|
||||
public readonly GachaDistributionType DistributionType;
|
||||
|
||||
private static readonly Func<GachaConfigType, bool> IsStandardWish = type => type is GachaConfigType.StandardWish;
|
||||
private static readonly Func<GachaConfigType, bool> IsAvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
|
||||
private static readonly Func<GachaConfigType, bool> IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
|
||||
private static readonly Func<GachaType, bool> IsStandardWish = type => type is GachaType.Standard;
|
||||
private static readonly Func<GachaType, bool> IsAvatarEventWish = type => type is GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar;
|
||||
private static readonly Func<GachaType, bool> IsWeaponEventWish = type => type is GachaType.ActivityWeapon;
|
||||
private static readonly Func<GachaType, bool> IsChronicledWish = type => type is GachaType.ActivityCity;
|
||||
|
||||
public TypedWishSummaryBuilderContext(
|
||||
ITaskContext taskContext,
|
||||
@@ -27,7 +28,7 @@ internal readonly struct TypedWishSummaryBuilderContext
|
||||
string name,
|
||||
int guaranteeOrangeThreshold,
|
||||
int guaranteePurpleThreshold,
|
||||
Func<GachaConfigType, bool> typeEvaluator,
|
||||
Func<GachaType, bool> typeEvaluator,
|
||||
GachaDistributionType distributionType)
|
||||
{
|
||||
TaskContext = taskContext;
|
||||
@@ -54,6 +55,11 @@ internal readonly struct TypedWishSummaryBuilderContext
|
||||
return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryWeaponWishName, 80, 10, IsWeaponEventWish, GachaDistributionType.WeaponEvent);
|
||||
}
|
||||
|
||||
public static TypedWishSummaryBuilderContext ChronicledWish(ITaskContext taskContext, HomaGachaLogClient gachaLogClient)
|
||||
{
|
||||
return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryChronicledWishName, 90, 10, IsChronicledWish, GachaDistributionType.Chronicled);
|
||||
}
|
||||
|
||||
public ValueTask<HutaoResponse<GachaDistribution>> GetGachaDistributionAsync()
|
||||
{
|
||||
return GachaLogClient.GetGachaDistributionAsync(DistributionType);
|
||||
|
||||
@@ -15,12 +15,14 @@ internal static class GachaArchiveOperation
|
||||
{
|
||||
archive = archives.SingleOrDefault(a => a.Uid == uid);
|
||||
|
||||
if (archive is null)
|
||||
if (archive is not null)
|
||||
{
|
||||
GachaArchive created = GachaArchive.From(uid);
|
||||
gachaLogDbService.AddGachaArchive(created);
|
||||
taskContext.InvokeOnMainThread(() => archives.Add(created));
|
||||
archive = created;
|
||||
return;
|
||||
}
|
||||
|
||||
GachaArchive created = GachaArchive.From(uid);
|
||||
gachaLogDbService.AddGachaArchive(created);
|
||||
taskContext.InvokeOnMainThread(() => archives.Add(created));
|
||||
archive = created;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿物品保存上下文
|
||||
/// </summary>
|
||||
internal readonly struct GachaItemSaveContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 待添加物品
|
||||
/// </summary>
|
||||
public readonly List<GachaItem> ItemsToAdd;
|
||||
|
||||
/// <summary>
|
||||
/// 是否懒惰
|
||||
/// </summary>
|
||||
public readonly bool IsLazy;
|
||||
|
||||
public readonly GachaConfigType QueryType;
|
||||
|
||||
/// <summary>
|
||||
/// 结尾 Id
|
||||
/// </summary>
|
||||
public readonly long EndId;
|
||||
|
||||
/// <summary>
|
||||
/// 数据集
|
||||
/// </summary>
|
||||
public readonly IGachaLogDbService GachaLogDbService;
|
||||
|
||||
public GachaItemSaveContext(List<GachaItem> itemsToAdd, bool isLazy, GachaConfigType queryType, long endId, IGachaLogDbService gachaLogDbService)
|
||||
{
|
||||
ItemsToAdd = itemsToAdd;
|
||||
IsLazy = isLazy;
|
||||
QueryType = queryType;
|
||||
EndId = endId;
|
||||
GachaLogDbService = gachaLogDbService;
|
||||
}
|
||||
|
||||
public void SaveItems(GachaArchive archive)
|
||||
{
|
||||
if (ItemsToAdd.Count > 0)
|
||||
{
|
||||
// 全量刷新
|
||||
if (!IsLazy)
|
||||
{
|
||||
GachaLogDbService.RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(archive.InnerId, QueryType, EndId);
|
||||
}
|
||||
|
||||
GachaLogDbService.AddGachaItemRange(ItemsToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,12 @@ internal static class GachaLog
|
||||
/// <summary>
|
||||
/// 查询类型
|
||||
/// </summary>
|
||||
public static readonly FrozenSet<GachaConfigType> QueryTypes = FrozenSet.ToFrozenSet(
|
||||
public static readonly FrozenSet<GachaType> QueryTypes = FrozenSet.ToFrozenSet(
|
||||
[
|
||||
GachaConfigType.NoviceWish,
|
||||
GachaConfigType.StandardWish,
|
||||
GachaConfigType.AvatarEventWish,
|
||||
GachaConfigType.WeaponEventWish,
|
||||
GachaType.NewBie,
|
||||
GachaType.Standard,
|
||||
GachaType.ActivityAvatar,
|
||||
GachaType.ActivityWeapon,
|
||||
GachaType.ActivityCity,
|
||||
]);
|
||||
}
|
||||
@@ -73,7 +73,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token)
|
||||
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType, CancellationToken token)
|
||||
{
|
||||
GachaItem? item = null;
|
||||
|
||||
@@ -103,7 +103,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
return item?.Id ?? 0L;
|
||||
}
|
||||
|
||||
public long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType)
|
||||
public long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaType queryType)
|
||||
{
|
||||
GachaItem? item = null;
|
||||
|
||||
@@ -132,7 +132,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
return item?.Id ?? 0L;
|
||||
}
|
||||
|
||||
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType)
|
||||
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType)
|
||||
{
|
||||
GachaItem? item = null;
|
||||
|
||||
@@ -205,7 +205,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
return item?.Id ?? long.MaxValue;
|
||||
}
|
||||
|
||||
public long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType)
|
||||
public long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaType queryType)
|
||||
{
|
||||
GachaItem? item = null;
|
||||
|
||||
@@ -226,7 +226,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
return item?.Id ?? long.MaxValue;
|
||||
}
|
||||
|
||||
public async ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token)
|
||||
public async ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType, CancellationToken token)
|
||||
{
|
||||
GachaItem? item = null;
|
||||
|
||||
@@ -266,7 +266,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
}
|
||||
}
|
||||
|
||||
public List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaConfigType queryType, long endId)
|
||||
public List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaType queryType, long endId)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
@@ -291,7 +291,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaConfigType queryType, long endId)
|
||||
public async ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaType queryType, long endId)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
@@ -368,7 +368,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId)
|
||||
public void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaType queryType, long endId)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
@@ -381,7 +381,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaConfigType queryType, long endId)
|
||||
public async ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaType queryType, long endId)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
|
||||
@@ -46,14 +46,14 @@ internal struct GachaLogFetchContext
|
||||
/// <summary>
|
||||
/// 当前类型
|
||||
/// </summary>
|
||||
public GachaConfigType CurrentType;
|
||||
public GachaType CurrentType;
|
||||
|
||||
private readonly GachaLogServiceMetadataContext serviceContext;
|
||||
private readonly IGachaLogDbService gachaLogDbService;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly bool isLazy;
|
||||
|
||||
public GachaLogFetchContext(IGachaLogDbService gachaLogDbService, ITaskContext taskContext, in GachaLogServiceMetadataContext serviceContext, bool isLazy)
|
||||
public GachaLogFetchContext(IGachaLogDbService gachaLogDbService, ITaskContext taskContext, GachaLogServiceMetadataContext serviceContext, bool isLazy)
|
||||
{
|
||||
this.gachaLogDbService = gachaLogDbService;
|
||||
this.taskContext = taskContext;
|
||||
@@ -66,7 +66,7 @@ internal struct GachaLogFetchContext
|
||||
/// </summary>
|
||||
/// <param name="configType">卡池类型</param>
|
||||
/// <param name="query">查询</param>
|
||||
public void ResetForProcessingType(GachaConfigType configType, in GachaLogQuery query)
|
||||
public void ResetForProcessingType(GachaType configType, in GachaLogQuery query)
|
||||
{
|
||||
DbEndId = null;
|
||||
CurrentType = configType;
|
||||
@@ -140,8 +140,18 @@ internal struct GachaLogFetchContext
|
||||
// While no item is fetched, archive can be null.
|
||||
if (TargetArchive is not null)
|
||||
{
|
||||
GachaItemSaveContext saveContext = new(ItemsToAdd, isLazy, QueryOptions.Type, QueryOptions.EndId, gachaLogDbService);
|
||||
saveContext.SaveItems(TargetArchive);
|
||||
if (ItemsToAdd.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 全量刷新
|
||||
if (!isLazy)
|
||||
{
|
||||
gachaLogDbService.RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(TargetArchive.InnerId, QueryOptions.Type, QueryOptions.EndId);
|
||||
}
|
||||
|
||||
gachaLogDbService.AddGachaItemRange(ItemsToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,33 +6,17 @@ using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿记录获取状态
|
||||
/// </summary>
|
||||
internal sealed class GachaLogFetchStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的祈愿记录获取状态
|
||||
/// </summary>
|
||||
/// <param name="configType">卡池类型</param>
|
||||
public GachaLogFetchStatus(GachaConfigType configType)
|
||||
public GachaLogFetchStatus(GachaType configType)
|
||||
{
|
||||
ConfigType = configType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证密钥是否过期
|
||||
/// </summary>
|
||||
public bool AuthKeyTimeout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 卡池类型
|
||||
/// </summary>
|
||||
public GachaConfigType ConfigType { get; set; }
|
||||
public GachaType ConfigType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前获取的物品
|
||||
/// </summary>
|
||||
public List<Item> Items { get; set; } = new(20);
|
||||
|
||||
public string Header
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.GachaLog.Factory;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using Snap.Hutao.Web.Hutao.GachaLog;
|
||||
@@ -40,7 +37,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
|
||||
if (await GetEndIdsFromCloudAsync(uid, token).ConfigureAwait(false) is { } endIds)
|
||||
{
|
||||
List<Web.Hutao.GachaLog.GachaItem> items = [];
|
||||
foreach ((GachaConfigType type, long endId) in endIds)
|
||||
foreach ((GachaType type, long endId) in endIds)
|
||||
{
|
||||
List<Web.Hutao.GachaLog.GachaItem> part = await gachaLogDbService
|
||||
.GetHutaoGachaItemListAsync(gachaArchive.InnerId, type, endId)
|
||||
@@ -55,14 +52,16 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ValueResult<bool, Guid>> RetrieveGachaItemsAsync(string uid, CancellationToken token = default)
|
||||
public async ValueTask<ValueResult<bool, Guid>> RetrieveGachaArchiveIdAsync(string uid, CancellationToken token = default)
|
||||
{
|
||||
GachaArchive? archive = await gachaLogDbService
|
||||
.GetGachaArchiveByUidAsync(uid, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
EndIds endIds = await CreateEndIdsAsync(archive, token).ConfigureAwait(false);
|
||||
Response<List<Web.Hutao.GachaLog.GachaItem>> resp = await homaGachaLogClient.RetrieveGachaItemsAsync(uid, endIds, token).ConfigureAwait(false);
|
||||
Response<List<Web.Hutao.GachaLog.GachaItem>> resp = await homaGachaLogClient
|
||||
.RetrieveGachaItemsAsync(uid, endIds, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!resp.IsOk())
|
||||
{
|
||||
@@ -75,7 +74,8 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
|
||||
await gachaLogDbService.AddGachaArchiveAsync(archive).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
List<Model.Entity.GachaItem> gachaItems = resp.Data.SelectList(i => Model.Entity.GachaItem.From(archive.InnerId, i));
|
||||
Guid archiveId = archive.InnerId;
|
||||
List<Model.Entity.GachaItem> gachaItems = resp.Data.SelectList(i => Model.Entity.GachaItem.From(archiveId, i));
|
||||
await gachaLogDbService.AddGachaItemsAsync(gachaItems).ConfigureAwait(false);
|
||||
return new(true, archive.InnerId);
|
||||
}
|
||||
@@ -94,10 +94,9 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
|
||||
{
|
||||
if (await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<WeaponId, Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
List<GachaEvent> gachaEvents = await metadataService.GetGachaEventListAsync(token).ConfigureAwait(false);
|
||||
HutaoStatisticsFactoryMetadataContext context = new(idAvatarMap, idWeaponMap, gachaEvents);
|
||||
HutaoStatisticsFactoryMetadataContext context = await metadataService
|
||||
.GetContextAsync<HutaoStatisticsFactoryMetadataContext>(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
GachaEventStatistics raw = response.Data;
|
||||
try
|
||||
@@ -126,7 +125,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
|
||||
private async ValueTask<EndIds> CreateEndIdsAsync(GachaArchive? archive, CancellationToken token)
|
||||
{
|
||||
EndIds endIds = new();
|
||||
foreach (GachaConfigType type in GachaLog.QueryTypes)
|
||||
foreach (GachaType type in GachaLog.QueryTypes)
|
||||
{
|
||||
if (archive is not null)
|
||||
{
|
||||
|
||||
@@ -5,10 +5,10 @@ using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.Diagnostics;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.InterChange.GachaLog;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.GachaLog.Factory;
|
||||
using Snap.Hutao.Service.GachaLog.QueryProvider;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using Snap.Hutao.Web.Response;
|
||||
@@ -55,20 +55,14 @@ internal sealed partial class GachaLogService : IGachaLogService
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<bool> InitializeAsync(CancellationToken token = default)
|
||||
{
|
||||
if (context.IsInitialized)
|
||||
if (context is { IsInitialized: true })
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
|
||||
Dictionary<string, Model.Metadata.Avatar.Avatar> nameAvatarMap = await metadataService.GetNameToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<string, Model.Metadata.Weapon.Weapon> nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
context = new(idAvatarMap, idWeaponMap, nameAvatarMap, nameWeaponMap);
|
||||
|
||||
context = await metadataService.GetContextAsync<GachaLogServiceMetadataContext>(token).ConfigureAwait(false);
|
||||
ArchiveCollection = gachaLogDbService.GetGachaArchiveCollection();
|
||||
return true;
|
||||
}
|
||||
@@ -182,7 +176,7 @@ internal sealed partial class GachaLogService : IGachaLogService
|
||||
ArgumentNullException.ThrowIfNull(ArchiveCollection);
|
||||
GachaLogFetchContext fetchContext = new(gachaLogDbService, taskContext, context, isLazy);
|
||||
|
||||
foreach (GachaConfigType configType in GachaLog.QueryTypes)
|
||||
foreach (GachaType configType in GachaLog.QueryTypes)
|
||||
{
|
||||
fetchContext.ResetForProcessingType(configType, query);
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog;
|
||||
@@ -13,65 +15,28 @@ namespace Snap.Hutao.Service.GachaLog;
|
||||
/// <summary>
|
||||
/// 祈愿记录服务上下文
|
||||
/// </summary>
|
||||
internal readonly struct GachaLogServiceMetadataContext
|
||||
internal sealed class GachaLogServiceMetadataContext : IMetadataContext,
|
||||
IMetadataSupportInitialization,
|
||||
IMetadataListGachaEventSource,
|
||||
IMetadataDictionaryIdAvatarSource,
|
||||
IMetadataDictionaryIdWeaponSource,
|
||||
IMetadataDictionaryNameAvatarSource,
|
||||
IMetadataDictionaryNameWeaponSource
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品缓存
|
||||
/// </summary>
|
||||
public readonly Dictionary<string, Item> ItemCache = [];
|
||||
public Dictionary<string, Item> ItemCache { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Id 角色 映射
|
||||
/// </summary>
|
||||
public readonly Dictionary<AvatarId, Avatar> IdAvatarMap;
|
||||
public List<GachaEvent> GachaEvents { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Id 武器 映射
|
||||
/// </summary>
|
||||
public readonly Dictionary<WeaponId, Weapon> IdWeaponMap;
|
||||
public Dictionary<AvatarId, Avatar> IdAvatarMap { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 名称 角色 映射
|
||||
/// </summary>
|
||||
public readonly Dictionary<string, Avatar> NameAvatarMap;
|
||||
public Dictionary<WeaponId, Weapon> IdWeaponMap { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 名称 武器 映射
|
||||
/// </summary>
|
||||
public readonly Dictionary<string, Weapon> NameWeaponMap;
|
||||
public Dictionary<string, Avatar> NameAvatarMap { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 是否初始化完成
|
||||
/// </summary>
|
||||
public readonly bool IsInitialized;
|
||||
public Dictionary<string, Weapon> NameWeaponMap { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的祈愿记录服务上下文
|
||||
/// </summary>
|
||||
/// <param name="idAvatarMap">Id 角色 映射</param>
|
||||
/// <param name="idWeaponMap">Id 武器 映射</param>
|
||||
/// <param name="nameAvatarMap">名称 角色 映射</param>
|
||||
/// <param name="nameWeaponMap">名称 武器 映射</param>
|
||||
public GachaLogServiceMetadataContext(
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap,
|
||||
Dictionary<WeaponId, Weapon> idWeaponMap,
|
||||
Dictionary<string, Avatar> nameAvatarMap,
|
||||
Dictionary<string, Weapon> nameWeaponMap)
|
||||
{
|
||||
IdAvatarMap = idAvatarMap;
|
||||
IdWeaponMap = idWeaponMap;
|
||||
NameAvatarMap = nameAvatarMap;
|
||||
NameWeaponMap = nameWeaponMap;
|
||||
public bool IsInitialized { get; set; }
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按名称获取物品
|
||||
/// </summary>
|
||||
/// <param name="name">名称</param>
|
||||
/// <param name="type">类型</param>
|
||||
/// <returns>物品</returns>
|
||||
public Item GetItemByNameAndType(string name, string type)
|
||||
{
|
||||
if (!ItemCache.TryGetValue(name, out Item? result))
|
||||
@@ -93,11 +58,6 @@ internal readonly struct GachaLogServiceMetadataContext
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按物品 Id 获取名称星级
|
||||
/// </summary>
|
||||
/// <param name="id">Id</param>
|
||||
/// <returns>名称星级</returns>
|
||||
public INameQuality GetNameQualityByItemId(uint id)
|
||||
{
|
||||
uint place = id.StringLength();
|
||||
@@ -109,12 +69,6 @@ internal readonly struct GachaLogServiceMetadataContext
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取物品 Id
|
||||
/// O(1)
|
||||
/// </summary>
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <returns>物品 Id</returns>
|
||||
public uint GetItemId(GachaLogItem item)
|
||||
{
|
||||
if (item.ItemType == SH.ModelInterchangeUIGFItemTypeAvatar)
|
||||
|
||||
@@ -19,7 +19,7 @@ internal interface IGachaLogDbService
|
||||
|
||||
ValueTask RemoveGachaArchiveByIdAsync(Guid archiveId);
|
||||
|
||||
void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId);
|
||||
void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaType queryType, long endId);
|
||||
|
||||
ValueTask<GachaArchive?> GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token);
|
||||
|
||||
@@ -31,25 +31,25 @@ internal interface IGachaLogDbService
|
||||
|
||||
ValueTask<List<GachaItem>> GetGachaItemListByArchiveIdAsync(Guid archiveId);
|
||||
|
||||
List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaConfigType queryType, long endId);
|
||||
List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaType queryType, long endId);
|
||||
|
||||
long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
|
||||
long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaType queryType);
|
||||
|
||||
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
|
||||
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType, CancellationToken token);
|
||||
|
||||
long GetOldestGachaItemIdByArchiveId(Guid archiveId);
|
||||
|
||||
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
|
||||
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaType queryType);
|
||||
|
||||
ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
|
||||
ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType, CancellationToken token);
|
||||
|
||||
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType);
|
||||
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaType queryType);
|
||||
|
||||
ValueTask<long> GetOldestGachaItemIdByArchiveIdAsync(Guid archiveId);
|
||||
|
||||
ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaConfigType queryType, long endId);
|
||||
ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaType queryType, long endId);
|
||||
|
||||
ValueTask AddGachaItemRangeAsync(List<GachaItem> items);
|
||||
|
||||
ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaConfigType queryType, long endId);
|
||||
ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaType queryType, long endId);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user