diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 029a7f8b9e..3728fb3f21 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); + return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); @@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); + return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); @@ -787,7 +787,7 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.MoveMouseTo(this.ChildrenOfType().Single(preset => preset.Preset.Value.Name == "Half Time 0.5x")); InputManager.Click(MouseButton.Left); }); - AddAssert("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.5)); + AddAssert("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.5)); // this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation, // it is instrumental in the reproduction of the failure scenario that this test is supposed to cover. @@ -796,7 +796,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("open customisation area", () => modSelectOverlay.CustomisationButton!.TriggerClick()); AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType().Single() .ChildrenOfType>().Single().TriggerClick()); - AddUntilStep("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.7)); + AddUntilStep("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.7)); } private void waitForColumnLoad() => AddUntilStep("all column content loaded", diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDifficultyMultiplierDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScoreMultiplierDisplay.cs similarity index 79% rename from osu.Game.Tests/Visual/UserInterface/TestSceneDifficultyMultiplierDisplay.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneScoreMultiplierDisplay.cs index 890c7295b4..c2ddd814b7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDifficultyMultiplierDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScoreMultiplierDisplay.cs @@ -1,10 +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 NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -12,17 +11,17 @@ using osu.Game.Overlays.Mods; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public partial class TestSceneDifficultyMultiplierDisplay : OsuTestScene + public partial class TestSceneScoreMultiplierDisplay : OsuTestScene { [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); [Test] - public void TestDifficultyMultiplierDisplay() + public void TestBasic() { - DifficultyMultiplierDisplay multiplierDisplay = null; + ScoreMultiplierDisplay multiplierDisplay = null!; - AddStep("create content", () => Child = multiplierDisplay = new DifficultyMultiplierDisplay + AddStep("create content", () => Child = multiplierDisplay = new ScoreMultiplierDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre @@ -34,7 +33,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddSliderStep("set multiplier", 0, 2, 1d, multiplier => { - if (multiplierDisplay != null) + if (multiplierDisplay.IsNotNull()) multiplierDisplay.Current.Value = multiplier; }); } diff --git a/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs b/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs deleted file mode 100644 index 952ca22678..0000000000 --- a/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; - -namespace osu.Game.Localisation -{ - public static class DifficultyMultiplierDisplayStrings - { - private const string prefix = @"osu.Game.Resources.Localisation.DifficultyMultiplierDisplay"; - - /// - /// "Difficulty Multiplier" - /// - public static LocalisableString DifficultyMultiplier => new TranslatableString(getKey(@"difficulty_multiplier"), @"Difficulty Multiplier"); - - private static string getKey(string key) => $@"{prefix}:{key}"; - } -} diff --git a/osu.Game/Localisation/ModSelectOverlayStrings.cs b/osu.Game/Localisation/ModSelectOverlayStrings.cs index 05dcf138d7..86ebebd293 100644 --- a/osu.Game/Localisation/ModSelectOverlayStrings.cs +++ b/osu.Game/Localisation/ModSelectOverlayStrings.cs @@ -44,6 +44,11 @@ namespace osu.Game.Localisation /// public static LocalisableString TabToSearch => new TranslatableString(getKey(@"tab_to_search"), @"tab to search..."); + /// + /// "Score Multiplier" + /// + public static LocalisableString ScoreMultiplier => new TranslatableString(getKey(@"score_multiplier"), @"Score Multiplier"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs index b55717fc17..7fccf0cc13 100644 --- a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs +++ b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Mods protected Box FrontBackground { get; private set; } = null!; [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; + protected OverlayColourProvider ColourProvider { get; private set; } = null!; [BackgroundDependencyLoader] private void load() @@ -98,9 +98,9 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - MainBackground.Colour = colourProvider.Background4; - FrontBackground.Colour = colourProvider.Background3; - Color4 glowColour = colourProvider.Background1; + MainBackground.Colour = ColourProvider.Background4; + FrontBackground.Colour = ColourProvider.Background3; + Color4 glowColour = ColourProvider.Background1; Content.BorderColour = ColourInfo.GradientVertical(MainBackground.Colour, glowColour); innerContent.BorderColour = ColourInfo.GradientVertical(FrontBackground.Colour, glowColour); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 33592f87f9..e1a49f8636 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Mods private DeselectAllModsButton deselectAllModsButton = null!; private Container aboveColumnsContent = null!; - private DifficultyMultiplierDisplay? multiplierDisplay; + private ScoreMultiplierDisplay? multiplierDisplay; private BeatmapAttributesDisplay? beatmapAttributesDisplay; protected ShearedButton BackButton { get; private set; } = null!; @@ -182,7 +182,7 @@ namespace osu.Game.Overlays.Mods aboveColumnsContent = new Container { RelativeSizeAxes = Axes.X, - Height = DifficultyMultiplierDisplay.HEIGHT, + Height = ScoreMultiplierDisplay.HEIGHT, Padding = new MarginPadding { Horizontal = 100 }, Child = SearchTextBox = new ShearedSearchTextBox { @@ -197,7 +197,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Top = DifficultyMultiplierDisplay.HEIGHT + PADDING, + Top = ScoreMultiplierDisplay.HEIGHT + PADDING, Bottom = PADDING }, RelativeSizeAxes = Axes.Both, @@ -266,7 +266,7 @@ namespace osu.Game.Overlays.Mods }, Children = new Drawable[] { - multiplierDisplay = new DifficultyMultiplierDisplay + multiplierDisplay = new ScoreMultiplierDisplay { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs similarity index 52% rename from osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs rename to osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs index 98dc3fa62e..c758632392 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs @@ -6,6 +6,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics; @@ -20,7 +22,7 @@ namespace osu.Game.Overlays.Mods /// /// On the mod select overlay, this provides a local updating view of the aggregate score multiplier coming from mods. /// - public partial class DifficultyMultiplierDisplay : ModFooterInformationDisplay, IHasCurrentValue + public partial class ScoreMultiplierDisplay : ModFooterInformationDisplay, IHasCurrentValue { public const float HEIGHT = 42; @@ -36,13 +38,12 @@ namespace osu.Game.Overlays.Mods private RollingCounter counter = null!; + private Box flashLayer = null!; + [Resolved] private OsuColour colours { get; set; } = null!; - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; - - public DifficultyMultiplierDisplay() + public ScoreMultiplierDisplay() { Current.Default = 1d; Current.Value = 1d; @@ -51,6 +52,27 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { + // You would think that we could add this to `Content`, but borders don't mix well + // with additive blending children elements. + AddInternal(new Container + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + CornerRadius = ShearedButton.CORNER_RADIUS, + Masking = true, + Children = new Drawable[] + { + flashLayer = new Box + { + Alpha = 0, + Blending = BlendingParameters.Additive, + RelativeSizeAxes = Axes.Both, + } + } + }); + LeftContent.AddRange(new Drawable[] { new OsuSpriteText @@ -58,20 +80,25 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.Centre, Origin = Anchor.Centre, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Text = DifficultyMultiplierDisplayStrings.DifficultyMultiplier, + Text = ModSelectOverlayStrings.ScoreMultiplier, Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) } }); - RightContent.Add(counter = new EffectCounter + RightContent.Add(new Container { - Margin = new MarginPadding(10), - AutoSizeAxes = Axes.Y, Width = 40, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + RelativeSizeAxes = Axes.Y, + Margin = new MarginPadding(10), Anchor = Anchor.Centre, Origin = Anchor.Centre, - Current = { BindTarget = Current } + Child = counter = new EffectCounter + { + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = Current } + } }); } @@ -81,8 +108,34 @@ namespace osu.Game.Overlays.Mods Current.BindValueChanged(e => { - var effect = calculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); - setColours(effect); + if (e.NewValue > Current.Default) + { + MainBackground + .FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); + counter.FadeColour(ColourProvider.Background5, transition_duration, Easing.OutQuint); + } + else if (e.NewValue < Current.Default) + { + MainBackground + .FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); + counter.FadeColour(ColourProvider.Background5, transition_duration, Easing.OutQuint); + } + else + { + MainBackground.FadeColour(ColourProvider.Background4, transition_duration, Easing.OutQuint); + counter.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); + } + + flashLayer + .FadeOutFromOne() + .FadeTo(0.15f, 60, Easing.OutQuint) + .Then().FadeOut(500, Easing.OutQuint); + + const float move_amount = 4; + if (e.NewValue > e.OldValue) + counter.MoveToY(Math.Max(-move_amount * 2, counter.Y - move_amount)).Then().MoveToY(0, transition_duration * 2, Easing.OutQuint); + else + counter.MoveToY(Math.Min(move_amount * 2, counter.Y + move_amount)).Then().MoveToY(0, transition_duration * 2, Easing.OutQuint); }, true); // required to prevent the counter initially rolling up from 0 to 1 @@ -90,56 +143,6 @@ namespace osu.Game.Overlays.Mods counter.SetCountWithoutRolling(Current.Value); } - /// - /// Fades colours of text and its background according to displayed value. - /// - /// Effect of the value. - private void setColours(ModEffect effect) - { - switch (effect) - { - case ModEffect.NotChanged: - MainBackground.FadeColour(colourProvider.Background4, transition_duration, Easing.OutQuint); - counter.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); - break; - - case ModEffect.DifficultyReduction: - MainBackground.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); - counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); - break; - - case ModEffect.DifficultyIncrease: - MainBackground.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); - counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); - break; - - default: - throw new ArgumentOutOfRangeException(nameof(effect)); - } - } - - /// - /// Converts signed integer into . Negative values are counted as difficulty reduction, positive as increase. - /// - /// Value to convert. Will arrive from comparison between bindable once it changes and it's . - /// Effect of the value. - private static ModEffect calculateEffectForComparison(int comparison) - { - if (comparison == 0) - return ModEffect.NotChanged; - if (comparison < 0) - return ModEffect.DifficultyReduction; - - return ModEffect.DifficultyIncrease; - } - - protected enum ModEffect - { - NotChanged, - DifficultyReduction, - DifficultyIncrease - } - private partial class EffectCounter : RollingCounter { protected override double RollingDuration => 500;