From f262f288fceef2e066761367a0f25297ff411da9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 6 Aug 2021 19:58:46 +0900 Subject: [PATCH 01/29] Fix DHO state is overwritten to `Idle` on `LoadComplete` The state may already be changed before `LoadComplete` is called because DHO is already added to the draw hierarchy. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 25f3b8931a..ff6168ee37 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -190,7 +190,9 @@ namespace osu.Game.Rulesets.Objects.Drawables comboIndexBindable.BindValueChanged(_ => UpdateComboColour()); comboIndexWithOffsetsBindable.BindValueChanged(_ => UpdateComboColour(), true); - updateState(ArmedState.Idle, true); + // If the state is changed, transforms are already initialized. + if (state.Value == ArmedState.Idle) + updateState(ArmedState.Idle, true); } /// From 53b98520340a9abf2d0b6415de8d78eb9c27ff12 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 6 Aug 2021 19:59:02 +0900 Subject: [PATCH 02/29] Add test case for DHO state change before load complete --- .../Gameplay/TestSceneDrawableHitObject.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 0ce71696bd..58f4c4c8db 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Tests.Visual; namespace osu.Game.Tests.Gameplay @@ -121,6 +123,18 @@ namespace osu.Game.Tests.Gameplay AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 666 && dho.LifetimeEnd == 999); } + [Test] + public void TestStateChangeBeforeLoadComplete() + { + TestDrawableHitObject dho = null; + AddStep("Add DHO and apply result", () => + { + Child = dho = new TestDrawableHitObject(new HitObject { StartTime = Time.Current }); + dho.MissForcefully(); + }); + AddAssert("DHO state is correct", () => dho.State.Value == ArmedState.Miss); + } + private class TestDrawableHitObject : DrawableHitObject { public const double INITIAL_LIFETIME_OFFSET = 100; @@ -141,6 +155,19 @@ namespace osu.Game.Tests.Gameplay if (SetLifetimeStartOnApply) LifetimeStart = LIFETIME_ON_APPLY; } + + public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss); + + protected override void UpdateHitStateTransforms(ArmedState state) + { + if (state != ArmedState.Miss) + { + base.UpdateHitStateTransforms(state); + return; + } + + this.FadeOut(1000); + } } private class TestLifetimeEntry : HitObjectLifetimeEntry From 52400961f6ee5974c1bb0205005954a597ed2e99 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 6 Aug 2021 20:59:26 +0900 Subject: [PATCH 03/29] Add open/close sounds to context menus --- .../UserInterface/TestSceneContextMenu.cs | 61 ++++++++++++++++--- .../Cursor/OsuContextMenuContainer.cs | 2 +- .../Graphics/UserInterface/OsuContextMenu.cs | 53 ++++++++++++++-- 3 files changed, 100 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs index 53693d1b70..3b43f8485a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs @@ -1,6 +1,8 @@ // 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 NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -68,13 +70,40 @@ namespace osu.Game.Tests.Visual.UserInterface ); } - private class MyContextMenuContainer : Container, IHasContextMenu + private static MenuItem[] makeMenu() { - public MenuItem[] ContextMenuItems => new MenuItem[] + return new MenuItem[] { new OsuMenuItem(@"Some option"), new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted), new OsuMenuItem(@"Another option"), + new OsuMenuItem(@"Nested option >") + { + Items = new MenuItem[] + { + new OsuMenuItem(@"Sub-One"), + new OsuMenuItem(@"Sub-Two"), + new OsuMenuItem(@"Sub-Three"), + new OsuMenuItem(@"Sub-Nested option >") + { + Items = new MenuItem[] + { + new OsuMenuItem(@"Double Sub-One"), + new OsuMenuItem(@"Double Sub-Two"), + new OsuMenuItem(@"Double Sub-Three"), + new OsuMenuItem(@"Sub-Sub-Nested option >") + { + Items = new MenuItem[] + { + new OsuMenuItem(@"Too Deep One"), + new OsuMenuItem(@"Too Deep Two"), + new OsuMenuItem(@"Too Deep Three"), + } + } + } + } + } + }, new OsuMenuItem(@"Choose me please"), new OsuMenuItem(@"And me too"), new OsuMenuItem(@"Trying to fill"), @@ -82,17 +111,29 @@ namespace osu.Game.Tests.Visual.UserInterface }; } + private class MyContextMenuContainer : Container, IHasContextMenu + { + public MenuItem[] ContextMenuItems => makeMenu(); + } + private class AnotherContextMenuContainer : Container, IHasContextMenu { - public MenuItem[] ContextMenuItems => new MenuItem[] + public MenuItem[] ContextMenuItems { - new OsuMenuItem(@"Simple option"), - new OsuMenuItem(@"Simple very very long option"), - new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)), - }; + get + { + List items = makeMenu().ToList(); + items.AddRange(new MenuItem[] + { + new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)), + }); + + return items.ToArray(); + } + } } } } diff --git a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs index fbb3fa0e6c..e64d2d98f4 100644 --- a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs @@ -9,6 +9,6 @@ namespace osu.Game.Graphics.Cursor { public class OsuContextMenuContainer : ContextMenuContainer { - protected override Menu CreateMenu() => new OsuContextMenu(); + protected override Menu CreateMenu() => new OsuContextMenu(true); } } diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs index 8c7b44f952..7cf8b3eca8 100644 --- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs @@ -3,6 +3,9 @@ using osuTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; @@ -13,8 +16,16 @@ namespace osu.Game.Graphics.UserInterface public class OsuContextMenu : OsuMenu { private const int fade_duration = 250; + private Sample sampleOpen; + private Sample sampleClose; + private Sample sampleClick; - public OsuContextMenu() + // todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed. + private bool wasOpened; + private readonly bool playClickSample; + private readonly Menu parentMenu; + + public OsuContextMenu(bool playClickSample = false, Menu parentMenu = null) : base(Direction.Vertical) { MaskingContainer.CornerRadius = 5; @@ -28,17 +39,49 @@ namespace osu.Game.Graphics.UserInterface ItemsContainer.Padding = new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL }; MaxHeight = 250; + + this.playClickSample = playClickSample; + this.parentMenu = parentMenu; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { BackgroundColour = colours.ContextMenuGray; + sampleClick = audio.Samples.Get($"UI/{HoverSampleSet.Default.GetDescription()}-select"); + sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); + sampleClose = audio.Samples.Get(@"UI/dropdown-close"); } - protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint); - protected override void AnimateClose() => this.FadeOut(fade_duration, Easing.OutQuint); + protected override void AnimateOpen() + { + this.FadeIn(fade_duration, Easing.OutQuint); - protected override Menu CreateSubMenu() => new OsuContextMenu(); + if (playClickSample) + sampleClick?.Play(); + + if (!wasOpened) + sampleOpen?.Play(); + + wasOpened = true; + } + + protected override void AnimateClose() + { + this.FadeOut(fade_duration, Easing.OutQuint); + + if (parentMenu?.State == MenuState.Closed) + { + wasOpened = false; + return; + } + + if (wasOpened) + sampleClose?.Play(); + + wasOpened = false; + } + + protected override Menu CreateSubMenu() => new OsuContextMenu(false, this); } } From 9e805dcd44843ef04ea23ed26d311d3a39daebc0 Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Sun, 8 Aug 2021 21:27:32 +1000 Subject: [PATCH 04/29] Fix legacy slider body colour interpolation --- .../Skinning/Legacy/LegacySliderBody.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index 744ded37c9..9e4b57ca10 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; using osuTK.Graphics; @@ -40,7 +39,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Color4 outerColour = AccentColour.Darken(0.1f); Color4 innerColour = lighten(AccentColour, 0.5f); - return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1); + // Stable doesn't use linear space / gamma-correct colour interpolation + // for slider bodies, so we can't use Interpolation.ValueAt(). + // Instead, we use a local method that interpolates between the colours directly in sRGB space. + return valueAt(position / realGradientPortion, outerColour, innerColour, 0, 1); } /// @@ -55,6 +57,26 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Math.Min(1, color.B * (1 + 0.5f * amount) + 1 * amount), color.A); } + + private static Color4 valueAt(double time, Color4 startColour, Color4 endColour, double startTime, double endTime) + { + if (startColour == endColour) + return startColour; + + double current = time - startTime; + double duration = endTime - startTime; + + if (duration == 0 || current == 0) + return startColour; + + float t = (float)Math.Max(0, Math.Min(1, current / duration)); + + return new Color4( + startColour.R + t * (endColour.R - startColour.R), + startColour.G + t * (endColour.G - startColour.G), + startColour.B + t * (endColour.B - startColour.B), + startColour.A + t * (endColour.A - startColour.A)); + } } } } From 140d29d53762b1d0df5e44fdcd125410769ca50b Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Sun, 8 Aug 2021 23:54:35 +1000 Subject: [PATCH 05/29] Use helper methods instead of local valueAt() method --- .../Skinning/Legacy/LegacySliderBody.cs | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index 9e4b57ca10..a8bb69c080 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; using osuTK.Graphics; @@ -36,13 +37,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy position -= realBorderPortion; - Color4 outerColour = AccentColour.Darken(0.1f); - Color4 innerColour = lighten(AccentColour, 0.5f); + // Stable interpolates slider body colour directly in sRGB space, and because + // Interpolation.ValueAt() uses linear space, we have to counteract applying it + // by calling ToSRGB() on the input colours, and ToLinear() on the resulting colour. - // Stable doesn't use linear space / gamma-correct colour interpolation - // for slider bodies, so we can't use Interpolation.ValueAt(). - // Instead, we use a local method that interpolates between the colours directly in sRGB space. - return valueAt(position / realGradientPortion, outerColour, innerColour, 0, 1); + Color4 outerColour = AccentColour.Darken(0.1f).ToSRGB(); + Color4 innerColour = lighten(AccentColour, 0.5f).ToSRGB(); + + return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1).ToLinear(); } /// @@ -57,26 +59,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Math.Min(1, color.B * (1 + 0.5f * amount) + 1 * amount), color.A); } - - private static Color4 valueAt(double time, Color4 startColour, Color4 endColour, double startTime, double endTime) - { - if (startColour == endColour) - return startColour; - - double current = time - startTime; - double duration = endTime - startTime; - - if (duration == 0 || current == 0) - return startColour; - - float t = (float)Math.Max(0, Math.Min(1, current / duration)); - - return new Color4( - startColour.R + t * (endColour.R - startColour.R), - startColour.G + t * (endColour.G - startColour.G), - startColour.B + t * (endColour.B - startColour.B), - startColour.A + t * (endColour.A - startColour.A)); - } } } } From c72224fa9412d72367a112e461029b864d5f782f Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Sun, 8 Aug 2021 13:45:13 -0400 Subject: [PATCH 06/29] Add "Mirror" mod to osu!catch --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 1 + .../Mods/CatchModMirror.cs | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index eafa1b9b9d..9fee6b2bc1 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -117,6 +117,7 @@ namespace osu.Game.Rulesets.Catch { new CatchModDifficultyAdjust(), new CatchModClassic(), + new CatchModMirror(), }; case ModType.Automation: diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs new file mode 100644 index 0000000000..8eb092bfb3 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -0,0 +1,39 @@ +// 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 osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModMirror : ModMirror, IApplicableToHitObject + { + public override string Description => "Fruits are flipped horizontally."; + + public void ApplyToHitObject(HitObject hitObject) + { + var catchObject = (CatchHitObject)hitObject; + + if (catchObject is BananaShower) + return; + + catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX; + + foreach (var nested in catchObject.NestedHitObjects.Cast()) + nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX; + + if (!(catchObject is JuiceStream juiceStream)) + return; + + var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + + juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); + } + } +} From 3a741affa325fac471a06febff810827a76f44d5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 8 Aug 2021 21:27:48 +0300 Subject: [PATCH 07/29] Remove whitespaces --- osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index 8eb092bfb3..ce5b78e12f 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Mods public class CatchModMirror : ModMirror, IApplicableToHitObject { public override string Description => "Fruits are flipped horizontally."; - + public void ApplyToHitObject(HitObject hitObject) { var catchObject = (CatchHitObject)hitObject; From 30cda318f93fe5947cb44335b9ed163e2294d6fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 15:57:18 +0900 Subject: [PATCH 08/29] Reorganise code slightly --- .../Mods/CatchModMirror.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index ce5b78e12f..7fe7e70fd0 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -16,24 +16,24 @@ namespace osu.Game.Rulesets.Catch.Mods public void ApplyToHitObject(HitObject hitObject) { - var catchObject = (CatchHitObject)hitObject; - - if (catchObject is BananaShower) + if (hitObject is BananaShower) return; + var catchObject = (CatchHitObject)hitObject; + catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX; foreach (var nested in catchObject.NestedHitObjects.Cast()) nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX; - if (!(catchObject is JuiceStream juiceStream)) - return; + if (catchObject is JuiceStream juiceStream) + { + var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); - var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); - foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); - - juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); + juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); + } } } } From 1f69c61fd87a5a8b983bdcdbbca6be6d7811e198 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 16:38:21 +0900 Subject: [PATCH 09/29] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 7a0a542ee9..f757847c10 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0a6522f15e..3a840296ac 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 00222877f1..217ec6089a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 24accdcab05b99e1b01a8f71d0f41609738d5d3c Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Mon, 9 Aug 2021 18:56:47 +1000 Subject: [PATCH 10/29] Add LegacyUtils class with non linear colour interpolation method --- .../Skinning/Legacy/LegacySliderBody.cs | 12 ++-- osu.Game/Utils/LegacyUtils.cs | 65 +++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Utils/LegacyUtils.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index a8bb69c080..92941665e0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -3,9 +3,9 @@ using System; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; +using osu.Game.Utils; using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy @@ -37,14 +37,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy position -= realBorderPortion; - // Stable interpolates slider body colour directly in sRGB space, and because - // Interpolation.ValueAt() uses linear space, we have to counteract applying it - // by calling ToSRGB() on the input colours, and ToLinear() on the resulting colour. + Color4 outerColour = AccentColour.Darken(0.1f); + Color4 innerColour = lighten(AccentColour, 0.5f); - Color4 outerColour = AccentColour.Darken(0.1f).ToSRGB(); - Color4 innerColour = lighten(AccentColour, 0.5f).ToSRGB(); - - return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1).ToLinear(); + return LegacyUtils.InterpolateNonLinear(position / realGradientPortion, outerColour, innerColour, 0, 1); } /// diff --git a/osu.Game/Utils/LegacyUtils.cs b/osu.Game/Utils/LegacyUtils.cs new file mode 100644 index 0000000000..9351125acd --- /dev/null +++ b/osu.Game/Utils/LegacyUtils.cs @@ -0,0 +1,65 @@ +// 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.Graphics; +using osu.Framework.Graphics.Transforms; +using osuTK.Graphics; + +namespace osu.Game.Utils +{ + public static class LegacyUtils + { + public static Color4 InterpolateNonLinear(double time, Color4 startColour, Color4 endColour, double startTime, double endTime, Easing easing = Easing.None) + => InterpolateNonLinear(time, startColour, endColour, startTime, endTime, new DefaultEasingFunction(easing)); + + public static Colour4 InterpolateNonLinear(double time, Colour4 startColour, Colour4 endColour, double startTime, double endTime, Easing easing = Easing.None) + => InterpolateNonLinear(time, startColour, endColour, startTime, endTime, new DefaultEasingFunction(easing)); + + /// + /// Interpolates between two sRGB s directly in sRGB space. + /// + public static Color4 InterpolateNonLinear(double time, Color4 startColour, Color4 endColour, double startTime, double endTime, TEasing easing) where TEasing : IEasingFunction + { + if (startColour == endColour) + return startColour; + + double current = time - startTime; + double duration = endTime - startTime; + + if (duration == 0 || current == 0) + return startColour; + + float t = Math.Max(0, Math.Min(1, (float)easing.ApplyEasing(current / duration))); + + return new Color4( + startColour.R + t * (endColour.R - startColour.R), + startColour.G + t * (endColour.G - startColour.G), + startColour.B + t * (endColour.B - startColour.B), + startColour.A + t * (endColour.A - startColour.A)); + } + + /// + /// Interpolates between two sRGB s directly in sRGB space. + /// + public static Colour4 InterpolateNonLinear(double time, Colour4 startColour, Colour4 endColour, double startTime, double endTime, in TEasing easing) where TEasing : IEasingFunction + { + if (startColour == endColour) + return startColour; + + double current = time - startTime; + double duration = endTime - startTime; + + if (duration == 0 || current == 0) + return startColour; + + float t = Math.Max(0, Math.Min(1, (float)easing.ApplyEasing(current / duration))); + + return new Colour4( + startColour.R + t * (endColour.R - startColour.R), + startColour.G + t * (endColour.G - startColour.G), + startColour.B + t * (endColour.B - startColour.B), + startColour.A + t * (endColour.A - startColour.A)); + } + } +} From ea6e441dec4201f10fe21bd71aea833f1cf5c9a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 19:18:13 +0900 Subject: [PATCH 11/29] Simplify method of marking players as playing in test scenes --- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 9 ++++++--- .../TestSceneMultiplayerGameplayLeaderboard.cs | 7 ++++--- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 14 ++++++++------ .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- .../Visual/Multiplayer/TestMultiplayerClient.cs | 11 ++++++++++- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 072e32370d..e9fae32335 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.UI; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { @@ -47,8 +48,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("start players silently", () => { - Client.CurrentMatchPlayingUserIds.Add(PLAYER_1_ID); - Client.CurrentMatchPlayingUserIds.Add(PLAYER_2_ID); + OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true); + OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true); + playingUserIds.Add(PLAYER_1_ID); playingUserIds.Add(PLAYER_2_ID); }); @@ -264,7 +266,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { foreach (int id in userIds) { - Client.CurrentMatchPlayingUserIds.Add(id); + OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true); + SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId); playingUserIds.Add(id); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 0e368b59dd..8121492a0b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -20,6 +20,7 @@ using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.Spectator; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { @@ -53,10 +54,10 @@ namespace osu.Game.Tests.Visual.Multiplayer var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); foreach (var user in users) + { SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - - // Todo: This is REALLY bad. - Client.CurrentMatchPlayingUserIds.AddRange(users); + OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true); + } Children = new Drawable[] { diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index bffb2d341a..14beb38cde 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -62,7 +62,9 @@ namespace osu.Game.Online.Multiplayer /// /// The users in the joined which are participating in the current gameplay loop. /// - public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); + public IBindableList CurrentMatchPlayingUserIds => PlayingUserIds; + + protected readonly BindableList PlayingUserIds = new BindableList(); public readonly Bindable CurrentMatchPlayingItem = new Bindable(); @@ -179,7 +181,7 @@ namespace osu.Game.Online.Multiplayer { APIRoom = null; Room = null; - CurrentMatchPlayingUserIds.Clear(); + PlayingUserIds.Clear(); RoomUpdated?.Invoke(); }); @@ -376,7 +378,7 @@ namespace osu.Game.Online.Multiplayer return; Room.Users.Remove(user); - CurrentMatchPlayingUserIds.Remove(user.UserID); + PlayingUserIds.Remove(user.UserID); RoomUpdated?.Invoke(); }, false); @@ -659,16 +661,16 @@ namespace osu.Game.Online.Multiplayer /// The new state of the user. private void updateUserPlayingState(int userId, MultiplayerUserState state) { - bool wasPlaying = CurrentMatchPlayingUserIds.Contains(userId); + bool wasPlaying = PlayingUserIds.Contains(userId); bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; if (isPlaying == wasPlaying) return; if (isPlaying) - CurrentMatchPlayingUserIds.Add(userId); + PlayingUserIds.Add(userId); else - CurrentMatchPlayingUserIds.Remove(userId); + PlayingUserIds.Remove(userId); } private Task scheduleAsync(Action action, CancellationToken cancellationToken = default) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index a10c16fcd5..7ee77759b0 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Play.HUD private UserLookupCache userLookupCache { get; set; } private readonly ScoreProcessor scoreProcessor; - private readonly BindableList playingUsers; + private readonly IBindableList playingUsers; private Bindable scoringMode; /// diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 43aadf5acb..cffaea5c94 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -50,7 +50,16 @@ namespace osu.Game.Tests.Visual.Multiplayer public void Disconnect() => isConnected.Value = false; - public void AddUser(User user) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(user.Id) { User = user }); + public MultiplayerRoomUser AddUser(User user, bool markAsPlaying = false) + { + var roomUser = new MultiplayerRoomUser(user.Id) { User = user }; + ((IMultiplayerClient)this).UserJoined(roomUser); + + if (markAsPlaying) + PlayingUserIds.Add(user.Id); + + return roomUser; + } public void AddNullUser(int userId) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(userId)); From 76e5a40b8ed15dfba6bf5812743a5bbf1f15a542 Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Mon, 9 Aug 2021 20:53:02 +1000 Subject: [PATCH 12/29] Remove unnecessary "in" keyword --- osu.Game/Utils/LegacyUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/LegacyUtils.cs b/osu.Game/Utils/LegacyUtils.cs index 9351125acd..64306adf50 100644 --- a/osu.Game/Utils/LegacyUtils.cs +++ b/osu.Game/Utils/LegacyUtils.cs @@ -42,7 +42,7 @@ namespace osu.Game.Utils /// /// Interpolates between two sRGB s directly in sRGB space. /// - public static Colour4 InterpolateNonLinear(double time, Colour4 startColour, Colour4 endColour, double startTime, double endTime, in TEasing easing) where TEasing : IEasingFunction + public static Colour4 InterpolateNonLinear(double time, Colour4 startColour, Colour4 endColour, double startTime, double endTime, TEasing easing) where TEasing : IEasingFunction { if (startColour == endColour) return startColour; From c5b490c4414d64a9dc80353850cad62d707aa85d Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Tue, 10 Aug 2021 11:29:31 +1000 Subject: [PATCH 13/29] Use non linear colour interpolation for legacy health display --- osu.Game/Skinning/LegacyHealthDisplay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 67280e4acd..b1cd1f86c0 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; using osu.Game.Screens.Play.HUD; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -83,10 +84,10 @@ namespace osu.Game.Skinning private static Color4 getFillColour(double hp) { if (hp < 0.2) - return Interpolation.ValueAt(0.2 - hp, Color4.Black, Color4.Red, 0, 0.2); + return LegacyUtils.InterpolateNonLinear(0.2 - hp, Color4.Black, Color4.Red, 0, 0.2); if (hp < epic_cutoff) - return Interpolation.ValueAt(0.5 - hp, Color4.White, Color4.Black, 0, 0.5); + return LegacyUtils.InterpolateNonLinear(0.5 - hp, Color4.White, Color4.Black, 0, 0.5); return Color4.White; } From 3aa72163f234ab22dff7a350b1aeba9cafdb76c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 15:14:43 +0900 Subject: [PATCH 14/29] Add simple download progress display to download buttons on playlist items --- osu.Game/Graphics/UserInterface/GrayButton.cs | 4 ++-- .../BeatmapListing/Panels/BeatmapPanelDownloadButton.cs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/GrayButton.cs b/osu.Game/Graphics/UserInterface/GrayButton.cs index 88c46f29e0..0a2c83d5a8 100644 --- a/osu.Game/Graphics/UserInterface/GrayButton.cs +++ b/osu.Game/Graphics/UserInterface/GrayButton.cs @@ -27,7 +27,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load() { - Children = new Drawable[] + AddRange(new Drawable[] { Background = new Box { @@ -42,7 +42,7 @@ namespace osu.Game.Graphics.UserInterface Size = new Vector2(13), Icon = icon, }, - }; + }); } } } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index cec1a5ac12..47b477ef9a 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -37,6 +37,13 @@ namespace osu.Game.Overlays.BeatmapListing.Panels RelativeSizeAxes = Axes.Both, }, }; + + button.Add(new DownloadProgressBar(beatmapSet) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Depth = -1, + }); } protected override void LoadComplete() From 5ea7909eeae16e20c265c95889f459cd0b7e64ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 16:18:57 +0900 Subject: [PATCH 15/29] Fix incorrectly failing test --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index dfb78a235b..5e30cb86cc 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -15,6 +15,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; using osu.Game.Online.Rooms; using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; @@ -215,7 +216,7 @@ namespace osu.Game.Tests.Visual.Multiplayer assertDownloadButtonVisible(false); void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}", - () => playlist.ChildrenOfType().Single().Alpha == (visible ? 1 : 0)); + () => playlist.ChildrenOfType().Single().Alpha == (visible ? 1 : 0)); } [Test] @@ -229,7 +230,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createPlaylist(byOnlineId, byChecksum); - AddAssert("download buttons shown", () => playlist.ChildrenOfType().All(d => d.IsPresent)); + AddAssert("download buttons shown", () => playlist.ChildrenOfType().All(d => d.IsPresent)); } [Test] From c63dfa21e130ebf85bf6f860a5fbb427f0eb2f26 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 10 Aug 2021 16:34:38 +0900 Subject: [PATCH 16/29] Always initialize DHO transforms on LoadComplete With the previous commit, the transform application is skipped when the state is already changed. But it turns out the previous commit breaks slider animation in the standard editor. This is probably due to the transforms are applied before nested hit objects are added. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ff6168ee37..29d8a475ef 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -190,9 +190,8 @@ namespace osu.Game.Rulesets.Objects.Drawables comboIndexBindable.BindValueChanged(_ => UpdateComboColour()); comboIndexWithOffsetsBindable.BindValueChanged(_ => UpdateComboColour(), true); - // If the state is changed, transforms are already initialized. - if (state.Value == ArmedState.Idle) - updateState(ArmedState.Idle, true); + // Apply transforms + updateState(State.Value, true); } /// From 2b9168157db3e99b2ee759cd71d5114c9642faa2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 16:50:39 +0900 Subject: [PATCH 17/29] Fix `CurrentMatchPlayingItem` not being reset on leaving a multiplayer room --- .../NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs | 4 ++++ osu.Game/Online/Multiplayer/MultiplayerClient.cs | 1 + 2 files changed, 5 insertions(+) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 0983b806e2..07ec86b0e7 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -24,6 +24,8 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddRepeatStep("add some users", () => Client.AddUser(new User { Id = id++ }), 5); checkPlayingUserCount(0); + AddAssert("playlist item is available", () => Client.CurrentMatchPlayingItem.Value != null); + changeState(3, MultiplayerUserState.WaitingForLoad); checkPlayingUserCount(3); @@ -41,6 +43,8 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddStep("leave room", () => Client.LeaveRoom()); checkPlayingUserCount(0); + + AddAssert("playlist item is null", () => Client.CurrentMatchPlayingItem.Value == null); } [Test] diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 14beb38cde..dafc737ba2 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -181,6 +181,7 @@ namespace osu.Game.Online.Multiplayer { APIRoom = null; Room = null; + CurrentMatchPlayingItem.Value = null; PlayingUserIds.Clear(); RoomUpdated?.Invoke(); From e0e0d5deb0f7debb5ffce87075a847304bdf759c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Aug 2021 17:25:10 +0900 Subject: [PATCH 18/29] Remove unused using --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 5e30cb86cc..93bdbb79f4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -14,7 +14,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; using osu.Game.Online.Rooms; -using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; From d9b5f235d88a28ba1577a94f479016b1ef81d397 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 17:36:58 +0900 Subject: [PATCH 19/29] Add xmldoc explaining thread safety limitations of `IModelManager` "events" --- osu.Game/Database/IModelManager.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 7f7e5565f1..8c314f1617 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -13,8 +13,16 @@ namespace osu.Game.Database public interface IModelManager where TModel : class { + /// + /// A bindable which contains a weak reference to the last item that was updated. + /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. + /// IBindable> ItemUpdated { get; } + /// + /// A bindable which contains a weak reference to the last item that was removed. + /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. + /// IBindable> ItemRemoved { get; } } } From b121d95400727bedb1c6d929d8ed5a741e904399 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 17:37:24 +0900 Subject: [PATCH 20/29] Avoid potential null reference exception in `OnlinePlayBeatmapAvailabilityTracker` --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 72ea84d4a8..86879ba245 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -59,8 +59,8 @@ namespace osu.Game.Online.Rooms protected override bool VerifyDatabasedModel(BeatmapSetInfo databasedSet) { - int? beatmapId = SelectedItem.Value.Beatmap.Value.OnlineBeatmapID; - string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; + int? beatmapId = SelectedItem.Value?.Beatmap.Value.OnlineBeatmapID; + string checksum = SelectedItem.Value?.Beatmap.Value.MD5Hash; var matchingBeatmap = databasedSet.Beatmaps.FirstOrDefault(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); From f86ef54e93dc1ea0e346554bfc5ddbec8f2312f0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Aug 2021 17:36:40 +0900 Subject: [PATCH 21/29] Fix incorrect legacy slider body alpha --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index 92941665e0..1c8dfeac52 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy // Roughly matches osu!stable's slider border portions. => base.CalculatedBorderPortion * 0.77f; - public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f); + public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, 0.7f); protected override Color4 ColourAt(float position) { From 60302e3daae38014ed9577ffd0c7d172482a05a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 18:46:20 +0900 Subject: [PATCH 22/29] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f757847c10..c1eddfd428 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3a840296ac..55bac93f30 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 217ec6089a..5a0b21f51d 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 58db6b758d07150acbd2b58a92e3b7bea9120c17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 18:47:39 +0900 Subject: [PATCH 23/29] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c1eddfd428..8de516240f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 55bac93f30..59283084db 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5a0b21f51d..c8d3d150db 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 4268e4d75028d5ca43cf9786bb0a5efdcfb950a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 Aug 2021 14:19:05 +0200 Subject: [PATCH 24/29] Fix nested menus layering close samples if multiple menu levels are closed --- .../Cursor/OsuContextMenuContainer.cs | 9 +++++ .../Graphics/UserInterface/OsuContextMenu.cs | 29 +++++---------- .../UserInterface/OsuContextMenuSamples.cs | 35 +++++++++++++++++++ 3 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/OsuContextMenuSamples.cs diff --git a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs index e64d2d98f4..171ad4ee65 100644 --- a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.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.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; @@ -9,6 +10,14 @@ namespace osu.Game.Graphics.Cursor { public class OsuContextMenuContainer : ContextMenuContainer { + [Cached] + private OsuContextMenuSamples samples = new OsuContextMenuSamples(); + + public OsuContextMenuContainer() + { + AddInternal(samples); + } + protected override Menu CreateMenu() => new OsuContextMenu(true); } } diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs index 7cf8b3eca8..cf201b18b4 100644 --- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs @@ -4,8 +4,6 @@ using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; @@ -16,16 +14,15 @@ namespace osu.Game.Graphics.UserInterface public class OsuContextMenu : OsuMenu { private const int fade_duration = 250; - private Sample sampleOpen; - private Sample sampleClose; - private Sample sampleClick; + + [Resolved] + private OsuContextMenuSamples samples { get; set; } // todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed. private bool wasOpened; private readonly bool playClickSample; - private readonly Menu parentMenu; - public OsuContextMenu(bool playClickSample = false, Menu parentMenu = null) + public OsuContextMenu(bool playClickSample = false) : base(Direction.Vertical) { MaskingContainer.CornerRadius = 5; @@ -41,16 +38,12 @@ namespace osu.Game.Graphics.UserInterface MaxHeight = 250; this.playClickSample = playClickSample; - this.parentMenu = parentMenu; } [BackgroundDependencyLoader] private void load(OsuColour colours, AudioManager audio) { BackgroundColour = colours.ContextMenuGray; - sampleClick = audio.Samples.Get($"UI/{HoverSampleSet.Default.GetDescription()}-select"); - sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); - sampleClose = audio.Samples.Get(@"UI/dropdown-close"); } protected override void AnimateOpen() @@ -58,10 +51,10 @@ namespace osu.Game.Graphics.UserInterface this.FadeIn(fade_duration, Easing.OutQuint); if (playClickSample) - sampleClick?.Play(); + samples.PlayClickSample(); if (!wasOpened) - sampleOpen?.Play(); + samples.PlayOpenSample(); wasOpened = true; } @@ -70,18 +63,12 @@ namespace osu.Game.Graphics.UserInterface { this.FadeOut(fade_duration, Easing.OutQuint); - if (parentMenu?.State == MenuState.Closed) - { - wasOpened = false; - return; - } - if (wasOpened) - sampleClose?.Play(); + samples.PlayCloseSample(); wasOpened = false; } - protected override Menu CreateSubMenu() => new OsuContextMenu(false, this); + protected override Menu CreateSubMenu() => new OsuContextMenu(); } } diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenuSamples.cs b/osu.Game/Graphics/UserInterface/OsuContextMenuSamples.cs new file mode 100644 index 0000000000..d67ea499e5 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuContextMenuSamples.cs @@ -0,0 +1,35 @@ +// 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.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; +using osu.Framework.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuContextMenuSamples : Component + { + private Sample sampleClick; + private Sample sampleOpen; + private Sample sampleClose; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + sampleClick = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); + sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); + sampleClose = audio.Samples.Get(@"UI/dropdown-close"); + } + + public void PlayClickSample() => Scheduler.AddOnce(playClickSample); + private void playClickSample() => sampleClick.Play(); + + public void PlayOpenSample() => Scheduler.AddOnce(playOpenSample); + private void playOpenSample() => sampleOpen.Play(); + + public void PlayCloseSample() => Scheduler.AddOnce(playCloseSample); + private void playCloseSample() => sampleClose.Play(); + } +} From 30cb4280a450664dd04fc13b31c80ec8f96798c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Aug 2021 22:21:54 +0200 Subject: [PATCH 25/29] Add test for catch beatmap mirroring --- .../Mods/CatchModMirrorTest.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs b/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs new file mode 100644 index 0000000000..8e66284a23 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs @@ -0,0 +1,122 @@ +// 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 NUnit.Framework; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Tests.Mods +{ + [TestFixture] + public class CatchModMirrorTest + { + [Test] + public void TestModMirror() + { + IBeatmap original = createBeatmap(false); + IBeatmap mirrored = createBeatmap(true); + + assertEffectivePositionsMirrored(original, mirrored); + } + + private static IBeatmap createBeatmap(bool withMirrorMod) + { + var beatmap = createRawBeatmap(); + var mirrorMod = new CatchModMirror(); + + var beatmapProcessor = new CatchBeatmapProcessor(beatmap); + beatmapProcessor.PreProcess(); + + foreach (var hitObject in beatmap.HitObjects) + { + hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + if (withMirrorMod) + mirrorMod.ApplyToHitObject(hitObject); + } + + beatmapProcessor.PostProcess(); + + return beatmap; + } + + private static IBeatmap createRawBeatmap() => new Beatmap + { + HitObjects = new List + { + new Fruit + { + OriginalX = 150, + StartTime = 0 + }, + new Fruit + { + OriginalX = 450, + StartTime = 500 + }, + new JuiceStream + { + OriginalX = 250, + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(new Vector2(-100, 1)), + new PathControlPoint(new Vector2(0, 2)), + new PathControlPoint(new Vector2(100, 3)), + new PathControlPoint(new Vector2(0, 4)) + } + }, + StartTime = 1000, + }, + new BananaShower + { + StartTime = 5000, + Duration = 5000 + } + } + }; + + private static void assertEffectivePositionsMirrored(IBeatmap original, IBeatmap mirrored) + { + if (original.HitObjects.Count != mirrored.HitObjects.Count) + Assert.Fail($"Top-level object count mismatch (original: {original.HitObjects.Count}, mirrored: {mirrored.HitObjects.Count})"); + + for (int i = 0; i < original.HitObjects.Count; ++i) + { + var originalObject = (CatchHitObject)original.HitObjects[i]; + var mirroredObject = (CatchHitObject)mirrored.HitObjects[i]; + + // banana showers themselves are exempt, as we only really care about their nested bananas' positions. + if (!effectivePositionMirrored(originalObject, mirroredObject) && !(originalObject is BananaShower)) + Assert.Fail($"{originalObject.GetType().Name} at time {originalObject.StartTime} is not mirrored ({printEffectivePositions(originalObject, mirroredObject)})"); + + if (originalObject.NestedHitObjects.Count != mirroredObject.NestedHitObjects.Count) + Assert.Fail($"{originalObject.GetType().Name} nested object count mismatch (original: {originalObject.NestedHitObjects.Count}, mirrored: {mirroredObject.NestedHitObjects.Count})"); + + for (int j = 0; j < originalObject.NestedHitObjects.Count; ++j) + { + var originalNested = (CatchHitObject)originalObject.NestedHitObjects[j]; + var mirroredNested = (CatchHitObject)mirroredObject.NestedHitObjects[j]; + + if (!effectivePositionMirrored(originalNested, mirroredNested)) + Assert.Fail($"{originalObject.GetType().Name}'s nested {originalNested.GetType().Name} at time {originalObject.StartTime} is not mirrored ({printEffectivePositions(originalNested, mirroredNested)})"); + } + } + } + + private static string printEffectivePositions(CatchHitObject original, CatchHitObject mirrored) + => $"original X: {original.EffectiveX}, mirrored X is: {mirrored.EffectiveX}, mirrored X should be: {CatchPlayfield.WIDTH - original.EffectiveX}"; + + private static bool effectivePositionMirrored(CatchHitObject original, CatchHitObject mirrored) + => Precision.AlmostEquals(original.EffectiveX, CatchPlayfield.WIDTH - mirrored.EffectiveX); + } +} From 637e5cb6b713bd942c6dd58d6f1a56c0ead646b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Aug 2021 21:42:41 +0200 Subject: [PATCH 26/29] Fix offsets not being mirrored --- .../Mods/CatchModMirrorTest.cs | 8 +++---- .../Mods/CatchModMirror.cs | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs b/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs index 8e66284a23..fbbfee6b60 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs @@ -36,15 +36,13 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods beatmapProcessor.PreProcess(); foreach (var hitObject in beatmap.HitObjects) - { hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - if (withMirrorMod) - mirrorMod.ApplyToHitObject(hitObject); - } - beatmapProcessor.PostProcess(); + if (withMirrorMod) + mirrorMod.ApplyToBeatmap(beatmap); + return beatmap; } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index 7fe7e70fd0..3aa8862a0a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Catch.UI; @@ -10,11 +12,22 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModMirror : ModMirror, IApplicableToHitObject + public class CatchModMirror : ModMirror, IApplicableToBeatmap { public override string Description => "Fruits are flipped horizontally."; - public void ApplyToHitObject(HitObject hitObject) + /// + /// is used instead of , + /// as applies offsets in . + /// runs after post-processing, while runs before it. + /// + public void ApplyToBeatmap(IBeatmap beatmap) + { + foreach (var hitObject in beatmap.HitObjects) + applyToHitObject(hitObject); + } + + private void applyToHitObject(HitObject hitObject) { if (hitObject is BananaShower) return; @@ -22,9 +35,13 @@ namespace osu.Game.Rulesets.Catch.Mods var catchObject = (CatchHitObject)hitObject; catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX; + catchObject.XOffset = -catchObject.XOffset; foreach (var nested in catchObject.NestedHitObjects.Cast()) + { nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX; + nested.XOffset = -nested.XOffset; + } if (catchObject is JuiceStream juiceStream) { From a9e53107d146ac1fad598136b439f9296a1c51a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Aug 2021 21:51:46 +0200 Subject: [PATCH 27/29] Also mirror banana showers --- .../Mods/CatchModMirror.cs | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index 3aa8862a0a..932c8cad85 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -29,11 +29,30 @@ namespace osu.Game.Rulesets.Catch.Mods private void applyToHitObject(HitObject hitObject) { - if (hitObject is BananaShower) - return; - var catchObject = (CatchHitObject)hitObject; + switch (catchObject) + { + case Fruit fruit: + mirrorEffectiveX(fruit); + break; + + case JuiceStream juiceStream: + mirrorEffectiveX(juiceStream); + mirrorJuiceStreamPath(juiceStream); + break; + + case BananaShower bananaShower: + mirrorBananaShower(bananaShower); + break; + } + } + + /// + /// Mirrors the effective X position of and its nested hit objects. + /// + private static void mirrorEffectiveX(CatchHitObject catchObject) + { catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX; catchObject.XOffset = -catchObject.XOffset; @@ -42,15 +61,27 @@ namespace osu.Game.Rulesets.Catch.Mods nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX; nested.XOffset = -nested.XOffset; } + } - if (catchObject is JuiceStream juiceStream) - { - var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); - foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + /// + /// Mirrors the path of the . + /// + private static void mirrorJuiceStreamPath(JuiceStream juiceStream) + { + var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); - juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); - } + juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); + } + + /// + /// Mirrors X positions of all bananas in the . + /// + private static void mirrorBananaShower(BananaShower bananaShower) + { + foreach (var banana in bananaShower.NestedHitObjects.OfType()) + banana.XOffset = CatchPlayfield.WIDTH - banana.XOffset; } } } From a3a9d0579f13166a975d75dec3cdf8cc40d9c511 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 12:33:14 +0900 Subject: [PATCH 28/29] Adjust checkbox / sliderbar animation speeds to match sound effects better --- osu.Game/Graphics/UserInterface/Nub.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 18d8b880ea..8d686e8c2f 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -21,6 +21,9 @@ namespace osu.Game.Graphics.UserInterface private const float border_width = 3; + private const double animate_in_duration = 150; + private const double animate_out_duration = 500; + public Nub() { Box fill; @@ -77,20 +80,26 @@ namespace osu.Game.Graphics.UserInterface if (value) { - this.FadeColour(GlowingAccentColour, 500, Easing.OutQuint); - FadeEdgeEffectTo(1, 500, Easing.OutQuint); + this.FadeColour(GlowingAccentColour, animate_in_duration, Easing.OutQuint); + FadeEdgeEffectTo(1, animate_in_duration, Easing.OutQuint); } else { - FadeEdgeEffectTo(0, 500); - this.FadeColour(AccentColour, 500); + FadeEdgeEffectTo(0, animate_out_duration); + this.FadeColour(AccentColour, animate_out_duration); } } } public bool Expanded { - set => this.ResizeTo(new Vector2(value ? EXPANDED_SIZE : COLLAPSED_SIZE, 12), 500, Easing.OutQuint); + set + { + if (value) + this.ResizeTo(new Vector2(EXPANDED_SIZE, 12), animate_in_duration, Easing.OutQuint); + else + this.ResizeTo(new Vector2(COLLAPSED_SIZE, 12), animate_out_duration, Easing.OutQuint); + } } private readonly Bindable current = new Bindable(); From 0ffe740ca1dcce1e2c7fa276f96f523b004b0fed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 16:24:47 +0900 Subject: [PATCH 29/29] Bring back `TestSceneOsuGame` I marked this as headless to avoid it being "ungrouped", but it turns out this is quite useful to have around and I have searched for it on multiple occasions. --- osu.Game.Tests/Visual/{ => Navigation}/TestSceneOsuGame.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename osu.Game.Tests/Visual/{ => Navigation}/TestSceneOsuGame.cs (98%) diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs similarity index 98% rename from osu.Game.Tests/Visual/TestSceneOsuGame.cs rename to osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index c52d846a68..26641214b1 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; -using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -30,10 +29,9 @@ using osu.Game.Skinning; using osu.Game.Utils; using osuTK.Graphics; -namespace osu.Game.Tests.Visual +namespace osu.Game.Tests.Visual.Navigation { [TestFixture] - [HeadlessTest] public class TestSceneOsuGame : OsuTestScene { private IReadOnlyList requiredGameDependencies => new[]