mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
refresh inventory
This commit is contained in:
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Model.Entity;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Table("inventory_items")]
|
||||
internal sealed class InventoryItem : IDbMappingForeignKeyFrom<InventoryItem, uint>
|
||||
internal sealed class InventoryItem : IDbMappingForeignKeyFrom<InventoryItem, uint>, IDbMappingForeignKeyFrom<InventoryItem, uint, uint>
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部Id
|
||||
@@ -56,4 +56,21 @@ internal sealed class InventoryItem : IDbMappingForeignKeyFrom<InventoryItem, ui
|
||||
ItemId = itemId,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的个数不为0的物品
|
||||
/// </summary>
|
||||
/// <param name="projectId">项目Id</param>
|
||||
/// <param name="itemId">物品Id</param>
|
||||
/// <param name="count">物品个数</param>
|
||||
/// <returns>新的个数不为0的物品</returns>
|
||||
public static InventoryItem From(in Guid projectId, in uint itemId, in uint count)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
ProjectId = projectId,
|
||||
ItemId = itemId,
|
||||
Count = count,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Abstraction;
|
||||
|
||||
internal interface ICultivatable
|
||||
{
|
||||
List<MaterialId> CultivationItems { get; }
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao.Model.Metadata.Avatar;
|
||||
/// <summary>
|
||||
/// 角色的接口实现部分
|
||||
/// </summary>
|
||||
internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource<ICalculableAvatar>
|
||||
internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource<ICalculableAvatar>, ICultivatable
|
||||
{
|
||||
/// <summary>
|
||||
/// [非元数据] 搭配数据
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao.Model.Metadata.Weapon;
|
||||
/// <summary>
|
||||
/// 武器的接口实现
|
||||
/// </summary>
|
||||
internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource<ICalculableWeapon>
|
||||
internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource<ICalculableWeapon>, ICultivatable
|
||||
{
|
||||
/// <summary>
|
||||
/// [非元数据] 搭配数据
|
||||
|
||||
@@ -1568,6 +1568,9 @@
|
||||
<data name="ViewModelCultivationProjectInvalidName" xml:space="preserve">
|
||||
<value>不能添加名称无效的计划</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationRefreshInventoryProgress" xml:space="preserve">
|
||||
<value>正在同步背包物品</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationRemoveProjectContent" xml:space="preserve">
|
||||
<value>此操作不可逆,此计划的养成物品与背包材料将会丢失</value>
|
||||
</data>
|
||||
@@ -1925,6 +1928,9 @@
|
||||
<data name="ViewPageCultivationNavigateAction" xml:space="preserve">
|
||||
<value>前往</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationRefreshInventory" xml:space="preserve">
|
||||
<value>同步背包物品</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationRemoveEntry" xml:space="preserve">
|
||||
<value>删除清单</value>
|
||||
</data>
|
||||
|
||||
@@ -2,15 +2,29 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Inventory;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.ViewModel.Cultivation;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using AvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
|
||||
using BatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption;
|
||||
using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
|
||||
using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item;
|
||||
using PromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.PromotionDelta;
|
||||
|
||||
namespace Snap.Hutao.Service.Cultivation;
|
||||
|
||||
@@ -24,7 +38,10 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
{
|
||||
private readonly ScopedDbCurrent<CultivateProject, Message.CultivateProjectChangedMessage> dbCurrent;
|
||||
private readonly ICultivationDbService cultivationDbService;
|
||||
private readonly IServiceScopeFactory serviceScopeFactory;
|
||||
private readonly IInventoryDbService inventoryDbService;
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly IUserService userService;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private ObservableCollection<CultivateProject>? projects;
|
||||
@@ -242,4 +259,133 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
await cultivationDbService.RemoveCultivateProjectByIdAsync(project.InnerId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask RefreshInventoryAsync(CultivateProject project)
|
||||
{
|
||||
List<ICultivatable> cultivatables =
|
||||
[
|
||||
.. await metadataService.GetAvatarListAsync().ConfigureAwait(false),
|
||||
.. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(weapon => weapon.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE),
|
||||
];
|
||||
|
||||
BatchConsumption? batchConsumption = default;
|
||||
using (IServiceScope scope = serviceScopeFactory.CreateScope())
|
||||
{
|
||||
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
|
||||
{
|
||||
CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService<CalculateClient>();
|
||||
|
||||
Response<BatchConsumption>? resp = await calculateClient
|
||||
.BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivatables))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!resp.IsOk())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
batchConsumption = resp.Data;
|
||||
}
|
||||
}
|
||||
|
||||
if (batchConsumption is { OverallConsume: { } items })
|
||||
{
|
||||
await inventoryDbService.RemoveInventoryItemRangeByProjectId(project.InnerId, true).ConfigureAwait(false);
|
||||
await inventoryDbService.AddInventoryItemRangeByProjectId(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<AvatarPromotionDelta> GeneratePromotionDeltas(List<ICultivatable> cultivatables)
|
||||
{
|
||||
List<Avatar> avatars = [];
|
||||
List<Weapon> weapons = [];
|
||||
HashSet<MaterialId> materialIds = [];
|
||||
|
||||
while (cultivatables.Count > 0)
|
||||
{
|
||||
ICultivatable bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First();
|
||||
|
||||
if (bestItem.CultivationItems.All(materialIds.Contains))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (bestItem)
|
||||
{
|
||||
case Avatar avatar:
|
||||
avatars.Add(avatar);
|
||||
break;
|
||||
case Weapon weapon:
|
||||
weapons.Add(weapon);
|
||||
break;
|
||||
default:
|
||||
throw HutaoException.NotSupported();
|
||||
}
|
||||
|
||||
foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(bestItem.CultivationItems))
|
||||
{
|
||||
materialIds.Add(materialId);
|
||||
}
|
||||
|
||||
cultivatables.Remove(bestItem);
|
||||
}
|
||||
|
||||
List<AvatarPromotionDelta> deltas = [];
|
||||
|
||||
for (int i = 0; i < Math.Max(avatars.Count, weapons.Count); i++)
|
||||
{
|
||||
Avatar? avatar = avatars.ElementAtOrDefault(i);
|
||||
Weapon? weapon = weapons.ElementAtOrDefault(i);
|
||||
|
||||
if (avatar is not null)
|
||||
{
|
||||
AvatarPromotionDelta delta = new()
|
||||
{
|
||||
AvatarId = avatar.Id,
|
||||
AvatarLevelCurrent = 1,
|
||||
AvatarLevelTarget = 90,
|
||||
SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta()
|
||||
{
|
||||
Id = skill.GroupId,
|
||||
LevelCurrent = 1,
|
||||
LevelTarget = 10,
|
||||
}),
|
||||
};
|
||||
|
||||
if (weapon is not null)
|
||||
{
|
||||
delta.Weapon = new()
|
||||
{
|
||||
Id = weapon.Id,
|
||||
LevelCurrent = 1,
|
||||
LevelTarget = 90,
|
||||
};
|
||||
}
|
||||
|
||||
deltas.Add(delta);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (weapon is not null)
|
||||
{
|
||||
AvatarPromotionDelta delta = new()
|
||||
{
|
||||
Weapon = new()
|
||||
{
|
||||
Id = weapon.Id,
|
||||
LevelCurrent = 1,
|
||||
LevelTarget = 90,
|
||||
},
|
||||
};
|
||||
|
||||
deltas.Add(delta);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
}
|
||||
@@ -66,4 +66,6 @@ internal interface ICultivationService
|
||||
/// <param name="project">项目</param>
|
||||
/// <returns>添加操作的结果</returns>
|
||||
ValueTask<ProjectAddResultKind> TryAddProjectAsync(CultivateProject project);
|
||||
|
||||
ValueTask RefreshInventoryAsync(CultivateProject project);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ internal interface IInventoryDbService
|
||||
{
|
||||
ValueTask AddInventoryItemRangeByProjectId(List<InventoryItem> items);
|
||||
|
||||
ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId);
|
||||
ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId, bool includeMora = false);
|
||||
|
||||
void UpdateInventoryItem(InventoryItem item);
|
||||
|
||||
|
||||
@@ -14,14 +14,14 @@ internal sealed partial class InventoryDbService : IInventoryDbService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId)
|
||||
public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId, bool includeMora = false)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await appDbContext.InventoryItems
|
||||
.AsNoTracking()
|
||||
.Where(a => a.ProjectId == projectId && a.ItemId != 202U) // 摩拉
|
||||
.Where(a => a.ProjectId == projectId && (includeMora || a.ItemId != 202U)) // 摩拉
|
||||
.ExecuteDeleteAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:shc="using:Snap.Hutao.Control"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shcca="using:Snap.Hutao.Control.Collection.Alternating"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
xmlns:shcp="using:Snap.Hutao.Control.Panel"
|
||||
|
||||
@@ -269,6 +269,10 @@
|
||||
Style="{ThemeResource CommandBarComboBoxStyle}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
</AppBarElementContainer>
|
||||
<AppBarButton
|
||||
Command="{Binding RefreshInventoryCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageCultivationRefreshInventory}"/>
|
||||
<AppBarButton
|
||||
Command="{Binding AddProjectCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Factory.ContentDialog;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
@@ -140,8 +141,8 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
CultivateEntries = entries;
|
||||
InventoryItems = cultivationService.GetInventoryItemViews(project, context, SaveInventoryItemCommand);
|
||||
|
||||
await UpdateInventoryItemsAsync().ConfigureAwait(false);
|
||||
await UpdateStatisticsItemsAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -178,6 +179,30 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
[Command("RefreshInventoryCommand")]
|
||||
private async Task RefreshInventoryAsync()
|
||||
{
|
||||
if (SelectedProject is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
|
||||
{
|
||||
ContentDialog dialog = await contentDialogFactory
|
||||
.CreateForIndeterminateProgressAsync(SH.ViewModelCultivationRefreshInventoryProgress)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
using (await dialog.BlockAsync(taskContext).ConfigureAwait(false))
|
||||
{
|
||||
await cultivationService.RefreshInventoryAsync(SelectedProject).ConfigureAwait(false);
|
||||
|
||||
await UpdateInventoryItemsAsync().ConfigureAwait(false);
|
||||
await UpdateStatisticsItemsAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask UpdateStatisticsItemsAsync()
|
||||
{
|
||||
if (SelectedProject is not null)
|
||||
@@ -201,6 +226,18 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask UpdateInventoryItemsAsync()
|
||||
{
|
||||
if (SelectedProject is not null)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
CultivationMetadataContext context = await metadataService.GetContextAsync<CultivationMetadataContext>().ConfigureAwait(false);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
InventoryItems = cultivationService.GetInventoryItemViews(SelectedProject, context, SaveInventoryItemCommand);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("NavigateToPageCommand")]
|
||||
private void NavigateToPage(string? typeString)
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ internal sealed partial class CalculateClient
|
||||
|
||||
public async ValueTask<Response<BatchConsumption>> BatchComputeAsync(UserAndUid userAndUid, List<AvatarPromotionDelta> deltas, CancellationToken token = default)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(deltas.Count, 8);
|
||||
//ArgumentOutOfRangeException.ThrowIfGreaterThan(deltas.Count, 8);
|
||||
|
||||
BatchConsumptionData data = new()
|
||||
{
|
||||
|
||||
@@ -42,5 +42,5 @@ internal sealed class Item
|
||||
public QualityType Level { get; set; }
|
||||
|
||||
[JsonPropertyName("lack_num")]
|
||||
public uint LackNum { get; set; }
|
||||
public int LackNum { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user