mirror of
https://github.com/ppy/osu.git
synced 2024-11-15 14:37:30 +08:00
Decouple AdvancedStats
from global mods
Closes https://github.com/ppy/osu/issues/30163. If I'm to be blunt, the decoupled stuff in song select makes my head spin. I spent a solid 20 minutes thinking how I was going to fix this one but then finally realised that generally most of the cause there was the fact that `AdvancedStats` was seeing the new rulesets *before* the "ensure global selected mods are valid for current ruleset" logic, and so decided to just _delay_ that until the decoupled transfer thingamajig happens. I was honestly considering combining `BeatmapInfo`, `Ruleset`, and `Mods` into one property on `AdvancedStats`. I figured I'd rather not push my luck and try the baseline version first, but I honestly think that direction is going to be required at some point to properly corral all of the decoupled madness taking place in song select.
This commit is contained in:
parent
a6f56036a2
commit
f1842d781e
@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddStep("select EZ mod", () =>
|
AddStep("select EZ mod", () =>
|
||||||
{
|
{
|
||||||
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
|
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
|
||||||
SelectedMods.Value = new[] { ruleset.CreateMod<ModEasy>() };
|
advancedStats.Mods.Value = new[] { ruleset.CreateMod<ModEasy>() };
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.FirstValue));
|
AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.FirstValue));
|
||||||
@ -101,7 +101,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddStep("select HR mod", () =>
|
AddStep("select HR mod", () =>
|
||||||
{
|
{
|
||||||
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
|
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
|
||||||
SelectedMods.Value = new[] { ruleset.CreateMod<ModHardRock>() };
|
advancedStats.Mods.Value = new[] { ruleset.CreateMod<ModHardRock>() };
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("circle size bar is red", () => barIsRed(advancedStats.FirstValue));
|
AddAssert("circle size bar is red", () => barIsRed(advancedStats.FirstValue));
|
||||||
@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
|
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
|
||||||
var difficultyAdjustMod = ruleset.CreateMod<ModDifficultyAdjust>().AsNonNull();
|
var difficultyAdjustMod = ruleset.CreateMod<ModDifficultyAdjust>().AsNonNull();
|
||||||
difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.Difficulty);
|
difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.Difficulty);
|
||||||
SelectedMods.Value = new[] { difficultyAdjustMod };
|
advancedStats.Mods.Value = new[] { difficultyAdjustMod };
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
|
AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
|
||||||
@ -143,7 +143,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
difficultyAdjustMod.ReadFromDifficulty(originalDifficulty);
|
difficultyAdjustMod.ReadFromDifficulty(originalDifficulty);
|
||||||
difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f;
|
difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f;
|
||||||
difficultyAdjustMod.ApproachRate.Value = originalDifficulty.ApproachRate + 2.2f;
|
difficultyAdjustMod.ApproachRate.Value = originalDifficulty.ApproachRate + 2.2f;
|
||||||
SelectedMods.Value = new[] { difficultyAdjustMod };
|
advancedStats.Mods.Value = new[] { difficultyAdjustMod };
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
|
AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
@ -36,9 +37,6 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
|
||||||
|
|
||||||
protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
|
protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
|
||||||
private readonly StatisticRow starDifficulty;
|
private readonly StatisticRow starDifficulty;
|
||||||
|
|
||||||
@ -69,6 +67,14 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public Bindable<RulesetInfo> Ruleset { get; } = new Bindable<RulesetInfo>();
|
public Bindable<RulesetInfo> Ruleset { get; } = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mods to be used for certain elements of display.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// No checks are done as to whether the mods specified are valid for the current <see cref="Ruleset"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public Bindable<IReadOnlyList<Mod>> Mods { get; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||||
|
|
||||||
public AdvancedStats(int columns = 1)
|
public AdvancedStats(int columns = 1)
|
||||||
{
|
{
|
||||||
switch (columns)
|
switch (columns)
|
||||||
@ -143,8 +149,7 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Ruleset.BindValueChanged(_ => updateStatistics());
|
Ruleset.BindValueChanged(_ => updateStatistics());
|
||||||
|
Mods.BindValueChanged(modsChanged, true);
|
||||||
mods.BindValueChanged(modsChanged, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModSettingChangeTracker modSettingChangeTracker;
|
private ModSettingChangeTracker modSettingChangeTracker;
|
||||||
@ -173,14 +178,14 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
{
|
{
|
||||||
BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty);
|
BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty);
|
||||||
|
|
||||||
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
|
foreach (var mod in Mods.Value.OfType<IApplicableToDifficulty>())
|
||||||
mod.ApplyToDifficulty(originalDifficulty);
|
mod.ApplyToDifficulty(originalDifficulty);
|
||||||
|
|
||||||
adjustedDifficulty = originalDifficulty;
|
adjustedDifficulty = originalDifficulty;
|
||||||
|
|
||||||
if (Ruleset.Value != null)
|
if (Ruleset.Value != null)
|
||||||
{
|
{
|
||||||
double rate = ModUtils.CalculateRateWithMods(mods.Value);
|
double rate = ModUtils.CalculateRateWithMods(Mods.Value);
|
||||||
|
|
||||||
adjustedDifficulty = Ruleset.Value.CreateInstance().GetRateAdjustedDisplayDifficulty(originalDifficulty, rate);
|
adjustedDifficulty = Ruleset.Value.CreateInstance().GetRateAdjustedDisplayDifficulty(originalDifficulty, rate);
|
||||||
|
|
||||||
@ -198,7 +203,7 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
// For the time being, the key count is static no matter what, because:
|
// For the time being, the key count is static no matter what, because:
|
||||||
// a) The method doesn't have knowledge of the active keymods. Doing so may require considerations for filtering.
|
// a) The method doesn't have knowledge of the active keymods. Doing so may require considerations for filtering.
|
||||||
// b) Using the difficulty adjustment mod to adjust OD doesn't have an effect on conversion.
|
// b) Using the difficulty adjustment mod to adjust OD doesn't have an effect on conversion.
|
||||||
int keyCount = baseDifficulty == null ? 0 : legacyRuleset.GetKeyCount(BeatmapInfo, mods.Value);
|
int keyCount = baseDifficulty == null ? 0 : legacyRuleset.GetKeyCount(BeatmapInfo, Mods.Value);
|
||||||
|
|
||||||
FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania;
|
FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania;
|
||||||
FirstValue.Value = (keyCount, keyCount);
|
FirstValue.Value = (keyCount, keyCount);
|
||||||
@ -236,7 +241,7 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
starDifficultyCancellationSource = new CancellationTokenSource();
|
starDifficultyCancellationSource = new CancellationTokenSource();
|
||||||
|
|
||||||
var normalStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, Ruleset.Value, null, starDifficultyCancellationSource.Token);
|
var normalStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, Ruleset.Value, null, starDifficultyCancellationSource.Token);
|
||||||
var moddedStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, Ruleset.Value, mods.Value, starDifficultyCancellationSource.Token);
|
var moddedStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, Ruleset.Value, Mods.Value, starDifficultyCancellationSource.Token);
|
||||||
|
|
||||||
Task.WhenAll(normalStarDifficultyTask, moddedStarDifficultyTask).ContinueWith(_ => Schedule(() =>
|
Task.WhenAll(normalStarDifficultyTask, moddedStarDifficultyTask).ContinueWith(_ => Schedule(() =>
|
||||||
{
|
{
|
||||||
|
@ -610,11 +610,6 @@ namespace osu.Game.Screens.Select
|
|||||||
beatmapInfoPrevious = beatmap;
|
beatmapInfoPrevious = beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can't run this in the debounced run due to the selected mods bindable not being debounced,
|
|
||||||
// since mods could be updated to the new ruleset instances while the decoupled bindable is held behind,
|
|
||||||
// therefore resulting in performing difficulty calculation with invalid states.
|
|
||||||
advancedStats.Ruleset.Value = ruleset;
|
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
// clear pending task immediately to track any potential nested debounce operation.
|
// clear pending task immediately to track any potential nested debounce operation.
|
||||||
@ -878,6 +873,8 @@ namespace osu.Game.Screens.Select
|
|||||||
ModSelect.Beatmap.Value = beatmap;
|
ModSelect.Beatmap.Value = beatmap;
|
||||||
|
|
||||||
advancedStats.BeatmapInfo = beatmap.BeatmapInfo;
|
advancedStats.BeatmapInfo = beatmap.BeatmapInfo;
|
||||||
|
advancedStats.Mods.Value = selectedMods.Value;
|
||||||
|
advancedStats.Ruleset.Value = Ruleset.Value;
|
||||||
|
|
||||||
bool beatmapSelected = beatmap is not DummyWorkingBeatmap;
|
bool beatmapSelected = beatmap is not DummyWorkingBeatmap;
|
||||||
|
|
||||||
@ -990,6 +987,12 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
Beatmap.BindValueChanged(updateCarouselSelection);
|
Beatmap.BindValueChanged(updateCarouselSelection);
|
||||||
|
|
||||||
|
selectedMods.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
if (decoupledRuleset.Value.Equals(rulesetNoDebounce))
|
||||||
|
advancedStats.Mods.Value = selectedMods.Value;
|
||||||
|
}, true);
|
||||||
|
|
||||||
boundLocalBindables = true;
|
boundLocalBindables = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user