mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 12:42:54 +08:00
Merge pull request #11900 from peppy/update-bpm-with-mods
Update the displayed BPM at song select with rate adjust mods
This commit is contained in:
commit
a792c3f13c
@ -7,6 +7,7 @@ using JetBrains.Annotations;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -111,8 +112,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
private void testInfoLabels(int expectedCount)
|
private void testInfoLabels(int expectedCount)
|
||||||
{
|
{
|
||||||
AddAssert("check info labels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
|
AddAssert("check info labels exists", () => infoWedge.Info.ChildrenOfType<BeatmapInfoWedge.BufferedWedgeInfo.InfoLabel>().Any());
|
||||||
AddAssert("check info labels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
|
AddAssert("check info labels count", () => infoWedge.Info.ChildrenOfType<BeatmapInfoWedge.BufferedWedgeInfo.InfoLabel>().Count() == expectedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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 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 default artist", () => infoWedge.Info.ArtistLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Artist);
|
||||||
AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any());
|
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<BeatmapInfoWedge.BufferedWedgeInfo.InfoLabel>().Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -5,7 +5,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -25,6 +24,7 @@ using osu.Framework.Graphics.Effects;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
@ -38,7 +38,11 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
|
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
|
||||||
|
|
||||||
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
[Resolved]
|
||||||
|
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||||
@ -63,11 +67,10 @@ namespace osu.Game.Screens.Select
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader]
|
||||||
private void load([CanBeNull] Bindable<RulesetInfo> parentRuleset)
|
private void load()
|
||||||
{
|
{
|
||||||
ruleset.BindTo(parentRuleset);
|
ruleset.BindValueChanged(_ => updateDisplay());
|
||||||
ruleset.ValueChanged += _ => updateDisplay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
@ -132,7 +135,7 @@ namespace osu.Game.Screens.Select
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value, beatmapDifficulty.Value)
|
LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value)
|
||||||
{
|
{
|
||||||
Shear = -Shear,
|
Shear = -Shear,
|
||||||
Depth = Info?.Depth + 1 ?? 0
|
Depth = Info?.Depth + 1 ?? 0
|
||||||
@ -160,20 +163,25 @@ namespace osu.Game.Screens.Select
|
|||||||
public OsuSpriteText ArtistLabel { get; private set; }
|
public OsuSpriteText ArtistLabel { get; private set; }
|
||||||
public BeatmapSetOnlineStatusPill StatusPill { get; private set; }
|
public BeatmapSetOnlineStatusPill StatusPill { get; private set; }
|
||||||
public FillFlowContainer MapperContainer { get; private set; }
|
public FillFlowContainer MapperContainer { get; private set; }
|
||||||
public FillFlowContainer InfoLabelContainer { get; private set; }
|
|
||||||
|
|
||||||
private ILocalisedBindableString titleBinding;
|
private ILocalisedBindableString titleBinding;
|
||||||
private ILocalisedBindableString artistBinding;
|
private ILocalisedBindableString artistBinding;
|
||||||
|
private FillFlowContainer infoLabelContainer;
|
||||||
|
private Container bpmLabelContainer;
|
||||||
|
|
||||||
private readonly WorkingBeatmap beatmap;
|
private readonly WorkingBeatmap beatmap;
|
||||||
private readonly RulesetInfo ruleset;
|
private readonly RulesetInfo ruleset;
|
||||||
|
private readonly IReadOnlyList<Mod> mods;
|
||||||
private readonly StarDifficulty starDifficulty;
|
private readonly StarDifficulty starDifficulty;
|
||||||
|
|
||||||
public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset, StarDifficulty difficulty)
|
private ModSettingChangeTracker settingChangeTracker;
|
||||||
|
|
||||||
|
public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList<Mod> mods, StarDifficulty difficulty)
|
||||||
: base(pixelSnapping: true)
|
: base(pixelSnapping: true)
|
||||||
{
|
{
|
||||||
this.beatmap = beatmap;
|
this.beatmap = beatmap;
|
||||||
ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset;
|
ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset;
|
||||||
|
this.mods = mods;
|
||||||
starDifficulty = difficulty;
|
starDifficulty = difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +192,6 @@ namespace osu.Game.Screens.Select
|
|||||||
var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||||
|
|
||||||
CacheDrawnFrameBuffer = true;
|
CacheDrawnFrameBuffer = true;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title));
|
titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title));
|
||||||
@ -302,12 +309,11 @@ namespace osu.Game.Screens.Select
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Children = getMapper(metadata)
|
Children = getMapper(metadata)
|
||||||
},
|
},
|
||||||
InfoLabelContainer = new FillFlowContainer
|
infoLabelContainer = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding { Top = 20 },
|
Margin = new MarginPadding { Top = 20 },
|
||||||
Spacing = new Vector2(20, 0),
|
Spacing = new Vector2(20, 0),
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Children = getInfoLabels()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,6 +325,8 @@ namespace osu.Game.Screens.Select
|
|||||||
// no difficulty means it can't have a status to show
|
// no difficulty means it can't have a status to show
|
||||||
if (beatmapInfo.Version == null)
|
if (beatmapInfo.Version == null)
|
||||||
StatusPill.Hide();
|
StatusPill.Hide();
|
||||||
|
|
||||||
|
addInfoLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0
|
private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0
|
||||||
@ -335,63 +343,91 @@ namespace osu.Game.Screens.Select
|
|||||||
ForceRedraw();
|
ForceRedraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
private InfoLabel[] getInfoLabels()
|
private void addInfoLabels()
|
||||||
{
|
{
|
||||||
var b = beatmap.Beatmap;
|
if (beatmap.Beatmap?.HitObjects?.Any() != true)
|
||||||
|
return;
|
||||||
|
|
||||||
List<InfoLabel> labels = new List<InfoLabel>();
|
infoLabelContainer.Children = new Drawable[]
|
||||||
|
|
||||||
if (b?.HitObjects?.Any() == true)
|
|
||||||
{
|
{
|
||||||
labels.Add(new InfoLabel(new BeatmapStatistic
|
new InfoLabel(new BeatmapStatistic
|
||||||
{
|
{
|
||||||
Name = "Length",
|
Name = "Length",
|
||||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length),
|
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length),
|
||||||
Content = TimeSpan.FromMilliseconds(b.BeatmapInfo.Length).ToString(@"m\:ss"),
|
Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"),
|
||||||
}));
|
}),
|
||||||
|
bpmLabelContainer = new Container
|
||||||
labels.Add(new InfoLabel(new BeatmapStatistic
|
|
||||||
{
|
{
|
||||||
Name = "BPM",
|
AutoSizeAxes = Axes.Both,
|
||||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm),
|
},
|
||||||
Content = getBPMRange(b),
|
new FillFlowContainer
|
||||||
}));
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Spacing = new Vector2(20, 0),
|
||||||
|
Children = getRulesetInfoLabels()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
settingChangeTracker = new ModSettingChangeTracker(mods);
|
||||||
|
settingChangeTracker.SettingChanged += _ => refreshBPMLabel();
|
||||||
|
|
||||||
|
refreshBPMLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private InfoLabel[] getRulesetInfoLabels()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IBeatmap playableBeatmap;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IBeatmap playableBeatmap;
|
// Try to get the beatmap with the user's ruleset
|
||||||
|
playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty<Mod>());
|
||||||
try
|
|
||||||
{
|
|
||||||
// Try to get the beatmap with the user's ruleset
|
|
||||||
playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty<Mod>());
|
|
||||||
}
|
|
||||||
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<Mod>());
|
|
||||||
}
|
|
||||||
|
|
||||||
labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)));
|
|
||||||
}
|
}
|
||||||
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<Mod>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<InfoLabel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string getBPMRange(IBeatmap beatmap)
|
private void refreshBPMLabel()
|
||||||
{
|
{
|
||||||
double bpmMax = beatmap.ControlPointInfo.BPMMaximum;
|
var b = beatmap.Beatmap;
|
||||||
double bpmMin = beatmap.ControlPointInfo.BPMMinimum;
|
if (b == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (Precision.AlmostEquals(bpmMin, bpmMax))
|
// this doesn't consider mods which apply variable rates, yet.
|
||||||
return $"{bpmMin:0}";
|
double rate = 1;
|
||||||
|
foreach (var mod in mods.OfType<IApplicableToRate>())
|
||||||
|
rate = mod.ApplyToRate(0, rate);
|
||||||
|
|
||||||
return $"{bpmMin:0}-{bpmMax:0} (mostly {60000 / beatmap.GetMostCommonBeatLength():0})";
|
double bpmMax = b.ControlPointInfo.BPMMaximum * rate;
|
||||||
|
double bpmMin = b.ControlPointInfo.BPMMinimum * rate;
|
||||||
|
double mostCommonBPM = 60000 / b.GetMostCommonBeatLength() * rate;
|
||||||
|
|
||||||
|
string labelText = Precision.AlmostEquals(bpmMin, bpmMax)
|
||||||
|
? $"{bpmMin:0}"
|
||||||
|
: $"{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)
|
private OsuSpriteText[] getMapper(BeatmapMetadata metadata)
|
||||||
@ -414,6 +450,12 @@ namespace osu.Game.Screens.Select
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
settingChangeTracker?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
public class InfoLabel : Container, IHasTooltip
|
public class InfoLabel : Container, IHasTooltip
|
||||||
{
|
{
|
||||||
public string TooltipText { get; }
|
public string TooltipText { get; }
|
||||||
|
Loading…
Reference in New Issue
Block a user