From 16d34bcc0a04b319a53d86f20a863a837eb8d73d Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 12 Apr 2021 16:02:19 -0400 Subject: [PATCH 01/76] Expose the latest end time of storyboard elements Co-authored-by: Marlina Bowring --- osu.Game/Storyboards/IStoryboardElement.cs | 2 ++ osu.Game/Storyboards/Storyboard.cs | 9 +++++++++ osu.Game/Storyboards/StoryboardSample.cs | 2 ++ osu.Game/Storyboards/StoryboardVideo.cs | 2 ++ 4 files changed, 15 insertions(+) diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index c4c150a8a4..03f8b97212 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -12,6 +12,8 @@ namespace osu.Game.Storyboards double StartTime { get; } + double EndTime { get; } + Drawable CreateDrawable(); } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 1ba25cc11e..41058d9cb3 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -36,6 +36,15 @@ namespace osu.Game.Storyboards /// public double? EarliestEventTime => Layers.SelectMany(l => l.Elements).OrderBy(e => e.StartTime).FirstOrDefault()?.StartTime; + /// + /// Across all layers, find the latest point in time that a storyboard element ends at. + /// Will return null if there are no elements. + /// + /// + /// This iterates all elements and as such should be used sparingly or stored locally. + /// Videos and samples return StartTime as their EndTIme. + /// + public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. /// diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index 5d6ce215f5..d0949c93a7 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -15,6 +15,8 @@ namespace osu.Game.Storyboards public double StartTime { get; } + public double EndTime => StartTime; + public int Volume { get; } public IEnumerable LookupNames => new[] diff --git a/osu.Game/Storyboards/StoryboardVideo.cs b/osu.Game/Storyboards/StoryboardVideo.cs index 4652e45852..1314bd7cb9 100644 --- a/osu.Game/Storyboards/StoryboardVideo.cs +++ b/osu.Game/Storyboards/StoryboardVideo.cs @@ -14,6 +14,8 @@ namespace osu.Game.Storyboards public double StartTime { get; } + public double EndTime => StartTime; + public StoryboardVideo(string path, int offset) { Path = path; From 25b8c2f257f8a52aba5761b819ea0ee7f62b86a4 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Wed, 14 Apr 2021 00:04:03 -0400 Subject: [PATCH 02/76] Allow skipping storyboard outro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuses SkipOverlay by calculating the endtime of the storyboard and using that as a "start point". Upon skipping the outro the score is instantly shown. When the end of the storyboard is reached the score screen automatically shows up. If the player holds ESC (pause) during the outro, the score is displayed The storyboard endtime is calculated by getting the latest endtime of the storyboard's elements, or simply returning 0 if there is no storyboard. Co-Authored-By: Marlina José --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 100 ++++++++++++++++++ .../Multiplayer/MultiplayerPlayer.cs | 1 + .../Screens/Play/GameplayClockContainer.cs | 25 +++++ osu.Game/Screens/Play/Player.cs | 34 ++++++ osu.Game/Screens/Play/PlayerConfiguration.cs | 5 + osu.Game/Storyboards/Storyboard.cs | 1 + 6 files changed, 166 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs new file mode 100644 index 0000000000..1c8b33bb09 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -0,0 +1,100 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking; +using osu.Game.Storyboards; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneStoryboardWithOutro : PlayerTestScene + { + protected new OutroPlayer Player => (OutroPlayer)base.Player; + + private Storyboard storyboard; + + private const double storyboard_duration = 2000; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.SetValue(OsuSetting.ShowStoryboard, true); + storyboard = new Storyboard(); + var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); + sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0); + storyboard.GetLayer("Background").Add(sprite); + } + + [Test] + public void TestStoryboardSkipOutro() + { + AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space)); + AddAssert("score shown", () => Player.IsScoreShown); + } + + [Test] + public void TestStoryboardNoSkipOutro() + { + AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); + AddUntilStep("storyboard ends", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= storyboard_duration); + AddWaitStep("wait for score", 10); + AddAssert("score shown", () => Player.IsScoreShown); + } + + [Test] + public void TestStoryboardExitToSkipOutro() + { + AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddStep("exit via pause", () => Player.ExitViaPause()); + AddAssert("score shown", () => Player.IsScoreShown); + } + + protected override bool AllowFail => false; + + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OutroPlayer(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new HitCircle()); + return beatmap; + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => + new ClockBackedTestWorkingBeatmap(beatmap, storyboard ?? this.storyboard, Clock, Audio); + + protected class OutroPlayer : TestPlayer + { + public void ExitViaPause() => PerformExit(true); + + public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen; + + public OutroPlayer() + : base(false) + { + } + + protected override Task ImportScore(Score score) + { + // avoid database errors from trying to store the score + return Task.CompletedTask; + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index aaacf891bb..b4e8c13e83 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -49,6 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer AllowPause = false, AllowRestart = false, AllowSkippingIntro = false, + AllowSkippingOutro = false, }) { this.userIds = userIds; diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index ddbb087962..fc45d661cf 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -228,6 +228,11 @@ namespace osu.Game.Screens.Play adjustableClock.ChangeSource(track); } + /// + /// Gets the endtime of the last element in the storyboard in ms, or start time of the last hitobject if there's no storyboard. + /// + public double StoryboardEndTime => beatmap.Storyboard.LatestEventTime ?? 0; + protected override void Update() { if (!IsPaused.Value) @@ -235,6 +240,8 @@ namespace osu.Game.Screens.Play userOffsetClock.ProcessFrame(); } + updateHasStoryboardEnded(); + base.Update(); } @@ -296,5 +303,23 @@ namespace osu.Game.Screens.Play { } } + + # region Storyboard outro logic + + public IBindable HasStoryboardEnded => hasStoryboardEnded; + + public bool HasTimeLeftInStoryboard => GameplayClock.CurrentTime <= StoryboardEndTime; + + private readonly BindableBool hasStoryboardEnded = new BindableBool(true); + + private void updateHasStoryboardEnded() + { + if (StoryboardEndTime == 0) + return; + + hasStoryboardEnded.Value = GameplayClock.CurrentTime >= StoryboardEndTime; + } + + # endregion } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index efe5d26409..13820738c7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -74,6 +74,8 @@ namespace osu.Game.Screens.Play private Bindable mouseWheelDisabled; + private Bindable storyboardEnabled; + private readonly Bindable storyboardReplacesBackground = new Bindable(); protected readonly Bindable LocalUserPlaying = new Bindable(); @@ -106,6 +108,8 @@ namespace osu.Game.Screens.Play private SkipOverlay skipOverlay; + private SkipOverlay skipOutroOverlay; + protected ScoreProcessor ScoreProcessor { get; private set; } protected HealthProcessor HealthProcessor { get; private set; } @@ -190,6 +194,8 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); + storyboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard); + if (game != null) gameActive.BindTo(game.IsActive); @@ -285,6 +291,9 @@ namespace osu.Game.Screens.Play ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState; HealthProcessor.Failed += onFail; + // Keep track of whether the storyboard ended after the playable portion + GameplayClockContainer.HasStoryboardEnded.ValueChanged += updateCompletionState; + foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); @@ -360,6 +369,10 @@ namespace osu.Game.Screens.Play { RequestSkip = performUserRequestedSkip }, + skipOutroOverlay = new SkipOverlay(GameplayClockContainer.StoryboardEndTime) + { + RequestSkip = scheduleCompletion + }, FailOverlay = new FailOverlay { OnRetry = Restart, @@ -389,6 +402,9 @@ namespace osu.Game.Screens.Play if (!Configuration.AllowSkippingIntro) skipOverlay.Expire(); + if (!Configuration.AllowSkippingOutro) + skipOutroOverlay.Expire(); + if (Configuration.AllowRestart) { container.Add(new HotkeyRetryOverlay @@ -403,6 +419,8 @@ namespace osu.Game.Screens.Play }); } + skipOutroOverlay.Hide(); + return container; } @@ -523,6 +541,14 @@ namespace osu.Game.Screens.Play Pause(); return; } + + // show the score if in storyboard outro (score has been set) + bool scoreReady = prepareScoreForDisplayTask != null && prepareScoreForDisplayTask.IsCompleted; + + if (scoreReady) + { + scheduleCompletion(); + } } this.Exit(); @@ -611,6 +637,14 @@ namespace osu.Game.Screens.Play return score.ScoreInfo; }); + // show skip overlay if storyboard is enabled and has an outro + if (storyboardEnabled.Value && GameplayClockContainer.HasTimeLeftInStoryboard) + { + skipOutroOverlay.Show(); + completionProgressDelegate = null; + return; + } + using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) scheduleCompletion(); } diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs index cd30ead638..ad29563d54 100644 --- a/osu.Game/Screens/Play/PlayerConfiguration.cs +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -24,5 +24,10 @@ namespace osu.Game.Screens.Play /// Whether the player should be allowed to skip the intro, advancing to the start of gameplay. /// public bool AllowSkippingIntro { get; set; } = true; + + /// + /// Whether the player should be allowed to skip the outro, advancing to the end of a storyboard. + /// + public bool AllowSkippingOutro { get; set; } = true; } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 41058d9cb3..e22c586048 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -45,6 +45,7 @@ namespace osu.Game.Storyboards /// Videos and samples return StartTime as their EndTIme. /// public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; + /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. /// From 0e545e1ed9df1d33e0d6506a9edc3a053d6aeb7a Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 15 Apr 2021 17:17:02 -0400 Subject: [PATCH 03/76] Add IHasDuration interface with EndTime for storyboard elements to implement --- osu.Game/Storyboards/IHasDuration.cs | 10 ++++++++++ osu.Game/Storyboards/IStoryboardElement.cs | 2 -- osu.Game/Storyboards/Storyboard.cs | 6 +----- osu.Game/Storyboards/StoryboardSample.cs | 2 -- osu.Game/Storyboards/StoryboardSprite.cs | 2 +- osu.Game/Storyboards/StoryboardVideo.cs | 2 -- 6 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Storyboards/IHasDuration.cs diff --git a/osu.Game/Storyboards/IHasDuration.cs b/osu.Game/Storyboards/IHasDuration.cs new file mode 100644 index 0000000000..98f75b8ee2 --- /dev/null +++ b/osu.Game/Storyboards/IHasDuration.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Storyboards +{ + public interface IHasDuration + { + double EndTime { get; } + } +} diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index 03f8b97212..c4c150a8a4 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -12,8 +12,6 @@ namespace osu.Game.Storyboards double StartTime { get; } - double EndTime { get; } - Drawable CreateDrawable(); } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index e22c586048..669f6ccc43 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -40,11 +40,7 @@ namespace osu.Game.Storyboards /// Across all layers, find the latest point in time that a storyboard element ends at. /// Will return null if there are no elements. /// - /// - /// This iterates all elements and as such should be used sparingly or stored locally. - /// Videos and samples return StartTime as their EndTIme. - /// - public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; + public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index d0949c93a7..5d6ce215f5 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -15,8 +15,6 @@ namespace osu.Game.Storyboards public double StartTime { get; } - public double EndTime => StartTime; - public int Volume { get; } public IEnumerable LookupNames => new[] diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index fdaa59d7d9..8f2c1e9e0c 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -11,7 +11,7 @@ using JetBrains.Annotations; namespace osu.Game.Storyboards { - public class StoryboardSprite : IStoryboardElement + public class StoryboardSprite : IStoryboardElement, IHasDuration { private readonly List loops = new List(); private readonly List triggers = new List(); diff --git a/osu.Game/Storyboards/StoryboardVideo.cs b/osu.Game/Storyboards/StoryboardVideo.cs index 1314bd7cb9..4652e45852 100644 --- a/osu.Game/Storyboards/StoryboardVideo.cs +++ b/osu.Game/Storyboards/StoryboardVideo.cs @@ -14,8 +14,6 @@ namespace osu.Game.Storyboards public double StartTime { get; } - public double EndTime => StartTime; - public StoryboardVideo(string path, int offset) { Path = path; From b15838b22027746fd9174e5fb72e10ade6a68cd2 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Fri, 16 Apr 2021 00:59:10 -0400 Subject: [PATCH 04/76] Move storyboard outro logic to DrawableStoryboard --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 17 +++++++++++-- .../Multiplayer/MultiplayerPlayer.cs | 3 +-- osu.Game/Screens/Play/DimmableStoryboard.cs | 3 +++ .../Screens/Play/GameplayClockContainer.cs | 20 --------------- osu.Game/Screens/Play/Player.cs | 25 ++++++++----------- osu.Game/Screens/Play/PlayerConfiguration.cs | 9 ++----- .../Drawables/DrawableStoryboard.cs | 23 +++++++++++++++++ ...on.cs => IStoryboardElementHasDuration.cs} | 2 +- osu.Game/Storyboards/Storyboard.cs | 2 +- osu.Game/Storyboards/StoryboardSprite.cs | 2 +- 10 files changed, 57 insertions(+), 49 deletions(-) rename osu.Game/Storyboards/{IHasDuration.cs => IStoryboardElementHasDuration.cs} (81%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 1c8b33bb09..27d203d5d6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -6,12 +6,14 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Storyboards; using osuTK; @@ -36,6 +38,16 @@ namespace osu.Game.Tests.Visual.Gameplay storyboard.GetLayer("Background").Add(sprite); } + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + AddStep("ignore user settings", () => + { + Player.DimmableStoryboard.IgnoreUserSettings.Value = true; + }); + } + [Test] public void TestStoryboardSkipOutro() { @@ -50,8 +62,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddUntilStep("storyboard ends", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= storyboard_duration); - AddWaitStep("wait for score", 10); - AddAssert("score shown", () => Player.IsScoreShown); + AddUntilStep("wait for score shown", () => Player.IsScoreShown); } [Test] @@ -85,6 +96,8 @@ namespace osu.Game.Tests.Visual.Gameplay public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen; + public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard; + public OutroPlayer() : base(false) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index b4e8c13e83..ae2042fbe8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -48,8 +48,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { AllowPause = false, AllowRestart = false, - AllowSkippingIntro = false, - AllowSkippingOutro = false, + AllowSkipping = false, }) { this.userIds = userIds; diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 58eb95b7c6..bebde1b15b 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osu.Game.Storyboards; @@ -61,5 +62,7 @@ namespace osu.Game.Screens.Play Add(storyboard); OverlayLayerContainer.Add(storyboard.OverlayLayer.CreateProxy()); } + + public IBindable HasStoryboardEnded => drawableStoryboard?.HasStoryboardEnded; } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index fc45d661cf..95419c0b35 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -240,8 +240,6 @@ namespace osu.Game.Screens.Play userOffsetClock.ProcessFrame(); } - updateHasStoryboardEnded(); - base.Update(); } @@ -303,23 +301,5 @@ namespace osu.Game.Screens.Play { } } - - # region Storyboard outro logic - - public IBindable HasStoryboardEnded => hasStoryboardEnded; - - public bool HasTimeLeftInStoryboard => GameplayClock.CurrentTime <= StoryboardEndTime; - - private readonly BindableBool hasStoryboardEnded = new BindableBool(true); - - private void updateHasStoryboardEnded() - { - if (StoryboardEndTime == 0) - return; - - hasStoryboardEnded.Value = GameplayClock.CurrentTime >= StoryboardEndTime; - } - - # endregion } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 14ba9e7e02..81d2621647 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -74,8 +74,6 @@ namespace osu.Game.Screens.Play private Bindable mouseWheelDisabled; - private Bindable storyboardEnabled; - private readonly Bindable storyboardReplacesBackground = new Bindable(); protected readonly Bindable LocalUserPlaying = new Bindable(); @@ -106,7 +104,7 @@ namespace osu.Game.Screens.Play private BreakTracker breakTracker; - private SkipOverlay skipOverlay; + private SkipOverlay skipIntroOverlay; private SkipOverlay skipOutroOverlay; @@ -194,8 +192,6 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); - storyboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard); - if (game != null) gameActive.BindTo(game.IsActive); @@ -250,7 +246,7 @@ namespace osu.Game.Screens.Play HUDOverlay.ShowHud.Value = false; HUDOverlay.ShowHud.Disabled = true; BreakOverlay.Hide(); - skipOverlay.Hide(); + skipIntroOverlay.Hide(); } DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting => @@ -292,7 +288,8 @@ namespace osu.Game.Screens.Play HealthProcessor.Failed += onFail; // Keep track of whether the storyboard ended after the playable portion - GameplayClockContainer.HasStoryboardEnded.ValueChanged += updateCompletionState; + if (DimmableStoryboard.HasStoryboardEnded != null) + DimmableStoryboard.HasStoryboardEnded.ValueChanged += updateCompletionState; foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); @@ -365,7 +362,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre }, - skipOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) + skipIntroOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) { RequestSkip = performUserRequestedSkip }, @@ -399,11 +396,8 @@ namespace osu.Game.Screens.Play } }; - if (!Configuration.AllowSkippingIntro) - skipOverlay.Expire(); - - if (!Configuration.AllowSkippingOutro) - skipOutroOverlay.Expire(); + if (!Configuration.AllowSkipping) + skipIntroOverlay.Expire(); if (Configuration.AllowRestart) { @@ -637,8 +631,9 @@ namespace osu.Game.Screens.Play return score.ScoreInfo; }); - // show skip overlay if storyboard is enabled and has an outro - if (storyboardEnabled.Value && GameplayClockContainer.HasTimeLeftInStoryboard) + var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && (!DimmableStoryboard.HasStoryboardEnded?.Value ?? false); + + if (storyboardHasOutro) { skipOutroOverlay.Show(); completionProgressDelegate = null; diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs index ad29563d54..18ee73374f 100644 --- a/osu.Game/Screens/Play/PlayerConfiguration.cs +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -21,13 +21,8 @@ namespace osu.Game.Screens.Play public bool AllowRestart { get; set; } = true; /// - /// Whether the player should be allowed to skip the intro, advancing to the start of gameplay. + /// Whether the player should be allowed to skip intros/outros, advancing to the start of gameplay or the end of a storyboard. /// - public bool AllowSkippingIntro { get; set; } = true; - - /// - /// Whether the player should be allowed to skip the outro, advancing to the end of a storyboard. - /// - public bool AllowSkippingOutro { get; set; } = true; + public bool AllowSkipping { get; set; } = true; } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index af4615c895..08c2bf1b4a 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using osuTK; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; @@ -82,5 +83,27 @@ namespace osu.Game.Storyboards.Drawables foreach (var layer in Children) layer.Enabled = passing ? layer.Layer.VisibleWhenPassing : layer.Layer.VisibleWhenFailing; } + + protected override void Update() + { + base.Update(); + updateHasStoryboardEnded(); + } + + /// + /// Whether the storyboard has ended after the gameplay portion of the beatmap. + /// + public IBindable HasStoryboardEnded => hasStoryboardEnded; + + private readonly BindableBool hasStoryboardEnded = new BindableBool(true); + + private void updateHasStoryboardEnded() + { + if (Storyboard.LatestEventTime == null) + return; + + var time = Clock.CurrentTime; + hasStoryboardEnded.Value = time >= Storyboard.LatestEventTime; + } } } diff --git a/osu.Game/Storyboards/IHasDuration.cs b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs similarity index 81% rename from osu.Game/Storyboards/IHasDuration.cs rename to osu.Game/Storyboards/IStoryboardElementHasDuration.cs index 98f75b8ee2..e6ee373812 100644 --- a/osu.Game/Storyboards/IHasDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs @@ -3,7 +3,7 @@ namespace osu.Game.Storyboards { - public interface IHasDuration + public interface IStoryboardElementHasDuration { double EndTime { get; } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 669f6ccc43..4fa64b7c34 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -40,7 +40,7 @@ namespace osu.Game.Storyboards /// Across all layers, find the latest point in time that a storyboard element ends at. /// Will return null if there are no elements. /// - public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; + public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 8f2c1e9e0c..51bdce3321 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -11,7 +11,7 @@ using JetBrains.Annotations; namespace osu.Game.Storyboards { - public class StoryboardSprite : IStoryboardElement, IHasDuration + public class StoryboardSprite : IStoryboardElement, IStoryboardElementHasDuration { private readonly List loops = new List(); private readonly List triggers = new List(); From 33a665224e6c1f77846e9f03927fa72b4cd3859f Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Fri, 16 Apr 2021 01:03:15 -0400 Subject: [PATCH 05/76] Clean up skipOutroOverlay if skipping is disabled --- osu.Game/Screens/Play/Player.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 81d2621647..7ad9c1a8af 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -397,7 +397,10 @@ namespace osu.Game.Screens.Play }; if (!Configuration.AllowSkipping) + { skipIntroOverlay.Expire(); + skipOutroOverlay.Expire(); + } if (Configuration.AllowRestart) { From 81be562379d5bb60023be3f20ecb1f8990993ef9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 12:28:31 -0400 Subject: [PATCH 06/76] Read StoryboardEndTime directly from Beatmap --- osu.Game/Screens/Play/GameplayClockContainer.cs | 5 ----- osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 95419c0b35..ddbb087962 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -228,11 +228,6 @@ namespace osu.Game.Screens.Play adjustableClock.ChangeSource(track); } - /// - /// Gets the endtime of the last element in the storyboard in ms, or start time of the last hitobject if there's no storyboard. - /// - public double StoryboardEndTime => beatmap.Storyboard.LatestEventTime ?? 0; - protected override void Update() { if (!IsPaused.Value) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7ad9c1a8af..e998f33cc1 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -366,7 +366,7 @@ namespace osu.Game.Screens.Play { RequestSkip = performUserRequestedSkip }, - skipOutroOverlay = new SkipOverlay(GameplayClockContainer.StoryboardEndTime) + skipOutroOverlay = new SkipOverlay(Beatmap.Value.Storyboard.LatestEventTime ?? 0) { RequestSkip = scheduleCompletion }, From 5a015290b9721e151a689abd29162d2deab1ad18 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 12:34:38 -0400 Subject: [PATCH 07/76] Add remarks back to LatestEventTime --- osu.Game/Storyboards/Storyboard.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 4fa64b7c34..0b02a4921b 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -40,6 +40,10 @@ namespace osu.Game.Storyboards /// Across all layers, find the latest point in time that a storyboard element ends at. /// Will return null if there are no elements. /// + /// + /// This iterates all elements and as such should be used sparingly or stored locally. + /// Videos and samples return StartTime as their EndTIme. + /// public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; /// From 7f5b1e84a16d42763f20ecb69359213e4d6868c9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 13:57:32 -0400 Subject: [PATCH 08/76] Update TestSceneStoryboardWithOutro.cs - Construct storyboard in CreateWorkingBeatmap() - Use GameplayClockContainer.GameplayClock.CurrentTime - Remove unnecessary lines --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 27d203d5d6..16474d23ea 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Testing; @@ -13,7 +12,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Scoring; -using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Storyboards; using osuTK; @@ -24,28 +22,14 @@ namespace osu.Game.Tests.Visual.Gameplay { protected new OutroPlayer Player => (OutroPlayer)base.Player; - private Storyboard storyboard; - private const double storyboard_duration = 2000; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.SetValue(OsuSetting.ShowStoryboard, true); - storyboard = new Storyboard(); - var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0); - storyboard.GetLayer("Background").Add(sprite); - } - [SetUpSteps] public override void SetUpSteps() { base.SetUpSteps(); - AddStep("ignore user settings", () => - { - Player.DimmableStoryboard.IgnoreUserSettings.Value = true; - }); + AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true)); + AddStep("set dim level to 0", () => LocalConfig.SetValue(OsuSetting.DimLevel, 0)); } [Test] @@ -61,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestStoryboardNoSkipOutro() { AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); - AddUntilStep("storyboard ends", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= storyboard_duration); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= storyboard_duration); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } @@ -87,8 +71,18 @@ namespace osu.Game.Tests.Visual.Gameplay return beatmap; } - protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => - new ClockBackedTestWorkingBeatmap(beatmap, storyboard ?? this.storyboard, Clock, Audio); + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) + { + if (storyboard == null) + { + storyboard = new Storyboard(); + var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); + sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0); + storyboard.GetLayer("Background").Add(sprite); + } + + return base.CreateWorkingBeatmap(beatmap, storyboard); + } protected class OutroPlayer : TestPlayer { @@ -96,8 +90,6 @@ namespace osu.Game.Tests.Visual.Gameplay public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen; - public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard; - public OutroPlayer() : base(false) { @@ -105,7 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Task ImportScore(Score score) { - // avoid database errors from trying to store the score return Task.CompletedTask; } } From e40cb6797df3896d6179dc0f85edef8168c5ee09 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 15:27:22 -0400 Subject: [PATCH 09/76] Use GetEndTime() to get storyboard endtime --- osu.Game/Storyboards/IStoryboardElement.cs | 13 +++++++++++++ .../Storyboards/IStoryboardElementHasDuration.cs | 3 +++ osu.Game/Storyboards/Storyboard.cs | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index c4c150a8a4..377ad57ab2 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -14,4 +14,17 @@ namespace osu.Game.Storyboards Drawable CreateDrawable(); } + + public static class StoryboardElementExtensions + { + /// + /// Returns the end time of this object. + /// + /// + /// This returns the where available, falling back to otherwise. + /// + /// The object. + /// The end time of this object. + public static double GetEndTime(this IStoryboardElement storyboardElement) => (storyboardElement as IStoryboardElementHasDuration)?.EndTime ?? storyboardElement.StartTime; + } } diff --git a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs index e6ee373812..daa0c55704 100644 --- a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs @@ -3,6 +3,9 @@ namespace osu.Game.Storyboards { + /// + /// A StoryboardElement that ends at a different time than its start time. + /// public interface IStoryboardElementHasDuration { double EndTime { get; } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 0b02a4921b..bc61f704dd 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -44,7 +44,7 @@ namespace osu.Game.Storyboards /// This iterates all elements and as such should be used sparingly or stored locally. /// Videos and samples return StartTime as their EndTIme. /// - public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; + public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderBy(e => e.GetEndTime()).LastOrDefault()?.GetEndTime(); /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. From fdcb5e924c9153de2065731689d902d887652bca Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 17:45:38 -0400 Subject: [PATCH 10/76] Initialize skipOutroOverlay with alpha 0, other small changes --- osu.Game/Screens/Play/Player.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e998f33cc1..ef0caa2fa3 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -247,6 +247,7 @@ namespace osu.Game.Screens.Play HUDOverlay.ShowHud.Disabled = true; BreakOverlay.Hide(); skipIntroOverlay.Hide(); + skipOutroOverlay.Hide(); } DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting => @@ -368,7 +369,8 @@ namespace osu.Game.Screens.Play }, skipOutroOverlay = new SkipOverlay(Beatmap.Value.Storyboard.LatestEventTime ?? 0) { - RequestSkip = scheduleCompletion + RequestSkip = scheduleCompletion, + Alpha = 0 }, FailOverlay = new FailOverlay { @@ -416,8 +418,6 @@ namespace osu.Game.Screens.Play }); } - skipOutroOverlay.Hide(); - return container; } @@ -539,13 +539,9 @@ namespace osu.Game.Screens.Play return; } - // show the score if in storyboard outro (score has been set) - bool scoreReady = prepareScoreForDisplayTask != null && prepareScoreForDisplayTask.IsCompleted; - - if (scoreReady) - { + // if the score is ready for display but results screen has not been pushed yet (e.g. storyboard is still playing beyond gameplay), then transition to results screen instead of exiting. + if (prepareScoreForDisplayTask != null) scheduleCompletion(); - } } this.Exit(); @@ -639,7 +635,6 @@ namespace osu.Game.Screens.Play if (storyboardHasOutro) { skipOutroOverlay.Show(); - completionProgressDelegate = null; return; } From 97bacbdc760942ccb7052441b249f6a1c87ce2bd Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 17:55:46 -0400 Subject: [PATCH 11/76] Show score after the end of the storyboard after it was toggled --- osu.Game/Screens/Play/DimmableStoryboard.cs | 3 ++- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index bebde1b15b..73b4153601 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -50,6 +50,7 @@ namespace osu.Game.Screens.Play return; drawableStoryboard = storyboard.CreateDrawable(); + HasStoryboardEnded.BindTo(drawableStoryboard.HasStoryboardEnded); if (async) LoadComponentAsync(drawableStoryboard, onStoryboardCreated); @@ -63,6 +64,6 @@ namespace osu.Game.Screens.Play OverlayLayerContainer.Add(storyboard.OverlayLayer.CreateProxy()); } - public IBindable HasStoryboardEnded => drawableStoryboard?.HasStoryboardEnded; + public IBindable HasStoryboardEnded = new BindableBool(true); } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 08c2bf1b4a..abfb4598ec 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -95,15 +95,13 @@ namespace osu.Game.Storyboards.Drawables /// public IBindable HasStoryboardEnded => hasStoryboardEnded; - private readonly BindableBool hasStoryboardEnded = new BindableBool(true); + private readonly BindableBool hasStoryboardEnded = new BindableBool(); private void updateHasStoryboardEnded() { - if (Storyboard.LatestEventTime == null) - return; - - var time = Clock.CurrentTime; - hasStoryboardEnded.Value = time >= Storyboard.LatestEventTime; + hasStoryboardEnded.Value = + Storyboard.LatestEventTime == null || + Time.Current >= Storyboard.LatestEventTime; } } } From c77f838fb051a4edaf2de036438045d489c10db2 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 21:49:07 -0400 Subject: [PATCH 12/76] HasStoryboardEnded doesn't trigger updateCompletionState() Scores won't be shown prematurely if the storyboard ends before the playable portion of the beatmap. --- osu.Game/Screens/Play/Player.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ef0caa2fa3..622d99f078 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -284,14 +284,16 @@ namespace osu.Game.Screens.Play ScoreProcessor.RevertResult(r); }; + DimmableStoryboard.HasStoryboardEnded.ValueChanged += _ => + { + if (ScoreProcessor.HasCompleted.Value) + scheduleCompletion(); + }; + // Bind the judgement processors to ourselves ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState; HealthProcessor.Failed += onFail; - // Keep track of whether the storyboard ended after the playable portion - if (DimmableStoryboard.HasStoryboardEnded != null) - DimmableStoryboard.HasStoryboardEnded.ValueChanged += updateCompletionState; - foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); @@ -630,7 +632,7 @@ namespace osu.Game.Screens.Play return score.ScoreInfo; }); - var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && (!DimmableStoryboard.HasStoryboardEnded?.Value ?? false); + var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) { From e9f8fa64b88b49c50c2ac9284e8d048034269e4d Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 21:49:29 -0400 Subject: [PATCH 13/76] Added a test for toggling the storyboard after the map is loaded --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 16474d23ea..e86b7591c6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -58,6 +58,16 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("score shown", () => Player.IsScoreShown); } + [TestCase(false)] + [TestCase(true)] + public void TestStoryboardToggle(bool enabledAtBeginning) + { + AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); + AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); + AddStep($"toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); + AddUntilStep("wait for score shown", () => Player.IsScoreShown); + } + protected override bool AllowFail => false; protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); From 7b1d40db7de4fc6544ebc1f4070d600a024df488 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 22:13:28 -0400 Subject: [PATCH 14/76] Remove redundant string interpolation --- osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index e86b7591c6..59d543d686 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); - AddStep($"toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); + AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } From a73bae7a66889f22f8a321745baf25eafafd3efe Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Apr 2021 06:35:43 +0300 Subject: [PATCH 15/76] Schedule completion when storyboard has actually ended --- osu.Game/Screens/Play/Player.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 622d99f078..cb8ebdcce7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -284,9 +284,9 @@ namespace osu.Game.Screens.Play ScoreProcessor.RevertResult(r); }; - DimmableStoryboard.HasStoryboardEnded.ValueChanged += _ => + DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded => { - if (ScoreProcessor.HasCompleted.Value) + if (storyboardEnded.NewValue && ScoreProcessor.HasCompleted.Value) scheduleCompletion(); }; From f6a09be62d4a88ec2f15a4040c3b1e1d9e0e2329 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Apr 2021 06:35:54 +0300 Subject: [PATCH 16/76] Add further xmldoc --- osu.Game/Screens/Play/DimmableStoryboard.cs | 10 ++++++++-- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 +- osu.Game/Storyboards/IStoryboardElement.cs | 8 ++++---- osu.Game/Storyboards/IStoryboardElementHasDuration.cs | 5 ++++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 73b4153601..f8cedddfbe 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -20,6 +20,14 @@ namespace osu.Game.Screens.Play private readonly Storyboard storyboard; private DrawableStoryboard drawableStoryboard; + /// + /// Whether the storyboard is considered finished. + /// + /// + /// This is true by default in here, until an actual drawable storyboard is loaded, in which case it'll bind to it. + /// + public IBindable HasStoryboardEnded = new BindableBool(true); + public DimmableStoryboard(Storyboard storyboard) { this.storyboard = storyboard; @@ -63,7 +71,5 @@ namespace osu.Game.Screens.Play Add(storyboard); OverlayLayerContainer.Add(storyboard.OverlayLayer.CreateProxy()); } - - public IBindable HasStoryboardEnded = new BindableBool(true); } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index abfb4598ec..a9a8b8a4ac 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -91,7 +91,7 @@ namespace osu.Game.Storyboards.Drawables } /// - /// Whether the storyboard has ended after the gameplay portion of the beatmap. + /// Whether the storyboard is considered finished. /// public IBindable HasStoryboardEnded => hasStoryboardEnded; diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index 377ad57ab2..2f05e92070 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -18,13 +18,13 @@ namespace osu.Game.Storyboards public static class StoryboardElementExtensions { /// - /// Returns the end time of this object. + /// Returns the end time of this storyboard element. /// /// /// This returns the where available, falling back to otherwise. /// - /// The object. - /// The end time of this object. - public static double GetEndTime(this IStoryboardElement storyboardElement) => (storyboardElement as IStoryboardElementHasDuration)?.EndTime ?? storyboardElement.StartTime; + /// The storyboard element. + /// The end time of this element. + public static double GetEndTime(this IStoryboardElement element) => (element as IStoryboardElementHasDuration)?.EndTime ?? element.StartTime; } } diff --git a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs index daa0c55704..b8d3b66694 100644 --- a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs @@ -4,10 +4,13 @@ namespace osu.Game.Storyboards { /// - /// A StoryboardElement that ends at a different time than its start time. + /// A that ends at a different time than its start time. /// public interface IStoryboardElementHasDuration { + /// + /// The time at which the ends. + /// double EndTime { get; } } } From f45aed125903673a1eaa27484bce71c5026d61e8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Apr 2021 07:16:00 +0300 Subject: [PATCH 17/76] Remove new line between skip overlay fields --- osu.Game/Screens/Play/Player.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cb8ebdcce7..da877fcd22 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -105,7 +105,6 @@ namespace osu.Game.Screens.Play private BreakTracker breakTracker; private SkipOverlay skipIntroOverlay; - private SkipOverlay skipOutroOverlay; protected ScoreProcessor ScoreProcessor { get; private set; } From 98460c8febc37c17acb949a864f528c32ef637cb Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 18 Apr 2021 00:45:24 -0400 Subject: [PATCH 18/76] Rename IStoryboardElementHasDuration, remove unnecessary step in tests, add Duration field --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 4 ---- osu.Game/Storyboards/IStoryboardElement.cs | 4 ++-- ...entHasDuration.cs => IStoryboardElementWithDuration.cs} | 7 ++++++- osu.Game/Storyboards/StoryboardSprite.cs | 4 +++- 4 files changed, 11 insertions(+), 8 deletions(-) rename osu.Game/Storyboards/{IStoryboardElementHasDuration.cs => IStoryboardElementWithDuration.cs} (70%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 59d543d686..fa1c4aa0d1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -35,7 +35,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardSkipOutro() { - AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space)); AddAssert("score shown", () => Player.IsScoreShown); @@ -44,7 +43,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardNoSkipOutro() { - AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= storyboard_duration); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } @@ -52,7 +50,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardExitToSkipOutro() { - AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("exit via pause", () => Player.ExitViaPause()); AddAssert("score shown", () => Player.IsScoreShown); @@ -63,7 +60,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestStoryboardToggle(bool enabledAtBeginning) { AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); - AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index 2f05e92070..9a059991e6 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -21,10 +21,10 @@ namespace osu.Game.Storyboards /// Returns the end time of this storyboard element. /// /// - /// This returns the where available, falling back to otherwise. + /// This returns the where available, falling back to otherwise. /// /// The storyboard element. /// The end time of this element. - public static double GetEndTime(this IStoryboardElement element) => (element as IStoryboardElementHasDuration)?.EndTime ?? element.StartTime; + public static double GetEndTime(this IStoryboardElement element) => (element as IStoryboardElementWithDuration)?.EndTime ?? element.StartTime; } } diff --git a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs similarity index 70% rename from osu.Game/Storyboards/IStoryboardElementHasDuration.cs rename to osu.Game/Storyboards/IStoryboardElementWithDuration.cs index b8d3b66694..02b438cb76 100644 --- a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs @@ -6,11 +6,16 @@ namespace osu.Game.Storyboards /// /// A that ends at a different time than its start time. /// - public interface IStoryboardElementHasDuration + public interface IStoryboardElementWithDuration : IStoryboardElement { /// /// The time at which the ends. /// double EndTime { get; } + + /// + /// The duration of the StoryboardElement. + /// + double Duration { get; } } } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 51bdce3321..0aaf264341 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -11,7 +11,7 @@ using JetBrains.Annotations; namespace osu.Game.Storyboards { - public class StoryboardSprite : IStoryboardElement, IStoryboardElementHasDuration + public class StoryboardSprite : IStoryboardElementWithDuration { private readonly List loops = new List(); private readonly List triggers = new List(); @@ -65,6 +65,8 @@ namespace osu.Game.Storyboards } } + public double Duration => EndTime - StartTime; + public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands); private delegate void DrawablePropertyInitializer(Drawable drawable, T value); From 99fab456b57adb11d4bbb6a92b959633fb33c21c Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 18 Apr 2021 23:25:20 -0400 Subject: [PATCH 19/76] Storyboard completion calls updateCompletionState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - If the storyboard ends after the beatmap, call updateCompletionState as if the score processor has completed at that time. (completionProgressDelegate is null here since earlier when the score processor actually completed, updateCompletionState returned after showing the skip overlay.) - If the storyboard ends before the beatmap does, updateCompletionState simply returns and waits until the score processor is completed. - If the storyboard and beatmap end at the exact same time, make sure updateCompletionState() is called only once by the score processor completion. Co-Authored-By: Marlina José --- osu.Game/Screens/Play/Player.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index da877fcd22..231fcbc174 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -285,8 +285,8 @@ namespace osu.Game.Screens.Play DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded => { - if (storyboardEnded.NewValue && ScoreProcessor.HasCompleted.Value) - scheduleCompletion(); + if (storyboardEnded.NewValue && completionProgressDelegate == null) + updateCompletionState(new ValueChangedEvent(true, ScoreProcessor.HasCompleted.Value)); }; // Bind the judgement processors to ourselves From fd1241cc8513c72674f723a4c8767d9d5672430d Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 18 Apr 2021 23:26:50 -0400 Subject: [PATCH 20/76] Added tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New tests: - storyboard ending during the failing animation - showResults = false - storyboard ending before score processor completion Co-Authored-By: Marlina José --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 92 ++++++++++++++++--- 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index fa1c4aa0d1..8326063f81 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -1,17 +1,22 @@ // 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.Threading.Tasks; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Storyboards; using osuTK; @@ -20,9 +25,15 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneStoryboardWithOutro : PlayerTestScene { + protected override bool HasCustomSteps => true; + protected new OutroPlayer Player => (OutroPlayer)base.Player; - private const double storyboard_duration = 2000; + private double currentStoryboardDuration; + + private bool showResults = true; + + private event Func currentFailConditions; [SetUpSteps] public override void SetUpSteps() @@ -30,11 +41,15 @@ namespace osu.Game.Tests.Visual.Gameplay base.SetUpSteps(); AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true)); AddStep("set dim level to 0", () => LocalConfig.SetValue(OsuSetting.DimLevel, 0)); + AddStep("reset fail conditions", () => currentFailConditions = (_, __) => false); + AddStep("set storyboard duration to 2s", () => currentStoryboardDuration = 2000); + AddStep("set ShowResults = true", () => showResults = true); } [Test] public void TestStoryboardSkipOutro() { + CreateTest(null); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space)); AddAssert("score shown", () => Player.IsScoreShown); @@ -43,13 +58,15 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardNoSkipOutro() { - AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= storyboard_duration); + CreateTest(null); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } [Test] public void TestStoryboardExitToSkipOutro() { + CreateTest(null); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("exit via pause", () => Player.ExitViaPause()); AddAssert("score shown", () => Player.IsScoreShown); @@ -59,16 +76,51 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(true)] public void TestStoryboardToggle(bool enabledAtBeginning) { + CreateTest(null); AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } - protected override bool AllowFail => false; + [Test] + public void TestOutroEndsDuringFailAnimation() + { + CreateTest(() => + { + AddStep("fail on first judgement", () => currentFailConditions = (_, __) => true); + AddStep("set storyboard duration to 1.3s", () => currentStoryboardDuration = 1300); + }); + AddUntilStep("wait for fail", () => Player.HasFailed); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); + AddUntilStep("wait for fail overlay", () => Player.FailOverlay.State.Value == Visibility.Visible); + } + + [Test] + public void TestShowResultsFalse() + { + CreateTest(() => + { + AddStep("set ShowResults = false", () => showResults = false); + }); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); + AddWaitStep("wait", 10); + AddAssert("no score shown", () => !Player.IsScoreShown); + } + + [Test] + public void TestStoryboardEndsBeforeCompletion() + { + CreateTest(() => AddStep("set storyboard duration to .1s", () => currentStoryboardDuration = 100)); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddUntilStep("wait for score shown", () => Player.IsScoreShown); + } + + protected override bool AllowFail => true; protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OutroPlayer(); + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OutroPlayer(currentFailConditions, showResults); protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { @@ -79,26 +131,38 @@ namespace osu.Game.Tests.Visual.Gameplay protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) { - if (storyboard == null) - { - storyboard = new Storyboard(); - var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0); - storyboard.GetLayer("Background").Add(sprite); - } + return base.CreateWorkingBeatmap(beatmap, createStoryboard(currentStoryboardDuration)); + } - return base.CreateWorkingBeatmap(beatmap, storyboard); + private Storyboard createStoryboard(double duration) + { + var storyboard = new Storyboard(); + var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); + sprite.TimelineGroup.Alpha.Add(Easing.None, 0, duration, 1, 0); + storyboard.GetLayer("Background").Add(sprite); + return storyboard; } protected class OutroPlayer : TestPlayer { public void ExitViaPause() => PerformExit(true); + public new FailOverlay FailOverlay => base.FailOverlay; + public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen; - public OutroPlayer() - : base(false) + private event Func failConditions; + + public OutroPlayer(Func failConditions, bool showResults = true) + : base(false, showResults) { + this.failConditions = failConditions; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + HealthProcessor.FailConditions += failConditions; } protected override Task ImportScore(Score score) From 0b36dd9bce6e4f958b51e91d52c7fd650abf2673 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 19 Apr 2021 01:23:21 -0400 Subject: [PATCH 21/76] Skip outro overlay and PerformExit() call updateCompletionState() instead of scheduleCompletion() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Marlina José --- osu.Game/Screens/Play/Player.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 231fcbc174..ef2e042260 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -290,7 +290,7 @@ namespace osu.Game.Screens.Play }; // Bind the judgement processors to ourselves - ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState; + ScoreProcessor.HasCompleted.BindValueChanged(e => updateCompletionState(e)); HealthProcessor.Failed += onFail; foreach (var mod in Mods.Value.OfType()) @@ -370,7 +370,7 @@ namespace osu.Game.Screens.Play }, skipOutroOverlay = new SkipOverlay(Beatmap.Value.Storyboard.LatestEventTime ?? 0) { - RequestSkip = scheduleCompletion, + RequestSkip = () => updateCompletionState(new ValueChangedEvent(true, true), true), Alpha = 0 }, FailOverlay = new FailOverlay @@ -542,7 +542,7 @@ namespace osu.Game.Screens.Play // if the score is ready for display but results screen has not been pushed yet (e.g. storyboard is still playing beyond gameplay), then transition to results screen instead of exiting. if (prepareScoreForDisplayTask != null) - scheduleCompletion(); + updateCompletionState(new ValueChangedEvent(true, true), true); } this.Exit(); @@ -581,7 +581,7 @@ namespace osu.Game.Screens.Play private ScheduledDelegate completionProgressDelegate; private Task prepareScoreForDisplayTask; - private void updateCompletionState(ValueChangedEvent completionState) + private void updateCompletionState(ValueChangedEvent completionState, bool skipStoryboardOutro = false) { // screen may be in the exiting transition phase. if (!this.IsCurrentScreen()) @@ -631,6 +631,12 @@ namespace osu.Game.Screens.Play return score.ScoreInfo; }); + if (skipStoryboardOutro) + { + scheduleCompletion(); + return; + } + var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) From abfa6aec87047027380c54f75fcba502da3941df Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 19 Apr 2021 01:58:19 -0400 Subject: [PATCH 22/76] Remove completionState parameter --- osu.Game/Screens/Play/Player.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ef2e042260..dd7cf944f7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -286,11 +286,11 @@ namespace osu.Game.Screens.Play DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded => { if (storyboardEnded.NewValue && completionProgressDelegate == null) - updateCompletionState(new ValueChangedEvent(true, ScoreProcessor.HasCompleted.Value)); + updateCompletionState(); }; // Bind the judgement processors to ourselves - ScoreProcessor.HasCompleted.BindValueChanged(e => updateCompletionState(e)); + ScoreProcessor.HasCompleted.BindValueChanged(_ => updateCompletionState()); HealthProcessor.Failed += onFail; foreach (var mod in Mods.Value.OfType()) @@ -370,7 +370,7 @@ namespace osu.Game.Screens.Play }, skipOutroOverlay = new SkipOverlay(Beatmap.Value.Storyboard.LatestEventTime ?? 0) { - RequestSkip = () => updateCompletionState(new ValueChangedEvent(true, true), true), + RequestSkip = () => updateCompletionState(true), Alpha = 0 }, FailOverlay = new FailOverlay @@ -542,7 +542,7 @@ namespace osu.Game.Screens.Play // if the score is ready for display but results screen has not been pushed yet (e.g. storyboard is still playing beyond gameplay), then transition to results screen instead of exiting. if (prepareScoreForDisplayTask != null) - updateCompletionState(new ValueChangedEvent(true, true), true); + updateCompletionState(true); } this.Exit(); @@ -581,13 +581,13 @@ namespace osu.Game.Screens.Play private ScheduledDelegate completionProgressDelegate; private Task prepareScoreForDisplayTask; - private void updateCompletionState(ValueChangedEvent completionState, bool skipStoryboardOutro = false) + private void updateCompletionState(bool skipStoryboardOutro = false) { // screen may be in the exiting transition phase. if (!this.IsCurrentScreen()) return; - if (!completionState.NewValue) + if (!ScoreProcessor.HasCompleted.Value) { completionProgressDelegate?.Cancel(); completionProgressDelegate = null; From 505a117862cce9818970a71833736c7a294b84c2 Mon Sep 17 00:00:00 2001 From: Denrage Date: Mon, 19 Apr 2021 23:41:51 +0200 Subject: [PATCH 23/76] splitted updateable part of wedge --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 16 +- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 261 ++++++++++-------- 2 files changed, 152 insertions(+), 125 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 7ea6373763..9c10c9751c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -112,8 +112,8 @@ namespace osu.Game.Tests.Visual.SongSelect private void testInfoLabels(int expectedCount) { - AddAssert("check info labels exists", () => infoWedge.Info.ChildrenOfType().Any()); - AddAssert("check info labels count", () => infoWedge.Info.ChildrenOfType().Count() == expectedCount); + AddAssert("check info labels exists", () => infoWedge.Info.ChildrenOfType().Any()); + AddAssert("check info labels count", () => infoWedge.Info.ChildrenOfType().Count() == expectedCount); } [Test] @@ -124,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.ChildrenOfType().Any()); + AddAssert("check no info labels", () => !infoWedge.Info.ChildrenOfType().Any()); } [Test] @@ -135,15 +135,15 @@ namespace osu.Game.Tests.Visual.SongSelect private void selectBeatmap([CanBeNull] IBeatmap b) { - BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null; + BeatmapInfoWedge.BufferedWedgeBackground backgroundBefore = null; AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { - infoBefore = infoWedge.Info; + backgroundBefore = infoWedge.Background; infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b); }); - AddUntilStep("wait for async load", () => infoWedge.Info != infoBefore); + AddUntilStep("wait for async load", () => infoWedge.Background != backgroundBefore); } private IBeatmap createTestBeatmap(RulesetInfo ruleset) @@ -193,7 +193,9 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new BufferedWedgeInfo Info => base.Info; + public new BufferedWedgeBackground Background => base.Background; + + public new WedgeInfoText Info => base.Info; } private class TestHitObject : ConvertHitObject, IHasPosition diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 4069dc82ed..0b72970bb0 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -49,7 +49,8 @@ namespace osu.Game.Screens.Select private IBindable beatmapDifficulty; - protected BufferedWedgeInfo Info; + protected BufferedWedgeBackground Background; + protected WedgeInfoText Info; public BeatmapInfoWedge() { @@ -110,9 +111,9 @@ namespace osu.Game.Screens.Select } } - public override bool IsPresent => base.IsPresent || Info == null; // Visibility is updated in the LoadComponentAsync callback + public override bool IsPresent => base.IsPresent || Background == null; // Visibility is updated in the LoadComponentAsync callback - private BufferedWedgeInfo loadingInfo; + private BufferedWedgeBackground loadingInfo; private void updateDisplay() { @@ -127,6 +128,10 @@ namespace osu.Game.Screens.Select Info?.FadeOut(250); Info?.Expire(); Info = null; + + Background?.FadeOut(250); + Background?.Expire(); + Background = null; } if (beatmap == null) @@ -135,17 +140,21 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) + LoadComponentAsync(loadingInfo = new BufferedWedgeBackground(beatmap) { Shear = -Shear, - Depth = Info?.Depth + 1 ?? 0 + Depth = Background?.Depth + 1 ?? 0 }, loaded => { // ensure we are the most recent loaded wedge. if (loaded != loadingInfo) return; removeOldInfo(); - Add(Info = loaded); + Add(Background = loaded); + Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) + { + Shear = -Shear + }); }); } } @@ -156,28 +165,26 @@ namespace osu.Game.Screens.Select cancellationSource?.Cancel(); } - public class BufferedWedgeInfo : BufferedContainer + public class WedgeInfoText : Container { - public OsuSpriteText VersionLabel { get; private set; } + public FillFlowContainer MapperContainer { get; private set; } public OsuSpriteText TitleLabel { get; private set; } public OsuSpriteText ArtistLabel { get; private set; } + public OsuSpriteText VersionLabel { get; private set; } public BeatmapSetOnlineStatusPill StatusPill { get; private set; } - public FillFlowContainer MapperContainer { get; private set; } private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; private Container bpmLabelContainer; + private ModSettingChangeTracker settingChangeTracker; 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) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; @@ -191,7 +198,6 @@ namespace osu.Game.Screens.Select var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - CacheDrawnFrameBuffer = true; RelativeSizeAxes = Axes.Both; titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); @@ -199,32 +205,6 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - // We will create the white-to-black gradient by modulating transparency and having - // a black backdrop. This results in an sRGB-space gradient and not linear space, - // transitioning from white to black more perceptually uniformly. - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - // We use a container, such that we can set the colour gradient to go across the - // vertices of the masked container instead of the vertices of the (larger) sprite. - new Container - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)), - Children = new[] - { - // Zoomed-in and cropped beatmap background - new BeatmapBackgroundSprite(beatmap) - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fill, - }, - }, - }, new DifficultyColourBar(starDifficulty) { RelativeSizeAxes = Axes.Y, @@ -329,51 +309,6 @@ namespace osu.Game.Screens.Select addInfoLabels(); } - private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 - ? new StarRatingDisplay(difficulty) - { - Margin = new MarginPadding { Bottom = 5 } - } - : Empty(); - - private void setMetadata(string source) - { - ArtistLabel.Text = artistBinding.Value; - TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; - 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 @@ -426,8 +361,43 @@ namespace osu.Game.Screens.Select CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm), Content = labelText }); + } - ForceRedraw(); + private void setMetadata(string source) + { + ArtistLabel.Text = artistBinding.Value; + TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; + } + + 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 OsuSpriteText[] getMapper(BeatmapMetadata metadata) @@ -450,10 +420,48 @@ namespace osu.Game.Screens.Select }; } - protected override void Dispose(bool isDisposing) + private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 + ? new StarRatingDisplay(difficulty) + { + Margin = new MarginPadding { Bottom = 5 } + } + : Empty(); + + private class DifficultyColourBar : Container { - base.Dispose(isDisposing); - settingChangeTracker?.Dispose(); + private readonly StarDifficulty difficulty; + + public DifficultyColourBar(StarDifficulty difficulty) + { + this.difficulty = difficulty; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + const float full_opacity_ratio = 0.7f; + + var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = difficultyColour, + Width = full_opacity_ratio, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + Colour = difficultyColour, + Alpha = 0.5f, + X = full_opacity_ratio, + Width = 1 - full_opacity_ratio, + } + }; + } } public class InfoLabel : Container, IHasTooltip @@ -515,41 +523,58 @@ namespace osu.Game.Screens.Select } } - private class DifficultyColourBar : Container + protected override void Dispose(bool isDisposing) { - private readonly StarDifficulty difficulty; + base.Dispose(isDisposing); + settingChangeTracker?.Dispose(); + } + } - public DifficultyColourBar(StarDifficulty difficulty) + public class BufferedWedgeBackground : BufferedContainer + { + private readonly WorkingBeatmap beatmap; + + public BufferedWedgeBackground(WorkingBeatmap beatmap) + : base(pixelSnapping: true) + { + this.beatmap = beatmap; + } + + [BackgroundDependencyLoader] + private void load(LocalisationManager localisation) + { + CacheDrawnFrameBuffer = true; + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] { - this.difficulty = difficulty; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - const float full_opacity_ratio = 0.7f; - - var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); - - Children = new Drawable[] + // We will create the white-to-black gradient by modulating transparency and having + // a black backdrop. This results in an sRGB-space gradient and not linear space, + // transitioning from white to black more perceptually uniformly. + new Box { - new Box + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + // We use a container, such that we can set the colour gradient to go across the + // vertices of the masked container instead of the vertices of the (larger) sprite. + new Container + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)), + Children = new[] { - RelativeSizeAxes = Axes.Both, - Colour = difficultyColour, - Width = full_opacity_ratio, + // Zoomed-in and cropped beatmap background + new BeatmapBackgroundSprite(beatmap) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill, + }, }, - new Box - { - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - Colour = difficultyColour, - Alpha = 0.5f, - X = full_opacity_ratio, - Width = 1 - full_opacity_ratio, - } - }; - } + }, + }; } } } From 6e72ee5f7647eb77b2cc1447e32baeb3d4c990c4 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 10:25:12 +0200 Subject: [PATCH 24/76] Added bindable stardifficulty to StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 79 +++++++++++++------ osu.Game/Screens/Select/BeatmapInfoWedge.cs | 64 +++++++++------ 2 files changed, 95 insertions(+), 48 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index f7e50fdc8a..081b782034 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -1,8 +1,10 @@ // 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.Globalization; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -22,7 +24,11 @@ namespace osu.Game.Screens.Ranking.Expanded /// public class StarRatingDisplay : CompositeDrawable { - private readonly StarDifficulty difficulty; + private CircularContainer colorContainer; + private OsuTextFlowContainer textContainer; + + private readonly StarDifficulty starDifficulty; + private readonly IBindable bindableStarDifficulty; /// /// Creates a new using an already computed . @@ -30,14 +36,21 @@ namespace osu.Game.Screens.Ranking.Expanded /// The already computed to display the star difficulty of. public StarRatingDisplay(StarDifficulty starDifficulty) { - difficulty = starDifficulty; + this.starDifficulty = starDifficulty; } - [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) + /// + /// Creates a new using a binded nullable . + /// + /// The binded nullable to display the star difficulty of. If null, a new instance of will be created + public StarRatingDisplay(IBindable starDifficulty) { - AutoSizeAxes = Axes.Both; + bindableStarDifficulty = starDifficulty; + } + private void setDifficulty(OsuColour colours) + { + var difficulty = bindableStarDifficulty == null ? starDifficulty : bindableStarDifficulty.Value ?? new StarDifficulty(); var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); string wholePart = starRatingParts[0]; string fractionPart = starRatingParts[1]; @@ -47,9 +60,36 @@ namespace osu.Game.Screens.Ranking.Expanded ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + colorContainer.Colour = backgroundColour; + + textContainer.Text = string.Empty; + + textContainer.With(t => + { + t.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + t.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) + { + AutoSizeAxes = Axes.Both; + InternalChildren = new Drawable[] { - new CircularContainer + colorContainer = new CircularContainer { RelativeSizeAxes = Axes.Both, Masking = true, @@ -58,7 +98,6 @@ namespace osu.Game.Screens.Ranking.Expanded new Box { RelativeSizeAxes = Axes.Both, - Colour = backgroundColour }, } }, @@ -78,32 +117,24 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + textContainer = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, TextAnchor = Anchor.BottomLeft, - }.With(t => - { - t.AddText($"{wholePart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - - t.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }) + }, } } }; + + if (bindableStarDifficulty != null) + { + bindableStarDifficulty.BindValueChanged(_ => setDifficulty(colours)); + } + + setDifficulty(colours); } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 0b72970bb0..9dccdd2897 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -105,7 +105,6 @@ namespace osu.Game.Screens.Select beatmapDifficulty?.UnbindAll(); beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); - beatmapDifficulty.BindValueChanged(_ => updateDisplay()); updateDisplay(); } @@ -151,7 +150,7 @@ namespace osu.Game.Screens.Select removeOldInfo(); Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) + Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty) { Shear = -Shear }); @@ -176,15 +175,16 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; + private Drawable starRatingDisplay; private Container bpmLabelContainer; private ModSettingChangeTracker settingChangeTracker; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; private readonly IReadOnlyList mods; - private readonly StarDifficulty starDifficulty; + private readonly IBindable starDifficulty; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, IBindable difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; @@ -241,12 +241,13 @@ namespace osu.Game.Screens.Select Shear = wedged_container_shear, Children = new[] { - createStarRatingDisplay(starDifficulty).With(display => + starRatingDisplay = new StarRatingDisplay(starDifficulty) { - display.Anchor = Anchor.TopRight; - display.Origin = Anchor.TopRight; - display.Shear = -wedged_container_shear; - }), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + Margin = new MarginPadding { Bottom = 5 } + }, StatusPill = new BeatmapSetOnlineStatusPill { Anchor = Anchor.TopRight, @@ -309,6 +310,18 @@ namespace osu.Game.Screens.Select addInfoLabels(); } + private void setStarRatingDisplayVisibility() + { + if (starDifficulty.Value.HasValue && starDifficulty.Value.Value.Stars > 0) + { + starRatingDisplay.Show(); + } + else + { + starRatingDisplay.Hide(); + } + } + private InfoLabel[] getRulesetInfoLabels() { try @@ -420,18 +433,14 @@ namespace osu.Game.Screens.Select }; } - private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 - ? new StarRatingDisplay(difficulty) - { - Margin = new MarginPadding { Bottom = 5 } - } - : Empty(); - private class DifficultyColourBar : Container { - private readonly StarDifficulty difficulty; + private Box solidDifficultyBox; + private Box transparentDifficultyBox; - public DifficultyColourBar(StarDifficulty difficulty) + private readonly IBindable difficulty; + + public DifficultyColourBar(IBindable difficulty) { this.difficulty = difficulty; } @@ -441,26 +450,33 @@ namespace osu.Game.Screens.Select { const float full_opacity_ratio = 0.7f; - var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); - Children = new Drawable[] { - new Box + solidDifficultyBox = new Box { RelativeSizeAxes = Axes.Both, - Colour = difficultyColour, Width = full_opacity_ratio, }, - new Box + transparentDifficultyBox = new Box { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, - Colour = difficultyColour, Alpha = 0.5f, X = full_opacity_ratio, Width = 1 - full_opacity_ratio, } }; + + difficulty.BindValueChanged(_ => setColour(colours)); + setColour(colours); + } + + private void setColour(OsuColour colours) + { + var difficultyColour = colours.ForDifficultyRating(difficulty.Value?.DifficultyRating ?? (new StarDifficulty()).DifficultyRating); + + solidDifficultyBox.Colour = difficultyColour; + transparentDifficultyBox.Colour = difficultyColour; } } From 4e6cd8082ea784ee4223f2f36e7c05f1253aeac5 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 12:00:04 +0200 Subject: [PATCH 25/76] WIP refresh BPM-Label on mod change --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 27 +++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 9dccdd2897..b3aa4f0ba5 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -150,7 +150,7 @@ namespace osu.Game.Screens.Select removeOldInfo(); Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty) + Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods, beatmapDifficulty) { Shear = -Shear }); @@ -181,10 +181,10 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - private readonly IReadOnlyList mods; + private readonly IBindable> mods; private readonly IBindable starDifficulty; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, IBindable difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IBindable> mods, IBindable difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; @@ -300,14 +300,16 @@ namespace osu.Game.Screens.Select } }; + addInfoLabels(); + titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); + starDifficulty.BindValueChanged(_ => setStarRatingDisplayVisibility(), true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); - addInfoLabels(); } private void setStarRatingDisplayVisibility() @@ -349,7 +351,7 @@ namespace osu.Game.Screens.Select return Array.Empty(); } - private void refreshBPMLabel() + private void refreshBPMLabel(IReadOnlyList mods) { var b = beatmap.Beatmap; if (b == null) @@ -407,10 +409,16 @@ namespace osu.Game.Screens.Select } }; - settingChangeTracker = new ModSettingChangeTracker(mods); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + // this is currently not triggering when a mod gets (de)selected + mods.BindValueChanged(mods => refreshModInformation(mods), true); + } - refreshBPMLabel(); + private void refreshModInformation(ValueChangedEvent> modsChangedEvent) + { + settingChangeTracker?.Dispose(); + settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); + refreshBPMLabel(mods.Value); } private OsuSpriteText[] getMapper(BeatmapMetadata metadata) @@ -467,8 +475,7 @@ namespace osu.Game.Screens.Select } }; - difficulty.BindValueChanged(_ => setColour(colours)); - setColour(colours); + difficulty.BindValueChanged(_ => setColour(colours), true); } private void setColour(OsuColour colours) From c5d35ab78733cdd64322467e378474e526096a83 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 12:35:31 +0200 Subject: [PATCH 26/76] removed mods binding passthrough --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index b3aa4f0ba5..c308f14f74 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,9 +41,6 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } - [Resolved] - private IBindable> mods { get; set; } - [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -150,7 +147,7 @@ namespace osu.Game.Screens.Select removeOldInfo(); Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods, beatmapDifficulty) + Add(Info = new WedgeInfoText(beatmap, ruleset.Value, beatmapDifficulty) { Shear = -Shear }); @@ -172,6 +169,9 @@ namespace osu.Game.Screens.Select public OsuSpriteText VersionLabel { get; private set; } public BeatmapSetOnlineStatusPill StatusPill { get; private set; } + [Resolved] + private IBindable> mods { get; set; } + private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; @@ -181,14 +181,12 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - private readonly IBindable> mods; private readonly IBindable starDifficulty; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IBindable> mods, IBindable difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IBindable difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; - this.mods = mods; starDifficulty = difficulty; } @@ -409,7 +407,6 @@ namespace osu.Game.Screens.Select } }; - // this is currently not triggering when a mod gets (de)selected mods.BindValueChanged(mods => refreshModInformation(mods), true); } @@ -418,7 +415,7 @@ namespace osu.Game.Screens.Select settingChangeTracker?.Dispose(); settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); - refreshBPMLabel(mods.Value); + refreshBPMLabel(modsChangedEvent.NewValue); } private OsuSpriteText[] getMapper(BeatmapMetadata metadata) From f799a6e733e0d2075f7feafd9333cfaf5c604fad Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 14:18:41 +0200 Subject: [PATCH 27/76] Removed StarDifficulty binding passthrough --- .../Ranking/Expanded/StarRatingDisplay.cs | 42 +++++++---- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 69 ++++++++++--------- 2 files changed, 67 insertions(+), 44 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 081b782034..1aa8f5ca73 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -3,6 +3,7 @@ using System; using System.Globalization; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -24,11 +25,16 @@ namespace osu.Game.Screens.Ranking.Expanded /// public class StarRatingDisplay : CompositeDrawable { + [Resolved] + private OsuColour colours { get; set; } + private CircularContainer colorContainer; private OsuTextFlowContainer textContainer; + private CancellationTokenSource cancellationTokenSource; + private IBindable bindableStarDifficulty; private readonly StarDifficulty starDifficulty; - private readonly IBindable bindableStarDifficulty; + private readonly BeatmapInfo beatmapInfo; /// /// Creates a new using an already computed . @@ -40,17 +46,16 @@ namespace osu.Game.Screens.Ranking.Expanded } /// - /// Creates a new using a binded nullable . + /// Creates a new using a to use a bindable for the difficulty. /// - /// The binded nullable to display the star difficulty of. If null, a new instance of will be created - public StarRatingDisplay(IBindable starDifficulty) + /// The to use to create a bindable for + public StarRatingDisplay(BeatmapInfo beatmapInfo) { - bindableStarDifficulty = starDifficulty; + this.beatmapInfo = beatmapInfo; } - private void setDifficulty(OsuColour colours) + private void setDifficulty(StarDifficulty difficulty) { - var difficulty = bindableStarDifficulty == null ? starDifficulty : bindableStarDifficulty.Value ?? new StarDifficulty(); var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); string wholePart = starRatingParts[0]; string fractionPart = starRatingParts[1]; @@ -83,8 +88,17 @@ namespace osu.Game.Screens.Ranking.Expanded } [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) + private void load(BeatmapDifficultyCache difficultyCache) { + if (beatmapInfo != null) + { + cancellationTokenSource?.Cancel(); + cancellationTokenSource = new CancellationTokenSource(); + + bindableStarDifficulty?.UnbindAll(); + bindableStarDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); + } + AutoSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -130,11 +144,15 @@ namespace osu.Game.Screens.Ranking.Expanded }; if (bindableStarDifficulty != null) - { - bindableStarDifficulty.BindValueChanged(_ => setDifficulty(colours)); - } + bindableStarDifficulty.BindValueChanged(valueChanged => setDifficulty(valueChanged.NewValue ?? new StarDifficulty()), true); + else + setDifficulty(starDifficulty); + } - setDifficulty(colours); + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + cancellationTokenSource?.Cancel(); } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index c308f14f74..e3afd53210 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,11 +41,6 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } - [Resolved] - private BeatmapDifficultyCache difficultyCache { get; set; } - - private IBindable beatmapDifficulty; - protected BufferedWedgeBackground Background; protected WedgeInfoText Info; @@ -87,8 +82,6 @@ namespace osu.Game.Screens.Select private WorkingBeatmap beatmap; - private CancellationTokenSource cancellationSource; - public WorkingBeatmap Beatmap { get => beatmap; @@ -97,12 +90,6 @@ namespace osu.Game.Screens.Select if (beatmap == value) return; beatmap = value; - cancellationSource?.Cancel(); - cancellationSource = new CancellationTokenSource(); - - beatmapDifficulty?.UnbindAll(); - beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); - updateDisplay(); } } @@ -147,7 +134,7 @@ namespace osu.Game.Screens.Select removeOldInfo(); Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value, beatmapDifficulty) + Add(Info = new WedgeInfoText(beatmap, ruleset.Value) { Shear = -Shear }); @@ -158,7 +145,6 @@ namespace osu.Game.Screens.Select protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - cancellationSource?.Cancel(); } public class WedgeInfoText : Container @@ -178,24 +164,30 @@ namespace osu.Game.Screens.Select private Drawable starRatingDisplay; private Container bpmLabelContainer; private ModSettingChangeTracker settingChangeTracker; + private CancellationTokenSource cancellationTokenSource; + private IBindable starDifficulty; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - private readonly IBindable starDifficulty; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IBindable difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; - starDifficulty = difficulty; } [BackgroundDependencyLoader] - private void load(LocalisationManager localisation) + private void load(LocalisationManager localisation, BeatmapDifficultyCache difficultyCache) { var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); + cancellationTokenSource?.Cancel(); + cancellationTokenSource = new CancellationTokenSource(); + + starDifficulty?.UnbindAll(); + starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); + RelativeSizeAxes = Axes.Both; titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); @@ -203,7 +195,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - new DifficultyColourBar(starDifficulty) + new DifficultyColourBar(beatmapInfo) { RelativeSizeAxes = Axes.Y, Width = 20, @@ -239,7 +231,7 @@ namespace osu.Game.Screens.Select Shear = wedged_container_shear, Children = new[] { - starRatingDisplay = new StarRatingDisplay(starDifficulty) + starRatingDisplay = new StarRatingDisplay(beatmapInfo) { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -313,13 +305,9 @@ namespace osu.Game.Screens.Select private void setStarRatingDisplayVisibility() { if (starDifficulty.Value.HasValue && starDifficulty.Value.Value.Stars > 0) - { starRatingDisplay.Show(); - } else - { starRatingDisplay.Hide(); - } } private InfoLabel[] getRulesetInfoLabels() @@ -440,21 +428,32 @@ namespace osu.Game.Screens.Select private class DifficultyColourBar : Container { + [Resolved] + private OsuColour colours { get; set; } + private Box solidDifficultyBox; private Box transparentDifficultyBox; + private CancellationTokenSource cancellationTokenSource; + private IBindable starDifficulty; - private readonly IBindable difficulty; + private readonly BeatmapInfo beatmapInfo; - public DifficultyColourBar(IBindable difficulty) + public DifficultyColourBar(BeatmapInfo beatmapInfo) { - this.difficulty = difficulty; + this.beatmapInfo = beatmapInfo; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(BeatmapDifficultyCache difficultyCache) { const float full_opacity_ratio = 0.7f; + cancellationTokenSource?.Cancel(); + cancellationTokenSource = new CancellationTokenSource(); + + starDifficulty?.UnbindAll(); + starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); + Children = new Drawable[] { solidDifficultyBox = new Box @@ -472,16 +471,22 @@ namespace osu.Game.Screens.Select } }; - difficulty.BindValueChanged(_ => setColour(colours), true); + starDifficulty.BindValueChanged(valueChangedEvent => setColour(valueChangedEvent), true); } - private void setColour(OsuColour colours) + private void setColour(ValueChangedEvent valueChanged) { - var difficultyColour = colours.ForDifficultyRating(difficulty.Value?.DifficultyRating ?? (new StarDifficulty()).DifficultyRating); + var difficultyColour = colours.ForDifficultyRating(valueChanged.NewValue?.DifficultyRating ?? (new StarDifficulty()).DifficultyRating); solidDifficultyBox.Colour = difficultyColour; transparentDifficultyBox.Colour = difficultyColour; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + cancellationTokenSource?.Cancel(); + } } public class InfoLabel : Container, IHasTooltip From df29e61147fd45f0f310453c19393266d10609dd Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 14:22:49 +0200 Subject: [PATCH 28/76] Fix CodeFactor error --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index e3afd53210..44dbd5a5c6 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -299,7 +299,6 @@ namespace osu.Game.Screens.Select // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); - } private void setStarRatingDisplayVisibility() From 583754b22a430dba73db9688d106dd7d734d6be4 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 14:29:53 +0200 Subject: [PATCH 29/76] Removed unnecessary whitespaces --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 44dbd5a5c6..7cb6f2caf3 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -598,7 +598,7 @@ namespace osu.Game.Screens.Select }, }, }, - }; + }; } } } From e9571b72cf149a1941717bd1a1e9a22015feca20 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 14:53:35 +0200 Subject: [PATCH 30/76] Fixed InspectCode --- osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs | 3 +-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 9 ++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 1aa8f5ca73..a3e9336648 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -1,7 +1,6 @@ // 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.Globalization; using System.Threading; using osu.Framework.Allocation; @@ -46,7 +45,7 @@ namespace osu.Game.Screens.Ranking.Expanded } /// - /// Creates a new using a to use a bindable for the difficulty. + /// Creates a new using a to use a bindable for the difficulty. /// /// The to use to create a bindable for public StarRatingDisplay(BeatmapInfo beatmapInfo) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 7cb6f2caf3..9077c115d4 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -142,11 +142,6 @@ namespace osu.Game.Screens.Select } } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - } - public class WedgeInfoText : Container { public FillFlowContainer MapperContainer { get; private set; } @@ -394,7 +389,7 @@ namespace osu.Game.Screens.Select } }; - mods.BindValueChanged(mods => refreshModInformation(mods), true); + mods.BindValueChanged(refreshModInformation, true); } private void refreshModInformation(ValueChangedEvent> modsChangedEvent) @@ -470,7 +465,7 @@ namespace osu.Game.Screens.Select } }; - starDifficulty.BindValueChanged(valueChangedEvent => setColour(valueChangedEvent), true); + starDifficulty.BindValueChanged(setColour, true); } private void setColour(ValueChangedEvent valueChanged) From de04caeace220426ed615efa6d3cabe4f76c88af Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 13:53:08 +0200 Subject: [PATCH 31/76] Fixed race condition in StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index a3e9336648..8367b1fc6d 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -28,9 +29,10 @@ namespace osu.Game.Screens.Ranking.Expanded private OsuColour colours { get; set; } private CircularContainer colorContainer; - private OsuTextFlowContainer textContainer; private CancellationTokenSource cancellationTokenSource; private IBindable bindableStarDifficulty; + private OsuSpriteText wholePartText; + private OsuSpriteText fractionPartText; private readonly StarDifficulty starDifficulty; private readonly BeatmapInfo beatmapInfo; @@ -66,24 +68,10 @@ namespace osu.Game.Screens.Ranking.Expanded colorContainer.Colour = backgroundColour; - textContainer.Text = string.Empty; + wholePartText.Text = $"{wholePart}"; + fractionPartText.Text = $"{separator}{fractionPart}"; - textContainer.With(t => - { - t.AddText($"{wholePart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - - t.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }); + } [BackgroundDependencyLoader] @@ -130,14 +118,28 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - textContainer = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, TextAnchor = Anchor.BottomLeft, - }, + }.With(t => + { + t.AddText(wholePartText = new OsuSpriteText(), s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size:14); + s.UseFullGlyphHeight = false; + }); + t.AddText(fractionPartText = new OsuSpriteText(), s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + }), } } }; From 9fba87f67ace6c74bb452d8b8720a78dfc97b82c Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 13:53:25 +0200 Subject: [PATCH 32/76] Moved Info and Background into own container --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 8 ++- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 58 +++++++++++++------ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 9c10c9751c..90bd4ceb88 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -135,15 +135,15 @@ namespace osu.Game.Tests.Visual.SongSelect private void selectBeatmap([CanBeNull] IBeatmap b) { - BeatmapInfoWedge.BufferedWedgeBackground backgroundBefore = null; + BeatmapInfoWedge.BeatmapInfoWedgeContainer containerBefore = null; AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { - backgroundBefore = infoWedge.Background; + containerBefore = infoWedge.Container; infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b); }); - AddUntilStep("wait for async load", () => infoWedge.Background != backgroundBefore); + AddUntilStep("wait for async load", () => infoWedge.Container != containerBefore); } private IBeatmap createTestBeatmap(RulesetInfo ruleset) @@ -196,6 +196,8 @@ namespace osu.Game.Tests.Visual.SongSelect public new BufferedWedgeBackground Background => base.Background; public new WedgeInfoText Info => base.Info; + + public new BeatmapInfoWedgeContainer Container => base.Container; } private class TestHitObject : ConvertHitObject, IHasPosition diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 9077c115d4..5bb7bbe9fd 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,8 +41,9 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } - protected BufferedWedgeBackground Background; - protected WedgeInfoText Info; + protected BeatmapInfoWedgeContainer Container; + protected WedgeInfoText Info => Container.Info; + protected BufferedWedgeBackground Background => Container.Background; public BeatmapInfoWedge() { @@ -94,9 +95,9 @@ namespace osu.Game.Screens.Select } } - public override bool IsPresent => base.IsPresent || Background == null; // Visibility is updated in the LoadComponentAsync callback + public override bool IsPresent => base.IsPresent || Container == null; // Visibility is updated in the LoadComponentAsync callback - private BufferedWedgeBackground loadingInfo; + private BeatmapInfoWedgeContainer loadingInfo; private void updateDisplay() { @@ -108,13 +109,9 @@ namespace osu.Game.Screens.Select { State.Value = beatmap == null ? Visibility.Hidden : Visibility.Visible; - Info?.FadeOut(250); - Info?.Expire(); - Info = null; - - Background?.FadeOut(250); - Background?.Expire(); - Background = null; + Container?.FadeOut(250); + Container?.Expire(); + Container = null; } if (beatmap == null) @@ -123,25 +120,50 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BufferedWedgeBackground(beatmap) + LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value) { Shear = -Shear, - Depth = Background?.Depth + 1 ?? 0 }, loaded => { // ensure we are the most recent loaded wedge. if (loaded != loadingInfo) return; removeOldInfo(); - Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value) - { - Shear = -Shear - }); + Add(Container = loaded); }); } } + public class BeatmapInfoWedgeContainer : Container + { + private readonly WorkingBeatmap beatmap; + private readonly RulesetInfo ruleset; + + internal BufferedWedgeBackground Background; + internal WedgeInfoText Info; + + public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) + { + this.beatmap = beatmap; + this.ruleset = ruleset; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + Background = new BufferedWedgeBackground(beatmap) + { + Depth = Background?.Depth + 1 ?? 0, + }, + Info = new WedgeInfoText(beatmap, ruleset), + }; + } + } + public class WedgeInfoText : Container { public FillFlowContainer MapperContainer { get; private set; } From d6928e91fd1046373476a3ba3a53d16ce55563b4 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 14:02:09 +0200 Subject: [PATCH 33/76] Removed BeatmapInfo in StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 45 +++++-------------- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 14 +++--- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 8367b1fc6d..ea4936bd82 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -29,13 +29,19 @@ namespace osu.Game.Screens.Ranking.Expanded private OsuColour colours { get; set; } private CircularContainer colorContainer; - private CancellationTokenSource cancellationTokenSource; - private IBindable bindableStarDifficulty; private OsuSpriteText wholePartText; private OsuSpriteText fractionPartText; + private StarDifficulty starDifficulty; - private readonly StarDifficulty starDifficulty; - private readonly BeatmapInfo beatmapInfo; + public StarDifficulty StarDifficulty + { + get => starDifficulty; + set + { + starDifficulty = value; + setDifficulty(starDifficulty); + } + } /// /// Creates a new using an already computed . @@ -46,15 +52,6 @@ namespace osu.Game.Screens.Ranking.Expanded this.starDifficulty = starDifficulty; } - /// - /// Creates a new using a to use a bindable for the difficulty. - /// - /// The to use to create a bindable for - public StarRatingDisplay(BeatmapInfo beatmapInfo) - { - this.beatmapInfo = beatmapInfo; - } - private void setDifficulty(StarDifficulty difficulty) { var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); @@ -71,21 +68,12 @@ namespace osu.Game.Screens.Ranking.Expanded wholePartText.Text = $"{wholePart}"; fractionPartText.Text = $"{separator}{fractionPart}"; - + } [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache difficultyCache) { - if (beatmapInfo != null) - { - cancellationTokenSource?.Cancel(); - cancellationTokenSource = new CancellationTokenSource(); - - bindableStarDifficulty?.UnbindAll(); - bindableStarDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); - } - AutoSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -144,16 +132,7 @@ namespace osu.Game.Screens.Ranking.Expanded } }; - if (bindableStarDifficulty != null) - bindableStarDifficulty.BindValueChanged(valueChanged => setDifficulty(valueChanged.NewValue ?? new StarDifficulty()), true); - else - setDifficulty(starDifficulty); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - cancellationTokenSource?.Cancel(); + setDifficulty(starDifficulty); } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 5bb7bbe9fd..7803b190e6 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; - private Drawable starRatingDisplay; + private StarRatingDisplay starRatingDisplay; private Container bpmLabelContainer; private ModSettingChangeTracker settingChangeTracker; private CancellationTokenSource cancellationTokenSource; @@ -246,9 +246,9 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Children = new[] + Children = new Drawable[] { - starRatingDisplay = new StarRatingDisplay(beatmapInfo) + starRatingDisplay = new StarRatingDisplay(starDifficulty.Value ?? new StarDifficulty()) { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -311,19 +311,21 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); - starDifficulty.BindValueChanged(_ => setStarRatingDisplayVisibility(), true); + starDifficulty.BindValueChanged(updateStarRatingDisplay, true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); } - private void setStarRatingDisplayVisibility() + private void updateStarRatingDisplay(ValueChangedEvent valueChanged) { - if (starDifficulty.Value.HasValue && starDifficulty.Value.Value.Stars > 0) + if (valueChanged.NewValue.HasValue && valueChanged.NewValue.Value.Stars > 0) starRatingDisplay.Show(); else starRatingDisplay.Hide(); + + starRatingDisplay.StarDifficulty = valueChanged.NewValue ?? new StarDifficulty(); } private InfoLabel[] getRulesetInfoLabels() From 0dfd0bb59d731a46419f4840883ff433b88ce5d1 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 14:16:16 +0200 Subject: [PATCH 34/76] Refactored background of BeatmapInfoWedge --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 2 - osu.Game/Screens/Select/BeatmapInfoWedge.cs | 57 +--------------- .../Select/BeatmapInfoWedgeBackground.cs | 66 +++++++++++++++++++ 3 files changed, 68 insertions(+), 57 deletions(-) create mode 100644 osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 90bd4ceb88..688cc9a035 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -193,8 +193,6 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new BufferedWedgeBackground Background => base.Background; - public new WedgeInfoText Info => base.Info; public new BeatmapInfoWedgeContainer Container => base.Container; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 7803b190e6..b499423412 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -11,7 +11,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -43,7 +42,6 @@ namespace osu.Game.Screens.Select protected BeatmapInfoWedgeContainer Container; protected WedgeInfoText Info => Container.Info; - protected BufferedWedgeBackground Background => Container.Background; public BeatmapInfoWedge() { @@ -123,6 +121,7 @@ namespace osu.Game.Screens.Select LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value) { Shear = -Shear, + Depth = Container?.Depth + 1 ?? 0, }, loaded => { // ensure we are the most recent loaded wedge. @@ -139,7 +138,6 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - internal BufferedWedgeBackground Background; internal WedgeInfoText Info; public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) @@ -155,10 +153,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - Background = new BufferedWedgeBackground(beatmap) - { - Depth = Background?.Depth + 1 ?? 0, - }, + new BeatmapInfoWedgeBackground(beatmap), Info = new WedgeInfoText(beatmap, ruleset), }; } @@ -572,53 +567,5 @@ namespace osu.Game.Screens.Select settingChangeTracker?.Dispose(); } } - - public class BufferedWedgeBackground : BufferedContainer - { - private readonly WorkingBeatmap beatmap; - - public BufferedWedgeBackground(WorkingBeatmap beatmap) - : base(pixelSnapping: true) - { - this.beatmap = beatmap; - } - - [BackgroundDependencyLoader] - private void load(LocalisationManager localisation) - { - CacheDrawnFrameBuffer = true; - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - // We will create the white-to-black gradient by modulating transparency and having - // a black backdrop. This results in an sRGB-space gradient and not linear space, - // transitioning from white to black more perceptually uniformly. - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - // We use a container, such that we can set the colour gradient to go across the - // vertices of the masked container instead of the vertices of the (larger) sprite. - new Container - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)), - Children = new[] - { - // Zoomed-in and cropped beatmap background - new BeatmapBackgroundSprite(beatmap) - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fill, - }, - }, - }, - }; - } - } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs new file mode 100644 index 0000000000..566f49a799 --- /dev/null +++ b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs @@ -0,0 +1,66 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Screens.Select +{ + internal class BeatmapInfoWedgeBackground : CompositeDrawable + { + private readonly WorkingBeatmap beatmap; + + public BeatmapInfoWedgeBackground(WorkingBeatmap beatmap) + { + this.beatmap = beatmap; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + InternalChild = new BufferedContainer(pixelSnapping: true) + { + CacheDrawnFrameBuffer = true, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + // We will create the white-to-black gradient by modulating transparency and having + // a black backdrop. This results in an sRGB-space gradient and not linear space, + // transitioning from white to black more perceptually uniformly. + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + // We use a container, such that we can set the colour gradient to go across the + // vertices of the masked container instead of the vertices of the (larger) sprite. + new Container + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)), + Children = new[] + { + // Zoomed-in and cropped beatmap background + new BeatmapBackgroundSprite(beatmap) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill, + }, + }, + }, + } + }; + } + } +} From 56a69ed95682e9096f2b8e4b518346b7a66b9308 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 15:53:28 +0200 Subject: [PATCH 35/76] Codestyle fixes --- osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index ea4936bd82..bfc336a677 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Globalization; -using System.Threading; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -67,8 +65,6 @@ namespace osu.Game.Screens.Ranking.Expanded wholePartText.Text = $"{wholePart}"; fractionPartText.Text = $"{separator}{fractionPart}"; - - } [BackgroundDependencyLoader] @@ -118,7 +114,7 @@ namespace osu.Game.Screens.Ranking.Expanded t.AddText(wholePartText = new OsuSpriteText(), s => { s.Colour = Color4.Black; - s.Font = s.Font.With(size:14); + s.Font = s.Font.With(size: 14); s.UseFullGlyphHeight = false; }); t.AddText(fractionPartText = new OsuSpriteText(), s => From 713344ebadc752b16c83bab92331af3c5850be25 Mon Sep 17 00:00:00 2001 From: Denrage Date: Fri, 23 Apr 2021 10:31:49 +0200 Subject: [PATCH 36/76] Reorganize methods --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 214 ++++++++++---------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index b499423412..cb5a276a5d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -323,6 +323,48 @@ namespace osu.Game.Screens.Select starRatingDisplay.StarDifficulty = valueChanged.NewValue ?? new StarDifficulty(); } + private void refreshModInformation(ValueChangedEvent> modsChangedEvent) + { + settingChangeTracker?.Dispose(); + settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); + refreshBPMLabel(modsChangedEvent.NewValue); + } + + private void setMetadata(string source) + { + ArtistLabel.Text = artistBinding.Value; + TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; + } + + 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() + } + }; + + mods.BindValueChanged(refreshModInformation, true); + } + private InfoLabel[] getRulesetInfoLabels() { try @@ -377,48 +419,6 @@ namespace osu.Game.Screens.Select }); } - private void setMetadata(string source) - { - ArtistLabel.Text = artistBinding.Value; - TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; - } - - 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() - } - }; - - mods.BindValueChanged(refreshModInformation, true); - } - - private void refreshModInformation(ValueChangedEvent> modsChangedEvent) - { - settingChangeTracker?.Dispose(); - settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); - refreshBPMLabel(modsChangedEvent.NewValue); - } - private OsuSpriteText[] getMapper(BeatmapMetadata metadata) { if (string.IsNullOrEmpty(metadata.Author?.Username)) @@ -439,6 +439,71 @@ 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; } + + public InfoLabel(BeatmapStatistic statistic) + { + TooltipText = statistic.Name; + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + Children = new[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"441288"), + Icon = FontAwesome.Solid.Square, + Rotation = 45, + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"f7dd55"), + Icon = FontAwesome.Regular.Circle, + Size = new Vector2(0.8f) + }, + statistic.CreateIcon().With(i => + { + i.Anchor = Anchor.Centre; + i.Origin = Anchor.Centre; + i.RelativeSizeAxes = Axes.Both; + i.Colour = Color4Extensions.FromHex(@"f7dd55"); + i.Size = new Vector2(0.64f); + }), + } + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = new Color4(255, 221, 85, 255), + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), + Margin = new MarginPadding { Left = 30 }, + Text = statistic.Content, + } + }; + } + } + private class DifficultyColourBar : Container { [Resolved] @@ -501,71 +566,6 @@ namespace osu.Game.Screens.Select cancellationTokenSource?.Cancel(); } } - - public class InfoLabel : Container, IHasTooltip - { - public string TooltipText { get; } - - public InfoLabel(BeatmapStatistic statistic) - { - TooltipText = statistic.Name; - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new Container - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20), - Children = new[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"441288"), - Icon = FontAwesome.Solid.Square, - Rotation = 45, - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"f7dd55"), - Icon = FontAwesome.Regular.Circle, - Size = new Vector2(0.8f) - }, - statistic.CreateIcon().With(i => - { - i.Anchor = Anchor.Centre; - i.Origin = Anchor.Centre; - i.RelativeSizeAxes = Axes.Both; - i.Colour = Color4Extensions.FromHex(@"f7dd55"); - i.Size = new Vector2(0.64f); - }), - } - }, - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Colour = new Color4(255, 221, 85, 255), - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), - Margin = new MarginPadding { Left = 30 }, - Text = statistic.Content, - } - }; - } - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - settingChangeTracker?.Dispose(); - } } } } From 59ae5ab913f74d75677576e10d3b3a9484a713a7 Mon Sep 17 00:00:00 2001 From: Denrage Date: Sat, 24 Apr 2021 13:25:29 +0200 Subject: [PATCH 37/76] Added transition in StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 112 +++++++++--------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index bfc336a677..748f58e430 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -27,9 +26,8 @@ namespace osu.Game.Screens.Ranking.Expanded private OsuColour colours { get; set; } private CircularContainer colorContainer; - private OsuSpriteText wholePartText; - private OsuSpriteText fractionPartText; private StarDifficulty starDifficulty; + private FillFlowContainer foregroundContainer; public StarDifficulty StarDifficulty { @@ -52,23 +50,15 @@ namespace osu.Game.Screens.Ranking.Expanded private void setDifficulty(StarDifficulty difficulty) { - var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + colorContainer.FadeColour(getDifficultyColour(difficulty), 250); - ColourInfo backgroundColour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); - - colorContainer.Colour = backgroundColour; - - wholePartText.Text = $"{wholePart}"; - fractionPartText.Text = $"{separator}{fractionPart}"; + foregroundContainer.Expire(); + foregroundContainer = null; + AddInternal(foregroundContainer = createForegroundContainer(difficulty)); } [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache difficultyCache) + private void load() { AutoSizeAxes = Axes.Both; @@ -78,6 +68,7 @@ namespace osu.Game.Screens.Ranking.Expanded { RelativeSizeAxes = Axes.Both, Masking = true, + Colour = getDifficultyColour(starDifficulty), Children = new Drawable[] { new Box @@ -86,49 +77,64 @@ namespace osu.Game.Screens.Ranking.Expanded }, } }, - new FillFlowContainer + foregroundContainer = createForegroundContainer(starDifficulty), + }; + } + + private ColourInfo getDifficultyColour(StarDifficulty difficulty) + { + return difficulty.DifficultyRating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + } + + private FillFlowContainer createForegroundContainer(StarDifficulty difficulty) + { + var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + string wholePart = starRatingParts[0]; + string fractionPart = starRatingParts[1]; + string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + + return new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2, 0), + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2, 0), - Children = new Drawable[] + new SpriteIcon { - new SpriteIcon + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(7), + Icon = FontAwesome.Solid.Star, + Colour = Color4.Black + }, + new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + TextAnchor = Anchor.BottomLeft, + }.With(t => + { + t.AddText($"{wholePart}", s => { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(7), - Icon = FontAwesome.Solid.Star, - Colour = Color4.Black - }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + t.AddText($"{separator}{fractionPart}", s => { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - TextAnchor = Anchor.BottomLeft, - }.With(t => - { - t.AddText(wholePartText = new OsuSpriteText(), s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - t.AddText(fractionPartText = new OsuSpriteText(), s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }), - } + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + }), } }; - - setDifficulty(starDifficulty); } } } From b2130fc600d8cff184ba6e51bc3ef7dbeed2f44c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 3 May 2021 01:56:32 +0300 Subject: [PATCH 38/76] Fix replay frames sort instability --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 0f25a45177..d6c9b9c6d9 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -4,7 +4,7 @@ #nullable enable using System; -using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Game.Input.Handlers; using osu.Game.Replays; @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Replays { // TODO: This replay frame ordering should be enforced on the Replay type. // Currently, the ordering can be broken if the frames are added after this construction. - replay.Frames.Sort((x, y) => x.Time.CompareTo(y.Time)); + replay.Frames = replay.Frames.OrderBy(f => f.Time).ToList(); this.replay = replay; currentFrameIndex = -1; From 943c497397245fdf1924860b951c89940e3b235e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 3 May 2021 02:02:14 +0300 Subject: [PATCH 39/76] Return back removed using --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index d6c9b9c6d9..bc8994bbe5 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Game.Input.Handlers; From 8c9cfb63013df07e0942e07fdb10918299cc034d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 14:28:35 +0900 Subject: [PATCH 40/76] Remove unsafe access to Composer.HitObjects --- .../Edit/Compose/Components/EditorBlueprintContainer.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 2a605f75d8..29dfff0e96 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -34,13 +34,6 @@ namespace osu.Game.Screens.Edit.Compose.Components [BackgroundDependencyLoader] private void load() { - // For non-pooled rulesets, hitobjects are already present in the playfield which allows the blueprints to be loaded in the async context. - if (Composer != null) - { - foreach (var obj in Composer.HitObjects) - AddBlueprintFor(obj.HitObject); - } - selectedHitObjects.BindTo(Beatmap.SelectedHitObjects); selectedHitObjects.CollectionChanged += (selectedObjects, args) => { From 3dd4b7b746ab72532ec0e5db0777edcce99afc6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:08:34 +0900 Subject: [PATCH 41/76] Fix use of non-existent word in `OsuFocusedOverlayContainer` xmldoc --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index e168f265dd..c0518247a9 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Graphics.Containers protected override bool BlockNonPositionalInput => true; /// - /// Temporary to allow for overlays in the main screen content to not dim theirselves. + /// Temporary to allow for overlays in the main screen content to not dim themselves. /// Should be eventually replaced by dimming which is aware of the target dim container (traverse parent for certain interface type?). /// protected virtual bool DimMainContent => true; From b28e1569caad7b6a1781dc32129f57a9d74cc92c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 20:09:48 +0900 Subject: [PATCH 42/76] Remove no-longer-relevant matching comment --- .../Screens/Edit/Compose/Components/EditorBlueprintContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 29dfff0e96..db322faf65 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -62,7 +62,6 @@ namespace osu.Game.Screens.Edit.Compose.Components if (Composer != null) { - // For pooled rulesets, blueprints must be added for hitobjects already "current" as they would've not been "current" during the async load addition process above. foreach (var obj in Composer.HitObjects) AddBlueprintFor(obj.HitObject); From e00af3e71d2f2f754e641698dede05e3ac089b5b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 09:45:58 +0300 Subject: [PATCH 43/76] Add test coverage --- .../NonVisual/FramedReplayInputHandlerTest.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index a42b7d54ee..2062c4b820 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; +using osu.Framework.Utils; using osu.Game.Replays; using osu.Game.Rulesets.Replays; @@ -278,6 +280,38 @@ namespace osu.Game.Tests.NonVisual setTime(-100, -100); } + [Test] + public void TestReplayFrameSortStability() + { + const double repeating_time = 5000; + + int data = 0; + + // 1. add a range of frames in which some of them have the constant time 5000, all without any "data". + // 2. randomize the frames. + // 3. assign "data" to each frame in ascending order. + replay.Frames.AddRange(Enumerable.Range(1, 250).Select(i => + { + if (RNG.NextBool()) + return new TestReplayFrame(repeating_time, true); + else + return new TestReplayFrame(i * 1000, true); + }).OrderBy(_ => RNG.Next()).Select(f => new TestReplayFrame(f.Time, true, ++data))); + + replay.HasReceivedAllFrames = true; + + // create a new handler with the replay for the frames to be sorted. + handler = new TestInputHandler(replay); + + // ensure sort stability by checking whether the "data" assigned to each time-repeated frame is in ascending order, as it was before sort. + var repeatingTimeFramesData = replay.Frames + .Cast() + .Where(f => f.Time == repeating_time) + .Select(f => f.Data); + + Assert.That(repeatingTimeFramesData, Is.Ordered.Ascending); + } + private void setReplayFrames() { replay.Frames = new List @@ -324,11 +358,13 @@ namespace osu.Game.Tests.NonVisual private class TestReplayFrame : ReplayFrame { public readonly bool IsImportant; + public readonly int Data; - public TestReplayFrame(double time, bool isImportant = false) + public TestReplayFrame(double time, bool isImportant = false, int data = 0) : base(time) { IsImportant = isImportant; + Data = data; } } From ffc88db47aa7377e78a6db933723a314a937e933 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:04:59 +0900 Subject: [PATCH 44/76] Implement Duration via the interface --- osu.Game/Storyboards/IStoryboardElementWithDuration.cs | 2 +- osu.Game/Storyboards/StoryboardSprite.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Storyboards/IStoryboardElementWithDuration.cs b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs index 02b438cb76..55f163ee07 100644 --- a/osu.Game/Storyboards/IStoryboardElementWithDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs @@ -16,6 +16,6 @@ namespace osu.Game.Storyboards /// /// The duration of the StoryboardElement. /// - double Duration { get; } + double Duration => EndTime - StartTime; } } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 0aaf264341..bf87e7d10e 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -65,8 +65,6 @@ namespace osu.Game.Storyboards } } - public double Duration => EndTime - StartTime; - public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands); private delegate void DrawablePropertyInitializer(Drawable drawable, T value); From b30145de40646e893d3123133fd46d67e52c23b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:35:50 +0900 Subject: [PATCH 45/76] Specify explicit primitive type --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1a11adad30..d424f90079 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -640,7 +640,7 @@ namespace osu.Game.Screens.Play return; } - var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; + bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) { From 4c7a4239f818662055b08f610b7ab588a3520042 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:36:05 +0900 Subject: [PATCH 46/76] Fix `AllowGameplayOverlays` potentially not working for outro skip overlay --- osu.Game/Screens/Play/Player.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d424f90079..6ed1a87d8a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -245,8 +245,6 @@ namespace osu.Game.Screens.Play HUDOverlay.ShowHud.Value = false; HUDOverlay.ShowHud.Disabled = true; BreakOverlay.Hide(); - skipIntroOverlay.Hide(); - skipOutroOverlay.Hide(); } DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting => @@ -398,7 +396,7 @@ namespace osu.Game.Screens.Play } }; - if (!Configuration.AllowSkipping) + if (!Configuration.AllowSkipping || !DrawableRuleset.AllowGameplayOverlays) { skipIntroOverlay.Expire(); skipOutroOverlay.Expire(); From b380be7169de5b4e2ca066d27fd5970a624e5cfb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:43:51 +0900 Subject: [PATCH 47/76] Add xmldoc for `updateCompletionState` --- osu.Game/Screens/Play/Player.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 6ed1a87d8a..cf26bc479a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -582,6 +582,11 @@ namespace osu.Game.Screens.Play private ScheduledDelegate completionProgressDelegate; private Task prepareScoreForDisplayTask; + /// + /// Handles changes in player state which may progress the completion of gameplay / this screen's lifetime. + /// + /// If in a state where a storyboard outro is to be played, offers the choice of skipping beyond it. + /// Thrown if this method is called more than once without changing state. private void updateCompletionState(bool skipStoryboardOutro = false) { // screen may be in the exiting transition phase. From 18779b1d1e8fa6d6b9fdb4e0d389b8b7f0d08d54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:48:13 +0900 Subject: [PATCH 48/76] Cache last event time value to avoid super expensive LINQ --- .../Drawables/DrawableStoryboard.cs | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index a9a8b8a4ac..4c42823779 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -20,6 +20,13 @@ namespace osu.Game.Storyboards.Drawables [Cached] public Storyboard Storyboard { get; } + /// + /// Whether the storyboard is considered finished. + /// + public IBindable HasStoryboardEnded => hasStoryboardEnded; + + private readonly BindableBool hasStoryboardEnded = new BindableBool(); + protected override Container Content { get; } protected override Vector2 DrawScale => new Vector2(Parent.DrawHeight / 480); @@ -40,6 +47,8 @@ namespace osu.Game.Storyboards.Drawables public override bool RemoveCompletedTransforms => false; + private double? lastEventEndTime; + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => @@ -74,6 +83,14 @@ namespace osu.Game.Storyboards.Drawables Add(layer.CreateDrawable()); } + + lastEventEndTime = Storyboard.LatestEventTime; + } + + protected override void Update() + { + base.Update(); + hasStoryboardEnded.Value = lastEventEndTime == null || Time.Current >= lastEventEndTime; } public DrawableStoryboardLayer OverlayLayer => Children.Single(layer => layer.Name == "Overlay"); @@ -83,25 +100,5 @@ namespace osu.Game.Storyboards.Drawables foreach (var layer in Children) layer.Enabled = passing ? layer.Layer.VisibleWhenPassing : layer.Layer.VisibleWhenFailing; } - - protected override void Update() - { - base.Update(); - updateHasStoryboardEnded(); - } - - /// - /// Whether the storyboard is considered finished. - /// - public IBindable HasStoryboardEnded => hasStoryboardEnded; - - private readonly BindableBool hasStoryboardEnded = new BindableBool(); - - private void updateHasStoryboardEnded() - { - hasStoryboardEnded.Value = - Storyboard.LatestEventTime == null || - Time.Current >= Storyboard.LatestEventTime; - } } } From acc9258eb217dfd04dfdc71f32498df3884caba6 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 00:59:22 -0700 Subject: [PATCH 49/76] Implement notes for settings --- osu.Game/Overlays/Settings/SettingsItem.cs | 33 +++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 0bd9750b0b..1d6535c289 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -19,6 +19,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings { @@ -36,6 +37,8 @@ namespace osu.Game.Overlays.Settings private SpriteText labelText; + private readonly OsuTextFlowContainer noteText; + public bool ShowsDefaultIndicator = true; public string TooltipText { get; set; } @@ -57,6 +60,19 @@ namespace osu.Game.Overlays.Settings } } + /// + /// Text to be displayed at the bottom of this . + /// Used for further explanation or indicating drawbacks of the current setting. + /// + public string NoteText + { + set + { + noteText.Alpha = 1; + noteText.Text = value; + } + } + public virtual Bindable Current { get => controlWithCurrent.Current; @@ -92,7 +108,16 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = Control = CreateControl() + Children = new[] + { + Control = CreateControl(), + noteText = new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0, + }, + }, }, }; @@ -108,6 +133,12 @@ namespace osu.Game.Overlays.Settings } } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + noteText.Colour = colours.Yellow; + } + private void updateDisabled() { if (labelText != null) From 796bd8e47eab8b8268bcbecae9349539b99d85a3 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 00:59:48 -0700 Subject: [PATCH 50/76] Add existing setting notes from stable --- .../Settings/Sections/Graphics/LayoutSettings.cs | 9 ++++++++- .../Sections/Graphics/RendererSettings.cs | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 4d5c2e06eb..a24dd1f64b 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -141,7 +141,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); - windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); + windowModeDropdown.Current.BindValueChanged(mode => + { + updateResolutionDropdown(); + + const string not_fullscreen_note = "Running without fullscreen mode will increase your input latency!"; + + windowModeDropdown.NoteText = mode.NewValue != WindowMode.Fullscreen ? not_fullscreen_note : string.Empty; + }, true); windowModes.BindCollectionChanged((sender, args) => { diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 8773e6763c..3ad201640d 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -13,6 +13,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { protected override string Header => "Renderer"; + private SettingsEnumDropdown frameLimiterDropdown; + [BackgroundDependencyLoader] private void load(FrameworkConfigManager config, OsuConfigManager osuConfig) { @@ -20,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Children = new Drawable[] { // TODO: this needs to be a custom dropdown at some point - new SettingsEnumDropdown + frameLimiterDropdown = new SettingsEnumDropdown { LabelText = "Frame limiter", Current = config.GetBindable(FrameworkSetting.FrameSync) @@ -37,5 +39,17 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + frameLimiterDropdown.Current.BindValueChanged(limit => + { + const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters. \"2x refresh rate\" is recommended."; + + frameLimiterDropdown.NoteText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; + }, true); + } } } From 0a649227385b82fb95787e2fd8989fae1fca258a Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 01:00:35 -0700 Subject: [PATCH 51/76] Add supporter note to background source setting --- .../UserInterface/MainMenuSettings.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 95e2e9da30..707f8cd314 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -2,8 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; +using osu.Game.Online.API; +using osu.Game.Users; namespace osu.Game.Overlays.Settings.Sections.UserInterface { @@ -11,9 +14,15 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { protected override string Header => "Main Menu"; + private IBindable user; + + private SettingsEnumDropdown backgroundSourceDropdown; + [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, IAPIProvider api) { + user = api.LocalUser.GetBoundCopy(); + Children = new Drawable[] { new SettingsCheckbox @@ -31,7 +40,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface LabelText = "Intro sequence", Current = config.GetBindable(OsuSetting.IntroSequence), }, - new SettingsEnumDropdown + backgroundSourceDropdown = new SettingsEnumDropdown { LabelText = "Background source", Current = config.GetBindable(OsuSetting.MenuBackgroundSource), @@ -43,5 +52,17 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + backgroundSourceDropdown.Current.BindValueChanged(source => + { + const string not_supporter_note = "Changes to this setting will only apply with an active osu!supporter tag."; + + backgroundSourceDropdown.NoteText = user.Value?.IsSupporter == false ? not_supporter_note : string.Empty; + }, true); + } } } From a5842130027d8e9560c9311e997c4916ebc35ba7 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 09:11:33 -0700 Subject: [PATCH 52/76] Use vertical padding instead of relative height for default button --- osu.Game/Overlays/Settings/SettingsItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 1d6535c289..6405431f6b 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -18,7 +18,6 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osuTK; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings @@ -172,6 +171,7 @@ namespace osu.Game.Overlays.Settings { RelativeSizeAxes = Axes.Y; Width = SettingsPanel.CONTENT_MARGINS; + Padding = new MarginPadding { Vertical = 1.5f }; Alpha = 0f; } @@ -194,7 +194,7 @@ namespace osu.Game.Overlays.Settings Type = EdgeEffectType.Glow, Radius = 2, }, - Size = new Vector2(0.33f, 0.8f), + Width = 0.33f, Child = new Box { RelativeSizeAxes = Axes.Both }, }; } From 4ceb9b1562e52475ba8da9652bd2d31501983ef3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 23:36:14 +0300 Subject: [PATCH 53/76] Avoid randomizing and overestimating logic with simple hardcoding Not sure what was in my mind while I was pushing that.. --- .../NonVisual/FramedReplayInputHandlerTest.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 2062c4b820..fe1186bf95 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Utils; using osu.Game.Replays; using osu.Game.Rulesets.Replays; @@ -281,26 +280,39 @@ namespace osu.Game.Tests.NonVisual } [Test] - public void TestReplayFrameSortStability() + public void TestReplayFramesSortStability() { const double repeating_time = 5000; - int data = 0; - - // 1. add a range of frames in which some of them have the constant time 5000, all without any "data". - // 2. randomize the frames. - // 3. assign "data" to each frame in ascending order. - replay.Frames.AddRange(Enumerable.Range(1, 250).Select(i => + // add a range of frames randomized in time but have a "data" assigned to them in ascending order. + replay.Frames.AddRange(new[] { - if (RNG.NextBool()) - return new TestReplayFrame(repeating_time, true); - else - return new TestReplayFrame(i * 1000, true); - }).OrderBy(_ => RNG.Next()).Select(f => new TestReplayFrame(f.Time, true, ++data))); + new TestReplayFrame(repeating_time, true, 0), + new TestReplayFrame(0, true, 1), + new TestReplayFrame(3000, true, 2), + new TestReplayFrame(repeating_time, true, 3), + new TestReplayFrame(repeating_time, true, 4), + new TestReplayFrame(6000, true, 5), + new TestReplayFrame(9000, true, 6), + new TestReplayFrame(repeating_time, true, 7), + new TestReplayFrame(repeating_time, true, 8), + new TestReplayFrame(1000, true, 9), + new TestReplayFrame(11000, true, 10), + new TestReplayFrame(21000, true, 11), + new TestReplayFrame(4000, true, 12), + new TestReplayFrame(repeating_time, true, 13), + new TestReplayFrame(repeating_time, true, 14), + new TestReplayFrame(8000, true, 15), + new TestReplayFrame(2000, true, 16), + new TestReplayFrame(7000, true, 17), + new TestReplayFrame(repeating_time, true, 18), + new TestReplayFrame(repeating_time, true, 19), + new TestReplayFrame(10000, true, 20), + }); replay.HasReceivedAllFrames = true; - // create a new handler with the replay for the frames to be sorted. + // create a new handler with the replay for the sort to be performed. handler = new TestInputHandler(replay); // ensure sort stability by checking whether the "data" assigned to each time-repeated frame is in ascending order, as it was before sort. From 45c0b74151c7f547838a98786c5859194ad3f442 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 23:41:46 +0300 Subject: [PATCH 54/76] Use LINQ select for data assigning for simplicity To avoid having to read through all of frames and ensure nothing is failing there --- .../NonVisual/FramedReplayInputHandlerTest.cs | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index fe1186bf95..a9f9dfdc83 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -284,31 +284,33 @@ namespace osu.Game.Tests.NonVisual { const double repeating_time = 5000; + int incrementingData = 0; + // add a range of frames randomized in time but have a "data" assigned to them in ascending order. replay.Frames.AddRange(new[] { - new TestReplayFrame(repeating_time, true, 0), - new TestReplayFrame(0, true, 1), - new TestReplayFrame(3000, true, 2), - new TestReplayFrame(repeating_time, true, 3), - new TestReplayFrame(repeating_time, true, 4), - new TestReplayFrame(6000, true, 5), - new TestReplayFrame(9000, true, 6), - new TestReplayFrame(repeating_time, true, 7), - new TestReplayFrame(repeating_time, true, 8), - new TestReplayFrame(1000, true, 9), - new TestReplayFrame(11000, true, 10), - new TestReplayFrame(21000, true, 11), - new TestReplayFrame(4000, true, 12), - new TestReplayFrame(repeating_time, true, 13), - new TestReplayFrame(repeating_time, true, 14), - new TestReplayFrame(8000, true, 15), - new TestReplayFrame(2000, true, 16), - new TestReplayFrame(7000, true, 17), - new TestReplayFrame(repeating_time, true, 18), - new TestReplayFrame(repeating_time, true, 19), - new TestReplayFrame(10000, true, 20), - }); + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(0, true), + new TestReplayFrame(3000, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(6000, true), + new TestReplayFrame(9000, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(1000, true), + new TestReplayFrame(11000, true), + new TestReplayFrame(21000, true), + new TestReplayFrame(4000, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(8000, true), + new TestReplayFrame(2000, true), + new TestReplayFrame(7000, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(10000, true), + }.Select(f => new TestReplayFrame(f.Time, true, incrementingData++))); replay.HasReceivedAllFrames = true; From 973475823735a94dce077f2b5e9005df9e689a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 4 May 2021 22:48:57 +0200 Subject: [PATCH 55/76] Simplify test case further --- .../NonVisual/FramedReplayInputHandlerTest.cs | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index a9f9dfdc83..a4fe2172e1 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -284,33 +284,31 @@ namespace osu.Game.Tests.NonVisual { const double repeating_time = 5000; - int incrementingData = 0; - // add a range of frames randomized in time but have a "data" assigned to them in ascending order. replay.Frames.AddRange(new[] { - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(0, true), - new TestReplayFrame(3000, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(6000, true), - new TestReplayFrame(9000, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(1000, true), - new TestReplayFrame(11000, true), - new TestReplayFrame(21000, true), - new TestReplayFrame(4000, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(8000, true), - new TestReplayFrame(2000, true), - new TestReplayFrame(7000, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(10000, true), - }.Select(f => new TestReplayFrame(f.Time, true, incrementingData++))); + repeating_time, + 0, + 3000, + repeating_time, + repeating_time, + 6000, + 9000, + repeating_time, + repeating_time, + 1000, + 11000, + 21000, + 4000, + repeating_time, + repeating_time, + 8000, + 2000, + 7000, + repeating_time, + repeating_time, + 10000 + }.Select((time, index) => new TestReplayFrame(time, true, index))); replay.HasReceivedAllFrames = true; @@ -321,7 +319,7 @@ namespace osu.Game.Tests.NonVisual var repeatingTimeFramesData = replay.Frames .Cast() .Where(f => f.Time == repeating_time) - .Select(f => f.Data); + .Select(f => f.FrameIndex); Assert.That(repeatingTimeFramesData, Is.Ordered.Ascending); } @@ -372,13 +370,13 @@ namespace osu.Game.Tests.NonVisual private class TestReplayFrame : ReplayFrame { public readonly bool IsImportant; - public readonly int Data; + public readonly int FrameIndex; - public TestReplayFrame(double time, bool isImportant = false, int data = 0) + public TestReplayFrame(double time, bool isImportant = false, int frameIndex = 0) : base(time) { IsImportant = isImportant; - Data = data; + FrameIndex = frameIndex; } } From f7d9fb094e8d764e01948a6a8104e6a5f8adc2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 4 May 2021 22:59:10 +0200 Subject: [PATCH 56/76] Reword & clarify comments --- osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index a4fe2172e1..407dec936b 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -284,7 +284,10 @@ namespace osu.Game.Tests.NonVisual { const double repeating_time = 5000; - // add a range of frames randomized in time but have a "data" assigned to them in ascending order. + // add a collection of frames in shuffled order time-wise; each frame also stores its original index to check stability later. + // data is hand-picked and breaks if the unstable List.Sort() is used. + // in theory this can still return a false-positive with another unstable algorithm if extremely unlucky, + // but there is no conceivable fool-proof way to prevent that anyways. replay.Frames.AddRange(new[] { repeating_time, @@ -315,7 +318,7 @@ namespace osu.Game.Tests.NonVisual // create a new handler with the replay for the sort to be performed. handler = new TestInputHandler(replay); - // ensure sort stability by checking whether the "data" assigned to each time-repeated frame is in ascending order, as it was before sort. + // ensure sort stability by checking that the frames with time == repeating_time are sorted in ascending frame index order themselves. var repeatingTimeFramesData = replay.Frames .Cast() .Where(f => f.Time == repeating_time) From 23b9d8c260058df9da31790b981ba50504b8aa5a Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 14:02:12 -0700 Subject: [PATCH 57/76] Fix alpha not being zero when string is set to empty and use inequality on supporter condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Settings/Sections/UserInterface/MainMenuSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 707f8cd314..b5ab6d2f60 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { const string not_supporter_note = "Changes to this setting will only apply with an active osu!supporter tag."; - backgroundSourceDropdown.NoteText = user.Value?.IsSupporter == false ? not_supporter_note : string.Empty; + backgroundSourceDropdown.NoteText = user.Value?.IsSupporter != true ? not_supporter_note : string.Empty; }, true); } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 6405431f6b..c6f17cfc23 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Settings { set { - noteText.Alpha = 1; + noteText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; noteText.Text = value; } } From 1472960319ecdd6f4627bdfd7aff9896b6bdcfe1 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Tue, 4 May 2021 21:35:36 -0400 Subject: [PATCH 58/76] Hide and disable skip outro overlay on rewind --- osu.Game/Screens/Play/Player.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cf26bc479a..88e617245b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -598,6 +598,7 @@ namespace osu.Game.Screens.Play completionProgressDelegate?.Cancel(); completionProgressDelegate = null; ValidForResume = true; + skipOutroOverlay.Hide(); return; } From 1d4a8bc0ae9df6ea3b796974953c6b903ddb1f5c Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Tue, 4 May 2021 22:23:36 -0400 Subject: [PATCH 59/76] Add visual test for rewinding --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 8326063f81..3229716b7e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -116,6 +116,15 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for score shown", () => Player.IsScoreShown); } + [Test] + public void TestStoryboardRewind() + { + CreateTest(null); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddStep("rewind", () => Player.GameplayClockContainer.Seek(-1000)); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); + } + protected override bool AllowFail => true; protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); From 6178f38c95277c91e2a07cb0766d9a33318d24aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 16:15:06 +0900 Subject: [PATCH 60/76] Reword unlimited frame rate warning a bit --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 3ad201640d..c42db1706f 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics frameLimiterDropdown.Current.BindValueChanged(limit => { - const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters. \"2x refresh rate\" is recommended."; + const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters, bad performance and overheating. It will not improve perceived latency. \"2x refresh rate\" is recommended."; frameLimiterDropdown.NoteText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; }, true); From 1288f69fada2d8457b2e21bd9341ff517ab72c58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 16:16:02 +0900 Subject: [PATCH 61/76] Rename to `WarningText` --- .../Settings/Sections/Graphics/LayoutSettings.cs | 2 +- .../Settings/Sections/Graphics/RendererSettings.cs | 2 +- .../Sections/UserInterface/MainMenuSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index a24dd1f64b..937bcc8abf 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -147,7 +147,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics const string not_fullscreen_note = "Running without fullscreen mode will increase your input latency!"; - windowModeDropdown.NoteText = mode.NewValue != WindowMode.Fullscreen ? not_fullscreen_note : string.Empty; + windowModeDropdown.WarningText = mode.NewValue != WindowMode.Fullscreen ? not_fullscreen_note : string.Empty; }, true); windowModes.BindCollectionChanged((sender, args) => diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index c42db1706f..70225ff6b8 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters, bad performance and overheating. It will not improve perceived latency. \"2x refresh rate\" is recommended."; - frameLimiterDropdown.NoteText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; + frameLimiterDropdown.WarningText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; }, true); } } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index b5ab6d2f60..7c4c88f344 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { const string not_supporter_note = "Changes to this setting will only apply with an active osu!supporter tag."; - backgroundSourceDropdown.NoteText = user.Value?.IsSupporter != true ? not_supporter_note : string.Empty; + backgroundSourceDropdown.WarningText = user.Value?.IsSupporter != true ? not_supporter_note : string.Empty; }, true); } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index c6f17cfc23..f4d7c72b7f 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Settings private SpriteText labelText; - private readonly OsuTextFlowContainer noteText; + private readonly OsuTextFlowContainer warningText; public bool ShowsDefaultIndicator = true; @@ -61,14 +61,14 @@ namespace osu.Game.Overlays.Settings /// /// Text to be displayed at the bottom of this . - /// Used for further explanation or indicating drawbacks of the current setting. + /// Generally used to recommend the user change their setting as the current one is considered sub-optimal. /// - public string NoteText + public string WarningText { set { - noteText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; - noteText.Text = value; + warningText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; + warningText.Text = value; } } @@ -110,7 +110,7 @@ namespace osu.Game.Overlays.Settings Children = new[] { Control = CreateControl(), - noteText = new OsuTextFlowContainer + warningText = new OsuTextFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -135,7 +135,7 @@ namespace osu.Game.Overlays.Settings [BackgroundDependencyLoader] private void load(OsuColour colours) { - noteText.Colour = colours.Yellow; + warningText.Colour = colours.Yellow; } private void updateDisabled() From 19ffcd00c27d04ce184f16cd55f141c803dfd011 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 16:19:06 +0900 Subject: [PATCH 62/76] Initialise warning text flow lazily as most items will not use it --- osu.Game/Overlays/Settings/SettingsItem.cs | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index f4d7c72b7f..09e458ad7e 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -36,12 +36,15 @@ namespace osu.Game.Overlays.Settings private SpriteText labelText; - private readonly OsuTextFlowContainer warningText; + private OsuTextFlowContainer warningText; public bool ShowsDefaultIndicator = true; public string TooltipText { get; set; } + [Resolved] + private OsuColour colours { get; set; } + public virtual LocalisableString LabelText { get => labelText?.Text ?? string.Empty; @@ -67,6 +70,18 @@ namespace osu.Game.Overlays.Settings { set { + if (warningText == null) + { + // construct lazily for cases where the label is not needed (may be provided by the Control). + FlowContent.Add(warningText = new OsuTextFlowContainer + { + Colour = colours.Yellow, + Margin = new MarginPadding { Bottom = 5 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }); + } + warningText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; warningText.Text = value; } @@ -110,12 +125,6 @@ namespace osu.Game.Overlays.Settings Children = new[] { Control = CreateControl(), - warningText = new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Alpha = 0, - }, }, }, }; @@ -132,12 +141,6 @@ namespace osu.Game.Overlays.Settings } } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - warningText.Colour = colours.Yellow; - } - private void updateDisabled() { if (labelText != null) From 08a45e9fc29d37b686a8430db7c37aab39894c00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 16:26:34 +0900 Subject: [PATCH 63/76] Remove dead code --- osu.Game/Overlays/Settings/SettingsItem.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 09e458ad7e..86a836d29b 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -230,12 +230,6 @@ namespace osu.Game.Overlays.Settings UpdateState(); } - public void SetButtonColour(Color4 buttonColour) - { - this.buttonColour = buttonColour; - UpdateState(); - } - public void UpdateState() => Scheduler.AddOnce(updateState); private void updateState() From 3cc9bad97968f220fc475f657f0189eb524b3f8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 17:49:33 +0900 Subject: [PATCH 64/76] Actually check for correct state of fade content in rewind test --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 3229716b7e..4138a81ebd 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Graphics; @@ -119,9 +120,16 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardRewind() { + SkipOverlay.FadeContainer fadeContainer() => Player.ChildrenOfType().First(); + CreateTest(null); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddUntilStep("skip overlay content becomes visible", () => fadeContainer().State == Visibility.Visible); + AddStep("rewind", () => Player.GameplayClockContainer.Seek(-1000)); + AddUntilStep("skip overlay content not visible", () => fadeContainer().State == Visibility.Hidden); + + AddUntilStep("skip overlay content becomes visible", () => fadeContainer().State == Visibility.Visible); AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); } From 9ec3255c505e99a826b3eba40f86f80829502562 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 17:50:25 +0900 Subject: [PATCH 65/76] Fix `SkipOverlay`'s `FadeContent` not getting correct state from parent --- osu.Game/Screens/Play/SkipOverlay.cs | 30 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index ddb78dfb67..ed49fc40b2 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -8,19 +8,19 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; using osu.Game.Screens.Ranking; using osuTK; using osuTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Containers; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Framework.Utils; -using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play { @@ -92,6 +92,18 @@ namespace osu.Game.Screens.Play private double fadeOutBeginTime => startTime - MasterGameplayClockContainer.MINIMUM_SKIP_TIME; + public override void Hide() + { + base.Hide(); + fadeContainer.Hide(); + } + + public override void Show() + { + base.Show(); + fadeContainer.Show(); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -147,7 +159,7 @@ namespace osu.Game.Screens.Play { } - private class FadeContainer : Container, IStateful + public class FadeContainer : Container, IStateful { public event Action StateChanged; @@ -170,7 +182,7 @@ namespace osu.Game.Screens.Play switch (state) { case Visibility.Visible: - // we may be triggered to become visible mnultiple times but we only want to transform once. + // we may be triggered to become visible multiple times but we only want to transform once. if (stateChanged) this.FadeIn(500, Easing.OutExpo); From 377af38d94541c48cab0d7083cae089b28916f20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 17:59:49 +0900 Subject: [PATCH 66/76] Remove unnecessary pixelSnapping parameter This was only required because there was text being rendered to the `BufferedContainer` content until now. Removing this should allow for better resolution in the background display (due to using a better minify scale mode). --- osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs index 566f49a799..0233112c69 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both; - InternalChild = new BufferedContainer(pixelSnapping: true) + InternalChild = new BufferedContainer() { CacheDrawnFrameBuffer = true, RelativeSizeAxes = Axes.Both, From 1410b8f36dce587d91b030bfe5b4736828bb074b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 18:30:57 +0900 Subject: [PATCH 67/76] Fix follow points displaying at incorrect locations when dragging a slider out-of-bounds --- .../Objects/Drawables/Connections/FollowPointConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 5541d0e790..cda4715280 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections Entry = null; } - private void onEntryInvalidated() => refreshPoints(); + private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints); private void refreshPoints() { From cf6ed7a7cf5bac2d8d8d463a1672b7318bd0b3d1 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 13:13:37 +0200 Subject: [PATCH 68/76] Refactored out changes in StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 145 +++++++----------- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 68 ++++---- .../Select/BeatmapInfoWedgeBackground.cs | 2 +- 3 files changed, 97 insertions(+), 118 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 748f58e430..f7e50fdc8a 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -22,22 +22,7 @@ namespace osu.Game.Screens.Ranking.Expanded /// public class StarRatingDisplay : CompositeDrawable { - [Resolved] - private OsuColour colours { get; set; } - - private CircularContainer colorContainer; - private StarDifficulty starDifficulty; - private FillFlowContainer foregroundContainer; - - public StarDifficulty StarDifficulty - { - get => starDifficulty; - set - { - starDifficulty = value; - setDifficulty(starDifficulty); - } - } + private readonly StarDifficulty difficulty; /// /// Creates a new using an already computed . @@ -45,94 +30,78 @@ namespace osu.Game.Screens.Ranking.Expanded /// The already computed to display the star difficulty of. public StarRatingDisplay(StarDifficulty starDifficulty) { - this.starDifficulty = starDifficulty; - } - - private void setDifficulty(StarDifficulty difficulty) - { - colorContainer.FadeColour(getDifficultyColour(difficulty), 250); - - foregroundContainer.Expire(); - foregroundContainer = null; - AddInternal(foregroundContainer = createForegroundContainer(difficulty)); + difficulty = starDifficulty; } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) { AutoSizeAxes = Axes.Both; - InternalChildren = new Drawable[] - { - colorContainer = new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Colour = getDifficultyColour(starDifficulty), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - }, - } - }, - foregroundContainer = createForegroundContainer(starDifficulty), - }; - } - - private ColourInfo getDifficultyColour(StarDifficulty difficulty) - { - return difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); - } - - private FillFlowContainer createForegroundContainer(StarDifficulty difficulty) - { var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); string wholePart = starRatingParts[0]; string fractionPart = starRatingParts[1]; string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - return new FillFlowContainer + ColourInfo backgroundColour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + + InternalChildren = new Drawable[] { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2, 0), - Children = new Drawable[] + new CircularContainer { - new SpriteIcon + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(7), - Icon = FontAwesome.Solid.Star, - Colour = Color4.Black - }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - TextAnchor = Anchor.BottomLeft, - }.With(t => - { - t.AddText($"{wholePart}", s => + new Box { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - t.AddText($"{separator}{fractionPart}", s => + RelativeSizeAxes = Axes.Both, + Colour = backgroundColour + }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2, 0), + Children = new Drawable[] + { + new SpriteIcon { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(7), + Icon = FontAwesome.Solid.Star, + Colour = Color4.Black + }, + new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + TextAnchor = Anchor.BottomLeft, + }.With(t => + { + t.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + t.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + }) + } } }; } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index cb5a276a5d..04063d5819 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -173,7 +173,7 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; - private StarRatingDisplay starRatingDisplay; + private Container topRightMetadataContainer; private Container bpmLabelContainer; private ModSettingChangeTracker settingChangeTracker; private CancellationTokenSource cancellationTokenSource; @@ -232,34 +232,15 @@ namespace osu.Game.Screens.Select }, } }, - new FillFlowContainer + topRightMetadataContainer = new Container { Name = "Topright-aligned metadata", Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Direction = FillDirection.Vertical, Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Children = new Drawable[] - { - starRatingDisplay = new StarRatingDisplay(starDifficulty.Value ?? new StarDifficulty()) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - Margin = new MarginPadding { Bottom = 5 } - }, - StatusPill = new BeatmapSetOnlineStatusPill - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - TextSize = 11, - TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, - Status = beatmapInfo.Status, - } - } + Child = createTopRightMetadataContainer(beatmapInfo, starDifficulty.Value ?? new StarDifficulty()) }, new FillFlowContainer { @@ -306,21 +287,50 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); - starDifficulty.BindValueChanged(updateStarRatingDisplay, true); + starDifficulty.BindValueChanged(updateTopRightMetadata, true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); } - private void updateStarRatingDisplay(ValueChangedEvent valueChanged) + private void updateTopRightMetadata(ValueChangedEvent valueChanged) { - if (valueChanged.NewValue.HasValue && valueChanged.NewValue.Value.Stars > 0) - starRatingDisplay.Show(); - else - starRatingDisplay.Hide(); + topRightMetadataContainer.Child.FadeOut(250); + topRightMetadataContainer.Child.Expire(); + topRightMetadataContainer.Child = createTopRightMetadataContainer(beatmap.BeatmapInfo, valueChanged.NewValue ?? new StarDifficulty()); + } - starRatingDisplay.StarDifficulty = valueChanged.NewValue ?? new StarDifficulty(); + private FillFlowContainer createTopRightMetadataContainer(BeatmapInfo beatmapInfo, StarDifficulty difficulty) + { + var container = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + }; + + if (difficulty.Stars > 0) + { + container.Add(new StarRatingDisplay(difficulty) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + Margin = new MarginPadding { Bottom = 5 } + }); + } + + container.Add(StatusPill = new BeatmapSetOnlineStatusPill + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + TextSize = 11, + TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, + Status = beatmapInfo.Status, + }); + + return container; } private void refreshModInformation(ValueChangedEvent> modsChangedEvent) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs index 0233112c69..f50fb4dc8a 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both; - InternalChild = new BufferedContainer() + InternalChild = new BufferedContainer { CacheDrawnFrameBuffer = true, RelativeSizeAxes = Axes.Both, From 4ef901d08d51e586e447f831821463150d14229c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 21:07:49 +0900 Subject: [PATCH 69/76] Remove unnecessary redirection property to `Container.Info` --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs | 4 ++-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 688cc9a035..ec19f00087 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -193,9 +193,9 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new WedgeInfoText Info => base.Info; - public new BeatmapInfoWedgeContainer Container => base.Container; + + public WedgeInfoText Info => base.Container.Info; } private class TestHitObject : ConvertHitObject, IHasPosition diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 04063d5819..c86bdc99ff 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,7 +41,6 @@ namespace osu.Game.Screens.Select private IBindable ruleset { get; set; } protected BeatmapInfoWedgeContainer Container; - protected WedgeInfoText Info => Container.Info; public BeatmapInfoWedge() { From 5049e2fbf9bf709df1f4b2614ef80c11e8f5d31e Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 15:11:38 +0200 Subject: [PATCH 70/76] Refactored out changes in DifficultyColourBar --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 66 +++++++++------------ 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index c86bdc99ff..448bf088dc 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -174,6 +174,7 @@ namespace osu.Game.Screens.Select private FillFlowContainer infoLabelContainer; private Container topRightMetadataContainer; private Container bpmLabelContainer; + private Container difficultyColourBarContainer; private ModSettingChangeTracker settingChangeTracker; private CancellationTokenSource cancellationTokenSource; private IBindable starDifficulty; @@ -206,10 +207,11 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - new DifficultyColourBar(beatmapInfo) + difficultyColourBarContainer = new Container { RelativeSizeAxes = Axes.Y, Width = 20, + Child = createDifficultyColourBar(starDifficulty.Value ?? new StarDifficulty()), }, new FillFlowContainer { @@ -286,20 +288,32 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); - starDifficulty.BindValueChanged(updateTopRightMetadata, true); + starDifficulty.BindValueChanged(updateDifficulty, true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); } - private void updateTopRightMetadata(ValueChangedEvent valueChanged) + private void updateDifficulty(ValueChangedEvent valueChanged) { + var difficulty = valueChanged.NewValue ?? new StarDifficulty(); + topRightMetadataContainer.Child.FadeOut(250); topRightMetadataContainer.Child.Expire(); - topRightMetadataContainer.Child = createTopRightMetadataContainer(beatmap.BeatmapInfo, valueChanged.NewValue ?? new StarDifficulty()); + topRightMetadataContainer.Child = createTopRightMetadataContainer(beatmap.BeatmapInfo, difficulty); + + difficultyColourBarContainer.Child.Expire(); + difficultyColourBarContainer.Child = createDifficultyColourBar(difficulty); } + private DifficultyColourBar createDifficultyColourBar(StarDifficulty difficulty) + => new DifficultyColourBar(difficulty) + { + RelativeSizeAxes = Axes.Y, + Width = 20, + }; + private FillFlowContainer createTopRightMetadataContainer(BeatmapInfo beatmapInfo, StarDifficulty difficulty) { var container = new FillFlowContainer @@ -515,64 +529,38 @@ namespace osu.Game.Screens.Select private class DifficultyColourBar : Container { - [Resolved] - private OsuColour colours { get; set; } + private readonly StarDifficulty difficulty; - private Box solidDifficultyBox; - private Box transparentDifficultyBox; - private CancellationTokenSource cancellationTokenSource; - private IBindable starDifficulty; - - private readonly BeatmapInfo beatmapInfo; - - public DifficultyColourBar(BeatmapInfo beatmapInfo) + public DifficultyColourBar(StarDifficulty difficulty) { - this.beatmapInfo = beatmapInfo; + this.difficulty = difficulty; } [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache difficultyCache) + private void load(OsuColour colours) { const float full_opacity_ratio = 0.7f; - cancellationTokenSource?.Cancel(); - cancellationTokenSource = new CancellationTokenSource(); - - starDifficulty?.UnbindAll(); - starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); + var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); Children = new Drawable[] { - solidDifficultyBox = new Box + new Box { RelativeSizeAxes = Axes.Both, + Colour = difficultyColour, Width = full_opacity_ratio, }, - transparentDifficultyBox = new Box + new Box { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, + Colour = difficultyColour, Alpha = 0.5f, X = full_opacity_ratio, Width = 1 - full_opacity_ratio, } }; - - starDifficulty.BindValueChanged(setColour, true); - } - - private void setColour(ValueChangedEvent valueChanged) - { - var difficultyColour = colours.ForDifficultyRating(valueChanged.NewValue?.DifficultyRating ?? (new StarDifficulty()).DifficultyRating); - - solidDifficultyBox.Colour = difficultyColour; - transparentDifficultyBox.Colour = difficultyColour; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - cancellationTokenSource?.Cancel(); } } } From 88506a51dd95ec070457889ad6c89b96ca777720 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 17:51:29 +0200 Subject: [PATCH 71/76] reduced complexity --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 75 ++++++++++----------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 448bf088dc..475a547c27 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -233,15 +233,35 @@ namespace osu.Game.Screens.Select }, } }, - topRightMetadataContainer = new Container + new FillFlowContainer { Name = "Topright-aligned metadata", Anchor = Anchor.TopRight, Origin = Anchor.TopRight, + Direction = FillDirection.Vertical, Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Child = createTopRightMetadataContainer(beatmapInfo, starDifficulty.Value ?? new StarDifficulty()) + Children = new Drawable[] + { + starRatingContainer = new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + Margin = new MarginPadding { Bottom = 5 } + }, + StatusPill = new BeatmapSetOnlineStatusPill + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + TextSize = 11, + TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, + Status = beatmapInfo.Status, + } + } }, new FillFlowContainer { @@ -299,51 +319,24 @@ namespace osu.Game.Screens.Select { var difficulty = valueChanged.NewValue ?? new StarDifficulty(); - topRightMetadataContainer.Child.FadeOut(250); - topRightMetadataContainer.Child.Expire(); - topRightMetadataContainer.Child = createTopRightMetadataContainer(beatmap.BeatmapInfo, difficulty); + if (starRatingContainer.Children.Count > 0) + { + starRatingContainer.Child.FadeOut(250); + starRatingContainer.Child.Expire(); + } - difficultyColourBarContainer.Child.Expire(); - difficultyColourBarContainer.Child = createDifficultyColourBar(difficulty); - } + starRatingContainer.Child = difficulty.Stars > 0 ? new StarRatingDisplay(difficulty) : Empty(); - private DifficultyColourBar createDifficultyColourBar(StarDifficulty difficulty) - => new DifficultyColourBar(difficulty) + if (difficultyColourBarContainer.Children.Count > 0) + { + difficultyColourBarContainer.Child.Expire(); + } + + difficultyColourBarContainer.Child = new DifficultyColourBar(difficulty) { RelativeSizeAxes = Axes.Y, Width = 20, }; - - private FillFlowContainer createTopRightMetadataContainer(BeatmapInfo beatmapInfo, StarDifficulty difficulty) - { - var container = new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - }; - - if (difficulty.Stars > 0) - { - container.Add(new StarRatingDisplay(difficulty) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - Margin = new MarginPadding { Bottom = 5 } - }); - } - - container.Add(StatusPill = new BeatmapSetOnlineStatusPill - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - TextSize = 11, - TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, - Status = beatmapInfo.Status, - }); - - return container; } private void refreshModInformation(ValueChangedEvent> modsChangedEvent) From 279750775848a7f6536ecbff54e69773d01c2464 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 17:56:07 +0200 Subject: [PATCH 72/76] Reorganized elements for readability --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 128 ++++++++++---------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 475a547c27..15e484e24c 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -132,39 +132,13 @@ namespace osu.Game.Screens.Select } } - public class BeatmapInfoWedgeContainer : Container - { - private readonly WorkingBeatmap beatmap; - private readonly RulesetInfo ruleset; - - internal WedgeInfoText Info; - - public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) - { - this.beatmap = beatmap; - this.ruleset = ruleset; - } - - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new BeatmapInfoWedgeBackground(beatmap), - Info = new WedgeInfoText(beatmap, ruleset), - }; - } - } - public class WedgeInfoText : Container { - public FillFlowContainer MapperContainer { get; private set; } + public OsuSpriteText VersionLabel { get; private set; } public OsuSpriteText TitleLabel { get; private set; } public OsuSpriteText ArtistLabel { get; private set; } - public OsuSpriteText VersionLabel { get; private set; } public BeatmapSetOnlineStatusPill StatusPill { get; private set; } + public FillFlowContainer MapperContainer { get; private set; } [Resolved] private IBindable> mods { get; set; } @@ -172,16 +146,17 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; - private Container topRightMetadataContainer; + private Container starRatingContainer; private Container bpmLabelContainer; private Container difficultyColourBarContainer; - private ModSettingChangeTracker settingChangeTracker; private CancellationTokenSource cancellationTokenSource; private IBindable starDifficulty; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; + private ModSettingChangeTracker settingChangeTracker; + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset) { this.beatmap = beatmap; @@ -211,7 +186,6 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Y, Width = 20, - Child = createDifficultyColourBar(starDifficulty.Value ?? new StarDifficulty()), }, new FillFlowContainer { @@ -304,8 +278,6 @@ namespace osu.Game.Screens.Select } }; - addInfoLabels(); - titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); starDifficulty.BindValueChanged(updateDifficulty, true); @@ -313,38 +285,8 @@ namespace osu.Game.Screens.Select // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); - } - private void updateDifficulty(ValueChangedEvent valueChanged) - { - var difficulty = valueChanged.NewValue ?? new StarDifficulty(); - - if (starRatingContainer.Children.Count > 0) - { - starRatingContainer.Child.FadeOut(250); - starRatingContainer.Child.Expire(); - } - - starRatingContainer.Child = difficulty.Stars > 0 ? new StarRatingDisplay(difficulty) : Empty(); - - if (difficultyColourBarContainer.Children.Count > 0) - { - difficultyColourBarContainer.Child.Expire(); - } - - difficultyColourBarContainer.Child = new DifficultyColourBar(difficulty) - { - RelativeSizeAxes = Axes.Y, - Width = 20, - }; - } - - private void refreshModInformation(ValueChangedEvent> modsChangedEvent) - { - settingChangeTracker?.Dispose(); - settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); - refreshBPMLabel(modsChangedEvent.NewValue); + addInfoLabels(); } private void setMetadata(string source) @@ -455,6 +397,38 @@ namespace osu.Game.Screens.Select }; } + private void updateDifficulty(ValueChangedEvent valueChanged) + { + var difficulty = valueChanged.NewValue ?? new StarDifficulty(); + + if (starRatingContainer.Children.Count > 0) + { + starRatingContainer.Child.FadeOut(250); + starRatingContainer.Child.Expire(); + } + + starRatingContainer.Child = difficulty.Stars > 0 ? new StarRatingDisplay(difficulty) : Empty(); + + if (difficultyColourBarContainer.Children.Count > 0) + { + difficultyColourBarContainer.Child.Expire(); + } + + difficultyColourBarContainer.Child = new DifficultyColourBar(difficulty) + { + RelativeSizeAxes = Axes.Y, + Width = 20, + }; + } + + private void refreshModInformation(ValueChangedEvent> modsChangedEvent) + { + settingChangeTracker?.Dispose(); + settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); + refreshBPMLabel(modsChangedEvent.NewValue); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -557,5 +531,31 @@ namespace osu.Game.Screens.Select } } } + + public class BeatmapInfoWedgeContainer : Container + { + private readonly WorkingBeatmap beatmap; + private readonly RulesetInfo ruleset; + + internal WedgeInfoText Info; + + public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) + { + this.beatmap = beatmap; + this.ruleset = ruleset; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new BeatmapInfoWedgeBackground(beatmap), + Info = new WedgeInfoText(beatmap, ruleset), + }; + } + } } } From bb385f425531c1efdf37fd258df9057edaf10fc9 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 18:15:59 +0200 Subject: [PATCH 73/76] Reverted difficulty and mod updates --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 117 +++++++++----------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 15e484e24c..4be7d3b0f4 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -40,6 +40,14 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } + [Resolved] + private IBindable> mods { get; set; } + + [Resolved] + private BeatmapDifficultyCache difficultyCache { get; set; } + + private IBindable beatmapDifficulty; + protected BeatmapInfoWedgeContainer Container; public BeatmapInfoWedge() @@ -80,6 +88,8 @@ namespace osu.Game.Screens.Select private WorkingBeatmap beatmap; + private CancellationTokenSource cancellationSource; + public WorkingBeatmap Beatmap { get => beatmap; @@ -88,6 +98,13 @@ namespace osu.Game.Screens.Select if (beatmap == value) return; beatmap = value; + cancellationSource?.Cancel(); + cancellationSource = new CancellationTokenSource(); + + beatmapDifficulty?.UnbindAll(); + beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); + beatmapDifficulty.BindValueChanged(_ => updateDisplay()); + updateDisplay(); } } @@ -117,7 +134,7 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value) + LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) { Shear = -Shear, Depth = Container?.Depth + 1 ?? 0, @@ -132,6 +149,12 @@ namespace osu.Game.Screens.Select } } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + cancellationSource?.Cancel(); + } + public class WedgeInfoText : Container { public OsuSpriteText VersionLabel { get; private set; } @@ -140,41 +163,32 @@ namespace osu.Game.Screens.Select public BeatmapSetOnlineStatusPill StatusPill { get; private set; } public FillFlowContainer MapperContainer { get; private set; } - [Resolved] - private IBindable> mods { get; set; } - private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; - private Container starRatingContainer; private Container bpmLabelContainer; - private Container difficultyColourBarContainer; - private CancellationTokenSource cancellationTokenSource; - private IBindable starDifficulty; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; + private readonly IReadOnlyList mods; + private readonly StarDifficulty starDifficulty; private ModSettingChangeTracker settingChangeTracker; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; + this.mods = mods; + starDifficulty = difficulty; } [BackgroundDependencyLoader] - private void load(LocalisationManager localisation, BeatmapDifficultyCache difficultyCache) + private void load(LocalisationManager localisation) { var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - cancellationTokenSource?.Cancel(); - cancellationTokenSource = new CancellationTokenSource(); - - starDifficulty?.UnbindAll(); - starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); - RelativeSizeAxes = Axes.Both; titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); @@ -182,7 +196,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - difficultyColourBarContainer = new Container + new DifficultyColourBar(starDifficulty) { RelativeSizeAxes = Axes.Y, Width = 20, @@ -216,16 +230,14 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Children = new Drawable[] + Children = new[] { - starRatingContainer = new Container + createStarRatingDisplay(starDifficulty).With(display => { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - Margin = new MarginPadding { Bottom = 5 } - }, + display.Anchor = Anchor.TopRight; + display.Origin = Anchor.TopRight; + display.Shear = -wedged_container_shear; + }), StatusPill = new BeatmapSetOnlineStatusPill { Anchor = Anchor.TopRight, @@ -280,7 +292,6 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); - starDifficulty.BindValueChanged(updateDifficulty, true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) @@ -289,6 +300,13 @@ namespace osu.Game.Screens.Select addInfoLabels(); } + private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 + ? new StarRatingDisplay(difficulty) + { + Margin = new MarginPadding { Bottom = 5 } + } + : Empty(); + private void setMetadata(string source) { ArtistLabel.Text = artistBinding.Value; @@ -320,7 +338,10 @@ namespace osu.Game.Screens.Select } }; - mods.BindValueChanged(refreshModInformation, true); + settingChangeTracker = new ModSettingChangeTracker(mods); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + + refreshBPMLabel(); } private InfoLabel[] getRulesetInfoLabels() @@ -350,7 +371,7 @@ namespace osu.Game.Screens.Select return Array.Empty(); } - private void refreshBPMLabel(IReadOnlyList mods) + private void refreshBPMLabel() { var b = beatmap.Beatmap; if (b == null) @@ -397,38 +418,6 @@ namespace osu.Game.Screens.Select }; } - private void updateDifficulty(ValueChangedEvent valueChanged) - { - var difficulty = valueChanged.NewValue ?? new StarDifficulty(); - - if (starRatingContainer.Children.Count > 0) - { - starRatingContainer.Child.FadeOut(250); - starRatingContainer.Child.Expire(); - } - - starRatingContainer.Child = difficulty.Stars > 0 ? new StarRatingDisplay(difficulty) : Empty(); - - if (difficultyColourBarContainer.Children.Count > 0) - { - difficultyColourBarContainer.Child.Expire(); - } - - difficultyColourBarContainer.Child = new DifficultyColourBar(difficulty) - { - RelativeSizeAxes = Axes.Y, - Width = 20, - }; - } - - private void refreshModInformation(ValueChangedEvent> modsChangedEvent) - { - settingChangeTracker?.Dispose(); - settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); - refreshBPMLabel(modsChangedEvent.NewValue); - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -536,13 +525,17 @@ namespace osu.Game.Screens.Select { private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; + private readonly StarDifficulty starDifficulty; + private readonly IReadOnlyList mods; internal WedgeInfoText Info; - public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) + public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset, IReadOnlyList mods, StarDifficulty difficulty) { this.beatmap = beatmap; this.ruleset = ruleset; + this.mods = mods; + starDifficulty = difficulty; } [BackgroundDependencyLoader] @@ -553,7 +546,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { new BeatmapInfoWedgeBackground(beatmap), - Info = new WedgeInfoText(beatmap, ruleset), + Info = new WedgeInfoText(beatmap, ruleset, mods, starDifficulty), }; } } From b6b9a696017ca41dd9befb1ca5b63c2fa129f008 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 18:50:49 +0200 Subject: [PATCH 74/76] Removed unnecessary class for wrapping --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 7 +-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 43 +++++-------------- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index ec19f00087..b9e92cba62 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.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; @@ -135,7 +136,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void selectBeatmap([CanBeNull] IBeatmap b) { - BeatmapInfoWedge.BeatmapInfoWedgeContainer containerBefore = null; + Container containerBefore = null; AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { @@ -193,9 +194,9 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new BeatmapInfoWedgeContainer Container => base.Container; + public new Container Container => base.Container; - public WedgeInfoText Info => base.Container.Info; + public new WedgeInfoText Info => base.Info; } private class TestHitObject : ConvertHitObject, IHasPosition diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 4be7d3b0f4..53ac97cc7d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -48,7 +48,8 @@ namespace osu.Game.Screens.Select private IBindable beatmapDifficulty; - protected BeatmapInfoWedgeContainer Container; + protected Container Container; + protected WedgeInfoText Info; public BeatmapInfoWedge() { @@ -111,7 +112,7 @@ namespace osu.Game.Screens.Select public override bool IsPresent => base.IsPresent || Container == null; // Visibility is updated in the LoadComponentAsync callback - private BeatmapInfoWedgeContainer loadingInfo; + private Container loadingInfo; private void updateDisplay() { @@ -134,10 +135,16 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) + LoadComponentAsync(loadingInfo = new Container { + RelativeSizeAxes = Axes.Both, Shear = -Shear, Depth = Container?.Depth + 1 ?? 0, + Children = new Drawable[] + { + new BeatmapInfoWedgeBackground(beatmap), + Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()), + } }, loaded => { // ensure we are the most recent loaded wedge. @@ -520,35 +527,5 @@ namespace osu.Game.Screens.Select } } } - - public class BeatmapInfoWedgeContainer : Container - { - private readonly WorkingBeatmap beatmap; - private readonly RulesetInfo ruleset; - private readonly StarDifficulty starDifficulty; - private readonly IReadOnlyList mods; - - internal WedgeInfoText Info; - - public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset, IReadOnlyList mods, StarDifficulty difficulty) - { - this.beatmap = beatmap; - this.ruleset = ruleset; - this.mods = mods; - starDifficulty = difficulty; - } - - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new BeatmapInfoWedgeBackground(beatmap), - Info = new WedgeInfoText(beatmap, ruleset, mods, starDifficulty), - }; - } - } } } From fe9ade6754360eb1e36c41f46c7d386d9b8565c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 May 2021 02:14:04 +0900 Subject: [PATCH 75/76] Rename Container to DisplayedContent --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 6 +++--- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 15 ++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index b9e92cba62..67c85a1120 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -140,11 +140,11 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { - containerBefore = infoWedge.Container; + containerBefore = infoWedge.DisplayedContent; infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b); }); - AddUntilStep("wait for async load", () => infoWedge.Container != containerBefore); + AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore); } private IBeatmap createTestBeatmap(RulesetInfo ruleset) @@ -194,7 +194,7 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new Container Container => base.Container; + public new Container DisplayedContent => base.DisplayedContent; public new WedgeInfoText Info => base.Info; } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 53ac97cc7d..d84052b94d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -48,7 +48,8 @@ namespace osu.Game.Screens.Select private IBindable beatmapDifficulty; - protected Container Container; + protected Container DisplayedContent; + protected WedgeInfoText Info; public BeatmapInfoWedge() @@ -110,7 +111,7 @@ namespace osu.Game.Screens.Select } } - public override bool IsPresent => base.IsPresent || Container == null; // Visibility is updated in the LoadComponentAsync callback + public override bool IsPresent => base.IsPresent || DisplayedContent == null; // Visibility is updated in the LoadComponentAsync callback private Container loadingInfo; @@ -124,9 +125,9 @@ namespace osu.Game.Screens.Select { State.Value = beatmap == null ? Visibility.Hidden : Visibility.Visible; - Container?.FadeOut(250); - Container?.Expire(); - Container = null; + DisplayedContent?.FadeOut(250); + DisplayedContent?.Expire(); + DisplayedContent = null; } if (beatmap == null) @@ -139,7 +140,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, Shear = -Shear, - Depth = Container?.Depth + 1 ?? 0, + Depth = DisplayedContent?.Depth + 1 ?? 0, Children = new Drawable[] { new BeatmapInfoWedgeBackground(beatmap), @@ -151,7 +152,7 @@ namespace osu.Game.Screens.Select if (loaded != loadingInfo) return; removeOldInfo(); - Add(Container = loaded); + Add(DisplayedContent = loaded); }); } } From cffeb8641f484a860580bbcce38f977563e68674 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 May 2021 02:14:57 +0900 Subject: [PATCH 76/76] Make setters private for protected containers --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index d84052b94d..18615d9192 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -48,9 +48,9 @@ namespace osu.Game.Screens.Select private IBindable beatmapDifficulty; - protected Container DisplayedContent; + protected Container DisplayedContent { get; private set; } - protected WedgeInfoText Info; + protected WedgeInfoText Info { get; private set; } public BeatmapInfoWedge() {