diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index 0249b6d9b1..5d2f6a14c7 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Diagnostics; using System.Linq; @@ -13,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Scoring; using osuTK; @@ -36,8 +35,8 @@ namespace osu.Game.Rulesets.Osu.Statistics private const float rotation = 45; - private BufferedContainer bufferedGrid; - private GridContainer pointGrid; + private BufferedContainer bufferedGrid = null!; + private GridContainer pointGrid = null!; private readonly ScoreInfo score; private readonly IBeatmap playableBeatmap; @@ -58,6 +57,8 @@ namespace osu.Game.Rulesets.Osu.Statistics [BackgroundDependencyLoader] private void load() { + const float line_extension = 0.2f; + InternalChild = new Container { Anchor = Anchor.Centre, @@ -66,76 +67,99 @@ namespace osu.Game.Rulesets.Osu.Statistics FillMode = FillMode.Fit, Children = new Drawable[] { - new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(inner_portion), - Masking = true, - BorderThickness = line_thickness, - BorderColour = Color4.White, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("#202624") - } - }, new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(inner_portion), + Masking = true, + BorderThickness = line_thickness, + BorderColour = Color4.White, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex("#202624") + } + }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(1), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Rotation = rotation, Child = new Container { RelativeSizeAxes = Axes.Both, - Masking = true, Children = new Drawable[] { - new Box + new Circle { Anchor = Anchor.Centre, Origin = Anchor.Centre, - EdgeSmoothness = new Vector2(1), RelativeSizeAxes = Axes.Y, - Height = 2, // We're rotating along a diagonal - we don't really care how big this is. - Width = line_thickness / 2, - Rotation = -rotation, - Alpha = 0.3f, + Width = line_thickness, + Height = inner_portion + line_extension, + Rotation = -rotation * 2, + Alpha = 0.6f, }, - new Box + new Circle { Anchor = Anchor.Centre, Origin = Anchor.Centre, - EdgeSmoothness = new Vector2(1), RelativeSizeAxes = Axes.Y, - Height = 2, // We're rotating along a diagonal - we don't really care how big this is. - Width = line_thickness / 2, // adjust for edgesmoothness - Rotation = rotation + Width = line_thickness, + Height = inner_portion + line_extension, }, + new OsuSpriteText + { + Text = "Overshoot", + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + Padding = new MarginPadding(3), + RelativePositionAxes = Axes.Both, + Y = -(inner_portion + line_extension) / 2, + }, + new OsuSpriteText + { + Text = "Undershoot", + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding(3), + RelativePositionAxes = Axes.Both, + Y = (inner_portion + line_extension) / 2, + }, + new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + RelativePositionAxes = Axes.Both, + Y = -(inner_portion + line_extension) / 2, + Margin = new MarginPadding(-line_thickness / 2), + Width = line_thickness, + Height = 10, + Rotation = 45, + }, + new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + RelativePositionAxes = Axes.Both, + Y = -(inner_portion + line_extension) / 2, + Margin = new MarginPadding(-line_thickness / 2), + Width = line_thickness, + Height = 10, + Rotation = -45, + } } }, }, - new Box - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Width = 10, - EdgeSmoothness = new Vector2(1), - Height = line_thickness / 2, // adjust for edgesmoothness - }, - new Box - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - EdgeSmoothness = new Vector2(1), - Width = line_thickness / 2, // adjust for edgesmoothness - Height = 10, - } } }, bufferedGrid = new BufferedContainer(cachedFrameBuffer: true) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 56b16301be..353acfa4ba 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -42,7 +42,8 @@ namespace osu.Game.Tests.Visual.Editing { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(90, 90) + Size = new Vector2(90, 90), + Scale = new Vector2(3), } }; }); @@ -64,17 +65,24 @@ namespace osu.Game.Tests.Visual.Editing InputManager.MoveMouseTo(tickMarkerHead.ScreenSpaceDrawQuad.Centre); InputManager.PressButton(MouseButton.Left); }); - AddStep("move to 8 and release", () => + AddStep("move to 1", () => InputManager.MoveMouseTo(getPositionForDivisor(1))); + AddStep("move to 16 and release", () => { - InputManager.MoveMouseTo(tickSliderBar.ScreenSpaceDrawQuad.Centre); + InputManager.MoveMouseTo(getPositionForDivisor(16)); InputManager.ReleaseButton(MouseButton.Left); }); - AddAssert("divisor is 8", () => bindableBeatDivisor.Value == 8); + AddAssert("divisor is 16", () => bindableBeatDivisor.Value == 16); AddStep("hold marker", () => InputManager.PressButton(MouseButton.Left)); - AddStep("move to 16", () => InputManager.MoveMouseTo(getPositionForDivisor(16))); - AddStep("move to ~10 and release", () => + AddStep("move to ~6 and release", () => + { + InputManager.MoveMouseTo(getPositionForDivisor(6)); + InputManager.ReleaseButton(MouseButton.Left); + }); + AddAssert("divisor clamped to 8", () => bindableBeatDivisor.Value == 8); + AddStep("move to ~10 and click", () => { InputManager.MoveMouseTo(getPositionForDivisor(10)); + InputManager.PressButton(MouseButton.Left); InputManager.ReleaseButton(MouseButton.Left); }); AddAssert("divisor clamped to 8", () => bindableBeatDivisor.Value == 8); @@ -82,12 +90,11 @@ namespace osu.Game.Tests.Visual.Editing private Vector2 getPositionForDivisor(int divisor) { - float relativePosition = (float)Math.Clamp(divisor, 0, 16) / 16; - var sliderDrawQuad = tickSliderBar.ScreenSpaceDrawQuad; - return new Vector2( - sliderDrawQuad.TopLeft.X + sliderDrawQuad.Width * relativePosition, - sliderDrawQuad.Centre.Y - ); + float localX = (1 - 1 / (float)divisor) * tickSliderBar.UsableWidth + tickSliderBar.RangePadding; + return tickSliderBar.ToScreenSpace(new Vector2( + localX, + tickSliderBar.DrawHeight / 2 + )); } [Test] diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorNavigation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorNavigation.cs deleted file mode 100644 index 5914290d40..0000000000 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorNavigation.cs +++ /dev/null @@ -1,57 +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 System.Linq; -using NUnit.Framework; -using osu.Framework.Extensions; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Extensions.ObjectExtensions; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.Rulesets.Mania; -using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Edit; -using osu.Game.Screens.Edit.GameplayTest; -using osu.Game.Screens.Select; -using osu.Game.Tests.Resources; - -namespace osu.Game.Tests.Visual.Editing -{ - public partial class TestSceneEditorNavigation : OsuGameTestScene - { - [Test] - public void TestEditorGameplayTestAlwaysUsesOriginalRuleset() - { - BeatmapSetInfo beatmapSet = null!; - - AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); - AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); - - AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); - AddUntilStep("wait for song select", - () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) - && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect - && songSelect.IsLoaded); - AddStep("switch ruleset", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo); - - AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); - AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); - AddStep("test gameplay", () => ((Editor)Game.ScreenStack.CurrentScreen).TestGameplay()); - - AddUntilStep("wait for player", () => - { - // notifications may fire at almost any inopportune time and cause annoying test failures. - // relentlessly attempt to dismiss any and all interfering overlays, which includes notifications. - // this is theoretically not foolproof, but it's the best that can be done here. - Game.CloseAllOverlays(); - return Game.ScreenStack.CurrentScreen is EditorPlayer editorPlayer && editorPlayer.IsLoaded; - }); - - AddAssert("current ruleset is osu!", () => Game.Ruleset.Value.Equals(new OsuRuleset().RulesetInfo)); - - AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(PlaySongSelect).Yield())); - AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); - AddAssert("previous ruleset restored", () => Game.Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo)); - } - } -} diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 603573058e..1b2bb57b84 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -3,15 +3,61 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Screens; +using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.GameplayTest; using osu.Game.Screens.Menu; +using osu.Game.Screens.Select; +using osu.Game.Tests.Resources; +using osuTK.Input; namespace osu.Game.Tests.Visual.Navigation { public partial class TestSceneBeatmapEditorNavigation : OsuGameTestScene { + [Test] + public void TestEditorGameplayTestAlwaysUsesOriginalRuleset() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); + AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); + + AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("wait for song select", + () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded); + AddStep("switch ruleset", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + AddStep("test gameplay", () => ((Editor)Game.ScreenStack.CurrentScreen).TestGameplay()); + + AddUntilStep("wait for player", () => + { + // notifications may fire at almost any inopportune time and cause annoying test failures. + // relentlessly attempt to dismiss any and all interfering overlays, which includes notifications. + // this is theoretically not foolproof, but it's the best that can be done here. + Game.CloseAllOverlays(); + return Game.ScreenStack.CurrentScreen is EditorPlayer editorPlayer && editorPlayer.IsLoaded; + }); + + AddAssert("current ruleset is osu!", () => Game.Ruleset.Value.Equals(new OsuRuleset().RulesetInfo)); + + AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(PlaySongSelect).Yield())); + AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + AddAssert("previous ruleset restored", () => Game.Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo)); + } + /// /// When entering the editor, a new beatmap is created as part of the asynchronous load process. /// This test ensures that in the case of an early exit from the editor (ie. while it's still loading) @@ -38,5 +84,63 @@ namespace osu.Game.Tests.Visual.Navigation BeatmapSetInfo[] allBeatmapSets() => Game.Realm.Run(realm => realm.All().Where(x => !x.DeletePending).ToArray()); } + + [Test] + public void TestExitEditorWithoutSelection() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); + AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); + + AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("wait for song select", + () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + + AddStep("escape once", () => InputManager.Key(Key.Escape)); + + AddUntilStep("wait for editor exit", () => Game.ScreenStack.CurrentScreen is not Editor); + } + + [Test] + public void TestExitEditorWithSelection() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); + AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); + + AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("wait for song select", + () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + + AddStep("make selection", () => + { + var beatmap = getEditorBeatmap(); + beatmap.SelectedHitObjects.AddRange(beatmap.HitObjects.Take(5)); + }); + + AddAssert("selection exists", () => getEditorBeatmap().SelectedHitObjects, () => Has.Count.GreaterThan(0)); + + AddStep("escape once", () => InputManager.Key(Key.Escape)); + + AddAssert("selection empty", () => getEditorBeatmap().SelectedHitObjects, () => Has.Count.Zero); + + AddStep("escape again", () => InputManager.Key(Key.Escape)); + + AddUntilStep("wait for editor exit", () => Game.ScreenStack.CurrentScreen is not Editor); + } + + private EditorBeatmap getEditorBeatmap() => ((Editor)Game.ScreenStack.CurrentScreen).ChildrenOfType().Single(); } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSegmentedGraph.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSegmentedGraph.cs index 1144b9053d..320ec48e07 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSegmentedGraph.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSegmentedGraph.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; @@ -137,8 +138,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (!objects.Any()) return; - double firstHit = objects.First().StartTime; - double lastHit = objects.Max(o => o.GetEndTime()); + (double firstHit, double lastHit) = BeatmapExtensions.CalculatePlayableBounds(objects); if (lastHit == 0) lastHit = objects.Last().StartTime; diff --git a/osu.Game/Beatmaps/BeatmapUpdater.cs b/osu.Game/Beatmaps/BeatmapUpdater.cs index af9f32f834..046adb8327 100644 --- a/osu.Game/Beatmaps/BeatmapUpdater.cs +++ b/osu.Game/Beatmaps/BeatmapUpdater.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; -using System.Linq; using System.Threading.Tasks; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Logging; @@ -11,7 +10,6 @@ using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Database; using osu.Game.Online.API; -using osu.Game.Rulesets.Objects; namespace osu.Game.Beatmaps { @@ -74,7 +72,7 @@ namespace osu.Game.Beatmaps var calculator = ruleset.CreateDifficultyCalculator(working); beatmap.StarRating = calculator.Calculate().StarRating; - beatmap.Length = calculateLength(working.Beatmap); + beatmap.Length = working.Beatmap.CalculatePlayableLength(); beatmap.BPM = 60000 / working.Beatmap.GetMostCommonBeatLength(); } @@ -82,20 +80,6 @@ namespace osu.Game.Beatmaps workingBeatmapCache.Invalidate(beatmapSet); }); - private double calculateLength(IBeatmap b) - { - if (!b.HitObjects.Any()) - return 0; - - var lastObject = b.HitObjects.Last(); - - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - double endTime = lastObject.GetEndTime(); - double startTime = b.HitObjects.First().StartTime; - - return endTime - startTime; - } - #region Implementation of IDisposable public void Dispose() diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index f6771f7adf..9dc3084cb5 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -104,6 +104,19 @@ namespace osu.Game.Beatmaps } } + /// + /// Find the total milliseconds between the first and last hittable objects. + /// + /// + /// This is cached to , so using that is preferable when available. + /// + public static double CalculatePlayableLength(this IBeatmap beatmap) => CalculatePlayableLength(beatmap.HitObjects); + + /// + /// Find the timestamps in milliseconds of the start and end of the playable region. + /// + public static (double start, double end) CalculatePlayableBounds(this IBeatmap beatmap) => CalculatePlayableBounds(beatmap.HitObjects); + /// /// Find the absolute end time of the latest in a beatmap. Will throw if beatmap contains no objects. /// @@ -114,5 +127,36 @@ namespace osu.Game.Beatmaps /// It's not super efficient so calls should be kept to a minimum. /// public static double GetLastObjectTime(this IBeatmap beatmap) => beatmap.HitObjects.Max(h => h.GetEndTime()); + + #region Helper methods + + /// + /// Find the total milliseconds between the first and last hittable objects. + /// + /// + /// This is cached to , so using that is preferable when available. + /// + public static double CalculatePlayableLength(IEnumerable objects) + { + (double start, double end) = CalculatePlayableBounds(objects); + + return end - start; + } + + /// + /// Find the timestamps in milliseconds of the start and end of the playable region. + /// + public static (double start, double end) CalculatePlayableBounds(IEnumerable objects) + { + if (!objects.Any()) + return (0, 0); + + double lastObjectTime = objects.Max(o => o.GetEndTime()); + double firstObjectTime = objects.First().StartTime; + + return (firstObjectTime, lastObjectTime); + } + + #endregion } } diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index 952ef3f182..05a666721a 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -17,6 +17,7 @@ namespace osu.Game.Graphics.Containers { public const float APPEAR_DURATION = 800; public const float DISAPPEAR_DURATION = 500; + public const float SHADOW_OPACITY = 0.2f; private const Easing easing_show = Easing.OutSine; private const Easing easing_hide = Easing.InSine; diff --git a/osu.Game/Overlays/FullscreenOverlay.cs b/osu.Game/Overlays/FullscreenOverlay.cs index 2cc8354e50..032821f215 100644 --- a/osu.Game/Overlays/FullscreenOverlay.cs +++ b/osu.Game/Overlays/FullscreenOverlay.cs @@ -56,6 +56,7 @@ namespace osu.Game.Overlays { Colour = Color4.Black.Opacity(0), Type = EdgeEffectType.Shadow, + Hollow = true, Radius = 10 }; @@ -101,7 +102,7 @@ namespace osu.Game.Overlays protected override void PopIn() { base.PopIn(); - FadeEdgeEffectTo(0.4f, WaveContainer.APPEAR_DURATION, Easing.Out); + FadeEdgeEffectTo(WaveContainer.SHADOW_OPACITY, WaveContainer.APPEAR_DURATION, Easing.Out); } protected override void PopOut() diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 71a4c58afd..f2eefb6e4b 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -5,9 +5,11 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Framework.Logging; @@ -16,6 +18,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Overlays.Notifications; using osu.Game.Resources.Localisation.Web; using osuTK; +using osuTK.Graphics; using NotificationsStrings = osu.Game.Localisation.NotificationsStrings; namespace osu.Game.Overlays @@ -72,6 +75,14 @@ namespace osu.Game.Overlays mainContent = new Container { RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0), + Type = EdgeEffectType.Shadow, + Radius = 10, + Hollow = true, + }, Children = new Drawable[] { new Box @@ -199,6 +210,7 @@ namespace osu.Game.Overlays this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); mainContent.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); + mainContent.FadeEdgeEffectTo(WaveContainer.SHADOW_OPACITY, WaveContainer.APPEAR_DURATION, Easing.Out); toastTray.FlushAllToasts(); } @@ -211,6 +223,7 @@ namespace osu.Game.Overlays this.MoveToX(WIDTH, TRANSITION_LENGTH, Easing.OutQuint); mainContent.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); + mainContent.FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.In); } private void notificationClosed() => Schedule(() => diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index aefaccdb5d..d571557993 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -10,15 +10,18 @@ using System.Threading.Tasks; using osuTK; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; +using osuTK.Graphics; namespace osu.Game.Overlays { @@ -105,6 +108,14 @@ namespace osu.Game.Overlays Add(SectionsContainer = new SettingsSectionsContainer { Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0), + Type = EdgeEffectType.Shadow, + Hollow = true, + Radius = 10 + }, + MaskingSmoothness = 0, RelativeSizeAxes = Axes.Both, ExpandableHeader = CreateHeader(), SelectedSection = { BindTarget = CurrentSection }, @@ -156,6 +167,8 @@ namespace osu.Game.Overlays ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); + SectionsContainer.FadeEdgeEffectTo(WaveContainer.SHADOW_OPACITY, WaveContainer.APPEAR_DURATION, Easing.Out); + // delay load enough to ensure it doesn't overlap with the initial animation. // this is done as there is still a brief stutter during load completion which is more visible if the transition is in progress. // the eventual goal would be to remove the need for this by splitting up load into smaller work pieces, or fixing the remaining @@ -175,6 +188,7 @@ namespace osu.Game.Overlays { base.PopOut(); + SectionsContainer.FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.In); ContentContainer.MoveToX(-WIDTH + ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); Sidebar?.MoveToX(-sidebar_width, TRANSITION_LENGTH, Easing.OutQuint); diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index aa8e202e22..1da224d850 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -154,12 +154,15 @@ namespace osu.Game.Screens.Edit /// /// The 0-based beat index. /// The beat divisor. + /// The list of valid divisors which can be chosen from. Assumes ordered from low to high. Defaults to if omitted. /// The applicable divisor. - public static int GetDivisorForBeatIndex(int index, int beatDivisor) + public static int GetDivisorForBeatIndex(int index, int beatDivisor, int[] validDivisors = null) { + validDivisors ??= PREDEFINED_DIVISORS; + int beat = index % beatDivisor; - foreach (int divisor in PREDEFINED_DIVISORS) + foreach (int divisor in validDivisors) { if ((beat * divisor) % beatDivisor == 0) return divisor; diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 9f422d5aa9..432c5ea280 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -383,7 +383,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { CurrentNumber.BindTo(this.beatDivisor = beatDivisor); - Padding = new MarginPadding { Horizontal = 5 }; + RangePadding = 5; + Padding = new MarginPadding { Horizontal = RangePadding }; } protected override void LoadComplete() @@ -398,15 +399,20 @@ namespace osu.Game.Screens.Edit.Compose.Components ClearInternal(); CurrentNumber.ValueChanged -= moveMarker; - foreach (int divisor in beatDivisor.ValidDivisors.Value.Presets) + int largestDivisor = beatDivisor.ValidDivisors.Value.Presets.Last(); + + for (int tickIndex = 0; tickIndex <= largestDivisor; tickIndex++) { - AddInternal(new Tick(divisor) + int divisor = BindableBeatDivisor.GetDivisorForBeatIndex(tickIndex, largestDivisor, (int[])beatDivisor.ValidDivisors.Value.Presets); + bool isSolidTick = divisor * (largestDivisor - tickIndex) == largestDivisor; + + AddInternal(new Tick(divisor, isSolidTick) { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, Colour = BindableBeatDivisor.GetColourFor(divisor, colours), - X = getMappedPosition(divisor), + X = tickIndex / (float)largestDivisor, }); } @@ -418,6 +424,11 @@ namespace osu.Game.Screens.Edit.Compose.Components private void moveMarker(ValueChangedEvent divisor) { marker.MoveToX(getMappedPosition(divisor.NewValue), 100, Easing.OutQuint); + + foreach (Tick tick in InternalChildren.OfType().Where(t => !t.AlwaysDisplayed)) + { + tick.FadeTo(divisor.NewValue % tick.Divisor == 0 ? 0.2f : 0f, 100, Easing.OutQuint); + } } protected override void UpdateValue(float value) @@ -483,13 +494,22 @@ namespace osu.Game.Screens.Edit.Compose.Components OnUserChange(Current.Value); } - private float getMappedPosition(float divisor) => MathF.Pow((divisor - 1) / (beatDivisor.ValidDivisors.Value.Presets.Last() - 1), 0.90f); + private float getMappedPosition(float divisor) => 1 - 1 / divisor; private partial class Tick : Circle { - public Tick(int divisor) + public readonly bool AlwaysDisplayed; + + public readonly int Divisor; + + public Tick(int divisor, bool alwaysDisplayed) { + AlwaysDisplayed = alwaysDisplayed; + Divisor = divisor; + Size = new Vector2(6f, 12) * BindableBeatDivisor.GetSize(divisor); + Alpha = alwaysDisplayed ? 1 : 0; + InternalChild = new Box { RelativeSizeAxes = Axes.Both }; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index cb7c083d87..56a6b18433 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -16,6 +16,7 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// A container which provides a "blueprint" display of items. /// Includes selection and manipulation support via a . /// - public abstract partial class BlueprintContainer : CompositeDrawable, IKeyBindingHandler + public abstract partial class BlueprintContainer : CompositeDrawable, IKeyBindingHandler, IKeyBindingHandler where T : class { protected DragBox DragBox { get; private set; } @@ -91,7 +92,6 @@ namespace osu.Game.Screens.Edit.Compose.Components }; SelectionHandler = CreateSelectionHandler(); - SelectionHandler.DeselectAll = DeselectAll; SelectionHandler.SelectedItems.BindTo(SelectedItems); AddRangeInternal(new[] @@ -279,6 +279,30 @@ namespace osu.Game.Screens.Edit.Compose.Components { } + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat) + return false; + + switch (e.Action) + { + case GlobalAction.Back: + if (SelectedItems.Count > 0) + { + DeselectAll(); + return true; + } + + break; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + #region Blueprint Addition/Removal protected virtual void AddBlueprintFor(T item) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 9e4fb26688..5cedf1ca42 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -197,9 +197,9 @@ namespace osu.Game.Screens.Edit.Compose.Components #region Selection Handling /// - /// Bind an action to deselect all selected blueprints. + /// Deselect all selected items. /// - internal Action DeselectAll { private get; set; } + protected void DeselectAll() => SelectedItems.Clear(); /// /// Handle a blueprint becoming selected. @@ -303,7 +303,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (blueprint.IsSelected) return false; - DeselectAll?.Invoke(); + DeselectAll(); blueprint.Select(); return true; } @@ -311,6 +311,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected void DeleteSelected() { DeleteItems(SelectedItems.ToArray()); + DeselectAll(); } #endregion diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs index f96d547747..35e0482f2b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs @@ -3,39 +3,14 @@ #nullable disable -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public partial class MatchTypePill : OnlinePlayComposite + public partial class MatchTypePill : OnlinePlayPill { - private OsuTextFlowContainer textFlow; - - public MatchTypePill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new PillContainer - { - Child = textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); @@ -45,8 +20,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onMatchTypeChanged(ValueChangedEvent type) { - textFlow.Clear(); - textFlow.AddText(type.NewValue.GetLocalisableDescription()); + TextFlow.Text = type.NewValue.GetLocalisableDescription(); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs new file mode 100644 index 0000000000..3e6d7a2e54 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs @@ -0,0 +1,37 @@ +// 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.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.OnlinePlay.Lounge.Components +{ + public abstract partial class OnlinePlayPill : OnlinePlayComposite + { + protected PillContainer Pill { get; private set; } = null!; + protected OsuTextFlowContainer TextFlow { get; private set; } = null!; + protected virtual FontUsage Font => OsuFont.GetFont(size: 12); + + protected OnlinePlayPill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = Pill = new PillContainer + { + Child = TextFlow = new OsuTextFlowContainer(s => s.Font = Font) + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + } + }; + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index 81ba48d135..d1365c02f3 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs @@ -5,40 +5,16 @@ using System.Linq; using Humanizer; -using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; -using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { /// /// A pill that displays the playlist item count. /// - public partial class PlaylistCountPill : OnlinePlayComposite + public partial class PlaylistCountPill : OnlinePlayPill { - private OsuTextFlowContainer count; - - public PlaylistCountPill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new PillContainer - { - Child = count = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); @@ -55,10 +31,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components ? Playlist.Count(i => !i.Expired) : PlaylistItemStats.Value.CountActive; - count.Clear(); - count.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); - count.AddText(" "); - count.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None)); + TextFlow.Clear(); + TextFlow.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + TextFlow.AddText(" "); + TextFlow.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None)); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs index 0175418a96..208c11c155 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs @@ -3,39 +3,14 @@ #nullable disable -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Online.Multiplayer; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public partial class QueueModePill : OnlinePlayComposite + public partial class QueueModePill : OnlinePlayPill { - private OsuTextFlowContainer textFlow; - - public QueueModePill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new PillContainer - { - Child = textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); @@ -45,8 +20,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onQueueModeChanged(ValueChangedEvent mode) { - textFlow.Clear(); - textFlow.AddText(mode.NewValue.GetLocalisableDescription()); + TextFlow.Text = mode.NewValue.GetLocalisableDescription(); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs index 5d67a18d1f..10f6e59260 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs @@ -5,58 +5,30 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; -using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public partial class RoomSpecialCategoryPill : OnlinePlayComposite + public partial class RoomSpecialCategoryPill : OnlinePlayPill { - private SpriteText text; - private PillContainer pill; - [Resolved] private OsuColour colours { get; set; } - public RoomSpecialCategoryPill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = pill = new PillContainer - { - Background = - { - Colour = colours.Pink, - Alpha = 1 - }, - Child = text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), - Colour = Color4.Black - } - }; - } + protected override FontUsage Font => base.Font.With(weight: FontWeight.SemiBold); protected override void LoadComplete() { base.LoadComplete(); + Pill.Background.Alpha = 1; + TextFlow.Colour = Color4.Black; + Category.BindValueChanged(c => { - text.Text = c.NewValue.GetLocalisableDescription(); - - var backgroundColour = colours.ForRoomCategory(Category.Value); - if (backgroundColour != null) - pill.Background.Colour = backgroundColour.Value; + TextFlow.Text = c.NewValue.GetLocalisableDescription(); + Pill.Background.Colour = colours.ForRoomCategory(c.NewValue) ?? colours.Pink; }, true); } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index 201314851e..ca9917ad00 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -8,43 +8,20 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { /// /// A pill that displays the room's current status. /// - public partial class RoomStatusPill : OnlinePlayComposite + public partial class RoomStatusPill : OnlinePlayPill { [Resolved] private OsuColour colours { get; set; } - private PillContainer pill; - private SpriteText statusText; - - public RoomStatusPill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = pill = new PillContainer - { - Child = statusText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), - Colour = Color4.Black - } - }; - } + protected override FontUsage Font => base.Font.With(weight: FontWeight.SemiBold); protected override void LoadComplete() { @@ -54,15 +31,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Status.BindValueChanged(_ => updateDisplay(), true); FinishTransforms(true); + + TextFlow.Colour = Colour4.Black; + Pill.Background.Alpha = 1; } private void updateDisplay() { RoomStatus status = getDisplayStatus(); - pill.Background.Alpha = 1; - pill.Background.FadeColour(status.GetAppropriateColour(colours), 100); - statusText.Text = status.Message; + Pill.Background.FadeColour(status.GetAppropriateColour(colours), 100); + TextFlow.Text = status.Message; } private RoomStatus getDisplayStatus() diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 4fbc937b59..f1dd2abc4a 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -23,7 +23,6 @@ namespace osu.Game.Screens.Play public Func> SaveReplay; public override string Header => "failed"; - public override string Description => "you're dead, try again?"; [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 81146a4ea6..7f4979b840 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; -using Humanizer; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -38,8 +35,8 @@ namespace osu.Game.Screens.Play public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - public Action OnRetry; - public Action OnQuit; + public Action? OnRetry; + public Action? OnQuit; /// /// Action that is invoked when is triggered. @@ -53,12 +50,13 @@ namespace osu.Game.Screens.Play public abstract string Header { get; } - public abstract string Description { get; } - - protected SelectionCycleFillFlowContainer InternalButtons; + protected SelectionCycleFillFlowContainer InternalButtons = null!; public IReadOnlyList Buttons => InternalButtons; - private FillFlowContainer retryCounterContainer; + private TextFlowContainer playInfoText = null!; + + [Resolved] + private GlobalActionContainer globalAction { get; set; } = null!; protected GameplayMenuOverlay() { @@ -86,36 +84,13 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Children = new Drawable[] { - new FillFlowContainer + new OsuSpriteText { + Text = Header, + Font = OsuFont.GetFont(size: 48), Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Children = new Drawable[] - { - new OsuSpriteText - { - Text = Header, - Font = OsuFont.GetFont(size: 30), - Spacing = new Vector2(5, 0), - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Colour = colours.Yellow, - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f) - }, - new OsuSpriteText - { - Text = Description, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f) - } - } + Colour = colours.Yellow, }, InternalButtons = new SelectionCycleFillFlowContainer { @@ -132,10 +107,11 @@ namespace osu.Game.Screens.Play Radius = 50 }, }, - retryCounterContainer = new FillFlowContainer + playInfoText = new OsuTextFlowContainer(cp => cp.Font = OsuFont.GetFont(size: 18)) { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, + TextAnchor = Anchor.TopCentre, AutoSizeAxes = Axes.Both, } } @@ -157,7 +133,8 @@ namespace osu.Game.Screens.Play return; retries = value; - if (retryCounterContainer != null) + + if (IsLoaded) updateRetryCount(); } } @@ -170,7 +147,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseMove(MouseMoveEvent e) => true; - protected void AddButton(string text, Color4 colour, Action action) + protected void AddButton(string text, Color4 colour, Action? action) { var button = new Button { @@ -222,30 +199,9 @@ namespace osu.Game.Screens.Play // "You've retried 1,065 times in this session" // "You've retried 1 time in this session" - retryCounterContainer.Children = new Drawable[] - { - new OsuSpriteText - { - Text = "You've retried ", - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - Font = OsuFont.GetFont(size: 18), - }, - new OsuSpriteText - { - Text = "time".ToQuantity(retries), - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18), - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - }, - new OsuSpriteText - { - Text = " in this session", - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - Font = OsuFont.GetFont(size: 18), - } - }; + playInfoText.Clear(); + playInfoText.AddText("Retry count: "); + playInfoText.AddText(retries.ToString(), cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); } private partial class Button : DialogButton @@ -260,9 +216,6 @@ namespace osu.Game.Screens.Play } } - [Resolved] - private GlobalActionContainer globalAction { get; set; } - protected override bool Handle(UIEvent e) { switch (e) diff --git a/osu.Game/Screens/Play/HUD/ArgonSongProgressGraph.cs b/osu.Game/Screens/Play/HUD/ArgonSongProgressGraph.cs index 0899476ed4..63ab9d15e0 100644 --- a/osu.Game/Screens/Play/HUD/ArgonSongProgressGraph.cs +++ b/osu.Game/Screens/Play/HUD/ArgonSongProgressGraph.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Graphics.UserInterface; @@ -26,8 +27,7 @@ namespace osu.Game.Screens.Play.HUD if (!objects.Any()) return; - double firstHit = objects.First().StartTime; - double lastHit = objects.Max(o => o.GetEndTime()); + (double firstHit, double lastHit) = BeatmapExtensions.CalculatePlayableBounds(objects); if (lastHit == 0) lastHit = objects.Last().StartTime; diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgressGraph.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgressGraph.cs index bee5978817..047c64a4a4 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgressGraph.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgressGraph.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Collections.Generic; using System.Diagnostics; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Play.HUD @@ -26,8 +27,7 @@ namespace osu.Game.Screens.Play.HUD if (!objects.Any()) return; - double firstHit = objects.First().StartTime; - double lastHit = objects.Max(o => o.GetEndTime()); + (double firstHit, double lastHit) = BeatmapExtensions.CalculatePlayableBounds(objects); if (lastHit == 0) lastHit = objects.Last().StartTime; diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index ebe2fb83e6..4391193df8 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -2,12 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Skinning; @@ -52,9 +52,9 @@ namespace osu.Game.Screens.Play.HUD set { objects = value; - FirstHitTime = objects.FirstOrDefault()?.StartTime ?? 0; - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - LastHitTime = objects.LastOrDefault()?.GetEndTime() ?? 0; + + (FirstHitTime, LastHitTime) = BeatmapExtensions.CalculatePlayableBounds(objects); + UpdateObjects(objects); } } diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index db42998c45..984f43d77a 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -24,7 +24,6 @@ namespace osu.Game.Screens.Play public override bool IsPresent => base.IsPresent || pauseLoop.IsPlaying; public override string Header => "paused"; - public override string Description => "you're not going to do what i think you're going to do, are ya?"; private SkinnableSound pauseLoop;