diff --git a/osu.Game.Rulesets.Catch.Tests/CatchRateAdjustedDisplayDifficultyTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchRateAdjustedDisplayDifficultyTest.cs
index 0ec3bfd911..3af581fcf3 100644
--- a/osu.Game.Rulesets.Catch.Tests/CatchRateAdjustedDisplayDifficultyTest.cs
+++ b/osu.Game.Rulesets.Catch.Tests/CatchRateAdjustedDisplayDifficultyTest.cs
@@ -22,8 +22,9 @@ namespace osu.Game.Rulesets.Catch.Tests
{
var ruleset = new CatchRuleset();
var difficulty = new BeatmapDifficulty { ApproachRate = originalApproachRate };
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, []);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, []);
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(originalApproachRate));
}
@@ -33,8 +34,9 @@ namespace osu.Game.Rulesets.Catch.Tests
{
var ruleset = new CatchRuleset();
var difficulty = new BeatmapDifficulty();
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, [new CatchModHalfTime()]);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, [new CatchModHalfTime()]);
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(1.67).Within(0.01));
}
@@ -44,8 +46,9 @@ namespace osu.Game.Rulesets.Catch.Tests
{
var ruleset = new CatchRuleset();
var difficulty = new BeatmapDifficulty();
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, [new CatchModDoubleTime()]);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, [new CatchModDoubleTime()]);
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(7.67).Within(0.01));
}
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 571c115a85..b927f958c0 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -11,6 +11,7 @@ using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Graphics;
+using osu.Game.Localisation;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Difficulty;
using osu.Game.Rulesets.Catch.Edit;
@@ -267,9 +268,9 @@ namespace osu.Game.Rulesets.Catch
}
///
- public override BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, IReadOnlyCollection mods)
+ public override BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods)
{
- BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty);
+ BeatmapDifficulty adjustedDifficulty = base.GetAdjustedDisplayDifficulty(beatmapInfo, mods);
double rate = ModUtils.CalculateRateWithMods(mods);
double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, CatchHitObject.PREEMPT_MAX, CatchHitObject.PREEMPT_MID, CatchHitObject.PREEMPT_MIN);
@@ -279,6 +280,16 @@ namespace osu.Game.Rulesets.Catch
return adjustedDifficulty;
}
+ public override IEnumerable GetBeatmapAttributesForDisplay(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods)
+ {
+ var originalDifficulty = beatmapInfo.Difficulty;
+ var adjustedDifficulty = GetAdjustedDisplayDifficulty(beatmapInfo, mods);
+
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.CircleSize, @"CS", originalDifficulty.CircleSize, adjustedDifficulty.CircleSize, 10);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.ApproachRate, @"AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate, 10);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.HPDrain, @"HP", originalDifficulty.DrainRate, adjustedDifficulty.DrainRate, 10);
+ }
+
public override bool EditorShowScrollSpeed => false;
}
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 96550618c0..c55465762b 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
}
}
- public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty, IReadOnlyList? mods = null)
+ public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty, IReadOnlyCollection? mods = null)
{
var converter = new ManiaBeatmapConverter(null, difficulty, new ManiaRuleset());
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 90d0080d6e..3ad77f4a84 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -12,6 +12,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Configuration;
using osu.Game.Graphics;
+using osu.Game.Localisation;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Difficulty;
@@ -415,9 +416,9 @@ namespace osu.Game.Rulesets.Mania
};
///
- public override BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, IReadOnlyCollection mods)
+ public override BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods)
{
- BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty);
+ BeatmapDifficulty adjustedDifficulty = base.GetAdjustedDisplayDifficulty(beatmapInfo, mods);
// notably, in mania, hit windows are designed to be independent of track playback rate (see `ManiaHitWindows.SpeedMultiplier`).
// *however*, to not make matters *too* simple, mania Hard Rock and Easy differ from all other rulesets
@@ -436,10 +437,25 @@ namespace osu.Game.Rulesets.Mania
perfectHitWindow /= ManiaModEasy.HIT_WINDOW_DIFFICULTY_MULTIPLIER;
adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(perfectHitWindow, ManiaHitWindows.PERFECT_WINDOW_RANGE);
+ adjustedDifficulty.CircleSize = ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), mods);
return adjustedDifficulty;
}
+ public override IEnumerable GetBeatmapAttributesForDisplay(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods)
+ {
+ // a special touch-up of key count is required to the original difficulty, since key conversion mods are not `IApplicableToDifficulty`
+ var originalDifficulty = new BeatmapDifficulty(beatmapInfo.Difficulty)
+ {
+ CircleSize = ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), [])
+ };
+ var adjustedDifficulty = GetAdjustedDisplayDifficulty(beatmapInfo, mods);
+
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.KeyCount, @"KC", originalDifficulty.CircleSize, adjustedDifficulty.CircleSize, 18);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.Accuracy, @"OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty, 10);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.HPDrain, @"HP", originalDifficulty.DrainRate, adjustedDifficulty.DrainRate, 10);
+ }
+
public override IRulesetFilterCriteria CreateRulesetFilterCriteria()
{
return new ManiaFilterCriteria();
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuRateAdjustedDisplayDifficultyTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuRateAdjustedDisplayDifficultyTest.cs
index 4108e9388d..fd929dd8f4 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuRateAdjustedDisplayDifficultyTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuRateAdjustedDisplayDifficultyTest.cs
@@ -22,8 +22,9 @@ namespace osu.Game.Rulesets.Osu.Tests
{
var ruleset = new OsuRuleset();
var difficulty = new BeatmapDifficulty { ApproachRate = originalApproachRate };
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, []);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, []);
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(originalApproachRate));
}
@@ -33,8 +34,9 @@ namespace osu.Game.Rulesets.Osu.Tests
{
var ruleset = new OsuRuleset();
var difficulty = new BeatmapDifficulty { OverallDifficulty = originalOverallDifficulty };
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, []);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, []);
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(originalOverallDifficulty));
}
@@ -44,8 +46,9 @@ namespace osu.Game.Rulesets.Osu.Tests
{
var ruleset = new OsuRuleset();
var difficulty = new BeatmapDifficulty();
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, [new OsuModHalfTime()]);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, [new OsuModHalfTime()]);
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(1.67).Within(0.01));
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(2.22).Within(0.01));
@@ -56,8 +59,9 @@ namespace osu.Game.Rulesets.Osu.Tests
{
var ruleset = new OsuRuleset();
var difficulty = new BeatmapDifficulty();
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, [new OsuModDoubleTime()]);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, [new OsuModDoubleTime()]);
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(7.67).Within(0.01));
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(7.77).Within(0.01));
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index be9f0e276b..8f0974067a 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -366,9 +366,9 @@ namespace osu.Game.Rulesets.Osu
///
///
- public override BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, IReadOnlyCollection mods)
+ public override BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapInfo difficulty, IReadOnlyCollection mods)
{
- BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty);
+ BeatmapDifficulty adjustedDifficulty = base.GetAdjustedDisplayDifficulty(difficulty, mods);
double rate = ModUtils.CalculateRateWithMods(mods);
double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, OsuHitObject.PREEMPT_MAX, OsuHitObject.PREEMPT_MID, OsuHitObject.PREEMPT_MIN);
diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoRateAdjustedDisplayDifficultyTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoRateAdjustedDisplayDifficultyTest.cs
index 2a5688ab11..0fb92e0d7d 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TaikoRateAdjustedDisplayDifficultyTest.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TaikoRateAdjustedDisplayDifficultyTest.cs
@@ -22,8 +22,9 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
var ruleset = new TaikoRuleset();
var difficulty = new BeatmapDifficulty { OverallDifficulty = originalOverallDifficulty };
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, []);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, []);
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(originalOverallDifficulty));
}
@@ -33,8 +34,9 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
var ruleset = new TaikoRuleset();
var difficulty = new BeatmapDifficulty();
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, [new TaikoModHalfTime()]);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, [new TaikoModHalfTime()]);
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(1.11).Within(0.01));
}
@@ -44,8 +46,9 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
var ruleset = new TaikoRuleset();
var difficulty = new BeatmapDifficulty();
+ var beatmapInfo = new BeatmapInfo { Difficulty = difficulty };
- var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(difficulty, [new TaikoModDoubleTime()]);
+ var adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(beatmapInfo, [new TaikoModDoubleTime()]);
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(8.89).Within(0.01));
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index 76488fdd26..d4c180c95e 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -33,6 +33,7 @@ using osu.Game.Screens.Ranking.Statistics;
using osu.Game.Skinning;
using osu.Game.Rulesets.Configuration;
using osu.Game.Configuration;
+using osu.Game.Localisation;
using osu.Game.Rulesets.Scoring.Legacy;
using osu.Game.Rulesets.Taiko.Configuration;
using osu.Game.Rulesets.Taiko.Edit.Setup;
@@ -271,9 +272,9 @@ namespace osu.Game.Rulesets.Taiko
}
///
- public override BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, IReadOnlyCollection mods)
+ public override BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods)
{
- BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty);
+ BeatmapDifficulty adjustedDifficulty = base.GetAdjustedDisplayDifficulty(beatmapInfo, mods);
double rate = ModUtils.CalculateRateWithMods(mods);
double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, TaikoHitWindows.GREAT_WINDOW_RANGE);
@@ -282,5 +283,15 @@ namespace osu.Game.Rulesets.Taiko
return adjustedDifficulty;
}
+
+ public override IEnumerable GetBeatmapAttributesForDisplay(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods)
+ {
+ var originalDifficulty = beatmapInfo.Difficulty;
+ var adjustedDifficulty = GetAdjustedDisplayDifficulty(beatmapInfo, mods);
+
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.Accuracy, @"OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty, 10);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.HPDrain, @"HP", originalDifficulty.DrainRate, adjustedDifficulty.DrainRate, 10);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.ScrollSpeed, @"SS", 1f, (float)(adjustedDifficulty.SliderMultiplier / originalDifficulty.SliderMultiplier), 4);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index 5da05826cf..b164c530cb 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -15,11 +15,11 @@ using Newtonsoft.Json.Linq;
using osu.Framework.Testing;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Sprites;
+using osu.Game.Localisation;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapSet.Scores;
-using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Select.Details;
@@ -274,7 +274,7 @@ namespace osu.Game.Tests.Visual.Online
public void TestSelectedModsDontAffectStatistics()
{
AddStep("show map", () => overlay.ShowBeatmapSet(getBeatmapSet()));
- AddAssert("AR displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value, () => Is.EqualTo((0, 0)));
+ AddAssert("AR displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == SongSelectStrings.ApproachRate).Value, () => Is.EqualTo((0, 0)));
AddStep("set AR10 diff adjust", () => SelectedMods.Value = new[]
{
new OsuModDifficultyAdjust
@@ -282,7 +282,7 @@ namespace osu.Game.Tests.Visual.Online
ApproachRate = { Value = 10 }
}
});
- AddAssert("AR still displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value, () => Is.EqualTo((0, 0)));
+ AddAssert("AR still displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == SongSelectStrings.ApproachRate).Value, () => Is.EqualTo((0, 0)));
}
[Test]
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
index 5cf503f21e..3afc8cd1a4 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
@@ -8,11 +8,10 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Testing;
+using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
-using osu.Game.Resources.Localisation.Web;
+using osu.Game.Localisation;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mods;
@@ -42,7 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private BeatmapInfo exampleBeatmapInfo => new BeatmapInfo
{
- Ruleset = rulesets.AvailableRulesets.First(),
+ Ruleset = rulesets.GetRuleset(0)!,
Difficulty = new BeatmapDifficulty
{
CircleSize = 7.2f,
@@ -56,30 +55,39 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestNoMod()
{
- AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
+ AddStep("set beatmap and ruleset", () =>
+ {
+ advancedStats.BeatmapInfo = exampleBeatmapInfo;
+ advancedStats.Ruleset.Value = exampleBeatmapInfo.Ruleset;
+ });
AddStep("no mods selected", () => SelectedMods.Value = Array.Empty());
- AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCs);
- AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
- AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain));
- AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
- AddAssert("approach rate bar is white", () => barIsWhite(advancedStats.ApproachRate));
+ AddAssert("first bar text is correct", () => advancedStats.GetStatistic(SongSelectStrings.CircleSize), () => Is.Not.Null);
+ AddAssert("circle size bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.CircleSize)));
+ AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.HPDrain)));
+ AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.Accuracy)));
+ AddAssert("approach rate bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.ApproachRate)));
}
[Test]
public void TestFirstBarText()
{
+ AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
AddStep("set ruleset to mania", () => advancedStats.Ruleset.Value = new ManiaRuleset().RulesetInfo);
- AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCsMania);
+ AddAssert("first bar text is correct", () => advancedStats.GetStatistic(SongSelectStrings.KeyCount), () => Is.Not.Null);
AddStep("set ruleset to osu", () => advancedStats.Ruleset.Value = new OsuRuleset().RulesetInfo);
- AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCs);
+ AddAssert("first bar text is correct", () => advancedStats.GetStatistic(SongSelectStrings.CircleSize), () => Is.Not.Null);
}
[Test]
public void TestEasyMod()
{
- AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
+ AddStep("set beatmap and ruleset", () =>
+ {
+ advancedStats.BeatmapInfo = exampleBeatmapInfo;
+ advancedStats.Ruleset.Value = exampleBeatmapInfo.Ruleset;
+ });
AddStep("select EZ mod", () =>
{
@@ -87,16 +95,20 @@ namespace osu.Game.Tests.Visual.SongSelect
advancedStats.Mods.Value = new[] { ruleset.CreateMod() };
});
- AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.FirstValue));
- AddAssert("HP drain bar is blue", () => barIsBlue(advancedStats.HpDrain));
- AddAssert("accuracy bar is blue", () => barIsBlue(advancedStats.Accuracy));
- AddAssert("approach rate bar is blue", () => barIsBlue(advancedStats.ApproachRate));
+ AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.GetStatistic(SongSelectStrings.CircleSize)));
+ AddAssert("HP drain bar is blue", () => barIsBlue(advancedStats.GetStatistic(SongSelectStrings.HPDrain)));
+ AddAssert("accuracy bar is blue", () => barIsBlue(advancedStats.GetStatistic(SongSelectStrings.Accuracy)));
+ AddAssert("approach rate bar is blue", () => barIsBlue(advancedStats.GetStatistic(SongSelectStrings.ApproachRate)));
}
[Test]
public void TestHardRockMod()
{
- AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
+ AddStep("set beatmap and ruleset", () =>
+ {
+ advancedStats.BeatmapInfo = exampleBeatmapInfo;
+ advancedStats.Ruleset.Value = exampleBeatmapInfo.Ruleset;
+ });
AddStep("select HR mod", () =>
{
@@ -104,16 +116,20 @@ namespace osu.Game.Tests.Visual.SongSelect
advancedStats.Mods.Value = new[] { ruleset.CreateMod() };
});
- AddAssert("circle size bar is red", () => barIsRed(advancedStats.FirstValue));
- AddAssert("HP drain bar is red", () => barIsRed(advancedStats.HpDrain));
- AddAssert("accuracy bar is red", () => barIsRed(advancedStats.Accuracy));
- AddAssert("approach rate bar is red", () => barIsRed(advancedStats.ApproachRate));
+ AddAssert("circle size bar is red", () => barIsRed(advancedStats.GetStatistic(SongSelectStrings.CircleSize)));
+ AddAssert("HP drain bar is red", () => barIsRed(advancedStats.GetStatistic(SongSelectStrings.HPDrain)));
+ AddAssert("accuracy bar is red", () => barIsRed(advancedStats.GetStatistic(SongSelectStrings.Accuracy)));
+ AddAssert("approach rate bar is red", () => barIsRed(advancedStats.GetStatistic(SongSelectStrings.ApproachRate)));
}
[Test]
public void TestUnchangedDifficultyAdjustMod()
{
- AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
+ AddStep("set beatmap and ruleset", () =>
+ {
+ advancedStats.BeatmapInfo = exampleBeatmapInfo;
+ advancedStats.Ruleset.Value = exampleBeatmapInfo.Ruleset;
+ });
AddStep("select unchanged Difficulty Adjust mod", () =>
{
@@ -122,16 +138,20 @@ namespace osu.Game.Tests.Visual.SongSelect
advancedStats.Mods.Value = new[] { difficultyAdjustMod };
});
- AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
- AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain));
- AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
- AddAssert("approach rate bar is white", () => barIsWhite(advancedStats.ApproachRate));
+ AddAssert("circle size bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.CircleSize)));
+ AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.HPDrain)));
+ AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.Accuracy)));
+ AddAssert("approach rate bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.ApproachRate)));
}
[Test]
public void TestChangedDifficultyAdjustMod()
{
- AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
+ AddStep("set beatmap and ruleset", () =>
+ {
+ advancedStats.BeatmapInfo = exampleBeatmapInfo;
+ advancedStats.Ruleset.Value = exampleBeatmapInfo.Ruleset;
+ });
AddStep("select changed Difficulty Adjust mod", () =>
{
@@ -144,10 +164,10 @@ namespace osu.Game.Tests.Visual.SongSelect
advancedStats.Mods.Value = new[] { difficultyAdjustMod };
});
- AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
- AddAssert("drain rate bar is blue", () => barIsBlue(advancedStats.HpDrain));
- AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
- AddAssert("approach rate bar is red", () => barIsRed(advancedStats.ApproachRate));
+ AddAssert("circle size bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.CircleSize)));
+ AddAssert("drain rate bar is blue", () => barIsBlue(advancedStats.GetStatistic(SongSelectStrings.HPDrain)));
+ AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.GetStatistic(SongSelectStrings.Accuracy)));
+ AddAssert("approach rate bar is red", () => barIsRed(advancedStats.GetStatistic(SongSelectStrings.ApproachRate)));
}
private bool barIsWhite(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == Color4.White;
@@ -156,10 +176,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private partial class TestAdvancedStats : AdvancedStats
{
- public new StatisticRow FirstValue => base.FirstValue;
- public new StatisticRow HpDrain => base.HpDrain;
- public new StatisticRow Accuracy => base.Accuracy;
- public new StatisticRow ApproachRate => base.ApproachRate;
+ public StatisticRow GetStatistic(LocalisableString title) => Flow.OfType().SingleOrDefault(row => row.Title == title);
}
}
}
diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapTitleWedge.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapTitleWedge.cs
index efd9f6a5cd..f1c6d3965f 100644
--- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapTitleWedge.cs
+++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapTitleWedge.cs
@@ -97,7 +97,9 @@ namespace osu.Game.Tests.Visual.SongSelectV2
selectBeatmap(null);
AddAssert("check default title", () => titleWedge.DisplayedTitle == Beatmap.Default.BeatmapInfo.Metadata.Title);
AddAssert("check default artist", () => titleWedge.DisplayedArtist == Beatmap.Default.BeatmapInfo.Metadata.Artist);
- AddAssert("check no statistics", () => difficultyDisplay.ChildrenOfType().All(d => !d.Statistics.Any()));
+ AddAssert("statistics not visible",
+ () => difficultyDisplay.ChildrenOfType()
+ .All(d => d.Alpha == 0 || d.ChildrenOfType().All(s => s.Alpha == 0)));
}
[Test]
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs
index cc76e28dfe..280185ba17 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs
@@ -23,10 +23,6 @@ namespace osu.Game.Beatmaps.Drawables
{
private OsuSpriteText difficultyName = null!;
private StarRatingDisplay starRating = null!;
- private OsuSpriteText overallDifficulty = null!;
- private OsuSpriteText drainRate = null!;
- private OsuSpriteText circleSize = null!;
- private OsuSpriteText approachRate = null!;
private OsuSpriteText bpm = null!;
private OsuSpriteText length = null!;
@@ -76,13 +72,6 @@ namespace osu.Game.Beatmaps.Drawables
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
- Children = new Drawable[]
- {
- circleSize = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
- drainRate = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
- overallDifficulty = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
- approachRate = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
- }
},
miscFillFlowContainer = new FillFlowContainer
{
@@ -131,21 +120,16 @@ namespace osu.Game.Beatmaps.Drawables
double bpmAdjusted = displayedContent.BeatmapInfo.BPM * rate;
- BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(displayedContent.BeatmapInfo.Difficulty);
-
- if (displayedContent.Mods != null)
- {
- foreach (var mod in displayedContent.Mods.OfType())
- mod.ApplyToDifficulty(originalDifficulty);
- }
-
Ruleset ruleset = displayedContent.Ruleset.CreateInstance();
- BeatmapDifficulty adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(originalDifficulty, displayedContent.Mods ?? []);
+ var beatmapAttributes = ruleset.GetBeatmapAttributesForDisplay(displayedContent.BeatmapInfo, displayedContent.Mods ?? [])
+ .Select(attr => new OsuSpriteText
+ {
+ Font = OsuFont.Style.Caption1,
+ Text = $@"{attr.Acronym}: {attr.AdjustedValue:0.##}"
+ });
- circleSize.Text = @"CS: " + adjustedDifficulty.CircleSize.ToString(@"0.##");
- drainRate.Text = @" HP: " + adjustedDifficulty.DrainRate.ToString(@"0.##");
- approachRate.Text = @" AR: " + adjustedDifficulty.ApproachRate.ToString(@"0.##");
- overallDifficulty.Text = @" OD: " + adjustedDifficulty.OverallDifficulty.ToString(@"0.##");
+ difficultyFillFlowContainer.Clear();
+ difficultyFillFlowContainer.AddRange(beatmapAttributes);
TimeSpan lengthTimeSpan = TimeSpan.FromMilliseconds(displayedContent.BeatmapInfo.Length / rate);
length.Text = "Length: " + lengthTimeSpan.ToFormattedDuration();
diff --git a/osu.Game/Localisation/SongSelectStrings.cs b/osu.Game/Localisation/SongSelectStrings.cs
index bfc5f3990f..71bf15360e 100644
--- a/osu.Game/Localisation/SongSelectStrings.cs
+++ b/osu.Game/Localisation/SongSelectStrings.cs
@@ -79,6 +79,11 @@ namespace osu.Game.Localisation
///
public static LocalisableString HPDrain => new TranslatableString(getKey(@"hp_drain"), @"HP Drain");
+ ///
+ /// "Scroll Speed"
+ ///
+ public static LocalisableString ScrollSpeed => new TranslatableString(getKey(@"scroll_speed"), @"Scroll Speed");
+
///
/// "Submitted"
///
diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs
index dcb9ecdfc8..4df7e18997 100644
--- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs
+++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs
@@ -1,7 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
+using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -9,9 +9,9 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
-using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets.Difficulty;
using osuTK;
namespace osu.Game.Overlays.Mods
@@ -84,25 +84,17 @@ namespace osu.Game.Overlays.Mods
if (data != null)
{
- attemptAdd("CS", bd => bd.CircleSize);
- attemptAdd("AR", bd => bd.ApproachRate);
- attemptAdd("OD", bd => bd.OverallDifficulty);
- attemptAdd("HP", bd => bd.DrainRate);
+ foreach (var attribute in data.Attributes)
+ {
+ if (!Precision.AlmostEquals(attribute.OriginalValue, attribute.AdjustedValue))
+ attributesFillFlow.Add(new AttributeDisplay(attribute.Acronym, attribute.OriginalValue, attribute.AdjustedValue));
+ }
}
if (attributesFillFlow.Any())
content.Show();
else
content.Hide();
-
- void attemptAdd(string name, Func lookup)
- {
- double originalValue = lookup(data.OriginalDifficulty);
- double adjustedValue = lookup(data.AdjustedDifficulty);
-
- if (!Precision.AlmostEquals(originalValue, adjustedValue))
- attributesFillFlow.Add(new AttributeDisplay(name, originalValue, adjustedValue));
- }
}
public void SetContent(Data? data)
@@ -121,13 +113,11 @@ namespace osu.Game.Overlays.Mods
public class Data
{
- public BeatmapDifficulty OriginalDifficulty { get; }
- public BeatmapDifficulty AdjustedDifficulty { get; }
+ public IReadOnlyCollection Attributes { get; }
- public Data(BeatmapDifficulty originalDifficulty, BeatmapDifficulty adjustedDifficulty)
+ public Data(IReadOnlyCollection attributes)
{
- OriginalDifficulty = originalDifficulty;
- AdjustedDifficulty = adjustedDifficulty;
+ Attributes = attributes;
}
}
diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs
index 14c02f5da7..f714cb3798 100644
--- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs
+++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs
@@ -32,11 +32,6 @@ namespace osu.Game.Overlays.Mods
private StarRatingDisplay starRatingDisplay = null!;
private BPMDisplay bpmDisplay = null!;
- private VerticalAttributeDisplay circleSizeDisplay = null!;
- private VerticalAttributeDisplay drainRateDisplay = null!;
- private VerticalAttributeDisplay approachRateDisplay = null!;
- private VerticalAttributeDisplay overallDifficultyDisplay = null!;
-
public Bindable BeatmapInfo { get; } = new Bindable();
public Bindable> Mods { get; } = new Bindable>();
@@ -84,13 +79,6 @@ namespace osu.Game.Overlays.Mods
});
RightContent.Alpha = 0;
- RightContent.AddRange(new Drawable[]
- {
- circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = -OsuGame.SHEAR, },
- approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = -OsuGame.SHEAR, },
- overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = -OsuGame.SHEAR, },
- drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = -OsuGame.SHEAR, },
- });
}
protected override void LoadComplete()
@@ -173,26 +161,30 @@ namespace osu.Game.Overlays.Mods
bpmDisplay.Current.Value = FormatUtils.RoundBPM(BeatmapInfo.Value.BPM, rate);
- BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty);
- BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(originalDifficulty);
-
- foreach (var mod in Mods.Value.OfType())
- mod.ApplyToDifficulty(adjustedDifficulty);
-
Ruleset ruleset = GameRuleset.Value.CreateInstance();
- adjustedDifficulty = ruleset.GetAdjustedDisplayDifficulty(adjustedDifficulty, Mods.Value);
+ var displayAttributes = ruleset.GetBeatmapAttributesForDisplay(BeatmapInfo.Value, Mods.Value).ToList();
- TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty);
+ TooltipContent = new AdjustedAttributesTooltip.Data(displayAttributes);
- circleSizeDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.CircleSize, adjustedDifficulty.CircleSize);
- drainRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.DrainRate, adjustedDifficulty.DrainRate);
- approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate);
- overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty);
+ // if there are not enough attribute displays, make more
+ for (int i = RightContent.Count; i < displayAttributes.Count; i++)
+ RightContent.Add(new VerticalAttributeDisplay { Shear = -OsuGame.SHEAR });
- circleSizeDisplay.Current.Value = adjustedDifficulty.CircleSize;
- drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate;
- approachRateDisplay.Current.Value = adjustedDifficulty.ApproachRate;
- overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty;
+ // populate all attribute displays that need to be visible...
+ for (int i = 0; i < displayAttributes.Count; i++)
+ {
+ var attribute = displayAttributes[i];
+ var display = (VerticalAttributeDisplay)RightContent[i];
+
+ display.Label = attribute.Acronym;
+ display.Current.Value = attribute.AdjustedValue;
+ display.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(attribute.OriginalValue, attribute.AdjustedValue);
+ display.Alpha = 1;
+ }
+
+ // and hide any extra ones
+ for (int i = displayAttributes.Count; i < RightContent.Count; i++)
+ RightContent[i].Alpha = 0;
});
private void updateCollapsedState()
diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs
index a3e24b486f..572d5f89e5 100644
--- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs
+++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs
@@ -33,7 +33,11 @@ namespace osu.Game.Overlays.Mods
///
/// Text to display in the top area of the display.
///
- public LocalisableString Label { get; protected set; }
+ public LocalisableString Label
+ {
+ get => text.Text;
+ set => text.Text = value;
+ }
private readonly EffectCounter counter;
private readonly OsuSpriteText text;
@@ -67,10 +71,8 @@ namespace osu.Game.Overlays.Mods
counter.Colour = newColor;
}
- public VerticalAttributeDisplay(LocalisableString label)
+ public VerticalAttributeDisplay()
{
- Label = label;
-
AutoSizeAxes = Axes.X;
Origin = Anchor.CentreLeft;
@@ -91,7 +93,6 @@ namespace osu.Game.Overlays.Mods
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- Text = Label,
Margin = new MarginPadding { Horizontal = 15 }, // to reserve space for 0.XX value
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold)
},
diff --git a/osu.Game/Rulesets/Difficulty/RulesetBeatmapDifficulty.cs b/osu.Game/Rulesets/Difficulty/RulesetBeatmapDifficulty.cs
new file mode 100644
index 0000000000..fc638cd417
--- /dev/null
+++ b/osu.Game/Rulesets/Difficulty/RulesetBeatmapDifficulty.cs
@@ -0,0 +1,29 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Localisation;
+using osu.Game.Beatmaps;
+
+namespace osu.Game.Rulesets.Difficulty
+{
+ ///
+ /// A is like a single property from ,
+ /// but adjusted for display in the context of a specific ruleset.
+ /// The reason why this record exists is that rulesets use in different ways.
+ /// Some rulesets completely ignore some fields from ,
+ /// some reuse fields in weird ways (like mania reusing to mean key count),
+ /// some want to provide specific extended information for a field
+ /// or adjust the "effective display" in different ways.
+ ///
+ /// The long label for this beatmap attribute.
+ /// A two-letter acronym for this beatmap attribute.
+ /// The value of this attribute before application of mods.
+ /// The "effective" value of this attribute after application of mods.
+ /// The highest allowable value of this attribute.
+ public record RulesetBeatmapAttribute(
+ LocalisableString Label,
+ string Acronym,
+ float OriginalValue,
+ float AdjustedValue,
+ float MaxValue);
+}
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index da3f628137..0dbe6e8845 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -17,6 +17,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Configuration;
using osu.Game.Extensions;
+using osu.Game.Localisation;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Difficulty;
@@ -385,10 +386,34 @@ namespace osu.Game.Rulesets
///
/// It is also not always correct, and arguably is never correct depending on your frame of mind.
///
- /// >The that will be adjusted.
+ /// The for which to display the adjusted difficulty.
/// The active mods.
/// The adjusted difficulty attributes.
- public virtual BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, IReadOnlyCollection mods) => new BeatmapDifficulty(difficulty);
+ public virtual BeatmapDifficulty GetAdjustedDisplayDifficulty(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods)
+ {
+ BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(beatmapInfo.Difficulty);
+
+ foreach (var mod in mods.OfType())
+ mod.ApplyToDifficulty(adjustedDifficulty);
+
+ return adjustedDifficulty;
+ }
+
+ ///
+ /// Returns a list of s to be displayed wherever it is wanted to display a given beatmap's difficulty information.
+ /// The returned data includes both material changes to difficulty from mods,
+ /// as well as "effective" adjustments coming from .
+ ///
+ public virtual IEnumerable GetBeatmapAttributesForDisplay(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods)
+ {
+ var originalDifficulty = beatmapInfo.Difficulty;
+ var adjustedDifficulty = GetAdjustedDisplayDifficulty(beatmapInfo, mods);
+
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.CircleSize, @"CS", originalDifficulty.CircleSize, adjustedDifficulty.CircleSize, 10);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.ApproachRate, @"AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate, 10);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.Accuracy, @"OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty, 10);
+ yield return new RulesetBeatmapAttribute(SongSelectStrings.HPDrain, @"HP", originalDifficulty.DrainRate, adjustedDifficulty.DrainRate, 10);
+ }
///
/// Creates ruleset-specific beatmap filter criteria to be used on the song select screen.
diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs
index 90a4af48f0..6403eb01b0 100644
--- a/osu.Game/Screens/Select/Details/AdvancedStats.cs
+++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs
@@ -16,8 +16,9 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Beatmaps;
using osu.Framework.Bindables;
using System.Collections.Generic;
-using osu.Game.Rulesets.Mods;
+using System.Diagnostics;
using System.Linq;
+using osu.Game.Rulesets.Mods;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Extensions;
@@ -34,10 +35,12 @@ namespace osu.Game.Screens.Select.Details
{
public partial class AdvancedStats : Container, IHasCustomTooltip
{
+ private readonly int columns;
+
[Resolved]
private BeatmapDifficultyCache difficultyCache { get; set; }
- protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
+ protected FillFlowContainer Flow { get; private set; }
private readonly StatisticRow starDifficulty;
public ITooltip GetCustomTooltip() => new AdjustedAttributesTooltip();
@@ -77,65 +80,48 @@ namespace osu.Game.Screens.Select.Details
public AdvancedStats(int columns = 1)
{
+ this.columns = columns;
+
switch (columns)
{
case 1:
- Child = new FillFlowContainer
+ Child = Flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
- FirstValue = new StatisticRow(), // circle size/key amount
- HpDrain = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsDrain },
- Accuracy = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAccuracy },
- ApproachRate = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAr },
- starDifficulty = new StatisticRow(10, true) { Title = BeatmapsetsStrings.ShowStatsStars },
+ starDifficulty = new StatisticRow(forceDecimalPlaces: true)
+ {
+ Title = BeatmapsetsStrings.ShowStatsStars,
+ MaxValue = 10,
+ },
},
};
break;
case 2:
- Child = new FillFlowContainer
+ Child = Flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Children = new[]
{
- FirstValue = new StatisticRow
- {
- Width = 0.5f,
- Padding = new MarginPadding { Right = 5, Vertical = 2.5f },
- }, // circle size/key amount
- HpDrain = new StatisticRow
- {
- Title = BeatmapsetsStrings.ShowStatsDrain,
- Width = 0.5f,
- Padding = new MarginPadding { Left = 5, Vertical = 2.5f },
- },
- Accuracy = new StatisticRow
- {
- Title = BeatmapsetsStrings.ShowStatsAccuracy,
- Width = 0.5f,
- Padding = new MarginPadding { Right = 5, Vertical = 2.5f },
- },
- ApproachRate = new StatisticRow
- {
- Title = BeatmapsetsStrings.ShowStatsAr,
- Width = 0.5f,
- Padding = new MarginPadding { Left = 5, Vertical = 2.5f },
- },
- starDifficulty = new StatisticRow(10, true)
+ starDifficulty = new StatisticRow(forceDecimalPlaces: true)
{
+ MaxValue = 10,
Title = BeatmapsetsStrings.ShowStatsStars,
Width = 0.5f,
- Padding = new MarginPadding { Right = 5, Vertical = 2.5f },
+ Padding = new MarginPadding { Horizontal = 5, Vertical = 2.5f },
},
},
};
break;
}
+
+ Debug.Assert(Flow != null);
+ Flow.SetLayoutPosition(starDifficulty, float.MaxValue);
}
[BackgroundDependencyLoader]
@@ -171,52 +157,39 @@ namespace osu.Game.Screens.Select.Details
private void updateStatistics()
{
- IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty;
- BeatmapDifficulty adjustedDifficulty = null;
-
- if (baseDifficulty != null)
+ if (BeatmapInfo != null && Ruleset.Value != null)
{
- BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty);
+ var displayAttributes = Ruleset.Value.CreateInstance().GetBeatmapAttributesForDisplay(BeatmapInfo, Mods.Value).ToList();
+ TooltipContent = new AdjustedAttributesTooltip.Data(displayAttributes);
- foreach (var mod in Mods.Value.OfType())
- mod.ApplyToDifficulty(originalDifficulty);
-
- adjustedDifficulty = originalDifficulty;
-
- if (Ruleset.Value != null)
+ // if there are not enough attribute displays, make more
+ // the subtraction of 1 is to exclude the star rating row which is always present (and always last)
+ for (int i = Flow.Count - 1; i < displayAttributes.Count; i++)
{
- adjustedDifficulty = Ruleset.Value.CreateInstance().GetAdjustedDisplayDifficulty(originalDifficulty, Mods.Value);
-
- TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty);
+ Flow.Add(new StatisticRow
+ {
+ Width = columns == 1 ? 1 : 0.5f,
+ Padding = columns == 1 ? new MarginPadding() : new MarginPadding { Horizontal = 5, Vertical = 2.5f },
+ });
}
+
+ // populate all attribute displays that need to be visible...
+ for (int i = 0; i < displayAttributes.Count; i++)
+ {
+ var attribute = displayAttributes[i];
+ var display = (StatisticRow)Flow.Where(r => r != starDifficulty).ElementAt(i);
+
+ display.Title = attribute.Label;
+ display.MaxValue = attribute.MaxValue;
+ display.Value = (attribute.OriginalValue, attribute.AdjustedValue);
+ display.Alpha = 1;
+ }
+
+ // and hide any extra ones
+ foreach (var row in Flow.Where(r => r != starDifficulty).Skip(displayAttributes.Count))
+ row.Alpha = 0;
}
- switch (Ruleset.Value?.OnlineID)
- {
- case 3:
- // Account for mania differences locally for now.
- // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes.
- ILegacyRuleset legacyRuleset = (ILegacyRuleset)Ruleset.Value.CreateInstance();
-
- // 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.
- // 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);
-
- FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania;
- FirstValue.Value = (keyCount, keyCount);
- break;
-
- default:
- FirstValue.Title = BeatmapsetsStrings.ShowStatsCs;
- FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize);
- break;
- }
-
- HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate);
- Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty);
- ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate);
-
updateStarDifficulty();
}
@@ -265,7 +238,6 @@ namespace osu.Game.Screens.Select.Details
private const float value_width = 25;
private const float name_width = 70;
- private readonly float maxValue;
private readonly bool forceDecimalPlaces;
private readonly OsuSpriteText name, valueText;
private readonly Bar bar;
@@ -280,6 +252,8 @@ namespace osu.Game.Screens.Select.Details
set => name.Text = value;
}
+ public float MaxValue { get; set; }
+
private (float baseValue, float? adjustedValue)? value;
public (float baseValue, float? adjustedValue) Value
@@ -292,10 +266,10 @@ namespace osu.Game.Screens.Select.Details
this.value = value;
- bar.Length = value.baseValue / maxValue;
+ bar.Length = value.baseValue / MaxValue;
valueText.Text = (value.adjustedValue ?? value.baseValue).ToString(forceDecimalPlaces ? "0.00" : "0.##");
- ModBar.Length = (value.adjustedValue ?? 0) / maxValue;
+ ModBar.Length = (value.adjustedValue ?? 0) / MaxValue;
if (Precision.AlmostEquals(value.baseValue, value.adjustedValue ?? value.baseValue, 0.05f))
ModBar.AccentColour = valueText.Colour = Color4.White;
@@ -312,9 +286,8 @@ namespace osu.Game.Screens.Select.Details
set => bar.AccentColour = value;
}
- public StatisticRow(float maxValue = 10, bool forceDecimalPlaces = false)
+ public StatisticRow(bool forceDecimalPlaces = false)
{
- this.maxValue = maxValue;
this.forceDecimalPlaces = forceDecimalPlaces;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
diff --git a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyDisplay.cs b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyDisplay.cs
index 2b1469d6e2..061eee1cc8 100644
--- a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyDisplay.cs
+++ b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyDisplay.cs
@@ -241,7 +241,7 @@ namespace osu.Game.Screens.SelectV2
if (beatmap.IsDefault)
{
ratingAndNameContainer.FadeOut(300, Easing.OutQuint);
- countStatisticsDisplay.Statistics = Array.Empty();
+ countStatisticsDisplay.FadeOut(300, Easing.OutQuint);
}
else
{
@@ -261,7 +261,7 @@ namespace osu.Game.Screens.SelectV2
{
if (beatmap.IsDefault)
{
- countStatisticsDisplay.Statistics = Array.Empty();
+ countStatisticsDisplay.FadeOut(300, Easing.OutQuint);
return;
}
@@ -279,6 +279,7 @@ namespace osu.Game.Screens.SelectV2
if (cancellationToken.IsCancellationRequested)
return;
+ countStatisticsDisplay.FadeIn(200, Easing.OutQuint);
countStatisticsDisplay.Statistics = statistics;
});
}, cancellationToken);
@@ -293,46 +294,11 @@ namespace osu.Game.Screens.SelectV2
return;
}
- BeatmapDifficulty originalDifficulty = beatmap.Value.BeatmapInfo.Difficulty;
- BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(originalDifficulty);
-
- foreach (var mod in mods.Value.OfType())
- mod.ApplyToDifficulty(adjustedDifficulty);
-
Ruleset rulesetInstance = ruleset.Value.CreateInstance();
- adjustedDifficulty = rulesetInstance.GetAdjustedDisplayDifficulty(adjustedDifficulty, mods.Value);
- difficultyStatisticsDisplay.TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty);
-
- StatisticDifficulty.Data firstStatistic;
-
- switch (ruleset.Value.OnlineID)
- {
- case 3:
- // Account for mania differences locally for now.
- // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes.
- ILegacyRuleset legacyRuleset = (ILegacyRuleset)rulesetInstance;
-
- // For the time being, the key count is static no matter what, because:
- // - The method doesn't have knowledge of the active keymods. Doing so may require considerations for filtering.
- // - Using the difficulty adjustment mod to adjust OD doesn't have an effect on conversion.
- int keyCount = legacyRuleset.GetKeyCount(beatmap.Value.BeatmapInfo, mods.Value);
-
- firstStatistic = new StatisticDifficulty.Data(SongSelectStrings.KeyCount, keyCount, keyCount, 10);
- break;
-
- default:
- firstStatistic = new StatisticDifficulty.Data(SongSelectStrings.CircleSize, originalDifficulty.CircleSize, adjustedDifficulty.CircleSize, 10);
- break;
- }
-
- difficultyStatisticsDisplay.Statistics = new[]
- {
- firstStatistic,
- new StatisticDifficulty.Data(SongSelectStrings.ApproachRate, originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate, 10),
- new StatisticDifficulty.Data(SongSelectStrings.Accuracy, originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty, 10),
- new StatisticDifficulty.Data(SongSelectStrings.HPDrain, originalDifficulty.DrainRate, adjustedDifficulty.DrainRate, 10),
- };
+ var displayAttributes = rulesetInstance.GetBeatmapAttributesForDisplay(beatmap.Value.BeatmapInfo, mods.Value).ToList();
+ difficultyStatisticsDisplay.TooltipContent = new AdjustedAttributesTooltip.Data(displayAttributes);
+ difficultyStatisticsDisplay.Statistics = displayAttributes.Select(a => new StatisticDifficulty.Data(a)).ToList();
});
protected override void Update()
diff --git a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyStatisticsDisplay.cs b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyStatisticsDisplay.cs
index 365ed9977b..595959cfce 100644
--- a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyStatisticsDisplay.cs
+++ b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyStatisticsDisplay.cs
@@ -144,11 +144,13 @@ namespace osu.Game.Screens.SelectV2
if (tiny)
{
statisticsFlow.Hide();
+ // Slow fade hides fill flow layout weirdness.
tinyStatisticsGrid.FadeIn(200, Easing.InQuint);
}
else
{
tinyStatisticsGrid.Hide();
+ // Slow fade hides fill flow layout weirdness.
statisticsFlow.FadeIn(200, Easing.InQuint);
}
@@ -164,12 +166,16 @@ namespace osu.Game.Screens.SelectV2
float statisticWidth = Math.Max(65, statisticsFlow.Max(s => s.LabelWidth));
foreach (var statistic in statisticsFlow)
+ {
statistic.Width = statisticWidth;
+ // Slow fade hides fill flow layout weirdness.
+ statistic.FadeIn(200, Easing.InQuint);
+ }
drawSizeLayout.Invalidate();
});
- private void updateStatistics()
+ private void updateStatistics() => Scheduler.AddOnce(() =>
{
if (statisticsFlow.Select(s => s.Value.Label)
.SequenceEqual(statistics.Select(s => s.Label)))
@@ -181,12 +187,13 @@ namespace osu.Game.Screens.SelectV2
{
statisticsFlow.ChildrenEnumerable = statistics.Select(d => new StatisticDifficulty
{
+ Alpha = 0,
AccentColour = accentColour,
Value = d
});
updateStatisticsSizing();
}
- }
+ });
private void updateTinyStatistics()
{
diff --git a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_StatisticDifficulty.cs b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_StatisticDifficulty.cs
index d0b6acca88..65d8ba3951 100644
--- a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_StatisticDifficulty.cs
+++ b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_StatisticDifficulty.cs
@@ -13,6 +13,7 @@ using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
+using osu.Game.Rulesets.Difficulty;
using osuTK;
using osuTK.Graphics;
@@ -191,7 +192,13 @@ namespace osu.Game.Screens.SelectV2
}
}
- public record Data(LocalisableString Label, float Value, float AdjustedValue, float Maximum, string? Content = null);
+ public record Data(LocalisableString Label, float Value, float AdjustedValue, float Maximum, string? Content = null)
+ {
+ public Data(RulesetBeatmapAttribute attribute)
+ : this(attribute.Label, attribute.OriginalValue, attribute.AdjustedValue, attribute.MaxValue)
+ {
+ }
+ }
}
}
}
diff --git a/osu.Game/Skinning/Components/BeatmapAttributeText.cs b/osu.Game/Skinning/Components/BeatmapAttributeText.cs
index 60a03f4351..3935277dfb 100644
--- a/osu.Game/Skinning/Components/BeatmapAttributeText.cs
+++ b/osu.Game/Skinning/Components/BeatmapAttributeText.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Threading;
using JetBrains.Annotations;
using osu.Framework.Allocation;
@@ -237,15 +236,9 @@ namespace osu.Game.Skinning.Components
BeatmapDifficulty computeDifficulty()
{
- BeatmapDifficulty difficulty = new BeatmapDifficulty(beatmap.Value.BeatmapInfo.Difficulty);
-
- foreach (var mod in mods.Value.OfType())
- mod.ApplyToDifficulty(difficulty);
-
- if (ruleset.Value is RulesetInfo rulesetInfo)
- difficulty = rulesetInfo.CreateInstance().GetAdjustedDisplayDifficulty(difficulty, mods.Value);
-
- return difficulty;
+ return ruleset.Value is RulesetInfo rulesetInfo
+ ? rulesetInfo.CreateInstance().GetAdjustedDisplayDifficulty(beatmap.Value.BeatmapInfo, mods.Value)
+ : new BeatmapDifficulty(beatmap.Value.BeatmapInfo.Difficulty);
}
}