From 9be36230f9bd5825aafc5b5f664480d76bdf43fb Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 May 2021 21:43:30 +0900 Subject: [PATCH 01/20] Introduce AutoGenerator subclass for frame based replay generation --- osu.Game/Rulesets/Replays/AutoGenerator.cs | 27 +++++------- .../Rulesets/Replays/FramedAutoGenerator.cs | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Rulesets/Replays/FramedAutoGenerator.cs diff --git a/osu.Game/Rulesets/Replays/AutoGenerator.cs b/osu.Game/Rulesets/Replays/AutoGenerator.cs index b3c609f2f4..4bdd5b094d 100644 --- a/osu.Game/Rulesets/Replays/AutoGenerator.cs +++ b/osu.Game/Rulesets/Replays/AutoGenerator.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; @@ -7,34 +7,27 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Replays { - public abstract class AutoGenerator : IAutoGenerator + public abstract class AutoGenerator { /// - /// Creates the auto replay and returns it. - /// Every subclass of OsuAutoGeneratorBase should implement this! + /// The default duration of a key press in milliseconds. /// - public abstract Replay Generate(); - - #region Parameters + public const double KEY_UP_DELAY = 50; /// - /// The beatmap we're making. + /// The beatmap the autoplay is generated for. /// - protected IBeatmap Beatmap; - - #endregion + protected IBeatmap Beatmap { get; } protected AutoGenerator(IBeatmap beatmap) { Beatmap = beatmap; } - #region Constants - - // Shared amongst all modes - public const double KEY_UP_DELAY = 50; - - #endregion + /// + /// Generate the replay of the autoplay. + /// + public abstract Replay Generate(); protected virtual HitObject GetNextObject(int currentIndex) { diff --git a/osu.Game/Rulesets/Replays/FramedAutoGenerator.cs b/osu.Game/Rulesets/Replays/FramedAutoGenerator.cs new file mode 100644 index 0000000000..091eb00232 --- /dev/null +++ b/osu.Game/Rulesets/Replays/FramedAutoGenerator.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Replays; + +namespace osu.Game.Rulesets.Replays +{ + public abstract class FramedAutoGenerator : AutoGenerator + where TFrame : ReplayFrame + { + /// + /// The replay frames of the autoplay. + /// + protected readonly List Frames = new List(); + + protected TFrame? LastFrame => Frames.Count == 0 ? null : Frames[^1]; + + protected FramedAutoGenerator(IBeatmap beatmap) + : base(beatmap) + { + } + + public sealed override Replay Generate() + { + Frames.Clear(); + GenerateFrames(); + + return new Replay + { + Frames = Frames.OrderBy(frame => frame.Time).Cast().ToList() + }; + } + + /// + /// Generate the replay frames of the autoplay and populate . + /// + protected abstract void GenerateFrames(); + } +} From ea35b72436d6c38a477d3ae3e8dfb11b15653248 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 May 2021 22:56:21 +0900 Subject: [PATCH 02/20] Remove unused IAutoGenerator interface --- osu.Game/Rulesets/Replays/IAutoGenerator.cs | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 osu.Game/Rulesets/Replays/IAutoGenerator.cs diff --git a/osu.Game/Rulesets/Replays/IAutoGenerator.cs b/osu.Game/Rulesets/Replays/IAutoGenerator.cs deleted file mode 100644 index b1905e2b6f..0000000000 --- a/osu.Game/Rulesets/Replays/IAutoGenerator.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Replays; - -namespace osu.Game.Rulesets.Replays -{ - public interface IAutoGenerator - { - Replay Generate(); - } -} From cf39178099c9178e5ef9136bfdabc6dbcdf5cc02 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 May 2021 21:53:34 +0900 Subject: [PATCH 03/20] Use FramedAutoGenerator in Taiko, Catch, Mania OsuAutoGenerator is not included in this change because it uses SortedList-like thing --- .../Replays/CatchAutoGenerator.cs | 20 ++++--------------- .../Replays/ManiaAutoGenerator.cs | 15 ++++---------- .../Replays/TaikoAutoGenerator.cs | 14 +++---------- 3 files changed, 11 insertions(+), 38 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 10230b6b78..2e1e4f7e77 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Replays; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; @@ -13,26 +12,19 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Catch.Replays { - internal class CatchAutoGenerator : AutoGenerator + internal class CatchAutoGenerator : FramedAutoGenerator { - public const double RELEASE_DELAY = 20; - public new CatchBeatmap Beatmap => (CatchBeatmap)base.Beatmap; public CatchAutoGenerator(IBeatmap beatmap) : base(beatmap) { - Replay = new Replay(); } - protected Replay Replay; - - private CatchReplayFrame currentFrame; - - public override Replay Generate() + protected override void GenerateFrames() { if (Beatmap.HitObjects.Count == 0) - return Replay; + return; // todo: add support for HT DT const double dash_speed = Catcher.BASE_SPEED; @@ -119,15 +111,11 @@ namespace osu.Game.Rulesets.Catch.Replays } } } - - return Replay; } private void addFrame(double time, float? position = null, bool dashing = false) { - var last = currentFrame; - currentFrame = new CatchReplayFrame(time, position, dashing, last); - Replay.Frames.Add(currentFrame); + Frames.Add(new CatchReplayFrame(time, position, dashing, LastFrame)); } } } diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index ada84dfac2..4fd105025c 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using osu.Game.Replays; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; @@ -11,7 +10,7 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Mania.Replays { - internal class ManiaAutoGenerator : AutoGenerator + internal class ManiaAutoGenerator : FramedAutoGenerator { public const double RELEASE_DELAY = 20; @@ -22,8 +21,6 @@ namespace osu.Game.Rulesets.Mania.Replays public ManiaAutoGenerator(ManiaBeatmap beatmap) : base(beatmap) { - Replay = new Replay(); - columnActions = new ManiaAction[Beatmap.TotalColumns]; var normalAction = ManiaAction.Key1; @@ -43,12 +40,10 @@ namespace osu.Game.Rulesets.Mania.Replays } } - protected Replay Replay; - - public override Replay Generate() + protected override void GenerateFrames() { if (Beatmap.HitObjects.Count == 0) - return Replay; + return; var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); @@ -70,10 +65,8 @@ namespace osu.Game.Rulesets.Mania.Replays } } - Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray())); + Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray())); } - - return Replay; } private IEnumerable generateActionPoints() diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index fa0134aa94..bd2b7338de 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -2,10 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Replays; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Taiko.Beatmaps; @@ -13,7 +11,7 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Taiko.Replays { - public class TaikoAutoGenerator : AutoGenerator + public class TaikoAutoGenerator : FramedAutoGenerator { public new TaikoBeatmap Beatmap => (TaikoBeatmap)base.Beatmap; @@ -22,16 +20,12 @@ namespace osu.Game.Rulesets.Taiko.Replays public TaikoAutoGenerator(IBeatmap beatmap) : base(beatmap) { - Replay = new Replay(); } - protected Replay Replay; - protected List Frames => Replay.Frames; - - public override Replay Generate() + protected override void GenerateFrames() { if (Beatmap.HitObjects.Count == 0) - return Replay; + return; bool hitButton = true; @@ -128,8 +122,6 @@ namespace osu.Game.Rulesets.Taiko.Replays hitButton = !hitButton; } - - return Replay; } } } From 95c74c906a33084142a71bd16b58d4063f237bfb Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 May 2021 21:57:03 +0900 Subject: [PATCH 04/20] Use FramedAutoGenerator in template projects --- .../Replays/EmptyFreeformAutoGenerator.cs | 12 ++---------- .../Replays/PippidonAutoGenerator.cs | 12 ++---------- .../Replays/EmptyScrollingAutoGenerator.cs | 12 ++---------- .../Replays/PippidonAutoGenerator.cs | 12 ++---------- 4 files changed, 8 insertions(+), 40 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Replays/EmptyFreeformAutoGenerator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Replays/EmptyFreeformAutoGenerator.cs index 6d8d4215a2..5d61136f54 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Replays/EmptyFreeformAutoGenerator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Replays/EmptyFreeformAutoGenerator.cs @@ -1,28 +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.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Replays; using osu.Game.Rulesets.EmptyFreeform.Objects; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.EmptyFreeform.Replays { - public class EmptyFreeformAutoGenerator : AutoGenerator + public class EmptyFreeformAutoGenerator : FramedAutoGenerator { - protected Replay Replay; - protected List Frames => Replay.Frames; - public new Beatmap Beatmap => (Beatmap)base.Beatmap; public EmptyFreeformAutoGenerator(IBeatmap beatmap) : base(beatmap) { - Replay = new Replay(); } - public override Replay Generate() + protected override void GenerateFrames() { Frames.Add(new EmptyFreeformReplayFrame()); @@ -35,8 +29,6 @@ namespace osu.Game.Rulesets.EmptyFreeform.Replays // todo: add required inputs and extra frames. }); } - - return Replay; } } } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs index 9c54b82e38..f795d7ef21 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs @@ -1,28 +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.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Replays; using osu.Game.Rulesets.Pippidon.Objects; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Pippidon.Replays { - public class PippidonAutoGenerator : AutoGenerator + public class PippidonAutoGenerator : FramedAutoGenerator { - protected Replay Replay; - protected List Frames => Replay.Frames; - public new Beatmap Beatmap => (Beatmap)base.Beatmap; public PippidonAutoGenerator(IBeatmap beatmap) : base(beatmap) { - Replay = new Replay(); } - public override Replay Generate() + protected override void GenerateFrames() { Frames.Add(new PippidonReplayFrame()); @@ -34,8 +28,6 @@ namespace osu.Game.Rulesets.Pippidon.Replays Position = hitObject.Position, }); } - - return Replay; } } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Replays/EmptyScrollingAutoGenerator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Replays/EmptyScrollingAutoGenerator.cs index 7923918842..ab27fd4dcd 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Replays/EmptyScrollingAutoGenerator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Replays/EmptyScrollingAutoGenerator.cs @@ -1,28 +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.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Replays; using osu.Game.Rulesets.EmptyScrolling.Objects; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.EmptyScrolling.Replays { - public class EmptyScrollingAutoGenerator : AutoGenerator + public class EmptyScrollingAutoGenerator : FramedAutoGenerator { - protected Replay Replay; - protected List Frames => Replay.Frames; - public new Beatmap Beatmap => (Beatmap)base.Beatmap; public EmptyScrollingAutoGenerator(IBeatmap beatmap) : base(beatmap) { - Replay = new Replay(); } - public override Replay Generate() + protected override void GenerateFrames() { Frames.Add(new EmptyScrollingReplayFrame()); @@ -34,8 +28,6 @@ namespace osu.Game.Rulesets.EmptyScrolling.Replays // todo: add required inputs and extra frames. }); } - - return Replay; } } } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs index bd99cdcdbd..df2cfb8731 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs @@ -2,29 +2,23 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Replays; using osu.Game.Rulesets.Pippidon.Objects; using osu.Game.Rulesets.Pippidon.UI; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Pippidon.Replays { - public class PippidonAutoGenerator : AutoGenerator + public class PippidonAutoGenerator : FramedAutoGenerator { - protected Replay Replay; - protected List Frames => Replay.Frames; - public new Beatmap Beatmap => (Beatmap)base.Beatmap; public PippidonAutoGenerator(IBeatmap beatmap) : base(beatmap) { - Replay = new Replay(); } - public override Replay Generate() + protected override void GenerateFrames() { int currentLane = 0; @@ -55,8 +49,6 @@ namespace osu.Game.Rulesets.Pippidon.Replays currentLane = hitObject.Lane; } - - return Replay; } private void addFrame(double time, PippidonAction direction) From 207f7f1e563ce252eb88affc19cdd09544d80212 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 7 May 2021 00:31:12 +0900 Subject: [PATCH 05/20] Rename FramedAutoGenerator -> AutoGenerator --- .../Replays/EmptyFreeformAutoGenerator.cs | 2 +- .../Replays/PippidonAutoGenerator.cs | 2 +- .../Replays/EmptyScrollingAutoGenerator.cs | 2 +- .../Replays/PippidonAutoGenerator.cs | 2 +- .../Replays/CatchAutoGenerator.cs | 2 +- .../Replays/ManiaAutoGenerator.cs | 2 +- .../Replays/TaikoAutoGenerator.cs | 2 +- osu.Game/Rulesets/Replays/AutoGenerator.cs | 36 +++++++++++++++ .../Rulesets/Replays/FramedAutoGenerator.cs | 44 ------------------- 9 files changed, 43 insertions(+), 51 deletions(-) delete mode 100644 osu.Game/Rulesets/Replays/FramedAutoGenerator.cs diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Replays/EmptyFreeformAutoGenerator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Replays/EmptyFreeformAutoGenerator.cs index 5d61136f54..62f394d1ce 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Replays/EmptyFreeformAutoGenerator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Replays/EmptyFreeformAutoGenerator.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.EmptyFreeform.Replays { - public class EmptyFreeformAutoGenerator : FramedAutoGenerator + public class EmptyFreeformAutoGenerator : AutoGenerator { public new Beatmap Beatmap => (Beatmap)base.Beatmap; diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs index f795d7ef21..612288257d 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Pippidon.Replays { - public class PippidonAutoGenerator : FramedAutoGenerator + public class PippidonAutoGenerator : AutoGenerator { public new Beatmap Beatmap => (Beatmap)base.Beatmap; diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Replays/EmptyScrollingAutoGenerator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Replays/EmptyScrollingAutoGenerator.cs index ab27fd4dcd..1058f756f3 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Replays/EmptyScrollingAutoGenerator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Replays/EmptyScrollingAutoGenerator.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.EmptyScrolling.Replays { - public class EmptyScrollingAutoGenerator : FramedAutoGenerator + public class EmptyScrollingAutoGenerator : AutoGenerator { public new Beatmap Beatmap => (Beatmap)base.Beatmap; diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs index df2cfb8731..724026273d 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Replays/PippidonAutoGenerator.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Pippidon.Replays { - public class PippidonAutoGenerator : FramedAutoGenerator + public class PippidonAutoGenerator : AutoGenerator { public new Beatmap Beatmap => (Beatmap)base.Beatmap; diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 2e1e4f7e77..a81703119a 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Catch.Replays { - internal class CatchAutoGenerator : FramedAutoGenerator + internal class CatchAutoGenerator : AutoGenerator { public new CatchBeatmap Beatmap => (CatchBeatmap)base.Beatmap; diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 4fd105025c..517b708691 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Mania.Replays { - internal class ManiaAutoGenerator : FramedAutoGenerator + internal class ManiaAutoGenerator : AutoGenerator { public const double RELEASE_DELAY = 20; diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index bd2b7338de..5fd281f9fa 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Taiko.Replays { - public class TaikoAutoGenerator : FramedAutoGenerator + public class TaikoAutoGenerator : AutoGenerator { public new TaikoBeatmap Beatmap => (TaikoBeatmap)base.Beatmap; diff --git a/osu.Game/Rulesets/Replays/AutoGenerator.cs b/osu.Game/Rulesets/Replays/AutoGenerator.cs index 4bdd5b094d..6ce857b14b 100644 --- a/osu.Game/Rulesets/Replays/AutoGenerator.cs +++ b/osu.Game/Rulesets/Replays/AutoGenerator.cs @@ -1,6 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; using osu.Game.Beatmaps; using osu.Game.Replays; using osu.Game.Rulesets.Objects; @@ -37,4 +40,37 @@ namespace osu.Game.Rulesets.Replays return Beatmap.HitObjects[currentIndex + 1]; } } + + public abstract class AutoGenerator : AutoGenerator + where TFrame : ReplayFrame + { + /// + /// The replay frames of the autoplay. + /// + protected readonly List Frames = new List(); + + [CanBeNull] + protected TFrame LastFrame => Frames.Count == 0 ? null : Frames[^1]; + + protected AutoGenerator(IBeatmap beatmap) + : base(beatmap) + { + } + + public sealed override Replay Generate() + { + Frames.Clear(); + GenerateFrames(); + + return new Replay + { + Frames = Frames.OrderBy(frame => frame.Time).Cast().ToList() + }; + } + + /// + /// Generate the replay frames of the autoplay and populate . + /// + protected abstract void GenerateFrames(); + } } diff --git a/osu.Game/Rulesets/Replays/FramedAutoGenerator.cs b/osu.Game/Rulesets/Replays/FramedAutoGenerator.cs deleted file mode 100644 index 091eb00232..0000000000 --- a/osu.Game/Rulesets/Replays/FramedAutoGenerator.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable enable - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Replays; - -namespace osu.Game.Rulesets.Replays -{ - public abstract class FramedAutoGenerator : AutoGenerator - where TFrame : ReplayFrame - { - /// - /// The replay frames of the autoplay. - /// - protected readonly List Frames = new List(); - - protected TFrame? LastFrame => Frames.Count == 0 ? null : Frames[^1]; - - protected FramedAutoGenerator(IBeatmap beatmap) - : base(beatmap) - { - } - - public sealed override Replay Generate() - { - Frames.Clear(); - GenerateFrames(); - - return new Replay - { - Frames = Frames.OrderBy(frame => frame.Time).Cast().ToList() - }; - } - - /// - /// Generate the replay frames of the autoplay and populate . - /// - protected abstract void GenerateFrames(); - } -} From 755588258e90883f22a89cee219d6e44373b5887 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 May 2021 16:56:24 +0900 Subject: [PATCH 06/20] Update `HealthDisplay` components to use DI to attach data source --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 35 ++++++++++++++++--- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 5 ++- .../TestSceneSkinEditorMultipleSkins.cs | 3 +- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- .../TestSceneSkinnableHealthDisplay.cs | 21 ++++++----- .../Screens/Play/HUD/DefaultHealthDisplay.cs | 2 +- osu.Game/Screens/Play/HUD/FailingLayer.cs | 11 +----- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 33 ++++++++++++----- osu.Game/Screens/Play/HUD/IHealthDisplay.cs | 26 -------------- .../Play/HUD/SkinnableHealthDisplay.cs | 15 +------- osu.Game/Screens/Play/HUDOverlay.cs | 22 +----------- osu.Game/Screens/Play/Player.cs | 4 ++- osu.Game/Skinning/LegacyHealthDisplay.cs | 12 ++----- 13 files changed, 81 insertions(+), 110 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/IHealthDisplay.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index 5a1a9d3d87..e426726def 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; @@ -22,22 +23,30 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public void SetUpSteps() + { + } + + private void create(HealthProcessor healthProcessor) { AddStep("create layer", () => { - Child = layer = new FailingLayer(); - layer.BindHealthProcessor(new DrainingHealthProcessor(1)); + Child = new HealthProcessorContainer(healthProcessor) + { + Child = layer = new FailingLayer() + }; + layer.ShowHealth.BindTo(showHealth); }); AddStep("show health", () => showHealth.Value = true); AddStep("enable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true)); - AddUntilStep("layer is visible", () => layer.IsPresent); } [Test] public void TestLayerFading() { + create(new DrainingHealthProcessor(1)); + AddSliderStep("current health", 0.0, 1.0, 1.0, val => { if (layer != null) @@ -53,6 +62,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerDisabledViaConfig() { + create(new DrainingHealthProcessor(1)); + AddUntilStep("layer is visible", () => layer.IsPresent); AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false)); AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddUntilStep("layer is not visible", () => !layer.IsPresent); @@ -61,7 +72,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerVisibilityWithAccumulatingProcessor() { - AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new AccumulatingHealthProcessor(1))); + create(new AccumulatingHealthProcessor(1)); + AddUntilStep("layer is not visible", () => !layer.IsPresent); AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddUntilStep("layer is not visible", () => !layer.IsPresent); } @@ -69,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerVisibilityWithDrainingProcessor() { - AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new DrainingHealthProcessor(1))); + create(new DrainingHealthProcessor(1)); AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddWaitStep("wait for potential fade", 10); AddAssert("layer is still visible", () => layer.IsPresent); @@ -78,6 +90,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerVisibilityWithDifferentOptions() { + create(new DrainingHealthProcessor(1)); + AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddStep("don't show health", () => showHealth.Value = false); @@ -96,5 +110,16 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true)); AddUntilStep("layer fade is visible", () => layer.IsPresent); } + + private class HealthProcessorContainer : Container + { + [Cached(typeof(HealthProcessor))] + private readonly HealthProcessor healthProcessor; + + public HealthProcessorContainer(HealthProcessor healthProcessor) + { + this.healthProcessor = healthProcessor; + } + } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 55c681b605..a451df026b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -23,6 +23,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); + [Cached] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(1); + // best way to check without exposing. private Drawable hideTarget => hudOverlay.KeyCounter; private FillFlowContainer keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().First(); @@ -143,7 +146,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create overlay", () => { - hudOverlay = new HUDOverlay(scoreProcessor, null, null, Array.Empty()); + hudOverlay = new HUDOverlay(scoreProcessor, null, Array.Empty()); // Add any key just to display the key counter visually. hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 086bcb19c3..270216564f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); - var hudOverlay = new HUDOverlay(scoreProcessor, null, drawableRuleset, Array.Empty()) + var hudOverlay = new HUDOverlay(scoreProcessor, drawableRuleset, Array.Empty()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -40,7 +40,6 @@ namespace osu.Game.Tests.Visual.Gameplay // Add any key just to display the key counter visually. hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); - hudOverlay.ComboCounter.Current.Value = 1; return new Container { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 8131c77b4b..12ce20f58e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay { SetContents(() => { - hudOverlay = new HUDOverlay(scoreProcessor, null, null, Array.Empty()); + hudOverlay = new HUDOverlay(scoreProcessor, null, Array.Empty()); // Add any key just to display the key counter visually. hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index 5bac8582d7..f06236d0a8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -4,11 +4,13 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Gameplay @@ -19,6 +21,9 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + [Cached] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + [SetUpSteps] public void SetUpSteps() { @@ -28,8 +33,7 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddStep(@"Reset all", delegate { - foreach (var s in healthDisplays) - s.Current.Value = 1; + healthProcessor.Health.Value = 1; }); } @@ -38,23 +42,18 @@ namespace osu.Game.Tests.Visual.Gameplay { AddRepeatStep(@"decrease hp", delegate { - foreach (var healthDisplay in healthDisplays) - healthDisplay.Current.Value -= 0.08f; + healthProcessor.Health.Value = 0.08f; }, 10); AddRepeatStep(@"increase hp without flash", delegate { - foreach (var healthDisplay in healthDisplays) - healthDisplay.Current.Value += 0.1f; + healthProcessor.Health.Value = 0.1f; }, 3); AddRepeatStep(@"increase hp with flash", delegate { - foreach (var healthDisplay in healthDisplays) - { - healthDisplay.Current.Value += 0.1f; - healthDisplay.Flash(new JudgementResult(null, new OsuJudgement())); - } + healthProcessor.Health.Value = 0.1f; + healthProcessor.ApplyResult(new JudgementResult(null, new OsuJudgement())); }, 3); } } diff --git a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs index e3cd71691d..27f81467cb 100644 --- a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs @@ -107,7 +107,7 @@ namespace osu.Game.Screens.Play.HUD GlowColour = colours.BlueDarker; } - public override void Flash(JudgementResult result) => Scheduler.AddOnce(flash); + protected override void Flash(JudgementResult result) => Scheduler.AddOnce(flash); private void flash() { diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 847b8a53cf..e071337f34 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -39,7 +39,6 @@ namespace osu.Game.Screens.Play.HUD private readonly Container boxes; private Bindable fadePlayfieldWhenHealthLow; - private HealthProcessor healthProcessor; public FailingLayer() { @@ -88,18 +87,10 @@ namespace osu.Game.Screens.Play.HUD updateState(); } - public override void BindHealthProcessor(HealthProcessor processor) - { - base.BindHealthProcessor(processor); - - healthProcessor = processor; - updateState(); - } - private void updateState() { // Don't display ever if the ruleset is not using a draining health display. - var showLayer = healthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value; + var showLayer = HealthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value; this.FadeTo(showLayer ? 1 : 0, fade_time, Easing.OutQuint); } diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 5c43e00192..2292f64989 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; @@ -11,26 +12,42 @@ namespace osu.Game.Screens.Play.HUD { /// /// A container for components displaying the current player health. - /// Gets bound automatically to the when inserted to hierarchy. + /// Gets bound automatically to the when inserted to hierarchy. /// - public abstract class HealthDisplay : Container, IHealthDisplay + public abstract class HealthDisplay : Container { + [Resolved] + protected HealthProcessor HealthProcessor { get; private set; } + public Bindable Current { get; } = new BindableDouble(1) { MinValue = 0, MaxValue = 1 }; - public virtual void Flash(JudgementResult result) + protected virtual void Flash(JudgementResult result) { } - /// - /// Bind the tracked fields of to this health display. - /// - public virtual void BindHealthProcessor(HealthProcessor processor) + [BackgroundDependencyLoader] + private void load() { - Current.BindTo(processor.Health); + Current.BindTo(HealthProcessor.Health); + + HealthProcessor.NewJudgement += onNewJudgement; + } + + private void onNewJudgement(JudgementResult judgement) + { + if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit) Flash(judgement); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (HealthProcessor != null) + HealthProcessor.NewJudgement -= onNewJudgement; } } } diff --git a/osu.Game/Screens/Play/HUD/IHealthDisplay.cs b/osu.Game/Screens/Play/HUD/IHealthDisplay.cs deleted file mode 100644 index b1a64bd844..0000000000 --- a/osu.Game/Screens/Play/HUD/IHealthDisplay.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Judgements; - -namespace osu.Game.Screens.Play.HUD -{ - /// - /// An interface providing a set of methods to update a health display. - /// - public interface IHealthDisplay : IDrawable - { - /// - /// The current health to be displayed. - /// - Bindable Current { get; } - - /// - /// Flash the display for a specified result type. - /// - /// The result type. - void Flash(JudgementResult result); - } -} diff --git a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs index 1f91f5e50f..ef0affa417 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs @@ -3,13 +3,12 @@ using System; using osu.Framework.Bindables; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableHealthDisplay : SkinnableDrawable, IHealthDisplay + public class SkinnableHealthDisplay : SkinnableDrawable { public Bindable Current { get; } = new BindableDouble(1) { @@ -17,8 +16,6 @@ namespace osu.Game.Screens.Play.HUD MaxValue = 1 }; - public void Flash(JudgementResult result) => skinnedCounter?.Flash(result); - private HealthProcessor processor; public void BindHealthProcessor(HealthProcessor processor) @@ -36,15 +33,5 @@ namespace osu.Game.Screens.Play.HUD { CentreComponent = false; } - - private IHealthDisplay skinnedCounter; - - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - - skinnedCounter = Drawable as IHealthDisplay; - skinnedCounter?.Current.BindTo(Current); - } } } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 83897c5167..31a6bc7678 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -47,7 +47,6 @@ namespace osu.Game.Screens.Play public Bindable ShowHealthbar = new Bindable(true); private readonly ScoreProcessor scoreProcessor; - private readonly HealthProcessor healthProcessor; private readonly DrawableRuleset drawableRuleset; private readonly IReadOnlyList mods; @@ -75,10 +74,9 @@ namespace osu.Game.Screens.Play private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements }; - public HUDOverlay(ScoreProcessor scoreProcessor, HealthProcessor healthProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods) + public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods) { this.scoreProcessor = scoreProcessor; - this.healthProcessor = healthProcessor; this.drawableRuleset = drawableRuleset; this.mods = mods; @@ -161,9 +159,6 @@ namespace osu.Game.Screens.Play if (scoreProcessor != null) BindScoreProcessor(scoreProcessor); - if (healthProcessor != null) - BindHealthProcessor(healthProcessor); - if (drawableRuleset != null) { BindDrawableRuleset(drawableRuleset); @@ -322,21 +317,6 @@ namespace osu.Game.Screens.Play { ScoreCounter?.Current.BindTo(processor.TotalScore); AccuracyCounter?.Current.BindTo(processor.Accuracy); - - if (HealthDisplay is IHealthDisplay shd) - { - processor.NewJudgement += judgement => - { - if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit) - shd.Flash(judgement); - }; - } - } - - protected virtual void BindHealthProcessor(HealthProcessor processor) - { - HealthDisplay?.BindHealthProcessor(processor); - FailingLayer?.BindHealthProcessor(processor); } public bool OnPressed(GlobalAction action) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 951ce334f6..1538899281 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -208,6 +208,8 @@ namespace osu.Game.Screens.Play HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); HealthProcessor.ApplyBeatmap(playableBeatmap); + dependencies.CacheAs(HealthProcessor); + if (!ScoreProcessor.Mode.Disabled) config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); @@ -343,7 +345,7 @@ namespace osu.Game.Screens.Play // display the cursor above some HUD elements. DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), - HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value) + HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value) { HoldToQuit = { diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 2e29abf453..c1979efbc2 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -16,7 +16,7 @@ using osuTK.Graphics; namespace osu.Game.Skinning { - public class LegacyHealthDisplay : CompositeDrawable, IHealthDisplay, ISkinnableComponent + public class LegacyHealthDisplay : HealthDisplay { private const double epic_cutoff = 0.5; @@ -28,12 +28,6 @@ namespace osu.Game.Skinning private bool isNewStyle; - public Bindable Current { get; } = new BindableDouble(1) - { - MinValue = 0, - MaxValue = 1 - }; - public LegacyHealthDisplay(Skin skin) { this.skin = skin; @@ -83,7 +77,7 @@ namespace osu.Game.Skinning marker.Position = fill.Position + new Vector2(fill.DrawWidth, isNewStyle ? fill.DrawHeight / 2 : 0); } - public void Flash(JudgementResult result) => marker.Flash(result); + protected override void Flash(JudgementResult result) => marker.Flash(result); private static Texture getTexture(Skin skin, string name) => skin.GetTexture($"scorebar-{name}"); @@ -254,7 +248,7 @@ namespace osu.Game.Skinning Main.ScaleTo(1.4f).Then().ScaleTo(1, 200, Easing.Out); } - public class LegacyHealthPiece : CompositeDrawable, IHealthDisplay + public class LegacyHealthPiece : CompositeDrawable { public Bindable Current { get; } = new Bindable(); From 84a4ff333eec332a3c5dbd9b167f15b17fc6db7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 May 2021 17:10:31 +0900 Subject: [PATCH 07/20] Update skin editor test scene to cache a `ScoreProcessor` --- .../Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 270216564f..d50d4aa920 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; @@ -17,6 +18,9 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneSkinEditorMultipleSkins : SkinnableTestScene { + [Cached] + private readonly ScoreProcessor scoreProcessor = new ScoreProcessor(); + [SetUpSteps] public void SetUpSteps() { @@ -28,8 +32,6 @@ namespace osu.Game.Tests.Visual.Gameplay var working = CreateWorkingBeatmap(ruleset.RulesetInfo); var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo); - ScoreProcessor scoreProcessor = new ScoreProcessor(); - var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); var hudOverlay = new HUDOverlay(scoreProcessor, drawableRuleset, Array.Empty()) @@ -40,6 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay // Add any key just to display the key counter visually. hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); + scoreProcessor.Combo.Value = 1; return new Container { From 8e78cac05826b2e01959e3971a8043d0f53cf9df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 May 2021 17:31:29 +0900 Subject: [PATCH 08/20] Fix `HealthProcessor` cached as derived type in test --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index a451df026b..accb2ba1d9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); - [Cached] + [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(1); // best way to check without exposing. From 6c255a05726faed8e6244ded666cee31966a5f72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 May 2021 17:47:33 +0900 Subject: [PATCH 09/20] Fix drain start time being weirdly incorrect --- osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs | 8 ++++---- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index e426726def..99facb2731 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerFading() { - create(new DrainingHealthProcessor(1)); + create(new DrainingHealthProcessor(0)); AddSliderStep("current health", 0.0, 1.0, 1.0, val => { @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerDisabledViaConfig() { - create(new DrainingHealthProcessor(1)); + create(new DrainingHealthProcessor(0)); AddUntilStep("layer is visible", () => layer.IsPresent); AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false)); AddStep("set health to 0.10", () => layer.Current.Value = 0.1); @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerVisibilityWithDrainingProcessor() { - create(new DrainingHealthProcessor(1)); + create(new DrainingHealthProcessor(0)); AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddWaitStep("wait for potential fade", 10); AddAssert("layer is still visible", () => layer.IsPresent); @@ -90,7 +90,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerVisibilityWithDifferentOptions() { - create(new DrainingHealthProcessor(1)); + create(new DrainingHealthProcessor(0)); AddStep("set health to 0.10", () => layer.Current.Value = 0.1); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index accb2ba1d9..861d4a4f7f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Gameplay private ScoreProcessor scoreProcessor = new ScoreProcessor(); [Cached(typeof(HealthProcessor))] - private HealthProcessor healthProcessor = new DrainingHealthProcessor(1); + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); // best way to check without exposing. private Drawable hideTarget => hudOverlay.KeyCounter; From 3044b1c432621823fb9bd25b5190b38988ff8696 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 May 2021 17:47:38 +0900 Subject: [PATCH 10/20] Add missing cache rules --- .../Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs | 3 +++ .../Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs | 3 +++ .../Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs | 5 +++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index d50d4aa920..ac5599cc27 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -21,6 +21,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly ScoreProcessor scoreProcessor = new ScoreProcessor(); + [Cached(typeof(HealthProcessor))] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + [SetUpSteps] public void SetUpSteps() { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 12ce20f58e..7a960a09fc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -27,6 +27,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); + [Cached(typeof(HealthProcessor))] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + private IEnumerable hudOverlays => CreatedDrawables.OfType(); // best way to check without exposing. diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index f06236d0a8..2d6a6d95c7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; @@ -21,7 +22,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached] + [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); [SetUpSteps] @@ -53,7 +54,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddRepeatStep(@"increase hp with flash", delegate { healthProcessor.Health.Value = 0.1f; - healthProcessor.ApplyResult(new JudgementResult(null, new OsuJudgement())); + healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement())); }, 3); } } From 111b501ced835f83a4001fc3948eb26816c08364 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 7 May 2021 18:04:38 +0900 Subject: [PATCH 11/20] Revert accidental removal of UTF-8 BOM --- osu.Game/Rulesets/Replays/AutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Replays/AutoGenerator.cs b/osu.Game/Rulesets/Replays/AutoGenerator.cs index 6ce857b14b..83e85146d4 100644 --- a/osu.Game/Rulesets/Replays/AutoGenerator.cs +++ b/osu.Game/Rulesets/Replays/AutoGenerator.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; From 9fe6e1096ab9d9264f97f52ba8ed7ae504983d0b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 May 2021 18:11:08 +0900 Subject: [PATCH 12/20] Remove cruft from `SkinnableHealthDisplay` --- .../Play/HUD/SkinnableHealthDisplay.cs | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs index ef0affa417..3ba6a33276 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs @@ -1,33 +1,12 @@ // 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 osu.Framework.Bindables; -using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { public class SkinnableHealthDisplay : SkinnableDrawable { - public Bindable Current { get; } = new BindableDouble(1) - { - MinValue = 0, - MaxValue = 1 - }; - - private HealthProcessor processor; - - public void BindHealthProcessor(HealthProcessor processor) - { - if (this.processor != null) - throw new InvalidOperationException("Can't bind to a processor more than once"); - - this.processor = processor; - - Current.BindTo(processor.Health); - } - public SkinnableHealthDisplay() : base(new HUDSkinComponent(HUDSkinComponents.HealthDisplay), _ => new DefaultHealthDisplay()) { From 132bb7832d31e2c30719c65dd4da18d8fbbc2691 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 12:06:33 +0900 Subject: [PATCH 13/20] Fix some regressions when updating test scenes --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 7 ++----- .../Gameplay/TestSceneSkinnableHealthDisplay.cs | 11 +++++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index 99facb2731..ebc431a78b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Configuration; @@ -21,17 +22,13 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private OsuConfigManager config { get; set; } - [SetUpSteps] - public void SetUpSteps() - { - } - private void create(HealthProcessor healthProcessor) { AddStep("create layer", () => { Child = new HealthProcessorContainer(healthProcessor) { + RelativeSizeAxes = Axes.Both, Child = layer = new FailingLayer() }; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index 2d6a6d95c7..4f50613416 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -43,18 +43,21 @@ namespace osu.Game.Tests.Visual.Gameplay { AddRepeatStep(@"decrease hp", delegate { - healthProcessor.Health.Value = 0.08f; + healthProcessor.Health.Value -= 0.08f; }, 10); AddRepeatStep(@"increase hp without flash", delegate { - healthProcessor.Health.Value = 0.1f; + healthProcessor.Health.Value += 0.1f; }, 3); AddRepeatStep(@"increase hp with flash", delegate { - healthProcessor.Health.Value = 0.1f; - healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement())); + healthProcessor.Health.Value += 0.1f; + healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Perfect + }); }, 3); } } From 1bbbe8042099a5b1c3b659b1b899e2970651425c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 12:20:22 +0900 Subject: [PATCH 14/20] Fix missing instances of `HealthProcessor` caching --- .../Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs | 1 + osu.Game/Screens/Play/Player.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 7c65601d50..ac5599cc27 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -18,6 +18,7 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneSkinEditorMultipleSkins : SkinnableTestScene { + [Cached] private readonly ScoreProcessor scoreProcessor = new ScoreProcessor(); [Cached(typeof(HealthProcessor))] diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 396a9f841d..ba34ed4750 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -208,6 +208,8 @@ namespace osu.Game.Screens.Play HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); HealthProcessor.ApplyBeatmap(playableBeatmap); + dependencies.CacheAs(HealthProcessor); + if (!ScoreProcessor.Mode.Disabled) config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); From 1d38fa29b5a853a2be449ff00e3732359b0fbe00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 12:23:04 +0900 Subject: [PATCH 15/20] Remove unused using statement --- osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index ebc431a78b..fb4c9d713a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; From 35a7226cd82120207014a652567a5e87febc25e2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 10 May 2021 13:41:04 +0900 Subject: [PATCH 16/20] Add newline --- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 2292f64989..6c2571cc28 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -39,7 +39,8 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult judgement) { - if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit) Flash(judgement); + if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit) + Flash(judgement); } protected override void Dispose(bool isDisposing) From 301dab1ce8d8eb481a4180f63a7aa213919279c2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 12:55:07 +0300 Subject: [PATCH 17/20] Refactor `StarRatingDisplay` to be mutable with a current bindable --- .../Ranking/TestSceneStarRatingDisplay.cs | 15 +++- .../Ranking/Expanded/StarRatingDisplay.cs | 87 ++++++++++++------- 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index d0067c3396..a043f506c1 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.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 NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Screens.Ranking.Expanded; @@ -10,8 +12,11 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneStarRatingDisplay : OsuTestScene { - public TestSceneStarRatingDisplay() + [SetUp] + public void SetUp() => Schedule(() => { + StarRatingDisplay changingStarRating; + Child = new FillFlowContainer { Anchor = Anchor.Centre, @@ -25,8 +30,14 @@ namespace osu.Game.Tests.Visual.Ranking new StarRatingDisplay(new StarDifficulty(5.67, 0)), new StarRatingDisplay(new StarDifficulty(6.78, 0)), new StarRatingDisplay(new StarDifficulty(10.11, 0)), + changingStarRating = new StarRatingDisplay(), } }; - } + + Scheduler.AddDelayed(() => + { + changingStarRating.Current.Value = new StarDifficulty(RNG.NextDouble(0, 10), RNG.Next()); + }, 500, true); + }); } } diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index f7e50fdc8a..a1f48fa811 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -3,12 +3,14 @@ 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; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -20,17 +22,29 @@ namespace osu.Game.Screens.Ranking.Expanded /// /// A pill that displays the star rating of a . /// - public class StarRatingDisplay : CompositeDrawable + public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { - private readonly StarDifficulty difficulty; + private Box background; + private OsuTextFlowContainer textFlow; + + [Resolved] + private OsuColour colours { get; set; } + + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } /// /// Creates a new using an already computed . /// /// The already computed to display the star difficulty of. - public StarRatingDisplay(StarDifficulty starDifficulty) + public StarRatingDisplay(StarDifficulty starDifficulty = default) { - difficulty = starDifficulty; + Current.Value = starDifficulty; } [BackgroundDependencyLoader] @@ -38,15 +52,6 @@ namespace osu.Game.Screens.Ranking.Expanded { AutoSizeAxes = Axes.Both; - var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - - ColourInfo backgroundColour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); - InternalChildren = new Drawable[] { new CircularContainer @@ -55,10 +60,9 @@ namespace osu.Game.Screens.Ranking.Expanded Masking = true, Children = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = backgroundColour }, } }, @@ -78,32 +82,51 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + textFlow = 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; - }); - }) + } } } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + Current.BindValueChanged(difficulty => updateDisplay(difficulty.NewValue), true); + } + + private void updateDisplay(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; + + background.Colour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + + textFlow.Clear(); + + textFlow.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + textFlow.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + } } } From ca772b60b183e33d0c42896f5340088c5f3206b8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 18:21:29 +0300 Subject: [PATCH 18/20] Use regular test steps rather than one-time set up and scheduling --- .../Visual/Ranking/TestSceneStarRatingDisplay.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index a043f506c1..2ff664a0d9 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs @@ -12,12 +12,12 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneStarRatingDisplay : OsuTestScene { - [SetUp] - public void SetUp() => Schedule(() => + [Test] + public void TestDisplay() { - StarRatingDisplay changingStarRating; + StarRatingDisplay changingStarRating = null; - Child = new FillFlowContainer + AddStep("load displays", () => Child = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -32,12 +32,12 @@ namespace osu.Game.Tests.Visual.Ranking new StarRatingDisplay(new StarDifficulty(10.11, 0)), changingStarRating = new StarRatingDisplay(), } - }; + }); - Scheduler.AddDelayed(() => + AddRepeatStep("change bottom rating", () => { changingStarRating.Current.Value = new StarDifficulty(RNG.NextDouble(0, 10), RNG.Next()); - }, 500, true); - }); + }, 10); + } } } From 1c49590ba2e37ca79e03f7de0599e20f8be3a2f3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 21:13:18 +0300 Subject: [PATCH 19/20] Apply further refactoring to star rating display UX-wise --- .../Ranking/Expanded/StarRatingDisplay.cs | 106 +++++++++++++----- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index a1f48fa811..77c43ad863 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -22,27 +22,50 @@ namespace osu.Game.Screens.Ranking.Expanded /// /// A pill that displays the star rating of a . /// - public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue + public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { private Box background; - private OsuTextFlowContainer textFlow; + private OsuSpriteText wholePart; + private OsuSpriteText fractionPart; + + private double displayedStarRating; + + protected double DisplayedStarRating + { + get => displayedStarRating; + set + { + displayedStarRating = value; + + var starRatingParts = value.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + wholePart.Text = starRatingParts[0]; + fractionPart.Text = starRatingParts[1]; + } + } [Resolved] private OsuColour colours { get; set; } - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); - public Bindable Current + public Bindable Current { get => current.Current; set => current.Current = value; } + /// + /// Creates a new without any set, displaying a placeholder until is changed. + /// + public StarRatingDisplay() + { + } + /// /// Creates a new using an already computed . /// /// The already computed to display the star difficulty of. - public StarRatingDisplay(StarDifficulty starDifficulty = default) + public StarRatingDisplay(StarDifficulty starDifficulty) { Current.Value = starDifficulty; } @@ -82,13 +105,40 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + new FillFlowContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - TextAnchor = Anchor.BottomLeft, + Children = new[] + { + wholePart = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Font = OsuFont.Numeric.With(size: 14, weight: FontWeight.Black), + UseFullGlyphHeight = false, + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Text = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, + Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), + UseFullGlyphHeight = false, + }, + fractionPart = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), + UseFullGlyphHeight = false, + } + } } } } @@ -98,35 +148,31 @@ namespace osu.Game.Screens.Ranking.Expanded protected override void LoadComplete() { base.LoadComplete(); - Current.BindValueChanged(difficulty => updateDisplay(difficulty.NewValue), true); + + Current.BindValueChanged(_ => updateDisplay(), true); + FinishTransforms(true); } - private void updateDisplay(StarDifficulty difficulty) + private void updateDisplay() { - var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + const double duration = 400; + const Easing easing = Easing.OutQuint; - background.Colour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + ColourInfo backgroundColour; - textFlow.Clear(); - - textFlow.AddText($"{wholePart}", s => + if (Current.Value == null) + backgroundColour = Color4.SlateGray.Opacity(0.3f); + else { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); + var rating = Current.Value.Value.DifficultyRating; - textFlow.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); + backgroundColour = rating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(rating); + } + + background.FadeColour(backgroundColour, duration, easing); + this.TransformTo(nameof(DisplayedStarRating), Current.Value?.Stars ?? 0.0f, duration, easing); } } } From afc9a1bf235ef9c3026292254d788ce98072158d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 10 May 2021 10:16:52 +0300 Subject: [PATCH 20/20] Remove rolling support and apply few adjustments --- .../Ranking/Expanded/StarRatingDisplay.cs | 81 +++++++------------ 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 77c43ad863..422833555f 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.Containers; using osuTK; using osuTK.Graphics; @@ -25,23 +25,7 @@ namespace osu.Game.Screens.Ranking.Expanded public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { private Box background; - private OsuSpriteText wholePart; - private OsuSpriteText fractionPart; - - private double displayedStarRating; - - protected double DisplayedStarRating - { - get => displayedStarRating; - set - { - displayedStarRating = value; - - var starRatingParts = value.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - wholePart.Text = starRatingParts[0]; - fractionPart.Text = starRatingParts[1]; - } - } + private OsuTextFlowContainer textFlow; [Resolved] private OsuColour colours { get; set; } @@ -105,40 +89,13 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - new FillFlowContainer + textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Children = new[] - { - wholePart = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Colour = Color4.Black, - Font = OsuFont.Numeric.With(size: 14, weight: FontWeight.Black), - UseFullGlyphHeight = false, - }, - new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Colour = Color4.Black, - Text = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, - Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), - UseFullGlyphHeight = false, - }, - fractionPart = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Colour = Color4.Black, - Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), - UseFullGlyphHeight = false, - } - } + TextAnchor = Anchor.BottomLeft, } } } @@ -158,21 +115,39 @@ namespace osu.Game.Screens.Ranking.Expanded const double duration = 400; const Easing easing = Easing.OutQuint; - ColourInfo backgroundColour; + double stars = Current.Value?.Stars ?? 0.00f; + + var starRatingParts = stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + string wholePart = starRatingParts[0]; + string fractionPart = starRatingParts[1]; + string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; if (Current.Value == null) - backgroundColour = Color4.SlateGray.Opacity(0.3f); + background.FadeColour(Color4.SlateGray.Opacity(0.3f)); else { var rating = Current.Value.Value.DifficultyRating; - backgroundColour = rating == DifficultyRating.ExpertPlus + background.FadeColour(rating == DifficultyRating.ExpertPlus ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(rating); + : (ColourInfo)colours.ForDifficultyRating(rating), duration, easing); } - background.FadeColour(backgroundColour, duration, easing); - this.TransformTo(nameof(DisplayedStarRating), Current.Value?.Stars ?? 0.0f, duration, easing); + textFlow.Clear(); + + textFlow.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + textFlow.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); } } }