mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
spiral abyss rework
This commit is contained in:
@@ -89,6 +89,12 @@ internal sealed class IdentityGenerator : IIncrementalGenerator
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
x:Class="Snap.Hutao.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwcw="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:cwc="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:cwcw="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Control;
|
||||
[TemplateVisualState(Name = "LoadingOut", GroupName = "CommonStates")]
|
||||
internal class Loading : Microsoft.UI.Xaml.Controls.ContentControl
|
||||
{
|
||||
private static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(Loading), new PropertyMetadata(default(bool), IsLoadingPropertyChanged));
|
||||
public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(Loading), new PropertyMetadata(default(bool), IsLoadingPropertyChanged));
|
||||
|
||||
private FrameworkElement? presenter;
|
||||
|
||||
|
||||
@@ -11,20 +11,6 @@ namespace Snap.Hutao.Extension;
|
||||
/// </summary>
|
||||
internal static partial class EnumerableExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 尝试添加物品
|
||||
/// </summary>
|
||||
/// <typeparam name="T">物品类型</typeparam>
|
||||
/// <param name="collection">集合</param>
|
||||
/// <param name="item">物品</param>
|
||||
public static void AddIfNotContains<T>(this Collection<T> collection, T item)
|
||||
{
|
||||
if (!collection.Contains(item))
|
||||
{
|
||||
collection.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this Collection<TSource>? source)
|
||||
{
|
||||
@@ -57,4 +43,17 @@ internal static partial class EnumerableExtension
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public static int FirstIndexOf<T>(this Collection<T> collection, Func<T, bool> predicate)
|
||||
{
|
||||
for (int index = 0; index < collection.Count; index++)
|
||||
{
|
||||
if (predicate(collection[index]))
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,14 +59,4 @@ internal sealed class SpiralAbyssEntry : ObservableObject,
|
||||
SpiralAbyss = spiralAbyss,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新深渊信息
|
||||
/// </summary>
|
||||
/// <param name="spiralAbyss">深渊信息</param>
|
||||
public void UpdateSpiralAbyss(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss)
|
||||
{
|
||||
SpiralAbyss = spiralAbyss;
|
||||
OnPropertyChanged(nameof(SpiralAbyss));
|
||||
}
|
||||
}
|
||||
@@ -862,7 +862,7 @@ namespace Snap.Hutao.Resource.Localization {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 打怪 的本地化字符串。
|
||||
/// 查找类似 击败怪物 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ModelMetadataTowerGoalTypeDefeatMonsters {
|
||||
get {
|
||||
@@ -871,7 +871,7 @@ namespace Snap.Hutao.Resource.Localization {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 守塔 的本地化字符串。
|
||||
/// 查找类似 守护目标 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ModelMetadataTowerGoalTypeDefendTarget {
|
||||
get {
|
||||
@@ -880,7 +880,7 @@ namespace Snap.Hutao.Resource.Localization {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 附加 的本地化字符串。
|
||||
/// 查找类似 附加:增援怪物 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ModelMetadataTowerWaveTypeAdditional {
|
||||
get {
|
||||
@@ -1060,7 +1060,7 @@ namespace Snap.Hutao.Resource.Localization {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 第一波附加:为第一波补充怪物 的本地化字符串。
|
||||
/// 查找类似 第一波附加:增援第一波怪物 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ModelMetadataTowerWaveTypeWave1Additional {
|
||||
get {
|
||||
|
||||
@@ -414,13 +414,13 @@
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerGoalTypeDefeatMonsters" xml:space="preserve">
|
||||
<value>打怪</value>
|
||||
<value>击败怪物</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerGoalTypeDefendTarget" xml:space="preserve">
|
||||
<value>守塔</value>
|
||||
<value>守护目标</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeAdditional" xml:space="preserve">
|
||||
<value>附加</value>
|
||||
<value>附加:增援怪物</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeGroupA" xml:space="preserve">
|
||||
<value>A组:不同的组同时在场,各自分波独立</value>
|
||||
@@ -480,7 +480,7 @@
|
||||
<value>第一波:击败所有怪物,下一波才会出现</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave1Additional" xml:space="preserve">
|
||||
<value>第一波附加:为第一波补充怪物</value>
|
||||
<value>第一波附加:增援第一波怪物</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave2" xml:space="preserve">
|
||||
<value>第二波:击败所有怪物,下一波才会出现</value>
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Metadata.Monster;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Metadata.Tower;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
@@ -70,10 +72,18 @@ internal interface IMetadataServiceIdDataMap
|
||||
/// <returns>字典</returns>
|
||||
ValueTask<Dictionary<ReliquaryMainAffixId, FightProperty>> GetIdToReliquaryMainPropertyMapAsync(CancellationToken token = default);
|
||||
|
||||
ValueTask<Dictionary<TowerScheduleId, TowerSchedule>> GetIdToTowerScheduleMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取ID到武器的字典
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>Id到武器的字典</returns>
|
||||
ValueTask<Dictionary<WeaponId, Weapon>> GetIdToWeaponMapAsync(CancellationToken token = default);
|
||||
|
||||
ValueTask<Dictionary<TowerLevelGroupId, List<TowerLevel>>> GetGroupIdToTowerLevelGroupMapAsync(CancellationToken token = default);
|
||||
|
||||
ValueTask<Dictionary<TowerFloorId, TowerFloor>> GetIdToTowerFloorMapAsync(CancellationToken token = default);
|
||||
|
||||
ValueTask<Dictionary<MonsterRelationshipId, Monster>> GetRelationshipIdToMonsterMapAsync(CancellationToken token = default);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Metadata.Monster;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Metadata.Tower;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
@@ -41,6 +43,21 @@ internal sealed partial class MetadataService
|
||||
return memoryCache.Set(cacheKey, dict);
|
||||
}
|
||||
|
||||
public async ValueTask<Dictionary<TowerLevelGroupId, List<TowerLevel>>> GetGroupIdToTowerLevelGroupMapAsync(CancellationToken token = default)
|
||||
{
|
||||
string cacheKey = $"{nameof(MetadataService)}.Cache.{FileNameTowerLevel}.Map.Group.{nameof(TowerLevelGroupId)}";
|
||||
|
||||
if (memoryCache.TryGetValue(cacheKey, out object? value))
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
return (Dictionary<TowerLevelGroupId, List<TowerLevel>>)value;
|
||||
}
|
||||
|
||||
List<TowerLevel> list = await FromCacheOrFileAsync<List<TowerLevel>>(FileNameTowerLevel, token).ConfigureAwait(false);
|
||||
Dictionary<TowerLevelGroupId, List<TowerLevel>> dict = list.GroupBy(l => l.GroupId).ToDictionary(g => g.Key, g => g.ToList());
|
||||
return memoryCache.Set(cacheKey, dict);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<AchievementId, Model.Metadata.Achievement.Achievement>> GetIdToAchievementMapAsync(CancellationToken token = default)
|
||||
{
|
||||
@@ -102,6 +119,16 @@ internal sealed partial class MetadataService
|
||||
return FromCacheAsDictionaryAsync<ReliquaryMainAffixId, FightProperty, ReliquaryMainAffix>(FileNameReliquaryMainAffix, r => r.Id, r => r.Type, token);
|
||||
}
|
||||
|
||||
public ValueTask<Dictionary<TowerFloorId, TowerFloor>> GetIdToTowerFloorMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<TowerFloorId, TowerFloor>(FileNameTowerFloor, t => t.Id, token);
|
||||
}
|
||||
|
||||
public ValueTask<Dictionary<TowerScheduleId, TowerSchedule>> GetIdToTowerScheduleMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<TowerScheduleId, TowerSchedule>(FileNameTowerSchedule, t => t.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<WeaponId, Weapon>> GetIdToWeaponMapAsync(CancellationToken token = default)
|
||||
{
|
||||
@@ -126,6 +153,11 @@ internal sealed partial class MetadataService
|
||||
return FromCacheAsDictionaryAsync<Level, Dictionary<GrowCurveType, float>, GrowCurve>(FileNameWeaponCurve, w => w.Level, w => w.Map, token);
|
||||
}
|
||||
|
||||
public ValueTask<Dictionary<MonsterRelationshipId, Monster>> GetRelationshipIdToMonsterMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<MonsterRelationshipId, Monster>(FileNameMonster, m => m.RelationshipId, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<string, Avatar>> GetNameToAvatarMapAsync(CancellationToken token = default)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ internal interface ISpiralAbyssRecordDbService
|
||||
{
|
||||
ValueTask AddSpiralAbyssEntryAsync(SpiralAbyssEntry entry);
|
||||
|
||||
ValueTask<ObservableCollection<SpiralAbyssEntry>> GetSpiralAbyssEntryCollectionByUidAsync(string uid);
|
||||
ValueTask<List<SpiralAbyssEntry>> GetSpiralAbyssEntryListByUidAsync(string uid);
|
||||
|
||||
ValueTask UpdateSpiralAbyssEntryAsync(SpiralAbyssEntry entry);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.ViewModel.SpiralAbyss;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
@@ -18,7 +19,9 @@ internal interface ISpiralAbyssRecordService
|
||||
/// </summary>
|
||||
/// <param name="userAndUid">当前角色</param>
|
||||
/// <returns>深渊记录集合</returns>
|
||||
ValueTask<ObservableCollection<SpiralAbyssEntry>> GetSpiralAbyssCollectionAsync(UserAndUid userAndUid);
|
||||
ValueTask<ObservableCollection<SpiralAbyssView>> GetSpiralAbyssViewCollectionAsync(UserAndUid userAndUid);
|
||||
|
||||
ValueTask<bool> InitializeAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 异步刷新深渊记录
|
||||
|
||||
@@ -15,19 +15,16 @@ internal sealed partial class SpiralAbyssRecordDbService : ISpiralAbyssRecordDbS
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public async ValueTask<ObservableCollection<SpiralAbyssEntry>> GetSpiralAbyssEntryCollectionByUidAsync(string uid)
|
||||
public async ValueTask<List<SpiralAbyssEntry>> GetSpiralAbyssEntryListByUidAsync(string uid)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
List<SpiralAbyssEntry> entries = await appDbContext.SpiralAbysses
|
||||
return await appDbContext.SpiralAbysses
|
||||
.Where(s => s.Uid == uid)
|
||||
.OrderByDescending(s => s.ScheduleId)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return entries.ToObservableCollection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
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;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||
using Snap.Hutao.Web.Response;
|
||||
@@ -18,15 +22,35 @@ namespace Snap.Hutao.Service.SpiralAbyss;
|
||||
[Injection(InjectAs.Scoped, typeof(ISpiralAbyssRecordService))]
|
||||
internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordService
|
||||
{
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly IOverseaSupportFactory<IGameRecordClient> gameRecordClientFactory;
|
||||
private readonly ISpiralAbyssRecordDbService spiralAbyssRecordDbService;
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private string? uid;
|
||||
private ObservableCollection<SpiralAbyssEntry>? spiralAbysses;
|
||||
private ObservableCollection<SpiralAbyssView>? spiralAbysses;
|
||||
private SpiralAbyssMetadataContext? metadataContext;
|
||||
|
||||
public async ValueTask<bool> InitializeAsync()
|
||||
{
|
||||
if (await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
metadataContext = new()
|
||||
{
|
||||
IdScheduleMap = await metadataService.GetIdToTowerScheduleMapAsync().ConfigureAwait(false),
|
||||
IdFloorMap = await metadataService.GetIdToTowerFloorMapAsync().ConfigureAwait(false),
|
||||
IdLevelGroupMap = await metadataService.GetGroupIdToTowerLevelGroupMapAsync().ConfigureAwait(false),
|
||||
IdMonsterMap = await metadataService.GetRelationshipIdToMonsterMapAsync().ConfigureAwait(false),
|
||||
IdAvatarMap = AvatarIds.WithPlayers(await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false)),
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ObservableCollection<SpiralAbyssEntry>> GetSpiralAbyssCollectionAsync(UserAndUid userAndUid)
|
||||
public async ValueTask<ObservableCollection<SpiralAbyssView>> GetSpiralAbyssViewCollectionAsync(UserAndUid userAndUid)
|
||||
{
|
||||
if (uid != userAndUid.Uid.Value)
|
||||
{
|
||||
@@ -34,9 +58,15 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
|
||||
}
|
||||
|
||||
uid = userAndUid.Uid.Value;
|
||||
spiralAbysses ??= await spiralAbyssRecordDbService
|
||||
.GetSpiralAbyssEntryCollectionByUidAsync(userAndUid.Uid.Value)
|
||||
.ConfigureAwait(false);
|
||||
if (spiralAbysses is null)
|
||||
{
|
||||
List<SpiralAbyssEntry> list = await spiralAbyssRecordDbService
|
||||
.GetSpiralAbyssEntryListByUidAsync(userAndUid.Uid.Value)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
ArgumentNullException.ThrowIfNull(metadataContext);
|
||||
spiralAbysses = list.SelectList(entity => SpiralAbyssView.From(entity, metadataContext)).ToObservableCollection();
|
||||
}
|
||||
|
||||
return spiralAbysses;
|
||||
}
|
||||
@@ -44,6 +74,11 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask RefreshSpiralAbyssAsync(UserAndUid userAndUid)
|
||||
{
|
||||
// request the index first
|
||||
await gameRecordClientFactory
|
||||
.Create(userAndUid.User.IsOversea)
|
||||
.GetPlayerInfoAsync(userAndUid)
|
||||
.ConfigureAwait(false);
|
||||
await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Last).ConfigureAwait(false);
|
||||
await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Current).ConfigureAwait(false);
|
||||
}
|
||||
@@ -60,20 +95,26 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
|
||||
Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss webSpiralAbyss = response.Data;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(spiralAbysses);
|
||||
if (spiralAbysses.SingleOrDefault(s => s.ScheduleId == webSpiralAbyss.ScheduleId) is { } existEntry)
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
existEntry.UpdateSpiralAbyss(webSpiralAbyss);
|
||||
ArgumentNullException.ThrowIfNull(metadataContext);
|
||||
|
||||
int index = spiralAbysses.FirstIndexOf(s => s.Entity.ScheduleId == webSpiralAbyss.ScheduleId);
|
||||
if (index > 0)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
await spiralAbyssRecordDbService.UpdateSpiralAbyssEntryAsync(existEntry).ConfigureAwait(false);
|
||||
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);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
spiralAbysses.Insert(0, newEntry);
|
||||
spiralAbysses.Insert(0, SpiralAbyssView.From(newEntry, metadataContext));
|
||||
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
await spiralAbyssRecordDbService.AddSpiralAbyssEntryAsync(newEntry).ConfigureAwait(false);
|
||||
|
||||
@@ -188,12 +188,17 @@
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DataTemplate x:DataType="shva:AchievementView">
|
||||
<Border
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BorderCardStyle}">
|
||||
<Border.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem IsEnabled="False" Text="{Binding Inner.Id}"/>
|
||||
</MenuFlyout>
|
||||
</Border.ContextFlyout>
|
||||
<Grid MinHeight="48" Padding="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
|
||||
@@ -27,15 +27,15 @@
|
||||
IsPaneOpen="True"
|
||||
OpenPaneLength="120"
|
||||
PaneBackground="Transparent"
|
||||
Visibility="{Binding SpiralAbyssView, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
|
||||
Visibility="{Binding SelectedView, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
|
||||
<SplitView.Pane>
|
||||
<ListView
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
ItemsSource="{Binding SpiralAbyssEntries}"
|
||||
SelectedItem="{Binding SelectedEntry, Mode=TwoWay}">
|
||||
SelectedItem="{Binding SelectedView, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Schedule}"/>
|
||||
<TextBlock Text="{Binding Entity.Schedule}"/>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
@@ -55,30 +55,32 @@
|
||||
Label="{shcm:ResourceString Name=ViewSpiralAbyssRefresh}"/>
|
||||
</CommandBar>
|
||||
</Pivot.RightHeader>
|
||||
<PivotItem DataContext="{Binding SpiralAbyssView}" Header="{shcm:ResourceString Name=ViewSpiralAbyssStatistics}">
|
||||
<PivotItem DataContext="{Binding SelectedView}" Header="{shcm:ResourceString Name=ViewSpiralAbyssStatistics}">
|
||||
<ScrollViewer>
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
|
||||
</Grid.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MaxWidth="600"/>
|
||||
<ColumnDefinition Width="286"/>
|
||||
<ColumnDefinition Width="286"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Margin="16,0,8,16"
|
||||
Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<StackPanel.Resources>
|
||||
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
|
||||
</StackPanel.Resources>
|
||||
<cwc:SettingsCard
|
||||
Margin="0,16,0,0"
|
||||
Content="{Binding TotalBattleTimes}"
|
||||
Header="{shcm:ResourceString Name=ViewSpiralAbyssBattleTimes}"/>
|
||||
<cwc:SettingsCard Content="{Binding TotalStar}" Header="{shcm:ResourceString Name=ViewSpiralAbyssTotalStar}"/>
|
||||
<cwc:SettingsCard Content="{Binding MaxFloor}" Header="{shcm:ResourceString Name=ViewSpiralAbyssMaxFloor}"/>
|
||||
<cwc:SettingsCard Content="{Binding TotalBattleTimes}" Header="{shcm:ResourceString Name=ViewSpiralAbyssBattleTimes}"/>
|
||||
<cwc:SettingsCard Content="{Binding TotalStar}" Header="{shcm:ResourceString Name=ViewSpiralAbyssTotalStar}"/>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewSpiralAbyssReveal}"/>
|
||||
|
||||
<TextBlock
|
||||
Margin="1,6,0,5"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewSpiralAbyssReveal}"/>
|
||||
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding Reveals}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
@@ -88,13 +90,19 @@
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<shvc:BottomTextControl Text="{Binding Value}">
|
||||
<shvc:ItemIcon Icon="{Binding Icon}" Quality="{Binding Quality}"/>
|
||||
<shvc:ItemIcon
|
||||
Width="52"
|
||||
Height="52"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="{Binding Quality}"/>
|
||||
</shvc:BottomTextControl>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewSpiralAbyssBattleHeader}"/>
|
||||
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<cwc:SettingsCard Header="{shcm:ResourceString Name=ViewSpiralAbyssDefeat}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
@@ -166,10 +174,9 @@
|
||||
</cwc:SettingsCard>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</ScrollViewer>
|
||||
</PivotItem>
|
||||
<PivotItem DataContext="{Binding SpiralAbyssView}" Header="{shcm:ResourceString Name=ViewSpiralAbyssDetail}">
|
||||
<PivotItem DataContext="{Binding SelectedView}" Header="{shcm:ResourceString Name=ViewSpiralAbyssDetail}">
|
||||
<ScrollViewer VerticalAlignment="Top" HorizontalScrollBarVisibility="Auto">
|
||||
<ItemsControl
|
||||
Margin="16,16,0,0"
|
||||
@@ -289,7 +296,7 @@
|
||||
</Grid>
|
||||
</SplitView.Content>
|
||||
</SplitView>
|
||||
<Grid Visibility="{Binding SpiralAbyssView, Converter={StaticResource EmptyObjectToVisibilityRevertConverter}}">
|
||||
<Grid Visibility="{Binding SelectedView, Converter={StaticResource EmptyObjectToVisibilityRevertConverter}}">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<shci:CachedImage
|
||||
Width="120"
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Monster;
|
||||
using Snap.Hutao.Model.Metadata.Tower;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.SpiralAbyss;
|
||||
|
||||
internal sealed class SpiralAbyssMetadataContext
|
||||
{
|
||||
public Dictionary<TowerScheduleId, TowerSchedule> IdScheduleMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<TowerFloorId, TowerFloor> IdFloorMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<TowerLevelGroupId, List<TowerLevel>> IdLevelGroupMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<MonsterRelationshipId, Monster> IdMonsterMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<AvatarId, Avatar> IdAvatarMap { get; set; } = default!;
|
||||
}
|
||||
@@ -27,42 +27,22 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
|
||||
{
|
||||
private readonly ISpiralAbyssRecordService spiralAbyssRecordService;
|
||||
private readonly HomaSpiralAbyssClient spiralAbyssClient;
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly IUserService userService;
|
||||
|
||||
private Dictionary<AvatarId, Model.Metadata.Avatar.Avatar>? idAvatarMap;
|
||||
private ObservableCollection<SpiralAbyssEntry>? spiralAbyssEntries;
|
||||
private SpiralAbyssEntry? selectedEntry;
|
||||
private SpiralAbyssView? spiralAbyssView;
|
||||
private ObservableCollection<SpiralAbyssView>? spiralAbyssEntries;
|
||||
private SpiralAbyssView? selectedView;
|
||||
|
||||
/// <summary>
|
||||
/// 深渊记录
|
||||
/// </summary>
|
||||
public ObservableCollection<SpiralAbyssEntry>? SpiralAbyssEntries { get => spiralAbyssEntries; set => SetProperty(ref spiralAbyssEntries, value); }
|
||||
public ObservableCollection<SpiralAbyssView>? SpiralAbyssEntries { get => spiralAbyssEntries; set => SetProperty(ref spiralAbyssEntries, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的深渊信息
|
||||
/// </summary>
|
||||
public SpiralAbyssEntry? SelectedEntry
|
||||
{
|
||||
get => selectedEntry; set
|
||||
{
|
||||
// We dont need to check the result here,
|
||||
// just refresh the view anyway.
|
||||
SetProperty(ref selectedEntry, value);
|
||||
if (value is not null && idAvatarMap is not null)
|
||||
{
|
||||
SpiralAbyssView = new(value.SpiralAbyss, idAvatarMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 深渊的只读视图
|
||||
/// </summary>
|
||||
public SpiralAbyssView? SpiralAbyssView { get => spiralAbyssView; set => SetProperty(ref spiralAbyssView, value); }
|
||||
public SpiralAbyssView? SelectedView { get => selectedView; set => SetProperty(ref selectedView, value); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Receive(UserChangedMessage message)
|
||||
@@ -73,17 +53,14 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
|
||||
}
|
||||
else
|
||||
{
|
||||
SpiralAbyssView = null;
|
||||
SelectedView = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async ValueTask<bool> InitializeUIAsync()
|
||||
{
|
||||
if (await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
if (await spiralAbyssRecordService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
|
||||
idAvatarMap = AvatarIds.WithPlayers(idAvatarMap);
|
||||
|
||||
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
|
||||
{
|
||||
await UpdateSpiralAbyssCollectionAsync(userAndUid).ConfigureAwait(false);
|
||||
@@ -100,13 +77,13 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
|
||||
|
||||
private async ValueTask UpdateSpiralAbyssCollectionAsync(UserAndUid userAndUid)
|
||||
{
|
||||
ObservableCollection<SpiralAbyssEntry>? temp = null;
|
||||
ObservableCollection<SpiralAbyssView>? collection = null;
|
||||
try
|
||||
{
|
||||
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
|
||||
{
|
||||
temp = await spiralAbyssRecordService
|
||||
.GetSpiralAbyssCollectionAsync(userAndUid)
|
||||
collection = await spiralAbyssRecordService
|
||||
.GetSpiralAbyssViewCollectionAsync(userAndUid)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -115,8 +92,8 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
|
||||
}
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
SpiralAbyssEntries = temp;
|
||||
SelectedEntry = SpiralAbyssEntries?.FirstOrDefault();
|
||||
SpiralAbyssEntries = collection;
|
||||
SelectedView = SpiralAbyssEntries?.FirstOrDefault();
|
||||
}
|
||||
|
||||
[Command("RefreshCommand")]
|
||||
@@ -140,7 +117,7 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
|
||||
}
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
SelectedEntry = SpiralAbyssEntries.FirstOrDefault();
|
||||
SelectedView = SpiralAbyssEntries.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// 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.Entity;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Tower;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.SpiralAbyss;
|
||||
@@ -9,27 +14,47 @@ namespace Snap.Hutao.ViewModel.SpiralAbyss;
|
||||
/// 深渊视图
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class SpiralAbyssView
|
||||
internal sealed class SpiralAbyssView : IEntityOnly<SpiralAbyssEntry>,
|
||||
IMappingFrom<SpiralAbyssView, SpiralAbyssEntry, SpiralAbyssMetadataContext>
|
||||
{
|
||||
private readonly SpiralAbyssEntry entity;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的深渊视图
|
||||
/// </summary>
|
||||
/// <param name="spiralAbyss">深渊信息</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <param name="idAvatarMap">Id角色映射</param>
|
||||
public SpiralAbyssView(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap)
|
||||
private SpiralAbyssView(SpiralAbyssEntry entity, SpiralAbyssMetadataContext context)
|
||||
{
|
||||
this.entity = entity;
|
||||
Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss = entity.SpiralAbyss;
|
||||
|
||||
TowerSchedule towerSchedule = context.IdScheduleMap[(uint)entity.ScheduleId];
|
||||
TimeFormatted = $"{towerSchedule.Open:yyyy/MM/dd HH:mm:ss} - {towerSchedule.Close:yyyy/MM/dd HH:mm:ss}";
|
||||
|
||||
BlessingName = towerSchedule.BuffName;
|
||||
Blessings = towerSchedule.Descriptions;
|
||||
|
||||
TotalBattleTimes = spiralAbyss.TotalBattleTimes;
|
||||
TotalStar = spiralAbyss.TotalStar;
|
||||
MaxFloor = spiralAbyss.MaxFloor;
|
||||
Reveals = spiralAbyss.RevealRank.SelectList(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId]));
|
||||
Defeat = spiralAbyss.DefeatRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault();
|
||||
Damage = spiralAbyss.DamageRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault();
|
||||
TakeDamage = spiralAbyss.TakeDamageRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault();
|
||||
NormalSkill = spiralAbyss.NormalSkillRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault();
|
||||
EnergySkill = spiralAbyss.EnergySkillRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault();
|
||||
Floors = spiralAbyss.Floors.Select(f => new FloorView(f, idAvatarMap)).Reverse().ToList();
|
||||
Reveals = spiralAbyss.RevealRank.SelectList(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId]));
|
||||
Defeat = spiralAbyss.DefeatRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault();
|
||||
Damage = spiralAbyss.DamageRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault();
|
||||
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();
|
||||
}
|
||||
|
||||
public SpiralAbyssEntry Entity { get => entity; }
|
||||
|
||||
public string TimeFormatted { get; }
|
||||
|
||||
public string BlessingName { get; }
|
||||
|
||||
public List<string> Blessings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 战斗次数
|
||||
/// </summary>
|
||||
@@ -79,4 +104,9 @@ internal sealed class SpiralAbyssView
|
||||
/// 层信息
|
||||
/// </summary>
|
||||
public List<FloorView> Floors { get; }
|
||||
|
||||
public static SpiralAbyssView From(SpiralAbyssEntry entity, SpiralAbyssMetadataContext context)
|
||||
{
|
||||
return new(entity, context);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user