From 31c52bd585a55f925676313671c23c437aff28e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Feb 2021 17:00:42 +0900 Subject: [PATCH 1/7] Update the displayed BPM at song select with rate adjust mods This only covers constant rate rate adjust mods. Mods like wind up/wind down will need a more complex implementation which we haven't really planned yet. --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 86cb561bc7..13ec106694 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -383,10 +383,18 @@ namespace osu.Game.Screens.Select return labels.ToArray(); } + [Resolved] + private IBindable> mods { get; set; } + private string getBPMRange(IBeatmap beatmap) { - double bpmMax = beatmap.ControlPointInfo.BPMMaximum; - double bpmMin = beatmap.ControlPointInfo.BPMMinimum; + // this doesn't consider mods which apply variable rates, yet. + double rate = 1; + foreach (var mod in mods.Value.OfType()) + rate = mod.ApplyToRate(0, rate); + + double bpmMax = beatmap.ControlPointInfo.BPMMaximum * rate; + double bpmMin = beatmap.ControlPointInfo.BPMMinimum * rate; if (Precision.AlmostEquals(bpmMin, bpmMax)) return $"{bpmMin:0}"; From 2db4b793d7ab2c064c1a6a1b924ef379b71a86ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Feb 2021 17:04:39 +0900 Subject: [PATCH 2/7] Also handle most common BPM display --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 13ec106694..311ed6ffb9 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -395,11 +395,12 @@ namespace osu.Game.Screens.Select double bpmMax = beatmap.ControlPointInfo.BPMMaximum * rate; double bpmMin = beatmap.ControlPointInfo.BPMMinimum * rate; + double mostCommonBPM = 60000 / beatmap.GetMostCommonBeatLength() * rate; if (Precision.AlmostEquals(bpmMin, bpmMax)) return $"{bpmMin:0}"; - return $"{bpmMin:0}-{bpmMax:0} (mostly {60000 / beatmap.GetMostCommonBeatLength():0})"; + return $"{bpmMin:0}-{bpmMax:0} (mostly {mostCommonBPM:0})"; } private OsuSpriteText[] getMapper(BeatmapMetadata metadata) From 98313a98bf4e0b89deb9b47219fc4e0fd3aaef4a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Feb 2021 21:48:02 +0900 Subject: [PATCH 3/7] DI mods in parent class and pass them down --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 311ed6ffb9..37808f6e94 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -39,6 +39,7 @@ namespace osu.Game.Screens.Select private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0); private readonly IBindable ruleset = new Bindable(); + private readonly IBindable> mods = new Bindable>(Array.Empty()); [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -64,9 +65,11 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(true)] - private void load([CanBeNull] Bindable parentRuleset) + private void load([CanBeNull] Bindable parentRuleset, [CanBeNull] Bindable> parentMods) { ruleset.BindTo(parentRuleset); + mods.BindTo(parentMods); + ruleset.ValueChanged += _ => updateDisplay(); } @@ -132,7 +135,7 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value, beatmapDifficulty.Value) + LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value) { Shear = -Shear, Depth = Info?.Depth + 1 ?? 0 @@ -167,13 +170,15 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; + private readonly IReadOnlyList mods; private readonly StarDifficulty starDifficulty; - public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset, StarDifficulty difficulty) + public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) : base(pixelSnapping: true) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; + this.mods = mods; starDifficulty = difficulty; } @@ -383,14 +388,11 @@ namespace osu.Game.Screens.Select return labels.ToArray(); } - [Resolved] - private IBindable> mods { get; set; } - private string getBPMRange(IBeatmap beatmap) { // this doesn't consider mods which apply variable rates, yet. double rate = 1; - foreach (var mod in mods.Value.OfType()) + foreach (var mod in mods.OfType()) rate = mod.ApplyToRate(0, rate); double bpmMax = beatmap.ControlPointInfo.BPMMaximum * rate; From de417a660d7121589abb9c0b0fe635f4e2f44eb0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Feb 2021 21:51:32 +0900 Subject: [PATCH 4/7] Make BPM update with changes in mod settings --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 113 ++++++++++++-------- 1 file changed, 68 insertions(+), 45 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 37808f6e94..9084435f44 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -25,6 +25,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; +using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -167,12 +168,15 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; + private Container bpmLabelContainer; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; private readonly IReadOnlyList mods; private readonly StarDifficulty starDifficulty; + private ModSettingChangeTracker settingChangeTracker; + public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) : base(pixelSnapping: true) { @@ -189,9 +193,11 @@ namespace osu.Game.Screens.Select var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); CacheDrawnFrameBuffer = true; - RelativeSizeAxes = Axes.Both; + settingChangeTracker = new ModSettingChangeTracker(mods); + settingChangeTracker.SettingChanged += _ => updateBPM(); + titleBinding = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title))); artistBinding = localisation.GetLocalisedString(new LocalisedString((metadata.ArtistUnicode, metadata.Artist))); @@ -312,7 +318,25 @@ namespace osu.Game.Screens.Select Margin = new MarginPadding { Top = 20 }, Spacing = new Vector2(20, 0), AutoSizeAxes = Axes.Both, - Children = getInfoLabels() + Children = new Drawable[] + { + new InfoLabel(new BeatmapStatistic + { + Name = "Length", + CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), + Content = TimeSpan.FromMilliseconds(beatmapInfo.Length).ToString(@"m\:ss"), + }), + bpmLabelContainer = new Container + { + AutoSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(20, 0), + Children = getRulesetInfoLabels() + } + } } } } @@ -324,6 +348,8 @@ namespace osu.Game.Screens.Select // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); + + updateBPM(); } private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 @@ -340,69 +366,60 @@ namespace osu.Game.Screens.Select ForceRedraw(); } - private InfoLabel[] getInfoLabels() + private InfoLabel[] getRulesetInfoLabels() { - var b = beatmap.Beatmap; - - List labels = new List(); - - if (b?.HitObjects?.Any() == true) + try { - labels.Add(new InfoLabel(new BeatmapStatistic - { - Name = "Length", - CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), - Content = TimeSpan.FromMilliseconds(b.BeatmapInfo.Length).ToString(@"m\:ss"), - })); - - labels.Add(new InfoLabel(new BeatmapStatistic - { - Name = "BPM", - CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm), - Content = getBPMRange(b), - })); + IBeatmap playableBeatmap; try { - IBeatmap playableBeatmap; - - try - { - // Try to get the beatmap with the user's ruleset - playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty()); - } - catch (BeatmapInvalidForRulesetException) - { - // Can't be converted to the user's ruleset, so use the beatmap's own ruleset - playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Array.Empty()); - } - - labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s))); + // Try to get the beatmap with the user's ruleset + playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty()); } - catch (Exception e) + catch (BeatmapInvalidForRulesetException) { - Logger.Error(e, "Could not load beatmap successfully!"); + // Can't be converted to the user's ruleset, so use the beatmap's own ruleset + playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Array.Empty()); } + + return playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)).ToArray(); + } + catch (Exception e) + { + Logger.Error(e, "Could not load beatmap successfully!"); } - return labels.ToArray(); + return Array.Empty(); } - private string getBPMRange(IBeatmap beatmap) + private void updateBPM() { + var b = beatmap.Beatmap; + if (b == null) + return; + // this doesn't consider mods which apply variable rates, yet. double rate = 1; foreach (var mod in mods.OfType()) rate = mod.ApplyToRate(0, rate); - double bpmMax = beatmap.ControlPointInfo.BPMMaximum * rate; - double bpmMin = beatmap.ControlPointInfo.BPMMinimum * rate; - double mostCommonBPM = 60000 / beatmap.GetMostCommonBeatLength() * rate; + double bpmMax = b.ControlPointInfo.BPMMaximum * rate; + double bpmMin = b.ControlPointInfo.BPMMinimum * rate; + double mostCommonBPM = 60000 / b.GetMostCommonBeatLength() * rate; - if (Precision.AlmostEquals(bpmMin, bpmMax)) - return $"{bpmMin:0}"; + string labelText = Precision.AlmostEquals(bpmMin, bpmMax) + ? $"{bpmMin:0}" + : $"{bpmMin:0}-{bpmMax:0} (mostly {mostCommonBPM:0})"; - return $"{bpmMin:0}-{bpmMax:0} (mostly {mostCommonBPM:0})"; + bpmLabelContainer.Child = new InfoLabel(new BeatmapStatistic + { + Name = "BPM", + CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm), + Content = labelText + }); + + ForceRedraw(); } private OsuSpriteText[] getMapper(BeatmapMetadata metadata) @@ -425,6 +442,12 @@ namespace osu.Game.Screens.Select }; } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + settingChangeTracker?.Dispose(); + } + public class InfoLabel : Container, IHasTooltip { public string TooltipText { get; } From 649ce20e354b2f30b08a3c60db94695e24aa5e34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Feb 2021 22:01:53 +0900 Subject: [PATCH 5/7] Fix up super weird and super wrong DI --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index d1b28e6607..97fe099975 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; -using JetBrains.Annotations; using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; @@ -39,8 +38,11 @@ namespace osu.Game.Screens.Select private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0); - private readonly IBindable ruleset = new Bindable(); - private readonly IBindable> mods = new Bindable>(Array.Empty()); + [Resolved] + private IBindable ruleset { get; set; } + + [Resolved] + private IBindable> mods { get; set; } [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -65,13 +67,10 @@ namespace osu.Game.Screens.Select }; } - [BackgroundDependencyLoader(true)] - private void load([CanBeNull] Bindable parentRuleset, [CanBeNull] Bindable> parentMods) + protected override void LoadComplete() { - ruleset.BindTo(parentRuleset); - mods.BindTo(parentMods); - - ruleset.ValueChanged += _ => updateDisplay(); + base.LoadComplete(); + ruleset.BindValueChanged(_ => updateDisplay(), true); } protected override void PopIn() From c3eb44137bfd109006f826fc196a2394a2675196 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Feb 2021 22:09:41 +0900 Subject: [PATCH 6/7] Move ValueChanged bind back to load() --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 97fe099975..fe2b7b7525 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -67,10 +67,10 @@ namespace osu.Game.Screens.Select }; } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load() { - base.LoadComplete(); - ruleset.BindValueChanged(_ => updateDisplay(), true); + ruleset.BindValueChanged(_ => updateDisplay()); } protected override void PopIn() From 01a48154126fbc2ca75cea6632349689ea41ce4f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 25 Feb 2021 23:36:02 +0900 Subject: [PATCH 7/7] Make labels disappear on null beatmap/no hitobjects --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 7 ++- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 61 +++++++++++-------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 07b67ca3ad..7ea6373763 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -7,6 +7,7 @@ using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; @@ -111,8 +112,8 @@ namespace osu.Game.Tests.Visual.SongSelect private void testInfoLabels(int expectedCount) { - AddAssert("check info labels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any()); - AddAssert("check info labels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount); + AddAssert("check info labels exists", () => infoWedge.Info.ChildrenOfType().Any()); + AddAssert("check info labels count", () => infoWedge.Info.ChildrenOfType().Count() == expectedCount); } [Test] @@ -123,7 +124,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("check default title", () => infoWedge.Info.TitleLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Title); AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Artist); AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any()); - AddAssert("check no info labels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); + AddAssert("check no info labels", () => !infoWedge.Info.ChildrenOfType().Any()); } [Test] diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index fe2b7b7525..36cc19cce3 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -163,10 +163,10 @@ namespace osu.Game.Screens.Select public OsuSpriteText ArtistLabel { get; private set; } public BeatmapSetOnlineStatusPill StatusPill { get; private set; } public FillFlowContainer MapperContainer { get; private set; } - public FillFlowContainer InfoLabelContainer { get; private set; } private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; + private FillFlowContainer infoLabelContainer; private Container bpmLabelContainer; private readonly WorkingBeatmap beatmap; @@ -194,9 +194,6 @@ namespace osu.Game.Screens.Select CacheDrawnFrameBuffer = true; RelativeSizeAxes = Axes.Both; - settingChangeTracker = new ModSettingChangeTracker(mods); - settingChangeTracker.SettingChanged += _ => updateBPM(); - titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); artistBinding = localisation.GetLocalisedString(new RomanisableString(metadata.ArtistUnicode, metadata.Artist)); @@ -312,30 +309,11 @@ namespace osu.Game.Screens.Select AutoSizeAxes = Axes.Both, Children = getMapper(metadata) }, - InfoLabelContainer = new FillFlowContainer + infoLabelContainer = new FillFlowContainer { Margin = new MarginPadding { Top = 20 }, Spacing = new Vector2(20, 0), AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new InfoLabel(new BeatmapStatistic - { - Name = "Length", - CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), - Content = TimeSpan.FromMilliseconds(beatmapInfo.Length).ToString(@"m\:ss"), - }), - bpmLabelContainer = new Container - { - AutoSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(20, 0), - Children = getRulesetInfoLabels() - } - } } } } @@ -348,7 +326,7 @@ namespace osu.Game.Screens.Select if (beatmapInfo.Version == null) StatusPill.Hide(); - updateBPM(); + addInfoLabels(); } private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 @@ -365,6 +343,37 @@ namespace osu.Game.Screens.Select ForceRedraw(); } + private void addInfoLabels() + { + if (beatmap.Beatmap?.HitObjects?.Any() != true) + return; + + infoLabelContainer.Children = new Drawable[] + { + new InfoLabel(new BeatmapStatistic + { + Name = "Length", + CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), + Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"), + }), + bpmLabelContainer = new Container + { + AutoSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(20, 0), + Children = getRulesetInfoLabels() + } + }; + + settingChangeTracker = new ModSettingChangeTracker(mods); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + + refreshBPMLabel(); + } + private InfoLabel[] getRulesetInfoLabels() { try @@ -392,7 +401,7 @@ namespace osu.Game.Screens.Select return Array.Empty(); } - private void updateBPM() + private void refreshBPMLabel() { var b = beatmap.Beatmap; if (b == null)