mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
cultivation improvement
This commit is contained in:
@@ -23,6 +23,11 @@ internal static class Activation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string LaunchGame = "LaunchGame";
|
public const string LaunchGame = "LaunchGame";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从剪贴板导入成就
|
||||||
|
/// </summary>
|
||||||
|
public const string ImportUIAFFromClipBoard = "ImportUIAFFromClipBoard";
|
||||||
|
|
||||||
private static readonly SemaphoreSlim ActivateSemaphore = new(1);
|
private static readonly SemaphoreSlim ActivateSemaphore = new(1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -180,7 +185,7 @@ internal static class Activation
|
|||||||
{
|
{
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
|
||||||
INavigationAwaiter navigationAwaiter = new NavigationExtra("InvokeByUri");
|
INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipBoard);
|
||||||
await Ioc.Default
|
await Ioc.Default
|
||||||
.GetRequiredService<INavigationService>()
|
.GetRequiredService<INavigationService>()
|
||||||
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
|
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ public class CultivateItem : ObservableObject
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsToday { get; }
|
public bool IsToday { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对应背包物品的个数
|
||||||
|
/// </summary>
|
||||||
|
public uint InventoryItemCount { get; set; }
|
||||||
|
|
||||||
private void FlipIsFinished()
|
private void FlipIsFinished()
|
||||||
{
|
{
|
||||||
IsFinished = !IsFinished;
|
IsFinished = !IsFinished;
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Snap.Hutao.Model.Metadata;
|
||||||
|
using Snap.Hutao.Service.Cultivation;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.Cultivation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 仅用于统计总数的养成物品
|
||||||
|
/// </summary>
|
||||||
|
public class StatisticsCultivateItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的统计用养成物品
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inner">材料</param>
|
||||||
|
/// <param name="entity">实体</param>
|
||||||
|
public StatisticsCultivateItem(Material inner, Entity.CultivateItem entity)
|
||||||
|
{
|
||||||
|
Inner = inner;
|
||||||
|
Count = entity.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元数据
|
||||||
|
/// </summary>
|
||||||
|
public Material Inner { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对应背包物品的个数
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对应背包物品的个数
|
||||||
|
/// </summary>
|
||||||
|
public uint TotalCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFinished { get => Count >= TotalCount; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 格式化总数
|
||||||
|
/// </summary>
|
||||||
|
public string CountFormatted { get => $"{Count}/{TotalCount}"; }
|
||||||
|
}
|
||||||
@@ -28,8 +28,14 @@ internal class Team : List<ComplexAvatar>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rate = $"上场 {team.Rate} 次";
|
Rate = $"上场 {team.Rate} 次";
|
||||||
|
Name = TeamPopularNameParser.GetName(ids.ToHashSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 队伍俗名
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上场次数
|
/// 上场次数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Metadata;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.Hutao;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 队伍俗名解析器
|
||||||
|
/// </summary>
|
||||||
|
internal static class TeamPopularNameParser
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 已知的队伍名称
|
||||||
|
/// </summary>
|
||||||
|
private static readonly ImmutableDictionary<string, ImmutableHashSet<int>> KnownTeamNames = new Dictionary<string, ImmutableHashSet<int>>()
|
||||||
|
{
|
||||||
|
["雷国|雷行香班"] = new HashSet<int>() { AvatarIds.Shougun, AvatarIds.Xingqiu, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||||
|
["雷国|雷夜香班"] = new HashSet<int>() { AvatarIds.Shougun, AvatarIds.Yelan, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||||
|
["雷国|雷万香班"] = new HashSet<int>() { AvatarIds.Shougun, AvatarIds.Kazuha, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||||
|
["雷九|雷九万班"] = new HashSet<int>() { AvatarIds.Shougun, AvatarIds.Sara, AvatarIds.Kazuha, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||||
|
["万达国际"] = new HashSet<int>() { AvatarIds.Kazuha, AvatarIds.Tartaglia, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||||
|
["胡行钟夜"] = new HashSet<int>() { AvatarIds.Hutao, AvatarIds.Xingqiu, AvatarIds.Zhongli, AvatarIds.Yelan, }.ToImmutableHashSet(),
|
||||||
|
["激晴|刻皇万妲"] = new HashSet<int>() { AvatarIds.Keqing, AvatarIds.Fischl, AvatarIds.Kazuha, AvatarIds.Nahida, }.ToImmutableHashSet(),
|
||||||
|
["永冻|神鹤万心"] = new HashSet<int>() { AvatarIds.Ayaka, AvatarIds.Shenhe, AvatarIds.Kazuha, AvatarIds.Kokomi, }.ToImmutableHashSet(),
|
||||||
|
["永冻|神钟万心"] = new HashSet<int>() { AvatarIds.Ayaka, AvatarIds.Zhongli, AvatarIds.Kazuha, AvatarIds.Kokomi, }.ToImmutableHashSet(),
|
||||||
|
["融甘|融化甘雨"] = new HashSet<int>() { AvatarIds.Ganyu, AvatarIds.Zhongli, AvatarIds.Xiangling, AvatarIds.Bennett, }.ToImmutableHashSet(),
|
||||||
|
["妮绽放|女主"] = new HashSet<int>() { AvatarIds.Nilou, AvatarIds.Kokomi, AvatarIds.Nahida, AvatarIds.PlayerGirl, }.ToImmutableHashSet(),
|
||||||
|
["妮绽放|男主"] = new HashSet<int>() { AvatarIds.Nilou, AvatarIds.Kokomi, AvatarIds.Nahida, AvatarIds.PlayerBoy, }.ToImmutableHashSet(),
|
||||||
|
["一斗岩队"] = new HashSet<int>() { AvatarIds.Itto, AvatarIds.Albedo, AvatarIds.Zhongli, AvatarIds.Gorou, }.ToImmutableHashSet(),
|
||||||
|
}.ToImmutableDictionary();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取队伍名称
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ids">队伍集</param>
|
||||||
|
/// <returns>队伍名称</returns>
|
||||||
|
public static string GetName(HashSet<int> ids)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, ImmutableHashSet<int>> entry in KnownTeamNames)
|
||||||
|
{
|
||||||
|
if (entry.Value.SetEquals(ids))
|
||||||
|
{
|
||||||
|
return entry.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "尚未命名";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,24 +74,24 @@ public enum WeaponType
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 法器
|
/// 法器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("单手剑")]
|
[Description("法器")]
|
||||||
WEAPON_CATALYST = 10,
|
WEAPON_CATALYST = 10,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 双手剑
|
/// 双手剑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("单手剑")]
|
[Description("双手剑")]
|
||||||
WEAPON_CLAYMORE = 11,
|
WEAPON_CLAYMORE = 11,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 弓
|
/// 弓
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("单手剑")]
|
[Description("弓")]
|
||||||
WEAPON_BOW = 12,
|
WEAPON_BOW = 12,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 长柄武器
|
/// 长柄武器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("单手剑")]
|
[Description("长柄武器")]
|
||||||
WEAPON_POLE = 13,
|
WEAPON_POLE = 13,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
||||||
Publisher="CN=DGP Studio"
|
Publisher="CN=DGP Studio"
|
||||||
Version="1.2.16.0" />
|
Version="1.2.19.0" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>胡桃</DisplayName>
|
<DisplayName>胡桃</DisplayName>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace Snap.Hutao.Service.Achievement;
|
|||||||
[Injection(InjectAs.Scoped, typeof(IAchievementService))]
|
[Injection(InjectAs.Scoped, typeof(IAchievementService))]
|
||||||
internal class AchievementService : IAchievementService
|
internal class AchievementService : IAchievementService
|
||||||
{
|
{
|
||||||
|
private readonly object saveAchievementLocker = new();
|
||||||
private readonly AppDbContext appDbContext;
|
private readonly AppDbContext appDbContext;
|
||||||
private readonly ILogger<AchievementService> logger;
|
private readonly ILogger<AchievementService> logger;
|
||||||
private readonly DbCurrent<EntityArchive, Message.AchievementArchiveChangedMessage> dbCurrent;
|
private readonly DbCurrent<EntityArchive, Message.AchievementArchiveChangedMessage> dbCurrent;
|
||||||
@@ -187,4 +188,19 @@ internal class AchievementService : IAchievementService
|
|||||||
logger.LogInformation(EventIds.Achievement, "{add} added, {update} updated, {remove} removed", result.Add, result.Update, result.Remove);
|
logger.LogInformation(EventIds.Achievement, "{add} added, {update} updated, {remove} removed", result.Add, result.Update, result.Remove);
|
||||||
logger.LogInformation(EventIds.Achievement, "Save achievements for [{name}] completed in {time}ms", name, time);
|
logger.LogInformation(EventIds.Achievement, "Save achievements for [{name}] completed in {time}ms", name, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void SaveAchievement(BindingAchievement achievement)
|
||||||
|
{
|
||||||
|
if (achievement.IsChecked)
|
||||||
|
{
|
||||||
|
// set to default allow multiple time add
|
||||||
|
achievement.Entity.InnerId = default;
|
||||||
|
appDbContext.Achievements.UpdateAndSave(achievement.Entity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appDbContext.Achievements.RemoveAndSave(achievement.Entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading.CodeAnalysis;
|
|
||||||
using Snap.Hutao.Model.InterChange.Achievement;
|
using Snap.Hutao.Model.InterChange.Achievement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
|
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
|
||||||
@@ -57,6 +56,12 @@ internal interface IAchievementService
|
|||||||
/// <returns>任务</returns>
|
/// <returns>任务</returns>
|
||||||
Task RemoveArchiveAsync(EntityArchive archive);
|
Task RemoveArchiveAsync(EntityArchive archive);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存单个成就
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="achievement">成就</param>
|
||||||
|
void SaveAchievement(BindingAchievement achievement);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保存成就
|
/// 保存成就
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -69,6 +74,5 @@ internal interface IAchievementService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="newArchive">新存档</param>
|
/// <param name="newArchive">新存档</param>
|
||||||
/// <returns>存档添加结果</returns>
|
/// <returns>存档添加结果</returns>
|
||||||
[ThreadAccess(ThreadAccessState.AnyThread)]
|
|
||||||
Task<ArchiveAddResult> TryAddArchiveAsync(EntityArchive newArchive);
|
Task<ArchiveAddResult> TryAddArchiveAsync(EntityArchive newArchive);
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ internal class CultivationService : ICultivationService
|
|||||||
.ToListAsync()
|
.ToListAsync()
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (CultivateEntry? entry in entries)
|
foreach (CultivateEntry entry in entries)
|
||||||
{
|
{
|
||||||
Guid entryId = entry.InnerId;
|
Guid entryId = entry.InnerId;
|
||||||
|
|
||||||
@@ -178,6 +178,70 @@ internal class CultivationService : ICultivationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<List<Model.Binding.Cultivation.StatisticsCultivateItem>> GetStatisticsCultivateItemsAsync(CultivateProject cultivateProject, List<Model.Metadata.Material> materials)
|
||||||
|
{
|
||||||
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
|
Guid projectId = cultivateProject.InnerId;
|
||||||
|
|
||||||
|
List<Model.Binding.Cultivation.StatisticsCultivateItem> resultItems = new();
|
||||||
|
|
||||||
|
List<CultivateEntry> entries = await appDbContext.CultivateEntries
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(e => e.ProjectId == projectId)
|
||||||
|
.ToListAsync()
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
foreach (CultivateEntry entry in entries)
|
||||||
|
{
|
||||||
|
Guid entryId = entry.InnerId;
|
||||||
|
|
||||||
|
List<CultivateItem> items = await appDbContext.CultivateItems
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(i => i.EntryId == entryId)
|
||||||
|
.OrderBy(i => i.ItemId)
|
||||||
|
.ToListAsync()
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
foreach (CultivateItem item in items)
|
||||||
|
{
|
||||||
|
if (item.IsFinished)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultItems.SingleOrDefault(i => i.Inner.Id == item.ItemId) is Model.Binding.Cultivation.StatisticsCultivateItem inPlaceItem)
|
||||||
|
{
|
||||||
|
inPlaceItem.Count += item.Count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resultItems.Add(new(materials.Single(m => m.Id == item.ItemId), item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<InventoryItem> inventoryItems = await appDbContext.InventoryItems
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(e => e.ProjectId == projectId)
|
||||||
|
.ToListAsync()
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
foreach (InventoryItem inventoryItem in inventoryItems)
|
||||||
|
{
|
||||||
|
if (resultItems.SingleOrDefault(i => i.Inner.Id == inventoryItem.ItemId) is Model.Binding.Cultivation.StatisticsCultivateItem inPlaceItem)
|
||||||
|
{
|
||||||
|
inPlaceItem.TotalCount += inventoryItem.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultItems.OrderByDescending(i => i.Count).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task RemoveCultivateEntryAsync(Guid entryId)
|
public async Task RemoveCultivateEntryAsync(Guid entryId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ internal interface ICultivationService
|
|||||||
/// <returns>项目集合</returns>
|
/// <returns>项目集合</returns>
|
||||||
ObservableCollection<CultivateProject> GetProjectCollection();
|
ObservableCollection<CultivateProject> GetProjectCollection();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取统计物品列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cultivateProject">养成计划</param>
|
||||||
|
/// <param name="materials">元数据</param>
|
||||||
|
/// <returns>统计物品列表</returns>
|
||||||
|
Task<List<StatisticsCultivateItem>> GetStatisticsCultivateItemsAsync(CultivateProject cultivateProject, List<Material> materials);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 删除养成清单
|
/// 删除养成清单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ internal class GameService : IGameService, IDisposable
|
|||||||
string commandLine = new CommandLineBuilder()
|
string commandLine = new CommandLineBuilder()
|
||||||
.AppendIf("-popupwindow", configuration.IsBorderless)
|
.AppendIf("-popupwindow", configuration.IsBorderless)
|
||||||
.Append("-screen-fullscreen", configuration.IsFullScreen ? 1 : 0)
|
.Append("-screen-fullscreen", configuration.IsFullScreen ? 1 : 0)
|
||||||
.AppendIfNotNull("-window-mode", configuration.WindowMode)
|
.AppendIf("-window-mode", configuration.IsExclusive, "exclusive")
|
||||||
.Append("-screen-width", configuration.ScreenWidth)
|
.Append("-screen-width", configuration.ScreenWidth)
|
||||||
.Append("-screen-height", configuration.ScreenHeight)
|
.Append("-screen-height", configuration.ScreenHeight)
|
||||||
.ToString();
|
.ToString();
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ namespace Snap.Hutao.Service.Game;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly struct LaunchConfiguration
|
internal readonly struct LaunchConfiguration
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为独占全屏
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsExclusive;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否全屏,全屏时无边框设置将被覆盖
|
/// 是否全屏,全屏时无边框设置将被覆盖
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -41,14 +46,16 @@ internal readonly struct LaunchConfiguration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的启动配置
|
/// 构造一个新的启动配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="isExclusive">独占全屏</param>
|
||||||
/// <param name="isFullScreen">全屏</param>
|
/// <param name="isFullScreen">全屏</param>
|
||||||
/// <param name="isBorderless">无边框</param>
|
/// <param name="isBorderless">无边框</param>
|
||||||
/// <param name="screenWidth">宽度</param>
|
/// <param name="screenWidth">宽度</param>
|
||||||
/// <param name="screenHeight">高度</param>
|
/// <param name="screenHeight">高度</param>
|
||||||
/// <param name="unlockFps">解锁帧率</param>
|
/// <param name="unlockFps">解锁帧率</param>
|
||||||
/// <param name="targetFps">目标帧率</param>
|
/// <param name="targetFps">目标帧率</param>
|
||||||
public LaunchConfiguration(bool isFullScreen, bool isBorderless, int screenWidth, int screenHeight, bool unlockFps, int targetFps)
|
public LaunchConfiguration(bool isExclusive, bool isFullScreen, bool isBorderless, int screenWidth, int screenHeight, bool unlockFps, int targetFps)
|
||||||
{
|
{
|
||||||
|
IsExclusive = isExclusive;
|
||||||
IsFullScreen = isFullScreen;
|
IsFullScreen = isFullScreen;
|
||||||
IsBorderless = isBorderless;
|
IsBorderless = isBorderless;
|
||||||
ScreenHeight = screenHeight;
|
ScreenHeight = screenHeight;
|
||||||
@@ -57,15 +64,4 @@ internal readonly struct LaunchConfiguration
|
|||||||
UnlockFPS = unlockFps;
|
UnlockFPS = unlockFps;
|
||||||
TargetFPS = targetFps;
|
TargetFPS = targetFps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 窗口模式字符串
|
|
||||||
/// </summary>
|
|
||||||
public string? WindowMode
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return IsFullScreen ? "exclusive" : IsBorderless ? "borderless" : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0-preview2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0-preview3" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<Page.Resources>
|
||||||
|
<shc:BindingProxy x:Key="BindingProxy" DataContext="{Binding}"/>
|
||||||
|
</Page.Resources>
|
||||||
|
|
||||||
<mxi:Interaction.Behaviors>
|
<mxi:Interaction.Behaviors>
|
||||||
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
|
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
|
||||||
</mxi:Interaction.Behaviors>
|
</mxi:Interaction.Behaviors>
|
||||||
@@ -77,12 +81,8 @@
|
|||||||
Command="{Binding RemoveArchiveCommand}"
|
Command="{Binding RemoveArchiveCommand}"
|
||||||
Icon="{shcm:FontIcon Glyph=}"
|
Icon="{shcm:FontIcon Glyph=}"
|
||||||
Label="删除当前存档"/>
|
Label="删除当前存档"/>
|
||||||
|
|
||||||
<AppBarSeparator/>
|
<AppBarSeparator/>
|
||||||
<AppBarButton
|
|
||||||
Command="{Binding RefreshFinishPercentCommand}"
|
|
||||||
Icon="{shcm:FontIcon Glyph=}"
|
|
||||||
Label="刷新成就进度"/>
|
|
||||||
<AppBarButton Icon="{shcm:FontIcon Glyph=}" Label="导入">
|
<AppBarButton Icon="{shcm:FontIcon Glyph=}" Label="导入">
|
||||||
<AppBarButton.Flyout>
|
<AppBarButton.Flyout>
|
||||||
<MenuFlyout Placement="BottomEdgeAlignedRight">
|
<MenuFlyout Placement="BottomEdgeAlignedRight">
|
||||||
@@ -102,6 +102,7 @@
|
|||||||
Icon="{shcm:FontIcon Glyph=}"
|
Icon="{shcm:FontIcon Glyph=}"
|
||||||
Label="导出"/>
|
Label="导出"/>
|
||||||
<AppBarSeparator/>
|
<AppBarSeparator/>
|
||||||
|
|
||||||
<AppBarToggleButton
|
<AppBarToggleButton
|
||||||
Command="{Binding SortIncompletedSwitchCommand}"
|
Command="{Binding SortIncompletedSwitchCommand}"
|
||||||
Icon="{shcm:FontIcon Glyph=}"
|
Icon="{shcm:FontIcon Glyph=}"
|
||||||
@@ -182,6 +183,8 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="6,0,12,0"
|
Margin="6,0,12,0"
|
||||||
Padding="16,0,0,0"
|
Padding="16,0,0,0"
|
||||||
|
Command="{Binding Path=DataContext.SaveAchievementCommand, Source={StaticResource BindingProxy}}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
IsChecked="{Binding IsChecked, Mode=TwoWay}"
|
IsChecked="{Binding IsChecked, Mode=TwoWay}"
|
||||||
Style="{StaticResource DefaultCheckBoxStyle}">
|
Style="{StaticResource DefaultCheckBoxStyle}">
|
||||||
<CheckBox.Content>
|
<CheckBox.Content>
|
||||||
|
|||||||
@@ -49,6 +49,54 @@
|
|||||||
<Pivot>
|
<Pivot>
|
||||||
<Pivot.RightHeader>
|
<Pivot.RightHeader>
|
||||||
<CommandBar DefaultLabelPosition="Right">
|
<CommandBar DefaultLabelPosition="Right">
|
||||||
|
<AppBarButton
|
||||||
|
Command="{Binding UpdateStatisticsItemsCommand}"
|
||||||
|
Icon="{shcm:FontIcon Glyph=}"
|
||||||
|
Label="材料统计">
|
||||||
|
<AppBarButton.Flyout>
|
||||||
|
<Flyout Placement="Bottom">
|
||||||
|
<Flyout.FlyoutPresenterStyle>
|
||||||
|
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
|
||||||
|
<Setter Property="MaxHeight" Value="480"/>
|
||||||
|
<Setter Property="Padding" Value="0,2,0,2"/>
|
||||||
|
<Setter Property="Background" Value="{ThemeResource FlyoutPresenterBackground}"/>
|
||||||
|
</Style>
|
||||||
|
</Flyout.FlyoutPresenterStyle>
|
||||||
|
<ItemsControl Margin="16,0,16,16" ItemsSource="{Binding StatisticsItems}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid Margin="0,16,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="160"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<shvc:ItemIcon
|
||||||
|
Grid.Column="0"
|
||||||
|
Width="32"
|
||||||
|
Height="32"
|
||||||
|
Icon="{Binding Inner.Icon, Converter={StaticResource ItemIconConverter}}"
|
||||||
|
Quality="{Binding Inner.RankLevel}"/>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="16,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding Inner.Name}"
|
||||||
|
TextTrimming="CharacterEllipsis"/>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="16,0,4,0"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding CountFormatted}"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Flyout>
|
||||||
|
</AppBarButton.Flyout>
|
||||||
|
</AppBarButton>
|
||||||
|
<AppBarSeparator/>
|
||||||
<AppBarElementContainer>
|
<AppBarElementContainer>
|
||||||
<ComboBox
|
<ComboBox
|
||||||
Height="36"
|
Height="36"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
xmlns:shc="using:Snap.Hutao.Control"
|
xmlns:shc="using:Snap.Hutao.Control"
|
||||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||||
|
xmlns:shmbh="using:Snap.Hutao.Model.Binding.Hutao"
|
||||||
xmlns:shv="using:Snap.Hutao.ViewModel"
|
xmlns:shv="using:Snap.Hutao.ViewModel"
|
||||||
xmlns:shvc="using:Snap.Hutao.View.Control"
|
xmlns:shvc="using:Snap.Hutao.View.Control"
|
||||||
d:DataContext="{d:DesignInstance shv:HutaoDatabaseViewModel}"
|
d:DataContext="{d:DesignInstance shv:HutaoDatabaseViewModel}"
|
||||||
@@ -20,6 +21,47 @@
|
|||||||
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
|
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
|
||||||
</mxi:Interaction.Behaviors>
|
</mxi:Interaction.Behaviors>
|
||||||
|
|
||||||
|
<Page.Resources>
|
||||||
|
<DataTemplate x:Key="TeamItemTemplate" x:DataType="shmbh:Team">
|
||||||
|
<Border Margin="12,0,12,12" Style="{StaticResource BorderCardStyle}">
|
||||||
|
<Grid Margin="6">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<cwuc:UniformGrid ColumnSpacing="6" Columns="4"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<shvc:ItemIcon
|
||||||
|
Width="48"
|
||||||
|
Height="48"
|
||||||
|
Icon="{Binding Icon}"
|
||||||
|
Quality="{Binding Quality}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
<StackPanel
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="{Binding Rate}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,2,0,0"
|
||||||
|
Opacity="0.6"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{Binding Name}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</Page.Resources>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Pivot>
|
<Pivot>
|
||||||
<Pivot.RightHeader>
|
<Pivot.RightHeader>
|
||||||
@@ -97,6 +139,39 @@
|
|||||||
</Pivot.ItemTemplate>
|
</Pivot.ItemTemplate>
|
||||||
</Pivot>
|
</Pivot>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
|
<PivotItem Header="队伍出场">
|
||||||
|
<Pivot ItemsSource="{Binding TeamAppearances}">
|
||||||
|
<Pivot.HeaderTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding Floor}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</Pivot.HeaderTemplate>
|
||||||
|
<Pivot.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<ScrollViewer Grid.Column="0" Margin="0,12,0,0">
|
||||||
|
<ItemsControl
|
||||||
|
ItemTemplate="{StaticResource TeamItemTemplate}"
|
||||||
|
ItemsPanel="{StaticResource ItemsStackPanelTemplate}"
|
||||||
|
ItemsSource="{Binding Up}"/>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Column="1" Margin="0,12,0,0">
|
||||||
|
<ItemsControl
|
||||||
|
ItemTemplate="{StaticResource TeamItemTemplate}"
|
||||||
|
ItemsPanel="{StaticResource ItemsStackPanelTemplate}"
|
||||||
|
ItemsSource="{Binding Down}"/>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</Pivot.ItemTemplate>
|
||||||
|
</Pivot>
|
||||||
|
</PivotItem>
|
||||||
<PivotItem Header="角色持有">
|
<PivotItem Header="角色持有">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -231,117 +306,6 @@
|
|||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem Header="队伍出场">
|
|
||||||
<Pivot ItemsSource="{Binding TeamAppearances}">
|
|
||||||
<Pivot.HeaderTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Text="{Binding Floor}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</Pivot.HeaderTemplate>
|
|
||||||
<Pivot.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
<ColumnDefinition/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<ScrollViewer Grid.Column="0" Margin="0,12,0,0">
|
|
||||||
<ItemsControl ItemsSource="{Binding Up}">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<ItemsStackPanel/>
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Border
|
|
||||||
Margin="12,0,12,12"
|
|
||||||
Background="{StaticResource CardBackgroundFillColorDefault}"
|
|
||||||
CornerRadius="{StaticResource CompatCornerRadius}">
|
|
||||||
<Grid Margin="6">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
<ColumnDefinition Width="120"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding}">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<cwuc:UniformGrid ColumnSpacing="6" Columns="4"/>
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<shvc:ItemIcon
|
|
||||||
Width="48"
|
|
||||||
Height="48"
|
|
||||||
Icon="{Binding Icon}"
|
|
||||||
Quality="{Binding Quality}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{Binding Rate}"/>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<ScrollViewer Grid.Column="1" Margin="0,12,0,0">
|
|
||||||
<ItemsControl ItemsSource="{Binding Down}">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<ItemsStackPanel/>
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Border
|
|
||||||
Margin="12,0,12,12"
|
|
||||||
Background="{StaticResource CardBackgroundFillColorDefault}"
|
|
||||||
CornerRadius="{StaticResource CompatCornerRadius}">
|
|
||||||
<Grid Margin="6">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
<ColumnDefinition Width="120"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding}">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<cwuc:UniformGrid ColumnSpacing="6" Columns="4"/>
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<shvc:ItemIcon
|
|
||||||
Width="48"
|
|
||||||
Height="48"
|
|
||||||
Icon="{Binding Icon}"
|
|
||||||
Quality="{Binding Quality}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{Binding Rate}"/>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</Pivot.ItemTemplate>
|
|
||||||
</Pivot>
|
|
||||||
</PivotItem>
|
|
||||||
</Pivot>
|
</Pivot>
|
||||||
</Grid>
|
</Grid>
|
||||||
</shc:ScopedPage>
|
</shc:ScopedPage>
|
||||||
|
|||||||
@@ -158,6 +158,17 @@
|
|||||||
</sc:SettingExpander>
|
</sc:SettingExpander>
|
||||||
</sc:SettingsGroup>
|
</sc:SettingsGroup>
|
||||||
<sc:SettingsGroup Header="外观">
|
<sc:SettingsGroup Header="外观">
|
||||||
|
<sc:Setting
|
||||||
|
Description="与游戏内浏览器不兼容,切屏等操作也能使游戏闪退"
|
||||||
|
Header="独占全屏"
|
||||||
|
Icon="">
|
||||||
|
<sc:Setting.ActionContent>
|
||||||
|
<ToggleSwitch
|
||||||
|
Width="120"
|
||||||
|
IsOn="{Binding IsExclusive, Mode=TwoWay}"
|
||||||
|
Style="{StaticResource ToggleSwitchSettingStyle}"/>
|
||||||
|
</sc:Setting.ActionContent>
|
||||||
|
</sc:Setting>
|
||||||
<sc:Setting
|
<sc:Setting
|
||||||
Description="覆盖默认的全屏状态"
|
Description="覆盖默认的全屏状态"
|
||||||
Header="全屏"
|
Header="全屏"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Snap.Hutao.Control;
|
|||||||
using Snap.Hutao.Control.Extension;
|
using Snap.Hutao.Control.Extension;
|
||||||
using Snap.Hutao.Core.IO;
|
using Snap.Hutao.Core.IO;
|
||||||
using Snap.Hutao.Core.IO.DataTransfer;
|
using Snap.Hutao.Core.IO.DataTransfer;
|
||||||
|
using Snap.Hutao.Core.LifeCycle;
|
||||||
using Snap.Hutao.Core.Threading.CodeAnalysis;
|
using Snap.Hutao.Core.Threading.CodeAnalysis;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Factory.Abstraction;
|
using Snap.Hutao.Factory.Abstraction;
|
||||||
@@ -33,26 +34,22 @@ namespace Snap.Hutao.ViewModel;
|
|||||||
/// 成就视图模型
|
/// 成就视图模型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Injection(InjectAs.Scoped)]
|
[Injection(InjectAs.Scoped)]
|
||||||
|
[SuppressMessage("", "SA1124")]
|
||||||
internal class AchievementViewModel
|
internal class AchievementViewModel
|
||||||
: ObservableObject,
|
: ObservableObject,
|
||||||
ISupportCancellation,
|
ISupportCancellation,
|
||||||
INavigationRecipient,
|
INavigationRecipient
|
||||||
IDisposable,
|
|
||||||
IRecipient<AchievementArchiveChangedMessage>
|
|
||||||
{
|
{
|
||||||
private static readonly SortDescription IncompletedItemsFirstSortDescription = new(nameof(Model.Binding.Achievement.Achievement.IsChecked), SortDirection.Ascending);
|
private static readonly SortDescription IncompletedItemsFirstSortDescription = new(nameof(Model.Binding.Achievement.Achievement.IsChecked), SortDirection.Ascending);
|
||||||
private static readonly SortDescription CompletionTimeSortDescription = new(nameof(Model.Binding.Achievement.Achievement.Time), SortDirection.Descending);
|
private static readonly SortDescription CompletionTimeSortDescription = new(nameof(Model.Binding.Achievement.Achievement.Time), SortDirection.Descending);
|
||||||
|
|
||||||
|
private readonly IAchievementService achievementService;
|
||||||
private readonly IMetadataService metadataService;
|
private readonly IMetadataService metadataService;
|
||||||
private readonly IInfoBarService infoBarService;
|
private readonly IInfoBarService infoBarService;
|
||||||
private readonly JsonSerializerOptions options;
|
private readonly JsonSerializerOptions options;
|
||||||
|
|
||||||
private readonly IAchievementService achievementService;
|
|
||||||
|
|
||||||
private readonly TaskCompletionSource<bool> openUICompletionSource = new();
|
private readonly TaskCompletionSource<bool> openUICompletionSource = new();
|
||||||
|
|
||||||
private bool disposed;
|
|
||||||
|
|
||||||
private AdvancedCollectionView? achievements;
|
private AdvancedCollectionView? achievements;
|
||||||
private List<Model.Binding.Achievement.AchievementGoal>? achievementGoals;
|
private List<Model.Binding.Achievement.AchievementGoal>? achievementGoals;
|
||||||
private Model.Binding.Achievement.AchievementGoal? selectedAchievementGoal;
|
private Model.Binding.Achievement.AchievementGoal? selectedAchievementGoal;
|
||||||
@@ -94,9 +91,7 @@ internal class AchievementViewModel
|
|||||||
RemoveArchiveCommand = asyncRelayCommandFactory.Create(RemoveArchiveAsync);
|
RemoveArchiveCommand = asyncRelayCommandFactory.Create(RemoveArchiveAsync);
|
||||||
SearchAchievementCommand = new RelayCommand<string>(SearchAchievement);
|
SearchAchievementCommand = new RelayCommand<string>(SearchAchievement);
|
||||||
SortIncompletedSwitchCommand = new RelayCommand(UpdateAchievementsSort);
|
SortIncompletedSwitchCommand = new RelayCommand(UpdateAchievementsSort);
|
||||||
RefreshFinishPercentCommand = new RelayCommand(UpdateAchievementFinishPercent);
|
SaveAchievementCommand = new RelayCommand<Model.Binding.Achievement.Achievement>(SaveAchievement);
|
||||||
|
|
||||||
messenger.Register(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -127,6 +122,10 @@ internal class AchievementViewModel
|
|||||||
if (SetProperty(ref selectedArchive, value))
|
if (SetProperty(ref selectedArchive, value))
|
||||||
{
|
{
|
||||||
achievementService.CurrentArchive = value;
|
achievementService.CurrentArchive = value;
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
UpdateAchievementsAsync(value).SafeForget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +158,7 @@ internal class AchievementViewModel
|
|||||||
{
|
{
|
||||||
SetProperty(ref selectedAchievementGoal, value);
|
SetProperty(ref selectedAchievementGoal, value);
|
||||||
SearchText = string.Empty;
|
SearchText = string.Empty;
|
||||||
UpdateAchievementFilter(value);
|
UpdateAchievementsFilter(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,36 +226,16 @@ internal class AchievementViewModel
|
|||||||
public ICommand SortIncompletedSwitchCommand { get; }
|
public ICommand SortIncompletedSwitchCommand { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 刷新完成百分比命令
|
/// 保存单个成就命令
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ICommand RefreshFinishPercentCommand { get; }
|
public ICommand SaveAchievementCommand { get; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Receive(AchievementArchiveChangedMessage message)
|
|
||||||
{
|
|
||||||
HandleArchiveChangeAsync(message.OldValue, message.NewValue).SafeForget();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!disposed)
|
|
||||||
{
|
|
||||||
if (Achievements != null && SelectedArchive != null)
|
|
||||||
{
|
|
||||||
achievementService.SaveAchievements(SelectedArchive, (Achievements.Source as IList<Model.Binding.Achievement.Achievement>)!);
|
|
||||||
}
|
|
||||||
|
|
||||||
disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<bool> ReceiveAsync(INavigationData data)
|
public async Task<bool> ReceiveAsync(INavigationData data)
|
||||||
{
|
{
|
||||||
if (await openUICompletionSource.Task.ConfigureAwait(false))
|
if (await openUICompletionSource.Task.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (data.Data is "InvokeByUri")
|
if (data.Data is Activation.ImportUIAFFromClipBoard)
|
||||||
{
|
{
|
||||||
await ImportUIAFFromClipboardAsync().ConfigureAwait(false);
|
await ImportUIAFFromClipboardAsync().ConfigureAwait(false);
|
||||||
return true;
|
return true;
|
||||||
@@ -268,43 +247,32 @@ internal class AchievementViewModel
|
|||||||
|
|
||||||
private static Task<ContentDialogResult> ShowImportResultDialogAsync(string title, string message)
|
private static Task<ContentDialogResult> ShowImportResultDialogAsync(string title, string message)
|
||||||
{
|
{
|
||||||
|
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
||||||
ContentDialog dialog = new()
|
ContentDialog dialog = new()
|
||||||
{
|
{
|
||||||
Title = title,
|
Title = title,
|
||||||
Content = message,
|
Content = message,
|
||||||
PrimaryButtonText = "确认",
|
PrimaryButtonText = "确认",
|
||||||
DefaultButton = ContentDialogButton.Primary,
|
DefaultButton = ContentDialogButton.Primary,
|
||||||
|
XamlRoot = mainWindow.Content.XamlRoot,
|
||||||
};
|
};
|
||||||
|
|
||||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
return dialog.ShowAsync().AsTask();
|
||||||
return dialog.InitializeWithWindow(mainWindow).ShowAsync().AsTask();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task<ContentDialogResult> ShowImportFailDialogAsync(string message)
|
private static Task<ContentDialogResult> ShowImportFailDialogAsync(string message)
|
||||||
{
|
{
|
||||||
|
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
||||||
ContentDialog dialog = new()
|
ContentDialog dialog = new()
|
||||||
{
|
{
|
||||||
Title = "导入失败",
|
Title = "导入失败",
|
||||||
Content = message,
|
Content = message,
|
||||||
PrimaryButtonText = "确认",
|
PrimaryButtonText = "确认",
|
||||||
DefaultButton = ContentDialogButton.Primary,
|
DefaultButton = ContentDialogButton.Primary,
|
||||||
|
XamlRoot = mainWindow.Content.XamlRoot,
|
||||||
};
|
};
|
||||||
|
|
||||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
return dialog.ShowAsync().AsTask();
|
||||||
return dialog.InitializeWithWindow(mainWindow).ShowAsync().AsTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleArchiveChangeAsync(Model.Entity.AchievementArchive? oldArchieve, Model.Entity.AchievementArchive? newArchieve)
|
|
||||||
{
|
|
||||||
if (oldArchieve != null && Achievements != null)
|
|
||||||
{
|
|
||||||
achievementService.SaveAchievements(oldArchieve, (Achievements.Source as IList<Model.Binding.Achievement.Achievement>)!);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newArchieve != null)
|
|
||||||
{
|
|
||||||
await UpdateAchievementsAsync(newArchieve).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OpenUIAsync()
|
private async Task OpenUIAsync()
|
||||||
@@ -339,20 +307,7 @@ internal class AchievementViewModel
|
|||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateAchievementsAsync(Model.Entity.AchievementArchive archive)
|
#region 存档操作
|
||||||
{
|
|
||||||
List<Model.Metadata.Achievement.Achievement> rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
|
|
||||||
List<Model.Binding.Achievement.Achievement> combined = achievementService.GetAchievements(archive, rawAchievements);
|
|
||||||
|
|
||||||
// Assemble achievements on the UI thread.
|
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
|
||||||
Achievements = new(combined, true);
|
|
||||||
|
|
||||||
UpdateAchievementFinishPercent();
|
|
||||||
UpdateAchievementFilter(SelectedAchievementGoal);
|
|
||||||
UpdateAchievementsSort();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AddArchiveAsync()
|
private async Task AddArchiveAsync()
|
||||||
{
|
{
|
||||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
||||||
@@ -404,7 +359,33 @@ internal class AchievementViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void SearchAchievement(string? search)
|
||||||
|
{
|
||||||
|
if (Achievements != null)
|
||||||
|
{
|
||||||
|
SetProperty(ref selectedAchievementGoal, null);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(search))
|
||||||
|
{
|
||||||
|
if (search.Length == 5 && int.TryParse(search, out int achiId))
|
||||||
|
{
|
||||||
|
Achievements.Filter = (object o) => ((Model.Binding.Achievement.Achievement)o).Inner.Id == achiId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Achievements.Filter = (object o) =>
|
||||||
|
{
|
||||||
|
Model.Binding.Achievement.Achievement achi = (Model.Binding.Achievement.Achievement)o;
|
||||||
|
return achi.Inner.Title.Contains(search) || achi.Inner.Description.Contains(search);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 导入导出
|
||||||
private async Task ExportAsUIAFToFileAsync()
|
private async Task ExportAsUIAFToFileAsync()
|
||||||
{
|
{
|
||||||
if (SelectedArchive == null || Achievements == null)
|
if (SelectedArchive == null || Achievements == null)
|
||||||
@@ -413,8 +394,6 @@ internal class AchievementViewModel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
achievementService.SaveAchievements(SelectedArchive, (Achievements.Source as IList<Model.Binding.Achievement.Achievement>)!);
|
|
||||||
|
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
IPickerFactory pickerFactory = Ioc.Default.GetRequiredService<IPickerFactory>();
|
IPickerFactory pickerFactory = Ioc.Default.GetRequiredService<IPickerFactory>();
|
||||||
FileSavePicker picker = pickerFactory.GetFileSavePicker();
|
FileSavePicker picker = pickerFactory.GetFileSavePicker();
|
||||||
@@ -440,30 +419,6 @@ internal class AchievementViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SearchAchievement(string? search)
|
|
||||||
{
|
|
||||||
if (Achievements != null)
|
|
||||||
{
|
|
||||||
SetProperty(ref selectedAchievementGoal, null);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(search))
|
|
||||||
{
|
|
||||||
if (search.Length == 5 && int.TryParse(search, out int achiId))
|
|
||||||
{
|
|
||||||
Achievements.Filter = (object o) => ((Model.Binding.Achievement.Achievement)o).Inner.Id == achiId;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Achievements.Filter = (object o) =>
|
|
||||||
{
|
|
||||||
Model.Binding.Achievement.Achievement achi = (Model.Binding.Achievement.Achievement)o;
|
|
||||||
return achi.Inner.Title.Contains(search) || achi.Inner.Description.Contains(search);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ImportUIAFFromClipboardAsync()
|
private async Task ImportUIAFFromClipboardAsync()
|
||||||
{
|
{
|
||||||
if (achievementService.CurrentArchive == null)
|
if (achievementService.CurrentArchive == null)
|
||||||
@@ -559,6 +514,21 @@ internal class AchievementViewModel
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private async Task UpdateAchievementsAsync(Model.Entity.AchievementArchive archive)
|
||||||
|
{
|
||||||
|
List<Model.Metadata.Achievement.Achievement> rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
|
||||||
|
List<Model.Binding.Achievement.Achievement> combined = achievementService.GetAchievements(archive, rawAchievements);
|
||||||
|
|
||||||
|
// Assemble achievements on the UI thread.
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
Achievements = new(combined, true);
|
||||||
|
|
||||||
|
UpdateAchievementsFinishPercent();
|
||||||
|
UpdateAchievementsFilter(SelectedAchievementGoal);
|
||||||
|
UpdateAchievementsSort();
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateAchievementsSort()
|
private void UpdateAchievementsSort()
|
||||||
{
|
{
|
||||||
@@ -576,7 +546,7 @@ internal class AchievementViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAchievementFilter(Model.Binding.Achievement.AchievementGoal? goal)
|
private void UpdateAchievementsFilter(Model.Binding.Achievement.AchievementGoal? goal)
|
||||||
{
|
{
|
||||||
if (Achievements != null)
|
if (Achievements != null)
|
||||||
{
|
{
|
||||||
@@ -586,16 +556,16 @@ internal class AchievementViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAchievementFinishPercent()
|
private void UpdateAchievementsFinishPercent()
|
||||||
{
|
{
|
||||||
int finished = 0;
|
int finished = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
if (Achievements != null && AchievementGoals != null)
|
if (Achievements != null && AchievementGoals != null)
|
||||||
{
|
{
|
||||||
Dictionary<int, AchievementGoalAggregation> counter = AchievementGoals.ToDictionary(x => x.Id, x => new AchievementGoalAggregation(x));
|
Dictionary<int, GoalAggregation> counter = AchievementGoals.ToDictionary(x => x.Id, x => new GoalAggregation(x));
|
||||||
foreach (Model.Binding.Achievement.Achievement achievement in Achievements.OfType<Model.Binding.Achievement.Achievement>())
|
foreach (Model.Binding.Achievement.Achievement achievement in Achievements.SourceCollection.OfType<Model.Binding.Achievement.Achievement>())
|
||||||
{
|
{
|
||||||
ref AchievementGoalAggregation aggregation = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
|
ref GoalAggregation aggregation = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
|
||||||
aggregation.Count += 1;
|
aggregation.Count += 1;
|
||||||
count += 1;
|
count += 1;
|
||||||
if (achievement.IsChecked)
|
if (achievement.IsChecked)
|
||||||
@@ -605,7 +575,7 @@ internal class AchievementViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (AchievementGoalAggregation aggregation1 in counter.Values)
|
foreach (GoalAggregation aggregation1 in counter.Values)
|
||||||
{
|
{
|
||||||
aggregation1.AchievementGoal.UpdateFinishPercent(aggregation1.Finished, aggregation1.Count);
|
aggregation1.AchievementGoal.UpdateFinishPercent(aggregation1.Finished, aggregation1.Count);
|
||||||
}
|
}
|
||||||
@@ -614,13 +584,22 @@ internal class AchievementViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct AchievementGoalAggregation
|
private void SaveAchievement(Model.Binding.Achievement.Achievement? achievement)
|
||||||
|
{
|
||||||
|
if (achievement != null)
|
||||||
|
{
|
||||||
|
achievementService.SaveAchievement(achievement);
|
||||||
|
UpdateAchievementsFinishPercent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct GoalAggregation
|
||||||
{
|
{
|
||||||
public readonly Model.Binding.Achievement.AchievementGoal AchievementGoal;
|
public readonly Model.Binding.Achievement.AchievementGoal AchievementGoal;
|
||||||
public int Finished;
|
public int Finished;
|
||||||
public int Count;
|
public int Count;
|
||||||
|
|
||||||
public AchievementGoalAggregation(Model.Binding.Achievement.AchievementGoal goal)
|
public GoalAggregation(Model.Binding.Achievement.AchievementGoal goal)
|
||||||
{
|
{
|
||||||
AchievementGoal = goal;
|
AchievementGoal = goal;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using Snap.Hutao.Control;
|
using Snap.Hutao.Control;
|
||||||
using Snap.Hutao.Factory.Abstraction;
|
using Snap.Hutao.Factory.Abstraction;
|
||||||
using Snap.Hutao.Message;
|
using Snap.Hutao.Message;
|
||||||
|
using Snap.Hutao.Model.Binding.Cultivation;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.Service.Cultivation;
|
using Snap.Hutao.Service.Cultivation;
|
||||||
@@ -20,7 +21,7 @@ namespace Snap.Hutao.ViewModel;
|
|||||||
/// 养成视图模型
|
/// 养成视图模型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Injection(InjectAs.Scoped)]
|
[Injection(InjectAs.Scoped)]
|
||||||
internal class CultivationViewModel : ObservableObject, ISupportCancellation, IRecipient<CultivateProjectChangedMessage>
|
internal class CultivationViewModel : ObservableObject, ISupportCancellation
|
||||||
{
|
{
|
||||||
private readonly ICultivationService cultivationService;
|
private readonly ICultivationService cultivationService;
|
||||||
private readonly IInfoBarService infoBarService;
|
private readonly IInfoBarService infoBarService;
|
||||||
@@ -31,6 +32,7 @@ internal class CultivationViewModel : ObservableObject, ISupportCancellation, IR
|
|||||||
private CultivateProject? selectedProject;
|
private CultivateProject? selectedProject;
|
||||||
private List<Model.Binding.Inventory.InventoryItem>? inventoryItems;
|
private List<Model.Binding.Inventory.InventoryItem>? inventoryItems;
|
||||||
private ObservableCollection<Model.Binding.Cultivation.CultivateEntry>? cultivateEntries;
|
private ObservableCollection<Model.Binding.Cultivation.CultivateEntry>? cultivateEntries;
|
||||||
|
private List<StatisticsCultivateItem>? statisticsItems;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的养成视图模型
|
/// 构造一个新的养成视图模型
|
||||||
@@ -40,14 +42,12 @@ internal class CultivationViewModel : ObservableObject, ISupportCancellation, IR
|
|||||||
/// <param name="asyncRelayCommandFactory">异步命令工厂</param>
|
/// <param name="asyncRelayCommandFactory">异步命令工厂</param>
|
||||||
/// <param name="metadataService">元数据服务</param>
|
/// <param name="metadataService">元数据服务</param>
|
||||||
/// <param name="logger">日志器</param>
|
/// <param name="logger">日志器</param>
|
||||||
/// <param name="messenger">消息器</param>
|
|
||||||
public CultivationViewModel(
|
public CultivationViewModel(
|
||||||
ICultivationService cultivationService,
|
ICultivationService cultivationService,
|
||||||
IInfoBarService infoBarService,
|
IInfoBarService infoBarService,
|
||||||
IAsyncRelayCommandFactory asyncRelayCommandFactory,
|
IAsyncRelayCommandFactory asyncRelayCommandFactory,
|
||||||
IMetadataService metadataService,
|
IMetadataService metadataService,
|
||||||
ILogger<CultivationViewModel> logger,
|
ILogger<CultivationViewModel> logger)
|
||||||
IMessenger messenger)
|
|
||||||
{
|
{
|
||||||
this.cultivationService = cultivationService;
|
this.cultivationService = cultivationService;
|
||||||
this.infoBarService = infoBarService;
|
this.infoBarService = infoBarService;
|
||||||
@@ -59,8 +59,7 @@ internal class CultivationViewModel : ObservableObject, ISupportCancellation, IR
|
|||||||
RemoveProjectCommand = asyncRelayCommandFactory.Create<CultivateProject>(RemoveProjectAsync);
|
RemoveProjectCommand = asyncRelayCommandFactory.Create<CultivateProject>(RemoveProjectAsync);
|
||||||
RemoveEntryCommand = asyncRelayCommandFactory.Create<Model.Binding.Cultivation.CultivateEntry>(RemoveEntryAsync);
|
RemoveEntryCommand = asyncRelayCommandFactory.Create<Model.Binding.Cultivation.CultivateEntry>(RemoveEntryAsync);
|
||||||
SaveInventoryItemCommand = new RelayCommand<Model.Binding.Inventory.InventoryItem>(SaveInventoryItem);
|
SaveInventoryItemCommand = new RelayCommand<Model.Binding.Inventory.InventoryItem>(SaveInventoryItem);
|
||||||
|
UpdateStatisticsItemsCommand = asyncRelayCommandFactory.Create(UpdateStatisticsItemsAsync);
|
||||||
messenger.Register(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -81,6 +80,10 @@ internal class CultivationViewModel : ObservableObject, ISupportCancellation, IR
|
|||||||
if (SetProperty(ref selectedProject, value))
|
if (SetProperty(ref selectedProject, value))
|
||||||
{
|
{
|
||||||
cultivationService.Current = value;
|
cultivationService.Current = value;
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
UpdateCultivateEntriesAndInventoryItemsAsync(value).SafeForget(logger);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,6 +98,11 @@ internal class CultivationViewModel : ObservableObject, ISupportCancellation, IR
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ObservableCollection<Model.Binding.Cultivation.CultivateEntry>? CultivateEntries { get => cultivateEntries; set => SetProperty(ref cultivateEntries, value); }
|
public ObservableCollection<Model.Binding.Cultivation.CultivateEntry>? CultivateEntries { get => cultivateEntries; set => SetProperty(ref cultivateEntries, value); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 统计列表
|
||||||
|
/// </summary>
|
||||||
|
public List<StatisticsCultivateItem>? StatisticsItems { get => statisticsItems; set => SetProperty(ref statisticsItems, value); }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 打开界面命令
|
/// 打开界面命令
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -120,11 +128,10 @@ internal class CultivationViewModel : ObservableObject, ISupportCancellation, IR
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ICommand SaveInventoryItemCommand { get; }
|
public ICommand SaveInventoryItemCommand { get; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <summary>
|
||||||
public void Receive(CultivateProjectChangedMessage message)
|
/// 展示统计物品命令
|
||||||
{
|
/// </summary>
|
||||||
UpdateCultivateEntriesAndInventoryItemsAsync(message.NewValue).SafeForget(logger);
|
public ICommand UpdateStatisticsItemsCommand { get; }
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OpenUIAsync()
|
private async Task OpenUIAsync()
|
||||||
{
|
{
|
||||||
@@ -209,4 +216,18 @@ internal class CultivationViewModel : ObservableObject, ISupportCancellation, IR
|
|||||||
cultivationService.SaveInventoryItem(inventoryItem);
|
cultivationService.SaveInventoryItem(inventoryItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task UpdateStatisticsItemsAsync()
|
||||||
|
{
|
||||||
|
if (await metadataService.InitializeAsync().ConfigureAwait(true))
|
||||||
|
{
|
||||||
|
if (SelectedProject != null)
|
||||||
|
{
|
||||||
|
List<Model.Metadata.Material> materials = await metadataService.GetMaterialsAsync().ConfigureAwait(false);
|
||||||
|
List<StatisticsCultivateItem> temp = await cultivationService.GetStatisticsCultivateItemsAsync(SelectedProject, materials).ConfigureAwait(false);
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
StatisticsItems = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +51,7 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
|
|||||||
private LaunchScheme? selectedScheme;
|
private LaunchScheme? selectedScheme;
|
||||||
private ObservableCollection<GameAccount>? gameAccounts;
|
private ObservableCollection<GameAccount>? gameAccounts;
|
||||||
private GameAccount? selectedGameAccount;
|
private GameAccount? selectedGameAccount;
|
||||||
|
private bool isExclusive;
|
||||||
private bool isFullScreen;
|
private bool isFullScreen;
|
||||||
private bool isBorderless;
|
private bool isBorderless;
|
||||||
private int screenWidth;
|
private int screenWidth;
|
||||||
@@ -106,15 +107,57 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public GameAccount? SelectedGameAccount { get => selectedGameAccount; set => SetProperty(ref selectedGameAccount, value); }
|
public GameAccount? SelectedGameAccount { get => selectedGameAccount; set => SetProperty(ref selectedGameAccount, value); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为独占全屏
|
||||||
|
/// </summary>
|
||||||
|
public bool IsExclusive
|
||||||
|
{
|
||||||
|
get => isExclusive; set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref isExclusive, value))
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
IsFullScreen = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全屏
|
/// 全屏
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsFullScreen { get => isFullScreen; set => SetProperty(ref isFullScreen, value); }
|
public bool IsFullScreen
|
||||||
|
{
|
||||||
|
get => isFullScreen; set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref isFullScreen, value))
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
IsBorderless = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 无边框
|
/// 无边框
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsBorderless { get => isBorderless; set => SetProperty(ref isBorderless, value); }
|
public bool IsBorderless
|
||||||
|
{
|
||||||
|
get => isBorderless; set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref isBorderless, value))
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
IsExclusive = false;
|
||||||
|
IsFullScreen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 宽度
|
/// 宽度
|
||||||
@@ -270,7 +313,7 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
|
|||||||
|
|
||||||
SaveSetting();
|
SaveSetting();
|
||||||
|
|
||||||
LaunchConfiguration configuration = new(IsFullScreen, IsBorderless, ScreenWidth, ScreenHeight, IsElevated && UnlockFps, TargetFps);
|
LaunchConfiguration configuration = new(IsExclusive, IsFullScreen, IsBorderless, ScreenWidth, ScreenHeight, IsElevated && UnlockFps, TargetFps);
|
||||||
await gameService.LaunchAsync(configuration).ConfigureAwait(false);
|
await gameService.LaunchAsync(configuration).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ internal class UserViewModel : ObservableObject
|
|||||||
if (SetProperty(ref selectedUser, value))
|
if (SetProperty(ref selectedUser, value))
|
||||||
{
|
{
|
||||||
userService.Current = value;
|
userService.Current = value;
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
value.SelectedUserGameRole = value.UserGameRoles.FirstOrFirstOrDefault(role => role.IsChosen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ internal class PatchClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新信息
|
/// 异步获取更新信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="token"></param>
|
/// <param name="token">取消令牌</param>
|
||||||
/// <returns></returns>
|
/// <returns>更新信息</returns>
|
||||||
public Task<Patch.PatchInformation?> GetPatchInformationAsync(CancellationToken token = default)
|
public Task<Patch.PatchInformation?> GetPatchInformationAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return httpClient.TryCatchGetFromJsonAsync<Patch.PatchInformation>(ApiEndpoints.PatcherHutaoStable, options, logger, token);
|
return httpClient.TryCatchGetFromJsonAsync<Patch.PatchInformation>(ApiEndpoints.PatcherHutaoStable, options, logger, token);
|
||||||
|
|||||||
Reference in New Issue
Block a user