mirror of
https://github.com/wangdage12/Snap.Hutao.git
synced 2026-07-31 04:10:25 +08:00
增加材料显示时的材料合并
This commit is contained in:
@@ -74,6 +74,8 @@ internal static class SettingKeys
|
|||||||
public const string CultivationWeapon90LevelCurrent = "Snap::Hutao::Cultivation::Weapon90::Level::Current";
|
public const string CultivationWeapon90LevelCurrent = "Snap::Hutao::Cultivation::Weapon90::Level::Current";
|
||||||
public const string CultivationWeapon90LevelTarget = "Snap::Hutao::Cultivation::Weapon90::Level::Target";
|
public const string CultivationWeapon90LevelTarget = "Snap::Hutao::Cultivation::Weapon90::Level::Target";
|
||||||
public const string ResinStatisticsSelectedDropDistribution = "Snap::Hutao::Cultivation::ResinStatistics::DropDistribution";
|
public const string ResinStatisticsSelectedDropDistribution = "Snap::Hutao::Cultivation::ResinStatistics::DropDistribution";
|
||||||
|
public const string CultivationStatisticsMergeUpgradeMaterials = "Snap::Hutao::Cultivation::Statistics::MergeUpgradeMaterials";
|
||||||
|
public const string CultivationStatisticsTalentSynthCritTenPercent = "Snap::Hutao::Cultivation::Statistics::TalentSynthCritTenPercent";
|
||||||
|
|
||||||
// GachaLog
|
// GachaLog
|
||||||
public const string IsEmptyHistoryWishVisible = "Snap::Hutao::GachaLog::HistoryWish::EmptyVisible";
|
public const string IsEmptyHistoryWishVisible = "Snap::Hutao::GachaLog::HistoryWish::EmptyVisible";
|
||||||
|
|||||||
@@ -2420,6 +2420,12 @@ Space Available: {2}</value>
|
|||||||
<data name="ViewPageCultivationMaterialListIncompleteFirstLabel" xml:space="preserve">
|
<data name="ViewPageCultivationMaterialListIncompleteFirstLabel" xml:space="preserve">
|
||||||
<value>Uncollected First</value>
|
<value>Uncollected First</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ViewPageCultivationMergeUpgradeMaterialsLabel" xml:space="preserve">
|
||||||
|
<value>Merge upgrade materials</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewPageCultivationTalentSynthCritTenPercentLabel" xml:space="preserve">
|
||||||
|
<value>Character talent (10%)</value>
|
||||||
|
</data>
|
||||||
<data name="ViewPageCultivationMaterialListResinStatisticsLabel" xml:space="preserve">
|
<data name="ViewPageCultivationMaterialListResinStatisticsLabel" xml:space="preserve">
|
||||||
<value>Resin Estimation</value>
|
<value>Resin Estimation</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -2471,6 +2471,12 @@
|
|||||||
<data name="ViewPageCultivationMaterialListIncompleteFirstLabel" xml:space="preserve">
|
<data name="ViewPageCultivationMaterialListIncompleteFirstLabel" xml:space="preserve">
|
||||||
<value>未集齐优先</value>
|
<value>未集齐优先</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ViewPageCultivationMergeUpgradeMaterialsLabel" xml:space="preserve">
|
||||||
|
<value>升级材料合并</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewPageCultivationTalentSynthCritTenPercentLabel" xml:space="preserve">
|
||||||
|
<value>人物天赋(10%)</value>
|
||||||
|
</data>
|
||||||
<data name="ViewPageCultivationMaterialListResinStatisticsLabel" xml:space="preserve">
|
<data name="ViewPageCultivationMaterialListResinStatisticsLabel" xml:space="preserve">
|
||||||
<value>树脂预估</value>
|
<value>树脂预估</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -124,6 +124,6 @@ internal sealed partial class CultivationResinStatisticsService : ICultivationRe
|
|||||||
|
|
||||||
private static double GetStatisticsCultivateItemTimes(StatisticsCultivateItem item)
|
private static double GetStatisticsCultivateItemTimes(StatisticsCultivateItem item)
|
||||||
{
|
{
|
||||||
return item.Count - (long)item.Current;
|
return item.Count - (long)item.DisplayCurrent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,12 +92,12 @@ internal sealed partial class CultivationService : ICultivationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<StatisticsCultivateItemCollection> GetStatisticsCultivateItemCollectionAsync(CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token)
|
public async ValueTask<StatisticsCultivateItemCollection> GetStatisticsCultivateItemCollectionAsync(CultivateProject cultivateProject, ICultivationMetadataContext context, CultivationStatisticsMergeOptions mergeOptions, CancellationToken token)
|
||||||
{
|
{
|
||||||
await taskContext.SwitchToBackgroundAsync();
|
await taskContext.SwitchToBackgroundAsync();
|
||||||
return SynchronizedGetStatisticsCultivateItemCollection(cultivateProject, context);
|
return SynchronizedGetStatisticsCultivateItemCollection(cultivateProject, context, mergeOptions);
|
||||||
|
|
||||||
StatisticsCultivateItemCollection SynchronizedGetStatisticsCultivateItemCollection(CultivateProject cultivateProject, ICultivationMetadataContext context)
|
StatisticsCultivateItemCollection SynchronizedGetStatisticsCultivateItemCollection(CultivateProject cultivateProject, ICultivationMetadataContext context, CultivationStatisticsMergeOptions mergeOptions)
|
||||||
{
|
{
|
||||||
Dictionary</* ItemId */ uint, StatisticsCultivateItem> resultItems = [];
|
Dictionary</* ItemId */ uint, StatisticsCultivateItem> resultItems = [];
|
||||||
Guid projectId = cultivateProject.InnerId;
|
Guid projectId = cultivateProject.InnerId;
|
||||||
@@ -129,6 +129,8 @@ internal sealed partial class CultivationService : ICultivationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CultivationStatisticsSurplusMerge.Apply(resultItems, context, mergeOptions);
|
||||||
|
|
||||||
return new(resultItems);
|
return new(resultItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Service.Cultivation;
|
||||||
|
|
||||||
|
internal readonly record struct CultivationStatisticsMergeOptions(bool MergeUpgradeMaterials, bool TalentSynthCritTenPercent);
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Intrinsic;
|
||||||
|
using Snap.Hutao.Model.Metadata;
|
||||||
|
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||||
|
using Snap.Hutao.ViewModel.Cultivation;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Service.Cultivation;
|
||||||
|
|
||||||
|
internal static class CultivationStatisticsSurplusMerge
|
||||||
|
{
|
||||||
|
private const uint TalentCombineType = 3U;
|
||||||
|
|
||||||
|
public static void Apply(Dictionary<uint, StatisticsCultivateItem> items, ICultivationMetadataContext context, CultivationStatisticsMergeOptions options)
|
||||||
|
{
|
||||||
|
if (!options.MergeUpgradeMaterials || items.Count is 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool talentCrit = options.TalentSynthCritTenPercent;
|
||||||
|
|
||||||
|
List<Combine> eligibleCombines = [];
|
||||||
|
foreach (Combine combine in context.ResultMaterialIdCombineMap.Values)
|
||||||
|
{
|
||||||
|
if (combine.RecipeType is not RecipeType.RECIPE_TYPE_COMBINE)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (combine.Materials.Length is not 1 || combine.Materials[0].Count is not 3 || combine.Result.Count is not 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
eligibleCombines.Add(combine);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<uint, double> virtualAmount = new(items.Count);
|
||||||
|
foreach ((uint id, StatisticsCultivateItem item) in items)
|
||||||
|
{
|
||||||
|
virtualAmount[id] = item.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (IGrouping<uint, Combine> group in eligibleCombines.GroupBy(static c => c.SubType))
|
||||||
|
{
|
||||||
|
List<Combine> groupCombines = [.. group];
|
||||||
|
HashSet<uint> ids = [];
|
||||||
|
foreach (Combine combine in groupCombines)
|
||||||
|
{
|
||||||
|
_ = ids.Add(combine.Result.Id);
|
||||||
|
_ = ids.Add(combine.Materials[0].Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[] sortedIds = [.. ids.OrderBy(id => GetRankLevel(context, id)).ThenBy(id => id)];
|
||||||
|
|
||||||
|
foreach (uint id in sortedIds)
|
||||||
|
{
|
||||||
|
Combine? upward = FindUpwardCombine(groupCombines, id);
|
||||||
|
if (upward is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!virtualAmount.TryGetValue(id, out double virt))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint need = items.TryGetValue(id, out StatisticsCultivateItem? row) ? row.Count : 0U;
|
||||||
|
double surplus = Math.Max(0D, virt - need);
|
||||||
|
long crafts = (long)(surplus / 3D);
|
||||||
|
if (crafts <= 0L)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double multiplier = talentCrit && upward.Type == TalentCombineType ? 1.1D : 1D;
|
||||||
|
double produced = crafts * multiplier;
|
||||||
|
uint resultId = upward.Result.Id;
|
||||||
|
|
||||||
|
virtualAmount[id] = virt - (crafts * 3L);
|
||||||
|
|
||||||
|
if (!virtualAmount.TryGetValue(resultId, out double atResult))
|
||||||
|
{
|
||||||
|
atResult = items.TryGetValue(resultId, out StatisticsCultivateItem? r) ? r.Current : 0D;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtualAmount[resultId] = atResult + produced;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ((uint id, StatisticsCultivateItem item) in items)
|
||||||
|
{
|
||||||
|
if (virtualAmount.TryGetValue(id, out double v))
|
||||||
|
{
|
||||||
|
item.MergeAdjustedCurrent = (uint)Math.Clamp(Math.Floor(v + 1e-6), 0D, uint.MaxValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Combine? FindUpwardCombine(List<Combine> groupCombines, uint ingredientId)
|
||||||
|
{
|
||||||
|
foreach (Combine combine in groupCombines)
|
||||||
|
{
|
||||||
|
if (combine.Materials is [{ Id: var mid, Count: 3 }] && mid == ingredientId)
|
||||||
|
{
|
||||||
|
return combine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static QualityType GetRankLevel(ICultivationMetadataContext context, uint materialId)
|
||||||
|
{
|
||||||
|
return context.GetMaterial(materialId).RankLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ internal interface ICultivationService
|
|||||||
|
|
||||||
ValueTask<ObservableCollection<CultivateEntryView>> GetCultivateEntryCollectionAsync(CultivateProject cultivateProject, ICultivationMetadataContext context);
|
ValueTask<ObservableCollection<CultivateEntryView>> GetCultivateEntryCollectionAsync(CultivateProject cultivateProject, ICultivationMetadataContext context);
|
||||||
|
|
||||||
ValueTask<StatisticsCultivateItemCollection> GetStatisticsCultivateItemCollectionAsync(CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token);
|
ValueTask<StatisticsCultivateItemCollection> GetStatisticsCultivateItemCollectionAsync(CultivateProject cultivateProject, ICultivationMetadataContext context, CultivationStatisticsMergeOptions mergeOptions, CancellationToken token);
|
||||||
|
|
||||||
ValueTask<ResinStatistics> GetResinStatisticsAsync(StatisticsCultivateItemCollection statisticsCultivateItems, CancellationToken token);
|
ValueTask<ResinStatistics> GetResinStatisticsAsync(StatisticsCultivateItemCollection statisticsCultivateItems, CancellationToken token);
|
||||||
|
|
||||||
|
|||||||
@@ -482,6 +482,19 @@
|
|||||||
IsChecked="{Binding IncompleteFirst, Mode=TwoWay}"
|
IsChecked="{Binding IncompleteFirst, Mode=TwoWay}"
|
||||||
Label="{shuxm:ResourceString Name=ViewPageCultivationMaterialListIncompleteFirstLabel}"
|
Label="{shuxm:ResourceString Name=ViewPageCultivationMaterialListIncompleteFirstLabel}"
|
||||||
Visibility="{Binding ElementName=MaterialListPivot, Path=SelectedItem.Tag, Converter={StaticResource MaterialListSelectedItemIsStatisticsViewConverter}}"/>
|
Visibility="{Binding ElementName=MaterialListPivot, Path=SelectedItem.Tag, Converter={StaticResource MaterialListSelectedItemIsStatisticsViewConverter}}"/>
|
||||||
|
<AppBarToggleButton
|
||||||
|
Command="{Binding RefreshStatisticsItemsCommand}"
|
||||||
|
Icon="{shuxm:FontIcon Glyph=}"
|
||||||
|
IsChecked="{Binding MergeUpgradeMaterials, Mode=TwoWay}"
|
||||||
|
Label="{shuxm:ResourceString Name=ViewPageCultivationMergeUpgradeMaterialsLabel}"
|
||||||
|
Visibility="{Binding ElementName=MaterialListPivot, Path=SelectedItem.Tag, Converter={StaticResource MaterialListSelectedItemIsStatisticsViewConverter}}"/>
|
||||||
|
<AppBarToggleButton
|
||||||
|
Command="{Binding RefreshStatisticsItemsCommand}"
|
||||||
|
Icon="{shuxm:FontIcon Glyph=}"
|
||||||
|
IsChecked="{Binding TalentSynthCritTenPercent, Mode=TwoWay}"
|
||||||
|
IsEnabled="{Binding MergeUpgradeMaterials}"
|
||||||
|
Label="{shuxm:ResourceString Name=ViewPageCultivationTalentSynthCritTenPercentLabel}"
|
||||||
|
Visibility="{Binding ElementName=MaterialListPivot, Path=SelectedItem.Tag, Converter={StaticResource MaterialListSelectedItemIsStatisticsViewConverter}}"/>
|
||||||
<AppBarButton Icon="{shuxm:FontIcon Glyph=}" Label="{shuxm:ResourceString Name=ViewPageCultivationMaterialListResinStatisticsLabel}">
|
<AppBarButton Icon="{shuxm:FontIcon Glyph=}" Label="{shuxm:ResourceString Name=ViewPageCultivationMaterialListResinStatisticsLabel}">
|
||||||
<AppBarButton.Flyout>
|
<AppBarButton.Flyout>
|
||||||
<Flyout FlyoutPresenterStyle="{ThemeResource FlyoutPresenterPadding0Style}" Placement="BottomEdgeAlignedRight">
|
<Flyout FlyoutPresenterStyle="{ThemeResource FlyoutPresenterPadding0Style}" Placement="BottomEdgeAlignedRight">
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Snap.Hutao.Core;
|
|||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.ExceptionService;
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
|
using Snap.Hutao.Core.Setting;
|
||||||
using Snap.Hutao.Factory.ContentDialog;
|
using Snap.Hutao.Factory.ContentDialog;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
using Snap.Hutao.Service.Cultivation;
|
using Snap.Hutao.Service.Cultivation;
|
||||||
@@ -70,6 +71,12 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial bool IncompleteFirst { get; set; }
|
public partial bool IncompleteFirst { get; set; }
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial bool MergeUpgradeMaterials { get; set; } = LocalSetting.Get(SettingKeys.CultivationStatisticsMergeUpgradeMaterials, false);
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial bool TalentSynthCritTenPercent { get; set; } = LocalSetting.Get(SettingKeys.CultivationStatisticsTalentSynthCritTenPercent, false);
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial ObservableCollection<StatisticsCultivateItem>? StatisticsItems { get; set; }
|
public partial ObservableCollection<StatisticsCultivateItem>? StatisticsItems { get; set; }
|
||||||
|
|
||||||
@@ -356,12 +363,19 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
|||||||
|
|
||||||
await taskContext.SwitchToBackgroundAsync();
|
await taskContext.SwitchToBackgroundAsync();
|
||||||
|
|
||||||
|
bool merge = MergeUpgradeMaterials;
|
||||||
|
bool talentCrit = merge && TalentSynthCritTenPercent;
|
||||||
|
LocalSetting.Set(SettingKeys.CultivationStatisticsMergeUpgradeMaterials, merge);
|
||||||
|
LocalSetting.Set(SettingKeys.CultivationStatisticsTalentSynthCritTenPercent, talentCrit);
|
||||||
|
|
||||||
CancellationToken token = exclusiveTokenProvider.GetNewToken();
|
CancellationToken token = exclusiveTokenProvider.GetNewToken();
|
||||||
StatisticsCultivateItemCollection statistics;
|
StatisticsCultivateItemCollection statistics;
|
||||||
ResinStatistics resinStatistics;
|
ResinStatistics resinStatistics;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
statistics = await cultivationService.GetStatisticsCultivateItemCollectionAsync(Projects.CurrentItem, metadataContext, token).ConfigureAwait(false);
|
statistics = await cultivationService
|
||||||
|
.GetStatisticsCultivateItemCollectionAsync(Projects.CurrentItem, metadataContext, new CultivationStatisticsMergeOptions(merge, talentCrit), token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
resinStatistics = await cultivationService.GetResinStatisticsAsync(statistics, token).ConfigureAwait(false);
|
resinStatistics = await cultivationService.GetResinStatisticsAsync(statistics, token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
|
|||||||
@@ -29,9 +29,16 @@ internal sealed class StatisticsCultivateItem
|
|||||||
|
|
||||||
public uint Current { get; set; }
|
public uint Current { get; set; }
|
||||||
|
|
||||||
public bool IsFinished { get => Current >= Count; }
|
/// <summary>
|
||||||
|
/// 升级材料合并后的展示用持有量;未启用合并时为 <see langword="null"/>。
|
||||||
|
/// </summary>
|
||||||
|
public uint? MergeAdjustedCurrent { get; set; }
|
||||||
|
|
||||||
public string FormattedCount { get => $"{Current}/{Count}"; }
|
public uint DisplayCurrent { get => MergeAdjustedCurrent ?? Current; }
|
||||||
|
|
||||||
|
public bool IsFinished { get => DisplayCurrent >= Count; }
|
||||||
|
|
||||||
|
public string FormattedCount { get => $"{DisplayCurrent}/{Count}"; }
|
||||||
|
|
||||||
public bool IsToday { get => Inner.IsItemOfToday(offset, true); }
|
public bool IsToday { get => Inner.IsItemOfToday(offset, true); }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user