spiral abyss rework 2

This commit is contained in:
DismissedLight
2023-09-17 23:33:40 +08:00
parent fb496497bf
commit 68d8baa70c
50 changed files with 512 additions and 151 deletions

View File

@@ -8,7 +8,10 @@ internal sealed class NotNullWhenAttribute : Attribute
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
public NotNullWhenAttribute(bool returnValue)
{
ReturnValue = returnValue;
}
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }

View File

@@ -49,7 +49,7 @@ public class JsonSerializeTest
NumberHandling = JsonNumberHandling.AllowReadingFromString,
};
Dictionary<int,string> sample = JsonSerializer.Deserialize<Dictionary<int, string>>(SmapleNumberKeyDictionaryJson, options)!;
Dictionary<int, string> sample = JsonSerializer.Deserialize<Dictionary<int, string>>(SmapleNumberKeyDictionaryJson, options)!;
Assert.AreEqual(sample[111], "12");
}

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Media;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using Windows.UI;

View File

@@ -39,7 +39,7 @@ internal struct Rgba32
/// </summary>
/// <param name="hex">色值字符串</param>
public Rgba32(string hex)
: this(Convert.ToUInt32(hex, 16))
: this(hex.Length == 6 ? Convert.ToUInt32($"{hex}FF", 16) : Convert.ToUInt32(hex, 16))
{
}

View File

@@ -74,7 +74,7 @@ internal sealed partial class DescriptionTextBlock : ContentControl
{
AppendText(textBlock, description[last..i]);
AppendLineBreak(textBlock);
i += 1;
i += 2;
last = i;
}

View File

@@ -0,0 +1,182 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents;
using Microsoft.UI.Xaml.Media;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Control.Media;
using Snap.Hutao.Control.Theme;
using Windows.Foundation;
using Windows.UI;
namespace Snap.Hutao.Control.Text;
[DependencyProperty("Description", typeof(string), "", nameof(OnDescriptionChanged))]
[DependencyProperty("TextStyle", typeof(Style), default(Style), nameof(OnTextStyleChanged))]
internal sealed partial class HtmlDescriptionTextBlock : ContentControl
{
private static readonly int ColorTagFullLength = "<color style='color:#FFFFFF;'></color>".Length;
private static readonly int ColorTagLeftLength = "<color style='color:#FFFFFF;'>".Length;
private static readonly int ItalicTagFullLength = "<i></i>".Length;
private static readonly int ItalicTagLeftLength = "<i>".Length;
private static readonly int BoldTagFullLength = "<b></b>".Length;
private static readonly int BoldTagLeftLength = "<b>".Length;
private readonly TypedEventHandler<FrameworkElement, object> actualThemeChangedEventHandler;
/// <summary>
/// 构造一个新的呈现描述文本的文本块
/// </summary>
public HtmlDescriptionTextBlock()
{
this.DisableInteraction();
Content = new TextBlock()
{
TextWrapping = TextWrapping.Wrap,
Style = TextStyle,
};
actualThemeChangedEventHandler = OnActualThemeChanged;
ActualThemeChanged += actualThemeChangedEventHandler;
}
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBlock textBlock = (TextBlock)((HtmlDescriptionTextBlock)d).Content;
ReadOnlySpan<char> description = (string)e.NewValue;
UpdateDescription(textBlock, description);
}
private static void OnTextStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBlock textBlock = (TextBlock)((HtmlDescriptionTextBlock)d).Content;
textBlock.Style = (Style)e.NewValue;
}
private static void UpdateDescription(TextBlock textBlock, in ReadOnlySpan<char> description)
{
textBlock.Inlines.Clear();
int last = 0;
for (int i = 0; i < description.Length;)
{
// newline
if (description[i..].StartsWith(@"<br>"))
{
AppendText(textBlock, description[last..i]);
AppendLineBreak(textBlock);
i += 4;
last = i;
}
// color tag
else if (description[i..].StartsWith("<c"))
{
AppendText(textBlock, description[last..i]);
string a = description.Slice(i + 21, 6).ToString();
Rgba32 color = new(a);
int length = description[(i + ColorTagLeftLength)..].IndexOf('<');
AppendColorText(textBlock, description.Slice(i + ColorTagLeftLength, length), color);
i += length + ColorTagFullLength;
last = i;
}
// italic
else if (description[i..].StartsWith("<i"))
{
AppendText(textBlock, description[last..i]);
int length = description[(i + ItalicTagLeftLength)..].IndexOf('<');
AppendItalicText(textBlock, description.Slice(i + ItalicTagLeftLength, length));
i += length + ItalicTagFullLength;
last = i;
}
// bold
else if (description[i..].StartsWith("<b"))
{
AppendText(textBlock, description[last..i]);
int length = description[(i + BoldTagLeftLength)..].IndexOf('<');
AppendBoldText(textBlock, description.Slice(i + BoldTagLeftLength, length));
i += length + BoldTagFullLength;
last = i;
}
else
{
i += 1;
}
if (i == description.Length - 1)
{
AppendText(textBlock, description[last..(i + 1)]);
}
}
}
private static void AppendText(TextBlock text, in ReadOnlySpan<char> slice)
{
text.Inlines.Add(new Run { Text = slice.ToString() });
}
private static void AppendColorText(TextBlock text, in ReadOnlySpan<char> slice, Rgba32 color)
{
Color targetColor;
if (ThemeHelper.IsDarkMode(text.ActualTheme))
{
targetColor = color;
}
else
{
// Make lighter in light mode
Hsl32 hsl = color.ToHsl();
hsl.L *= 0.3;
targetColor = Rgba32.FromHsl(hsl);
}
text.Inlines.Add(new Run
{
Text = slice.ToString(),
Foreground = new SolidColorBrush(targetColor),
});
}
private static void AppendBoldText(TextBlock text, in ReadOnlySpan<char> slice)
{
text.Inlines.Add(new Run
{
Text = slice.ToString(),
FontWeight = FontWeights.Bold,
});
}
private static void AppendItalicText(TextBlock text, in ReadOnlySpan<char> slice)
{
text.Inlines.Add(new Run
{
Text = slice.ToString(),
FontStyle = Windows.UI.Text.FontStyle.Italic,
});
}
private static void AppendLineBreak(TextBlock text)
{
text.Inlines.Add(new LineBreak());
}
private void OnActualThemeChanged(FrameworkElement sender, object args)
{
// Simply re-apply texts
UpdateDescription((TextBlock)Content, Description);
}
}

View File

@@ -3,7 +3,6 @@
using CommunityToolkit.WinUI.Notifications;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.DailyNote;

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
namespace Snap.Hutao.Core.LifeCycle;

View File

@@ -115,7 +115,7 @@ internal static class LocalSetting
return Get<Windows.Foundation.Rect>(key, defaultValue);
}
public static ApplicationDataCompositeValue Get(string key, ApplicationDataCompositeValue defaultValue)
public static ApplicationDataCompositeValue Get(string key, ApplicationDataCompositeValue defaultValue)
{
return Get<ApplicationDataCompositeValue>(key, defaultValue);
}

View File

@@ -15,7 +15,7 @@ internal static partial class EnumerableExtension
public static bool IsNullOrEmpty<TKey, TValue>([NotNullWhen(false)] this Dictionary<TKey, TValue>? source)
where TKey : notnull
{
if (source is { Count: >0 })
if (source is { Count: > 0 })
{
return false;
}

View File

@@ -26,13 +26,7 @@ internal sealed class SpiralAbyssEntry : ObservableObject,
/// <summary>
/// 计划Id
/// </summary>
public int ScheduleId { get; set; }
/// <summary>
/// 视图 中使用的计划 Id 字符串
/// </summary>
[NotMapped]
public string Schedule { get => SH.ModelEntitySpiralAbyssScheduleFormat.Format(ScheduleId); }
public uint ScheduleId { get; set; }
/// <summary>
/// Uid

View File

@@ -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;
internal static class MonsterRelationship
{
public static MonsterRelationshipId Normalize(MonsterRelationshipId id)
{
return (uint)id switch
{
5021U => 502U, // 幻形豕兽 · 水 (强化)
5040U => 504U, // 幻形蟹 · 水
5041U => 504U, // 幻形蟹 · 水 (强化)
5070U => 507U, // 幻形花鼠 · 水
5071U => 507U, // 幻形花鼠 · 水 (强化)
60402U => 60401U, // (火)岩龙蜥
60403U => 60401U, // (冰)岩龙蜥
60404U => 60401U, // (雷)岩龙蜥
_ => id,
};
}
}

View File

@@ -1,8 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Tower;
internal sealed class TowerWave

View File

@@ -28,7 +28,10 @@ internal class NameDescription
Description = description;
}
public static NameDescription Default => DefaultValue;
public static NameDescription Default
{
get => DefaultValue;
}
/// <summary>
/// 名称

View File

@@ -7,5 +7,8 @@ internal sealed class NameValueDefaults
{
private static readonly NameValue<string> StringValue = new(SH.ModelNameValueDefaultName, SH.ModelNameValueDefaultDescription);
public static NameValue<string> String => StringValue;
public static NameValue<string> String
{
get => StringValue;
}
}

View File

@@ -13,7 +13,6 @@ using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
using Snap.Hutao.Web.Response;
using System.Runtime.CompilerServices;
using Windows.Perception.Spatial;
using CalculateAvatar = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Avatar;
using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;

View File

@@ -4,7 +4,6 @@
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity.Database;
using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
namespace Snap.Hutao.Service.AvatarInfo;

View File

@@ -9,7 +9,6 @@ using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Enka;
using Snap.Hutao.Web.Enka.Model;
using Snap.Hutao.Web.Hoyolab;
using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
namespace Snap.Hutao.Service.AvatarInfo;

View File

@@ -3,7 +3,6 @@
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.Cultivation;

View File

@@ -8,7 +8,6 @@ using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Notification;
using System.Globalization;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Service.DailyNote;

View File

@@ -49,7 +49,7 @@ internal readonly struct GachaItemSaveContext
// 全量刷新
if (!IsLazy)
{
GachaLogDbService.RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(archive.InnerId, QueryType, EndId);
GachaLogDbService.RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(archive.InnerId, QueryType, EndId);
}
GachaLogDbService.AddGachaItemRange(ItemsToAdd);

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using System.IO;
namespace Snap.Hutao.Service.Game;

View File

@@ -1,20 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO.Ini;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Game.Locator;
using Snap.Hutao.Service.Game.Package;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
using Snap.Hutao.Web.Response;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using static Snap.Hutao.Service.Game.GameConstants;
namespace Snap.Hutao.Service.Game;
internal sealed class LaunchStatus

View File

@@ -1,8 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Abstraction;
namespace Snap.Hutao.Service.Game.Unlocker;
/// <summary>

View File

@@ -3,7 +3,6 @@
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.SpiralAbyss;
namespace Snap.Hutao.Service.Hutao;

View File

@@ -5,11 +5,6 @@ using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Snap.Hutao.Service.Inventroy;

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.SpiralAbyss;
@@ -10,7 +9,7 @@ internal interface ISpiralAbyssRecordDbService
{
ValueTask AddSpiralAbyssEntryAsync(SpiralAbyssEntry entry);
ValueTask<List<SpiralAbyssEntry>> GetSpiralAbyssEntryListByUidAsync(string uid);
ValueTask<Dictionary<uint, SpiralAbyssEntry>> GetSpiralAbyssEntryListByUidAsync(string uid);
ValueTask UpdateSpiralAbyssEntryAsync(SpiralAbyssEntry entry);
}

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
using Snap.Hutao.ViewModel.SpiralAbyss;
using Snap.Hutao.ViewModel.User;
using System.Collections.ObjectModel;

View File

@@ -5,7 +5,6 @@ using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.SpiralAbyss;
@@ -15,16 +14,18 @@ internal sealed partial class SpiralAbyssRecordDbService : ISpiralAbyssRecordDbS
{
private readonly IServiceProvider serviceProvider;
public async ValueTask<List<SpiralAbyssEntry>> GetSpiralAbyssEntryListByUidAsync(string uid)
public async ValueTask<Dictionary<uint, SpiralAbyssEntry>> GetSpiralAbyssEntryListByUidAsync(string uid)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.SpiralAbysses
List<SpiralAbyssEntry> entries = await appDbContext.SpiralAbysses
.Where(s => s.Uid == uid)
.OrderByDescending(s => s.ScheduleId)
.ToListAsync()
.ConfigureAwait(false);
return entries.ToDictionary(e => e.ScheduleId);
}
}

View File

@@ -4,7 +4,6 @@
using Snap.Hutao.Core.DependencyInjection.Abstraction;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.ViewModel.SpiralAbyss;
using Snap.Hutao.ViewModel.User;
@@ -60,12 +59,15 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
uid = userAndUid.Uid.Value;
if (spiralAbysses is null)
{
List<SpiralAbyssEntry> list = await spiralAbyssRecordDbService
Dictionary<uint, SpiralAbyssEntry> entryMap = await spiralAbyssRecordDbService
.GetSpiralAbyssEntryListByUidAsync(userAndUid.Uid.Value)
.ConfigureAwait(false);
ArgumentNullException.ThrowIfNull(metadataContext);
spiralAbysses = list.SelectList(entity => SpiralAbyssView.From(entity, metadataContext)).ToObservableCollection();
spiralAbysses = metadataContext.IdScheduleMap.Values
.Select(sch => SpiralAbyssView.From(entryMap.GetValueOrDefault(sch.Id), sch, metadataContext))
.Reverse()
.ToObservableCollection();
}
return spiralAbysses;
@@ -79,6 +81,7 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
.Create(userAndUid.User.IsOversea)
.GetPlayerInfoAsync(userAndUid)
.ConfigureAwait(false);
await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Last).ConfigureAwait(false);
await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Current).ConfigureAwait(false);
}
@@ -97,27 +100,32 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
ArgumentNullException.ThrowIfNull(spiralAbysses);
ArgumentNullException.ThrowIfNull(metadataContext);
int index = spiralAbysses.FirstIndexOf(s => s.Entity.ScheduleId == webSpiralAbyss.ScheduleId);
int index = spiralAbysses.FirstIndexOf(s => s.ScheduleId == webSpiralAbyss.ScheduleId);
if (index > 0)
{
await taskContext.SwitchToBackgroundAsync();
SpiralAbyssView view = spiralAbysses[index];
view.Entity.SpiralAbyss = webSpiralAbyss;
await spiralAbyssRecordDbService.UpdateSpiralAbyssEntryAsync(view.Entity).ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
spiralAbysses.RemoveAt(index);
spiralAbysses.Insert(index, SpiralAbyssView.From(view.Entity, metadataContext));
}
else
{
SpiralAbyssEntry newEntry = SpiralAbyssEntry.From(userAndUid.Uid.Value, webSpiralAbyss);
if (view.Entity is not null)
{
view.Entity.SpiralAbyss = webSpiralAbyss;
await spiralAbyssRecordDbService.UpdateSpiralAbyssEntryAsync(view.Entity).ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
spiralAbysses.Insert(0, SpiralAbyssView.From(newEntry, metadataContext));
await taskContext.SwitchToMainThreadAsync();
spiralAbysses.RemoveAt(index);
spiralAbysses.Insert(index, SpiralAbyssView.From(view.Entity, metadataContext));
return;
}
else
{
SpiralAbyssEntry newEntry = SpiralAbyssEntry.From(userAndUid.Uid.Value, webSpiralAbyss);
await taskContext.SwitchToBackgroundAsync();
await spiralAbyssRecordDbService.AddSpiralAbyssEntryAsync(newEntry).ConfigureAwait(false);
await taskContext.SwitchToBackgroundAsync();
await spiralAbyssRecordDbService.AddSpiralAbyssEntryAsync(newEntry).ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
spiralAbysses.Insert(index, SpiralAbyssView.From(newEntry, metadataContext));
}
}
}
}

View File

@@ -26,7 +26,7 @@
Grid.Row="1"
DisplayMode="Inline"
IsPaneOpen="True"
OpenPaneLength="256"
OpenPaneLength="248"
PaneBackground="Transparent"
Visibility="{Binding SelectedView, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
<SplitView.Pane>
@@ -37,9 +37,13 @@
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,6">
<TextBlock Text="{Binding Entity.Schedule}"/>
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock Text="{Binding Schedule}"/>
<TextBlock Opacity="0.8" Text="{Binding MaxFloor}"/>
</StackPanel>
<TextBlock
Opacity="0.7"
Opacity="0.6"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding TimeFormatted}"/>
</StackPanel>
@@ -208,10 +212,7 @@
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl
Margin="16,16,0,0"
ItemsPanel="{StaticResource HorizontalStackPanelTemplate}"
ItemsSource="{Binding Floors}">
<ItemsControl Margin="16,16,0,0" ItemsSource="{Binding Floors}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
@@ -220,6 +221,8 @@
Style="{StaticResource BorderCardStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
@@ -247,12 +250,26 @@
</Grid>
<ItemsControl
Grid.Row="1"
Margin="8,0,0,0"
ItemsSource="{Binding Disorders}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock
Opacity="0.7"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl
Grid.Row="3"
Margin="0,0,0,8"
ItemsSource="{Binding Levels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,8,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition/>
@@ -279,11 +296,36 @@
TextAlignment="Center"/>
</StackPanel>
</Grid>
<ItemsControl
Grid.Row="2"
Margin="8,0,0,0"
ItemsSource="{Binding Battles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<cwc:UniformGrid Columns="2"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Gadget.Name}" Visibility="{Binding Gadget, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
<ToolTipService.ToolTip>
<shct:HtmlDescriptionTextBlock Description="{Binding Gadget.Description}"/>
</ToolTipService.ToolTip>
</TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl
Grid.Row="3"
Margin="8,0,0,0"
ItemsSource="{Binding Battles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<cwc:UniformGrid Columns="2"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>

View File

@@ -12,7 +12,6 @@ using Snap.Hutao.Service.User;
using Snap.Hutao.View.Control;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Request.QueryString;
using System.Collections.ObjectModel;
namespace Snap.Hutao.ViewModel.DailyNote;

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Graphics.Canvas.Svg;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.Database;

View File

@@ -1,8 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.Notifications;
using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Core;
using Snap.Hutao.Core.Setting;

View File

@@ -5,7 +5,6 @@ using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.ViewModel.SpiralAbyss;
@@ -15,16 +14,6 @@ namespace Snap.Hutao.ViewModel.SpiralAbyss;
[HighQuality]
internal class AvatarView : INameIconSide, IMappingFrom<AvatarView, Avatar>
{
/// <summary>
/// 构造一个新的角色视图
/// </summary>
/// <param name="avatarId">角色Id</param>
/// <param name="idAvatarMap">Id角色映射</param>
public AvatarView(in AvatarId avatarId, Dictionary<AvatarId, Avatar> idAvatarMap)
: this(idAvatarMap[avatarId])
{
}
/// <summary>
/// 构造一个新的角色视图
/// </summary>

View File

@@ -1,7 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Metadata.Tower;
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
namespace Snap.Hutao.ViewModel.SpiralAbyss;
@@ -10,26 +12,49 @@ namespace Snap.Hutao.ViewModel.SpiralAbyss;
/// 上下半视图
/// </summary>
[HighQuality]
internal sealed class BattleView
internal sealed class BattleView : IMappingFrom<BattleView, TowerLevel, uint, SpiralAbyssMetadataContext>
{
/// <summary>
/// 构造一个新的上下半视图
/// </summary>
/// <param name="battle">战斗</param>
/// <param name="idAvatarMap">Id角色映射</param>
public BattleView(Battle battle, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap)
private BattleView(TowerLevel towerLevel, uint battleIndex, SpiralAbyssMetadataContext context)
{
Time = $"{DateTimeOffset.FromUnixTimeSeconds(battle.Timestamp).ToLocalTime():yyyy.MM.dd HH:mm:ss}";
Avatars = battle.Avatars.SelectList(a => AvatarView.From(idAvatarMap[a.Id]));
IndexValue = battleIndex;
Gadget = battleIndex switch
{
1U => towerLevel.FirstGadget,
2U => towerLevel.SecondGadget,
_ => default,
};
MonsterWaves = battleIndex switch
{
1U => towerLevel.FirstWaves.SelectList(w => BattleWave.From(w, context)),
2U => towerLevel.FirstWaves.SelectList(w => BattleWave.From(w, context)),
_ => default!,
};
}
/// <summary>
/// 时间
/// </summary>
public string Time { get; }
public string? Time { get; private set; }
/// <summary>
/// 角色
/// </summary>
public List<AvatarView> Avatars { get; }
public List<AvatarView>? Avatars { get; private set; }
public NameDescription? Gadget { get; }
public List<BattleWave> MonsterWaves { get; }
internal uint IndexValue { get; }
public static BattleView From(TowerLevel level, uint index, SpiralAbyssMetadataContext context)
{
return new(level, index, context);
}
public void WithSpiralAbyssBattle(Battle battle, SpiralAbyssMetadataContext context)
{
Time = $"{DateTimeOffset.FromUnixTimeSeconds(battle.Timestamp).ToLocalTime():yyyy.MM.dd HH:mm:ss}";
Avatars = battle.Avatars.SelectList(a => AvatarView.From(context.IdAvatarMap[a.Id]));
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Tower;
namespace Snap.Hutao.ViewModel.SpiralAbyss;
internal sealed class BattleWave : IMappingFrom<BattleWave, TowerWave, SpiralAbyssMetadataContext>
{
private BattleWave(TowerWave towerWave, SpiralAbyssMetadataContext context)
{
Description = towerWave.Type.GetLocalizedDescription();
Monsters = towerWave.Monsters.SelectList(m => MonsterView.From(m, context.IdMonsterMap[MonsterRelationship.Normalize(m.Id)]));
}
public string Description { get; }
public List<MonsterView> Monsters { get; }
public static BattleWave From(TowerWave tower, SpiralAbyssMetadataContext context)
{
return new(tower, context);
}
}

View File

@@ -1,7 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Model.Metadata.Tower;
namespace Snap.Hutao.ViewModel.SpiralAbyss;
@@ -9,21 +10,19 @@ namespace Snap.Hutao.ViewModel.SpiralAbyss;
/// 层视图
/// </summary>
[HighQuality]
internal sealed class FloorView
internal sealed class FloorView : IMappingFrom<FloorView, TowerFloor, SpiralAbyssMetadataContext>
{
/// <summary>
/// 构造一个新的层视图
/// </summary>
/// <param name="floor">层</param>
/// <param name="idAvatarMap">Id角色映射</param>
public FloorView(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.Floor floor, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap)
public FloorView(TowerFloor floor, SpiralAbyssMetadataContext context)
{
Index = SH.ModelBindingHutaoComplexRankFloor.Format(floor.Index);
SettleTime = $"{DateTimeOffset.FromUnixTimeSeconds(floor.SettleTime).ToLocalTime():yyyy.MM.dd HH:mm:ss}";
Star = floor.Star;
Levels = floor.Levels.SelectList(l => new LevelView(l, idAvatarMap));
IndexValue = floor.Index;
Disorders = floor.Descriptions;
Levels = context.IdLevelGroupMap[floor.LevelGroupId].SortBy(l => l.Index).SelectList(l => LevelView.From(l, context));
}
public bool Engaged { get; private set; }
/// <summary>
/// 层号
/// </summary>
@@ -32,15 +31,39 @@ internal sealed class FloorView
/// <summary>
/// 时间
/// </summary>
public string SettleTime { get; }
public string? SettleTime { get; private set; }
/// <summary>
/// 星数
/// </summary>
public int Star { get; }
public int Star { get; private set; }
public List<string> Disorders { get; }
/// <summary>
/// 间信息
/// </summary>
public List<LevelView> Levels { get; }
internal uint IndexValue { get; }
public static FloorView From(TowerFloor floor, SpiralAbyssMetadataContext context)
{
return new(floor, context);
}
public void WithSpiralAbyssFloor(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.Floor floor, SpiralAbyssMetadataContext context)
{
SettleTime = $"{DateTimeOffset.FromUnixTimeSeconds(floor.SettleTime).ToLocalTime():yyyy.MM.dd HH:mm:ss}";
Star = floor.Star;
Engaged = true;
foreach (LevelView levelView in Levels)
{
if (floor.Levels.SingleOrDefault(l => l.Index == levelView.IndexValue) is { } level)
{
levelView.WithSpiralAbyssLevel(level, context);
}
}
}
}

View File

@@ -1,7 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Model.Metadata.Tower;
namespace Snap.Hutao.ViewModel.SpiralAbyss;
@@ -9,18 +10,17 @@ namespace Snap.Hutao.ViewModel.SpiralAbyss;
/// 间视图
/// </summary>
[HighQuality]
internal sealed class LevelView
internal sealed class LevelView : IMappingFrom<LevelView, TowerLevel, SpiralAbyssMetadataContext>
{
/// <summary>
/// 构造一个新的间视图
/// </summary>
/// <param name="level">间</param>
/// <param name="idAvatarMap">Id角色映射</param>
public LevelView(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.Level level, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap)
private LevelView(TowerLevel towerLevel, SpiralAbyssMetadataContext context)
{
Index = SH.ModelBindingHutaoComplexRankLevel.Format(level.Index);
Star = level.Star;
Battles = level.Battles.SelectList(b => new BattleView(b, idAvatarMap));
Index = SH.ModelBindingHutaoComplexRankLevel.Format(towerLevel.Index);
IndexValue = towerLevel.Index;
Battles = new()
{
BattleView.From(towerLevel, 1, context),
BattleView.From(towerLevel, 2, context),
};
}
/// <summary>
@@ -31,10 +31,30 @@ internal sealed class LevelView
/// <summary>
/// 星数
/// </summary>
public int Star { get; }
public int Star { get; private set; }
/// <summary>
/// 上下半
/// </summary>
public List<BattleView> Battles { get; }
public List<BattleView> Battles { get; private set; }
internal uint IndexValue { get; set; }
public static LevelView From(TowerLevel towerLevel, SpiralAbyssMetadataContext context)
{
return new(towerLevel, context);
}
public void WithSpiralAbyssLevel(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.Level level, SpiralAbyssMetadataContext context)
{
Star = level.Star;
foreach (BattleView battleView in Battles)
{
if (level.Battles.SingleOrDefault(b => b.Index == battleView.IndexValue) is { } battle)
{
battleView.WithSpiralAbyssBattle(battle, context);
}
}
}
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Metadata.Tower;
namespace Snap.Hutao.ViewModel.SpiralAbyss;
internal sealed class MonsterView : INameIcon, IMappingFrom<MonsterView, TowerMonster, Model.Metadata.Monster.Monster>
{
private MonsterView(TowerMonster towerMonster, Model.Metadata.Monster.Monster metaMonster)
{
Name = metaMonster.Name;
Icon = MonsterIconConverter.IconNameToUri(metaMonster.Icon);
Affixes = towerMonster.Affixes;
Count = towerMonster.Count;
AttackMonolith = towerMonster.AttackMonolith;
}
public string Name { get; }
public Uri Icon { get; }
public List<NameDescription>? Affixes { get; }
public uint Count { get; }
public bool AttackMonolith { get; }
public static MonsterView From(TowerMonster tower, Model.Metadata.Monster.Monster meta)
{
return new MonsterView(tower, meta);
}
}

View File

@@ -3,10 +3,6 @@
using CommunityToolkit.Mvvm.Messaging;
using Snap.Hutao.Message;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.SpiralAbyss;
using Snap.Hutao.Service.User;

View File

@@ -4,9 +4,7 @@
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Tower;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.ViewModel.SpiralAbyss;
@@ -15,7 +13,8 @@ namespace Snap.Hutao.ViewModel.SpiralAbyss;
/// </summary>
[HighQuality]
internal sealed class SpiralAbyssView : IEntityOnly<SpiralAbyssEntry?>,
IMappingFrom<SpiralAbyssView, SpiralAbyssEntry, SpiralAbyssMetadataContext>
IMappingFrom<SpiralAbyssView, SpiralAbyssEntry, SpiralAbyssMetadataContext>,
IMappingFrom<SpiralAbyssView, SpiralAbyssEntry?, TowerSchedule, SpiralAbyssMetadataContext>
{
private readonly SpiralAbyssEntry? entity;
@@ -25,7 +24,7 @@ internal sealed class SpiralAbyssView : IEntityOnly<SpiralAbyssEntry?>,
/// <param name="entity">实体</param>
/// <param name="idAvatarMap">Id角色映射</param>
private SpiralAbyssView(SpiralAbyssEntry entity, SpiralAbyssMetadataContext context)
: this(context.IdScheduleMap[(uint)entity.ScheduleId], context)
: this(context.IdScheduleMap[entity.ScheduleId], context)
{
this.entity = entity;
@@ -39,17 +38,31 @@ internal sealed class SpiralAbyssView : IEntityOnly<SpiralAbyssEntry?>,
TakeDamage = spiralAbyss.TakeDamageRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault();
NormalSkill = spiralAbyss.NormalSkillRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault();
EnergySkill = spiralAbyss.EnergySkillRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault();
Floors = spiralAbyss.Floors.Select(f => new FloorView(f, context.IdAvatarMap)).Reverse().ToList();
Engaged = true;
foreach (Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.Floor webFloor in spiralAbyss.Floors)
{
Floors.Single(f => f.IndexValue == webFloor.Index).WithSpiralAbyssFloor(webFloor, context);
}
}
private SpiralAbyssView(TowerSchedule towerSchedule, SpiralAbyssMetadataContext context)
{
ScheduleId = towerSchedule.Id;
TimeFormatted = $"{towerSchedule.Open:yyyy.MM.dd HH:mm} - {towerSchedule.Close:yyyy.MM.dd HH:mm}";
BlessingName = towerSchedule.BuffName;
Blessings = towerSchedule.Descriptions;
Floors = towerSchedule.FloorIds.Select(id => FloorView.From(context.IdFloorMap[id], context)).Reverse().ToList();
}
public uint ScheduleId { get; }
/// <summary>
/// 视图 中使用的计划 Id 字符串
/// </summary>
public string Schedule { get => SH.ModelEntitySpiralAbyssScheduleFormat.Format(ScheduleId); }
public SpiralAbyssEntry? Entity { get => entity; }
public string TimeFormatted { get; }
@@ -58,6 +71,8 @@ internal sealed class SpiralAbyssView : IEntityOnly<SpiralAbyssEntry?>,
public List<string> Blessings { get; }
public bool Engaged { get; }
/// <summary>
/// 战斗次数
/// </summary>
@@ -112,4 +127,16 @@ internal sealed class SpiralAbyssView : IEntityOnly<SpiralAbyssEntry?>,
{
return new(entity, context);
}
public static SpiralAbyssView From(SpiralAbyssEntry? entity, TowerSchedule meta, SpiralAbyssMetadataContext context)
{
if (entity is not null)
{
return new(entity, context);
}
else
{
return new(meta, context);
}
}
}

View File

@@ -13,7 +13,7 @@ internal sealed class Floor
/// 层号
/// </summary>
[JsonPropertyName("index")]
public int Index { get; set; }
public uint Index { get; set; }
/// <summary>
/// 图标

View File

@@ -13,7 +13,7 @@ internal sealed class Level
/// 索引
/// </summary>
[JsonPropertyName("index")]
public int Index { get; set; }
public uint Index { get; set; }
/// <summary>
/// 星数

View File

@@ -13,7 +13,7 @@ internal sealed class SpiralAbyss
/// 计划Id
/// </summary>
[JsonPropertyName("schedule_id")]
public int ScheduleId { get; set; }
public uint ScheduleId { get; set; }
/// <summary>
/// 开始时间

View File

@@ -3,8 +3,6 @@
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Service;
using Snap.Hutao.Service.Hutao;
using Snap.Hutao.Web.Hutao.GachaLog;
using System.Net.Http;
namespace Snap.Hutao.Web.Hutao.Geetest;

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Web.Hutao.SpiralAbyss;
using Snap.Hutao.Web.Response;
using System.Net.Http;
using System.Security.Cryptography;

View File

@@ -25,7 +25,7 @@ internal sealed class SimpleFloor
/// <summary>
/// 层遍号 1-12|9-12
/// </summary>
public int Index { get; set; }
public uint Index { get; set; }
/// <summary>
/// 星数

View File

@@ -25,7 +25,7 @@ internal sealed class SimpleLevel
/// <summary>
/// 间遍号 1-3
/// </summary>
public int Index { get; set; }
public uint Index { get; set; }
/// <summary>
/// 星数

View File

@@ -26,7 +26,7 @@ internal sealed class SimpleSpiralAbyss
/// <summary>
/// 计划Id
/// </summary>
public int ScheduleId { get; set; }
public uint ScheduleId { get; set; }
/// <summary>
/// 总战斗次数