mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
avatar filter re-enable
This commit is contained in:
@@ -19,9 +19,24 @@ public static class EnumExtension
|
||||
public static string GetDescription<TEnum>(this TEnum @enum)
|
||||
where TEnum : struct, Enum
|
||||
{
|
||||
string enumName = Must.NotNull(Enum.GetName(@enum)!);
|
||||
string enumName = Enum.GetName(@enum)!;
|
||||
FieldInfo? field = @enum.GetType().GetField(enumName);
|
||||
DescriptionAttribute? attr = field?.GetCustomAttribute<DescriptionAttribute>();
|
||||
return attr?.Description ?? enumName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取枚举的描述
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">枚举的类型</typeparam>
|
||||
/// <param name="enum">枚举值</param>
|
||||
/// <returns>描述</returns>
|
||||
public static string? GetDescriptionOrNull<TEnum>(this TEnum @enum)
|
||||
where TEnum : struct, Enum
|
||||
{
|
||||
string enumName = Enum.GetName(@enum)!;
|
||||
FieldInfo? field = @enum.GetType().GetField(enumName);
|
||||
DescriptionAttribute? attr = field?.GetCustomAttribute<DescriptionAttribute>();
|
||||
return attr?.Description;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
<Window
|
||||
x:Class="Snap.Hutao.LaunchGameWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shv="using:Snap.Hutao.ViewModel"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid
|
||||
Name="RootGrid"
|
||||
d:DataContext="{d:DesignInstance shv:LaunchGameViewModel}">
|
||||
|
||||
<Grid Name="RootGrid" d:DataContext="{d:DesignInstance shv:LaunchGameViewModel}">
|
||||
|
||||
<mxi:Interaction.Behaviors>
|
||||
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
|
||||
</mxi:Interaction.Behaviors>
|
||||
@@ -27,17 +25,17 @@
|
||||
Grid.Row="0"
|
||||
Height="32">
|
||||
<TextBlock
|
||||
Text="选择账号并启动"
|
||||
TextWrapping="NoWrap"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="12,0,0,0"/>
|
||||
Margin="12,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="选择账号并启动"
|
||||
TextWrapping="NoWrap"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
ItemsSource="{Binding GameAccounts}"
|
||||
SelectedItem="{Binding SelectedGameAccount,Mode=TwoWay}">
|
||||
SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -46,7 +44,7 @@
|
||||
<TextBlock
|
||||
Opacity="0.8"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding AttachUid,TargetNullValue=该账号尚未绑定 UID}"/>
|
||||
Text="{Binding AttachUid, TargetNullValue=该账号尚未绑定 UID}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
@@ -54,10 +52,10 @@
|
||||
</ListView>
|
||||
|
||||
<Button
|
||||
Margin="16"
|
||||
Grid.Row="2"
|
||||
Margin="16"
|
||||
HorizontalAlignment="Stretch"
|
||||
Content="启动游戏"
|
||||
Command="{Binding LaunchCommand}"/>
|
||||
Command="{Binding LaunchCommand}"
|
||||
Content="启动游戏"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<Window
|
||||
x:Class="Snap.Hutao.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shv="using:Snap.Hutao.View"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<shv:TitleView
|
||||
Margin="48,0,0,0"
|
||||
x:Name="TitleBarView"
|
||||
Height="44"
|
||||
x:Name="TitleBarView"/>
|
||||
|
||||
Margin="48,0,0,0"/>
|
||||
|
||||
<shv:MainView/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -10,6 +10,23 @@ namespace Snap.Hutao.Model.Binding.Cultivation;
|
||||
/// </summary>
|
||||
public class CultivateEntry : ItemBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的养成清单入口
|
||||
/// </summary>
|
||||
/// <param name="entry">实体入口</param>
|
||||
/// <param name="itemBase">对应物品</param>
|
||||
/// <param name="items">物品列表</param>
|
||||
public CultivateEntry(Entity.CultivateEntry entry, ItemBase itemBase, List<CultivateItem> items)
|
||||
{
|
||||
Id = entry.Id;
|
||||
EntryId = entry.InnerId;
|
||||
Name = itemBase.Name;
|
||||
Icon = itemBase.Icon;
|
||||
Badge = itemBase.Badge;
|
||||
Quality = itemBase.Quality;
|
||||
Items = items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
|
||||
@@ -16,11 +16,13 @@ public enum AssociationType
|
||||
/// <summary>
|
||||
/// 蒙德
|
||||
/// </summary>
|
||||
[Description("蒙德")]
|
||||
ASSOC_TYPE_MONDSTADT,
|
||||
|
||||
/// <summary>
|
||||
/// 璃月
|
||||
/// </summary>
|
||||
[Description("璃月")]
|
||||
ASSOC_TYPE_LIYUE,
|
||||
|
||||
/// <summary>
|
||||
@@ -31,35 +33,42 @@ public enum AssociationType
|
||||
/// <summary>
|
||||
/// 愚人众
|
||||
/// </summary>
|
||||
[Description("愚人众")]
|
||||
ASSOC_TYPE_FATUI,
|
||||
|
||||
/// <summary>
|
||||
/// 稻妻
|
||||
/// </summary>
|
||||
[Description("稻妻")]
|
||||
ASSOC_TYPE_INAZUMA,
|
||||
|
||||
/// <summary>
|
||||
/// 游侠
|
||||
/// </summary>
|
||||
[Description("游侠")]
|
||||
ASSOC_TYPE_RANGER,
|
||||
|
||||
/// <summary>
|
||||
/// 须弥
|
||||
/// </summary>
|
||||
[Description("须弥")]
|
||||
ASSOC_TYPE_SUMERU,
|
||||
|
||||
/// <summary>
|
||||
/// 枫丹
|
||||
/// </summary>
|
||||
[Description("枫丹")]
|
||||
ASSOC_TYPE_FONTAINE,
|
||||
|
||||
/// <summary>
|
||||
/// 纳塔
|
||||
/// </summary>
|
||||
[Description("纳塔")]
|
||||
ASSOC_TYPE_NATLAN,
|
||||
|
||||
/// <summary>
|
||||
/// 至冬
|
||||
/// </summary>
|
||||
[Description("至冬")]
|
||||
ASSOC_TYPE_SNEZHNAYA,
|
||||
}
|
||||
@@ -16,25 +16,30 @@ public enum BodyType
|
||||
/// <summary>
|
||||
/// 男孩
|
||||
/// </summary>
|
||||
[Description("少男")]
|
||||
BODY_BOY,
|
||||
|
||||
/// <summary>
|
||||
/// 女孩
|
||||
/// </summary>
|
||||
[Description("少女")]
|
||||
BODY_GIRL,
|
||||
|
||||
/// <summary>
|
||||
/// 成女
|
||||
/// </summary>
|
||||
[Description("成女")]
|
||||
BODY_LADY,
|
||||
|
||||
/// <summary>
|
||||
/// 成男
|
||||
/// </summary>
|
||||
[Description("成男")]
|
||||
BODY_MALE,
|
||||
|
||||
/// <summary>
|
||||
/// 萝莉
|
||||
/// </summary>
|
||||
[Description("萝莉")]
|
||||
BODY_LOLI,
|
||||
}
|
||||
@@ -18,30 +18,36 @@ public enum ItemQuality
|
||||
/// <summary>
|
||||
/// 一星
|
||||
/// </summary>
|
||||
[Description("一星")]
|
||||
QUALITY_WHITE = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 二星
|
||||
/// </summary>
|
||||
[Description("二星")]
|
||||
QUALITY_GREEN = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 三星
|
||||
/// </summary>
|
||||
[Description("三星")]
|
||||
QUALITY_BLUE = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 四星
|
||||
/// </summary>
|
||||
[Description("四星")]
|
||||
QUALITY_PURPLE = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 五星
|
||||
/// </summary>
|
||||
[Description("五星")]
|
||||
QUALITY_ORANGE = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 限定五星
|
||||
/// </summary>
|
||||
[Description("限定五星")]
|
||||
QUALITY_ORANGE_SP = 105,
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public enum WeaponType
|
||||
/// <summary>
|
||||
/// 单手剑
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
WEAPON_SWORD_ONE_HAND = 1,
|
||||
|
||||
#region Not Used
|
||||
@@ -73,20 +74,24 @@ public enum WeaponType
|
||||
/// <summary>
|
||||
/// 法器
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
WEAPON_CATALYST = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 双手剑
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
WEAPON_CLAYMORE = 11,
|
||||
|
||||
/// <summary>
|
||||
/// 弓
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
WEAPON_BOW = 12,
|
||||
|
||||
/// <summary>
|
||||
/// 长柄武器
|
||||
/// </summary>
|
||||
[Description("单手剑")]
|
||||
WEAPON_POLE = 13,
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Snap.Hutao.Model;
|
||||
|
||||
/// <summary>
|
||||
/// 可选择的对象
|
||||
/// 默认为选中状态
|
||||
/// </summary>
|
||||
/// <typeparam name="T">值的类型</typeparam>
|
||||
public class Selectable<T> : ObservableObject
|
||||
where T : class
|
||||
{
|
||||
private readonly Action? selectedChanged;
|
||||
|
||||
private bool isSelected = true;
|
||||
private T value;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的可选择的对象
|
||||
/// </summary>
|
||||
/// <param name="value">值</param>
|
||||
/// <param name="onSelectedChanged">选中的值发生变化时调用</param>
|
||||
public Selectable(T value, Action? onSelectedChanged = null)
|
||||
{
|
||||
this.value = value;
|
||||
selectedChanged = onSelectedChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指示当前对象是否选中
|
||||
/// </summary>
|
||||
public bool IsSelected
|
||||
{
|
||||
get => isSelected;
|
||||
set
|
||||
{
|
||||
SetProperty(ref isSelected, value);
|
||||
selectedChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存放的对象
|
||||
/// </summary>
|
||||
public T Value { get => value; set => SetProperty(ref this.value, value); }
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
||||
Publisher="CN=DGP Studio"
|
||||
Version="1.2.10.0" />
|
||||
Version="1.2.12.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>胡桃</DisplayName>
|
||||
|
||||
@@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Binding.Cultivation;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -136,43 +135,45 @@ internal class CultivationService : ICultivationService
|
||||
Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap,
|
||||
Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap)
|
||||
{
|
||||
// TODO: cache the collection
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
Guid projectId = cultivateProject.InnerId;
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
List<BindingCultivateEntry> bindingEntries = new();
|
||||
foreach (Model.Entity.CultivateEntry? entry in await appDbContext.CultivateEntries.ToListAsync().ConfigureAwait(false))
|
||||
|
||||
Guid projectId = cultivateProject.InnerId;
|
||||
|
||||
List<BindingCultivateEntry> results = new();
|
||||
List<CultivateEntry> entries = await appDbContext.CultivateEntries
|
||||
.Where(e => e.ProjectId == projectId)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (CultivateEntry? entry in entries)
|
||||
{
|
||||
Guid entryId = entry.InnerId;
|
||||
|
||||
List<BindingCultivateItem> items = new();
|
||||
foreach (Model.Entity.CultivateItem? item in await appDbContext.CultivateItems.Where(i => i.EntryId == entryId).OrderBy(i => i.ItemId).ToListAsync().ConfigureAwait(false))
|
||||
List<BindingCultivateItem> resultItems = new();
|
||||
List<CultivateItem> items = await appDbContext.CultivateItems
|
||||
.Where(i => i.EntryId == entryId)
|
||||
.OrderBy(i => i.ItemId).ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (CultivateItem item in items)
|
||||
{
|
||||
items.Add(new(metadata.Single(m => m.Id == item.ItemId), item));
|
||||
resultItems.Add(new(metadata.Single(m => m.Id == item.ItemId), item));
|
||||
}
|
||||
|
||||
Model.Binding.Gacha.Abstraction.ItemBase itemBase = entry.Type switch
|
||||
{
|
||||
CultivateType.AvatarAndSkill => idAvatarMap[entry.Id].ToItemBase(),
|
||||
CultivateType.Weapon => idWeaponMap[entry.Id].ToItemBase(),
|
||||
Model.Binding.Cultivation.CultivateType.AvatarAndSkill => idAvatarMap[entry.Id].ToItemBase(),
|
||||
Model.Binding.Cultivation.CultivateType.Weapon => idWeaponMap[entry.Id].ToItemBase(),
|
||||
_ => null!, // TODO: support furniture calc
|
||||
};
|
||||
|
||||
bindingEntries.Add(new()
|
||||
{
|
||||
Id = entry.Id,
|
||||
EntryId = entryId,
|
||||
Name = itemBase.Name,
|
||||
Icon = itemBase.Icon,
|
||||
Badge = itemBase.Badge,
|
||||
Quality = itemBase.Quality,
|
||||
Items = items,
|
||||
});
|
||||
results.Add(new(entry, itemBase, resultItems));
|
||||
}
|
||||
|
||||
return new(bindingEntries);
|
||||
return new(results);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +198,7 @@ internal class CultivationService : ICultivationService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> SaveConsumptionAsync(CultivateType type, int itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items)
|
||||
public async Task<bool> SaveConsumptionAsync(Model.Binding.Cultivation.CultivateType type, int itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items)
|
||||
{
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
@@ -210,19 +211,20 @@ internal class CultivationService : ICultivationService
|
||||
}
|
||||
|
||||
Guid projectId = Current!.InnerId;
|
||||
Model.Entity.CultivateEntry? entry = await appDbContext.CultivateEntries
|
||||
CultivateEntry? entry = await appDbContext.CultivateEntries
|
||||
.SingleOrDefaultAsync(e => e.ProjectId == projectId && e.Id == itemId)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
entry = Model.Entity.CultivateEntry.Create(projectId, type, itemId);
|
||||
entry = CultivateEntry.Create(projectId, type, itemId);
|
||||
await appDbContext.CultivateEntries.AddAndSaveAsync(entry).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Guid entryId = entry.InnerId;
|
||||
await appDbContext.CultivateItems.Where(i => i.EntryId == entryId).ExecuteDeleteAsync().ConfigureAwait(false);
|
||||
IEnumerable<Model.Entity.CultivateItem> toAdd = items.Select(i => Model.Entity.CultivateItem.Create(entryId, i.Id, i.Num));
|
||||
|
||||
IEnumerable<CultivateItem> toAdd = items.Select(i => CultivateItem.Create(entryId, i.Id, i.Num));
|
||||
await appDbContext.CultivateItems.AddRangeAndSaveAsync(toAdd).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -257,4 +259,4 @@ internal class CultivationService : ICultivationService
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,12 +36,11 @@
|
||||
<AutoSuggestBox
|
||||
Width="240"
|
||||
Height="36"
|
||||
Margin="12,6,12,0"
|
||||
Margin="12,6,6,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
PlaceholderText="搜索成就名称,描述或编号"
|
||||
QueryIcon="{shcm:FontIcon Glyph=}"
|
||||
Style="{StaticResource DefaultAutoSuggestBoxStyle}"
|
||||
Text="{Binding SearchText, Mode=TwoWay}">
|
||||
<mxi:Interaction.Behaviors>
|
||||
<mxic:EventTriggerBehavior EventName="QuerySubmitted">
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
@@ -36,7 +37,6 @@
|
||||
PreferredSelectedIndex="9"
|
||||
Source="{Binding Proud, Mode=OneWay, Converter={StaticResource DescParamDescriptor}}"/>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -72,6 +72,23 @@
|
||||
<CommandBar.Content>
|
||||
<shcp:PanelSelector x:Name="ItemsPanelSelector" Margin="6,8,0,0"/>
|
||||
</CommandBar.Content>
|
||||
<AppBarElementContainer>
|
||||
<AutoSuggestBox
|
||||
Width="240"
|
||||
Height="36"
|
||||
Margin="16,6,6,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
PlaceholderText="筛选角色"
|
||||
QueryIcon="{shcm:FontIcon Glyph=}"
|
||||
Text="{Binding FilterText, Mode=TwoWay}">
|
||||
<mxi:Interaction.Behaviors>
|
||||
<mxic:EventTriggerBehavior EventName="QuerySubmitted">
|
||||
<mxic:InvokeCommandAction Command="{Binding FilterCommand}" CommandParameter="{Binding FilterText}"/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
</mxi:Interaction.Behaviors>
|
||||
</AutoSuggestBox>
|
||||
</AppBarElementContainer>
|
||||
<AppBarButton
|
||||
Command="{Binding CultivateCommand}"
|
||||
CommandParameter="{Binding Selected}"
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.WinUI.UI;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Factory.Abstraction;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Binding.Cultivation;
|
||||
using Snap.Hutao.Model.Binding.Hutao;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
@@ -16,6 +18,7 @@ using Snap.Hutao.Service.Hutao;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.View.Dialog;
|
||||
using System.Collections.Immutable;
|
||||
using CalcAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
|
||||
using CalcClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
|
||||
using CalcConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption;
|
||||
@@ -33,15 +36,9 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly IHutaoCache hutaoCache;
|
||||
|
||||
// filters
|
||||
private readonly List<Selectable<string>> filterElementInfos;
|
||||
private readonly List<Selectable<Pair<string, AssociationType>>> filterAssociationInfos;
|
||||
private readonly List<Selectable<Pair<string, WeaponType>>> filterWeaponTypeInfos;
|
||||
private readonly List<Selectable<Pair<string, ItemQuality>>> filterQualityInfos;
|
||||
private readonly List<Selectable<Pair<string, BodyType>>> filterBodyInfos;
|
||||
|
||||
private AdvancedCollectionView? avatars;
|
||||
private Avatar? selected;
|
||||
private string? filterText;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的角色资料视图模型
|
||||
@@ -55,52 +52,7 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
this.hutaoCache = hutaoCache;
|
||||
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
|
||||
CultivateCommand = asyncRelayCommandFactory.Create<Avatar>(CultivateAsync);
|
||||
|
||||
filterElementInfos = new()
|
||||
{
|
||||
new("火", OnFilterChanged),
|
||||
new("水", OnFilterChanged),
|
||||
new("草", OnFilterChanged),
|
||||
new("雷", OnFilterChanged),
|
||||
new("冰", OnFilterChanged),
|
||||
new("风", OnFilterChanged),
|
||||
new("岩", OnFilterChanged),
|
||||
};
|
||||
|
||||
filterAssociationInfos = new()
|
||||
{
|
||||
new(new("蒙德", AssociationType.ASSOC_TYPE_MONDSTADT), OnFilterChanged),
|
||||
new(new("璃月", AssociationType.ASSOC_TYPE_LIYUE), OnFilterChanged),
|
||||
new(new("稻妻", AssociationType.ASSOC_TYPE_INAZUMA), OnFilterChanged),
|
||||
new(new("须弥", AssociationType.ASSOC_TYPE_SUMERU), OnFilterChanged),
|
||||
new(new("愚人众", AssociationType.ASSOC_TYPE_FATUI), OnFilterChanged),
|
||||
new(new("游侠", AssociationType.ASSOC_TYPE_RANGER), OnFilterChanged),
|
||||
};
|
||||
|
||||
filterWeaponTypeInfos = new()
|
||||
{
|
||||
new(new("单手剑", WeaponType.WEAPON_SWORD_ONE_HAND), OnFilterChanged),
|
||||
new(new("法器", WeaponType.WEAPON_CATALYST), OnFilterChanged),
|
||||
new(new("双手剑", WeaponType.WEAPON_CLAYMORE), OnFilterChanged),
|
||||
new(new("弓", WeaponType.WEAPON_BOW), OnFilterChanged),
|
||||
new(new("长柄武器", WeaponType.WEAPON_POLE), OnFilterChanged),
|
||||
};
|
||||
|
||||
filterQualityInfos = new()
|
||||
{
|
||||
new(new("限定五星", ItemQuality.QUALITY_ORANGE_SP), OnFilterChanged),
|
||||
new(new("五星", ItemQuality.QUALITY_ORANGE), OnFilterChanged),
|
||||
new(new("四星", ItemQuality.QUALITY_PURPLE), OnFilterChanged),
|
||||
};
|
||||
|
||||
filterBodyInfos = new()
|
||||
{
|
||||
new(new("成女", BodyType.BODY_LADY), OnFilterChanged),
|
||||
new(new("少女", BodyType.BODY_GIRL), OnFilterChanged),
|
||||
new(new("幼女", BodyType.BODY_LOLI), OnFilterChanged),
|
||||
new(new("成男", BodyType.BODY_MALE), OnFilterChanged),
|
||||
new(new("少男", BodyType.BODY_BOY), OnFilterChanged),
|
||||
};
|
||||
FilterCommand = new RelayCommand<string>(ApplyFilter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -114,44 +66,9 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
public Avatar? Selected { get => selected; set => SetProperty(ref selected, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用元素信息集合
|
||||
/// 筛选文本
|
||||
/// </summary>
|
||||
public IList<Selectable<string>> FilterElementInfos
|
||||
{
|
||||
get => filterElementInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用所属国家集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, AssociationType>>> FilterAssociationInfos
|
||||
{
|
||||
get => filterAssociationInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用武器信息集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, WeaponType>>> FilterWeaponTypeInfos
|
||||
{
|
||||
get => filterWeaponTypeInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用星级信息集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, ItemQuality>>> FilterQualityInfos
|
||||
{
|
||||
get => filterQualityInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用体型信息集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, BodyType>>> FilterBodyInfos
|
||||
{
|
||||
get => filterBodyInfos;
|
||||
}
|
||||
public string? FilterText { get => filterText; set => SetProperty(ref filterText, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 打开页面命令
|
||||
@@ -163,6 +80,11 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
/// </summary>
|
||||
public ICommand CultivateCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 筛选命令
|
||||
/// </summary>
|
||||
public ICommand FilterCommand { get; }
|
||||
|
||||
private async Task OpenUIAsync()
|
||||
{
|
||||
if (await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
@@ -194,55 +116,13 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFilterChanged()
|
||||
{
|
||||
if (Avatars is not null)
|
||||
{
|
||||
List<string> targetElements = filterElementInfos
|
||||
.Where(e => e.IsSelected)
|
||||
.Select(e => e.Value)
|
||||
.ToList();
|
||||
|
||||
List<AssociationType> targetAssociations = filterAssociationInfos
|
||||
.Where(e => e.IsSelected)
|
||||
.Select(e => e.Value.Value)
|
||||
.ToList();
|
||||
|
||||
List<WeaponType> targetWeaponTypes = filterWeaponTypeInfos
|
||||
.Where(e => e.IsSelected)
|
||||
.Select(e => e.Value.Value)
|
||||
.ToList();
|
||||
|
||||
List<ItemQuality> targetQualities = FilterQualityInfos
|
||||
.Where(e => e.IsSelected)
|
||||
.Select(e => e.Value.Value)
|
||||
.ToList();
|
||||
|
||||
List<BodyType> targetBodies = filterBodyInfos
|
||||
.Where(e => e.IsSelected)
|
||||
.Select(e => e.Value.Value)
|
||||
.ToList();
|
||||
|
||||
Avatars.Filter = (object o) => o is Avatar avatar
|
||||
&& targetElements.Contains(avatar.FetterInfo.VisionBefore)
|
||||
&& targetAssociations.Contains(avatar.FetterInfo.Association)
|
||||
&& targetWeaponTypes.Contains(avatar.Weapon)
|
||||
&& targetQualities.Contains(avatar.Quality)
|
||||
&& targetBodies.Contains(avatar.Body);
|
||||
|
||||
if (!Avatars.Contains(Selected))
|
||||
{
|
||||
Avatars.MoveCurrentToFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CultivateAsync(Avatar? avatar)
|
||||
{
|
||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
if (avatar != null)
|
||||
{
|
||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
IUserService userService = Ioc.Default.GetRequiredService<IUserService>();
|
||||
|
||||
if (userService.Current != null)
|
||||
{
|
||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
||||
@@ -252,8 +132,11 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
|
||||
if (isOk)
|
||||
{
|
||||
CalcClient calculateClient = Ioc.Default.GetRequiredService<CalcClient>();
|
||||
CalcConsumption? consumption = await calculateClient.ComputeAsync(userService.Current.Entity, delta).ConfigureAwait(false);
|
||||
CalcConsumption? consumption = await Ioc.Default
|
||||
.GetRequiredService<CalcClient>()
|
||||
.ComputeAsync(userService.Current.Entity, delta)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (consumption != null)
|
||||
{
|
||||
List<CalcItem> items = CalcItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
|
||||
@@ -279,4 +162,84 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyFilter(string? input)
|
||||
{
|
||||
if (Avatars != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
Avatars.Filter = AvatarFilter.Compile(input);
|
||||
|
||||
if (!Avatars.Contains(Selected))
|
||||
{
|
||||
Avatars.MoveCurrentToFirst();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Avatars.Filter = null!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class AvatarFilter
|
||||
{
|
||||
private static readonly ImmutableList<string> AssociationTypes = Enum.GetValues<AssociationType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
private static readonly ImmutableList<string> WeaponTypes = Enum.GetValues<WeaponType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
private static readonly ImmutableList<string> ItemQualities = Enum.GetValues<ItemQuality>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
private static readonly ImmutableList<string> BodyTypes = Enum.GetValues<BodyType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
|
||||
|
||||
public static Predicate<object> Compile(string input)
|
||||
{
|
||||
return (object o) => o is Avatar avatar && DoFilter(input, avatar);
|
||||
}
|
||||
|
||||
private static bool DoFilter(string input, Avatar avatar)
|
||||
{
|
||||
bool keep = false;
|
||||
|
||||
foreach (StringSegment segment in new StringTokenizer(input, ' '.Enumerate().ToArray()))
|
||||
{
|
||||
string value = segment.ToString();
|
||||
|
||||
if (value == "火" || value == "水" || value == "草" || value == "雷" || value == "冰" || value == "风" || value == "岩")
|
||||
{
|
||||
keep = keep || avatar.FetterInfo.VisionBefore == value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AssociationTypes.Contains(value))
|
||||
{
|
||||
keep = keep || avatar.FetterInfo.Association.GetDescriptionOrNull() == value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (WeaponTypes.Contains(value))
|
||||
{
|
||||
keep = keep || avatar.Weapon.GetDescriptionOrNull() == value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ItemQualities.Contains(value))
|
||||
{
|
||||
keep = keep || avatar.Quality.GetDescriptionOrNull() == value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BodyTypes.Contains(value))
|
||||
{
|
||||
keep = keep || avatar.Body.GetDescriptionOrNull() == value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (avatar.Name == value)
|
||||
{
|
||||
keep = true;
|
||||
}
|
||||
}
|
||||
|
||||
return keep;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,13 +80,13 @@ internal static class HttpClientExtensions
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HttpClientJsonExtensions.PostAsJsonAsync{TValue}(HttpClient, string?, TValue, JsonSerializerOptions?, CancellationToken)"/>
|
||||
internal static async Task<TResult?> TryCatchPostAsJsonAsync<TValue, TResult>(this HttpClient httpClient, string requestUri, TValue value, JsonSerializerOptions options, CancellationToken token = default)
|
||||
internal static async Task<TResult?> TryCatchPostAsJsonAsync<TValue, TResult>(this HttpClient httpClient, string requestUri, TValue value, CancellationToken token = default)
|
||||
where TResult : class
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpResponseMessage message = await httpClient.PostAsJsonAsync(requestUri, value, options, token).ConfigureAwait(false);
|
||||
return await message.Content.ReadFromJsonAsync<TResult>(options, token).ConfigureAwait(false);
|
||||
HttpResponseMessage message = await httpClient.PostAsJsonAsync(requestUri, value, token).ConfigureAwait(false);
|
||||
return await message.Content.ReadFromJsonAsync<TResult>(cancellationToken: token).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
|
||||
@@ -16,17 +16,14 @@ internal class HomaClient2
|
||||
{
|
||||
private const string HutaoAPI = "https://homa.snapgenshin.com";
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly JsonSerializerOptions options;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的胡桃日志客户端
|
||||
/// </summary>
|
||||
/// <param name="httpClient">Http客户端</param>
|
||||
/// <param name="options">Json序列化选项</param>
|
||||
public HomaClient2(HttpClient httpClient, JsonSerializerOptions options)
|
||||
public HomaClient2(HttpClient httpClient)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,7 +41,7 @@ internal class HomaClient2
|
||||
};
|
||||
|
||||
Response<string>? a = await httpClient
|
||||
.TryCatchPostAsJsonAsync<HutaoLog, Response<string>>($"{HutaoAPI}/HutaoLog/Upload", log, options)
|
||||
.TryCatchPostAsJsonAsync<HutaoLog, Response<string>>($"{HutaoAPI}/HutaoLog/Upload", log)
|
||||
.ConfigureAwait(false);
|
||||
return a?.Data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user