diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs new file mode 100644 index 0000000000..3d3517ada4 --- /dev/null +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -0,0 +1,175 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Select.Details; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.SongSelect +{ + [System.ComponentModel.Description("Advanced beatmap statistics display")] + public class TestSceneAdvancedStats : OsuTestScene + { + private TestAdvancedStats advancedStats; + + [Resolved] + private RulesetStore rulesets { get; set; } + + [Resolved] + private OsuColour colours { get; set; } + + [SetUp] + public void Setup() => Schedule(() => Child = advancedStats = new TestAdvancedStats + { + Width = 500 + }); + + private BeatmapInfo exampleBeatmapInfo => new BeatmapInfo + { + RulesetID = 0, + Ruleset = rulesets.AvailableRulesets.First(), + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 7.2f, + DrainRate = 3, + OverallDifficulty = 5.7f, + ApproachRate = 3.5f + }, + StarDifficulty = 4.5f + }; + + [Test] + public void TestNoMod() + { + AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + + AddStep("no mods selected", () => SelectedMods.Value = Array.Empty()); + + AddAssert("first bar text is Circle Size", () => advancedStats.ChildrenOfType().First().Text == "Circle Size"); + 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)); + } + + [Test] + public void TestManiaFirstBarText() + { + AddStep("set beatmap", () => advancedStats.Beatmap = new BeatmapInfo + { + Ruleset = rulesets.GetRuleset(3), + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 5, + DrainRate = 4.3f, + OverallDifficulty = 4.5f, + ApproachRate = 3.1f + }, + StarDifficulty = 8 + }); + + AddAssert("first bar text is Key Count", () => advancedStats.ChildrenOfType().First().Text == "Key Count"); + } + + [Test] + public void TestEasyMod() + { + AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + + AddStep("select EZ mod", () => + { + var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); + SelectedMods.Value = new[] { ruleset.GetAllMods().OfType().Single() }; + }); + + 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)); + } + + [Test] + public void TestHardRockMod() + { + AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + + AddStep("select HR mod", () => + { + var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); + SelectedMods.Value = new[] { ruleset.GetAllMods().OfType().Single() }; + }); + + 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)); + } + + [Test] + public void TestUnchangedDifficultyAdjustMod() + { + AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + + AddStep("select unchanged Difficulty Adjust mod", () => + { + var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); + var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single(); + difficultyAdjustMod.ReadFromDifficulty(advancedStats.Beatmap.BaseDifficulty); + SelectedMods.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)); + } + + [Test] + public void TestChangedDifficultyAdjustMod() + { + AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + + AddStep("select changed Difficulty Adjust mod", () => + { + var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); + var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single(); + var originalDifficulty = advancedStats.Beatmap.BaseDifficulty; + var adjustedDifficulty = new BeatmapDifficulty + { + CircleSize = originalDifficulty.CircleSize, + DrainRate = originalDifficulty.DrainRate - 0.5f, + OverallDifficulty = originalDifficulty.OverallDifficulty, + ApproachRate = originalDifficulty.ApproachRate + 2.2f, + }; + difficultyAdjustMod.ReadFromDifficulty(adjustedDifficulty); + SelectedMods.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)); + } + + private bool barIsWhite(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == Color4.White; + private bool barIsBlue(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == colours.BlueDark; + private bool barIsRed(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == colours.Red; + + private 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; + } + } +} diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index 6aa5a76490..acf037198f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -3,14 +3,8 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; namespace osu.Game.Tests.Visual.SongSelect @@ -180,27 +174,5 @@ namespace osu.Game.Tests.Visual.SongSelect OnlineBeatmapID = 162, }); } - - [Resolved] - private RulesetStore rulesets { get; set; } - - [Resolved] - private OsuColour colours { get; set; } - - [Test] - public void TestModAdjustments() - { - TestAllMetrics(); - - Ruleset ruleset = rulesets.AvailableRulesets.First().CreateInstance(); - - AddStep("with EZ mod", () => SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) }); - - AddAssert("first bar coloured blue", () => details.ChildrenOfType().Skip(1).First().AccentColour == colours.BlueDark); - - AddStep("with HR mod", () => SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) }); - - AddAssert("first bar coloured red", () => details.ChildrenOfType().Skip(1).First().AccentColour == colours.Red); - } } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 56c400e869..7ab91677a9 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using osu.Game.Rulesets.Mods; using System.Linq; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Overlays.Settings; @@ -26,7 +27,8 @@ namespace osu.Game.Screens.Select.Details [Resolved] private IBindable> mods { get; set; } - private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty; + protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; + private readonly StatisticRow starDifficulty; private BeatmapInfo beatmap; @@ -52,10 +54,10 @@ namespace osu.Game.Screens.Select.Details Spacing = new Vector2(4f), Children = new[] { - firstValue = new StatisticRow(), //circle size/key amount - hpDrain = new StatisticRow { Title = "HP Drain" }, - accuracy = new StatisticRow { Title = "Accuracy" }, - approachRate = new StatisticRow { Title = "Approach Rate" }, + FirstValue = new StatisticRow(), //circle size/key amount + HpDrain = new StatisticRow { Title = "HP Drain" }, + Accuracy = new StatisticRow { Title = "Accuracy" }, + ApproachRate = new StatisticRow { Title = "Approach Rate" }, starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" }, }, }; @@ -122,24 +124,24 @@ namespace osu.Game.Screens.Select.Details 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 - firstValue.Title = "Key Count"; - firstValue.Value = (baseDifficulty?.CircleSize ?? 0, null); + FirstValue.Title = "Key Count"; + FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null); break; default: - firstValue.Title = "Circle Size"; - firstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); + FirstValue.Title = "Circle Size"; + FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); break; } starDifficulty.Value = ((float)(Beatmap?.StarDifficulty ?? 0), null); - hpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate); - accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty); - approachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate); + HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate); + Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty); + ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate); } - private class StatisticRow : Container, IHasAccentColour + public class StatisticRow : Container, IHasAccentColour { private const float value_width = 25; private const float name_width = 70; @@ -147,7 +149,8 @@ namespace osu.Game.Screens.Select.Details private readonly float maxValue; private readonly bool forceDecimalPlaces; private readonly OsuSpriteText name, valueText; - private readonly Bar bar, modBar; + private readonly Bar bar; + public readonly Bar ModBar; [Resolved] private OsuColour colours { get; set; } @@ -173,14 +176,14 @@ namespace osu.Game.Screens.Select.Details 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 (value.adjustedValue > value.baseValue) - modBar.AccentColour = valueText.Colour = colours.Red; + if (Precision.AlmostEquals(value.baseValue, value.adjustedValue ?? value.baseValue, 0.05f)) + ModBar.AccentColour = valueText.Colour = Color4.White; + else if (value.adjustedValue > value.baseValue) + ModBar.AccentColour = valueText.Colour = colours.Red; else if (value.adjustedValue < value.baseValue) - modBar.AccentColour = valueText.Colour = colours.BlueDark; - else - modBar.AccentColour = valueText.Colour = Color4.White; + ModBar.AccentColour = valueText.Colour = colours.BlueDark; } } @@ -217,7 +220,7 @@ namespace osu.Game.Screens.Select.Details BackgroundColour = Color4.White.Opacity(0.5f), Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 }, }, - modBar = new Bar + ModBar = new Bar { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,