From ff6f797a3e0eef6e950b048386e72ca93598f0ae Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 11 Sep 2025 18:10:41 +0900 Subject: [PATCH] Add initial freemods button --- .../TestSceneFooterButtonFreeModsV2.cs | 44 +++++ .../OnlinePlay/FooterButtonFreeModsV2.cs | 171 ++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 osu.Game.Tests/Visual/Playlists/TestSceneFooterButtonFreeModsV2.cs create mode 100644 osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs diff --git a/osu.Game.Tests/Visual/Playlists/TestSceneFooterButtonFreeModsV2.cs b/osu.Game.Tests/Visual/Playlists/TestSceneFooterButtonFreeModsV2.cs new file mode 100644 index 0000000000..7eb82d5fd1 --- /dev/null +++ b/osu.Game.Tests/Visual/Playlists/TestSceneFooterButtonFreeModsV2.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.OnlinePlay; + +namespace osu.Game.Tests.Visual.Playlists +{ + public partial class TestSceneFooterButtonFreeModsV2 : OsuTestScene + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + + public TestSceneFooterButtonFreeModsV2() + { + ModSelectOverlay modSelectOverlay; + Add(modSelectOverlay = new TestModSelectOverlay()); + + FooterButtonFreeModsV2 button; + Add(button = new FooterButtonFreeModsV2(modSelectOverlay) + { + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + X = -100, + }); + + button.FreeMods.Value = new OsuRuleset().CreateAllMods().ToArray(); + } + + private partial class TestModSelectOverlay : UserModSelectOverlay + { + public TestModSelectOverlay() + : base(OverlayColourScheme.Aquamarine) + { + IsValidMod = _ => true; + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs new file mode 100644 index 0000000000..37755cd326 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs @@ -0,0 +1,171 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +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.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; +using osu.Game.Overlays; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Footer; +using osu.Game.Screens.Play.HUD; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay +{ + public partial class FooterButtonFreeModsV2 : ScreenFooterButton + { + private const float bar_height = 30f; + + public readonly Bindable> FreeMods = new Bindable>(); + public readonly IBindable Freestyle = new Bindable(); + + public new Action Action + { + set => throw new NotSupportedException("The click action is handled by the button itself."); + } + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + private ModDisplay modDisplay = null!; + private Container modContainer = null!; + private ModCountText overflowModCountDisplay = null!; + + public FooterButtonFreeModsV2(ModSelectOverlay overlay) + : base(overlay) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Text = "Freemods"; + Icon = FontAwesome.Solid.ExchangeAlt; + AccentColour = colours.Lime1; + + AddRange(new[] + { + new Container + { + Y = -5f, + Depth = float.MaxValue, + Origin = Anchor.BottomLeft, + Shear = OsuGame.SHEAR, + CornerRadius = CORNER_RADIUS, + Size = new Vector2(BUTTON_WIDTH, bar_height), + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 4, + // Figma says 50% opacity, but it does not match up visually if taken at face value, and looks bad. + Colour = Colour4.Black.Opacity(0.25f), + Offset = new Vector2(0, 2), + }, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both, + }, + modContainer = new Container + { + CornerRadius = CORNER_RADIUS, + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background3, + RelativeSizeAxes = Axes.Both, + }, + modDisplay = new ModDisplay(showExtendedInformation: true) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shear = -OsuGame.SHEAR, + Scale = new Vector2(0.5f), + Current = { BindTarget = FreeMods }, + ExpansionMode = ExpansionMode.AlwaysContracted, + }, + overflowModCountDisplay = new ModCountText { Mods = { BindTarget = FreeMods }, }, + } + }, + } + }, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Freestyle.BindValueChanged(f => Enabled.Value = !f.NewValue, true); + } + + protected override void Update() + { + base.Update(); + + if (FreeMods.Value.Count == 0) + return; + + if (modDisplay.DrawWidth * modDisplay.Scale.X > modContainer.DrawWidth) + overflowModCountDisplay.Show(); + else + overflowModCountDisplay.Hide(); + } + + private partial class ModCountText : CompositeDrawable + { + public readonly Bindable> Mods = new Bindable>(); + + private OsuSpriteText text = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + protected override void LoadComplete() + { + base.LoadComplete(); + + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colourProvider.Background3, + Alpha = 0.8f, + RelativeSizeAxes = Axes.Both, + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Torus.With(size: 14f, weight: FontWeight.Bold), + Shear = -OsuGame.SHEAR, + } + }; + + Mods.BindValueChanged(v => text.Text = ModSelectOverlayStrings.Mods(v.NewValue.Count).ToUpper(), true); + } + } + } +}