From 0fb75233ffe501b51ca5cf605f3390c87695dcb9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 10 Dec 2024 23:02:26 +0900 Subject: [PATCH] Add "freeplay" button to multiplayer song select --- .../OnlinePlay/FooterButtonFreePlay.cs | 94 +++++++++++++++++++ .../OnlinePlay/OnlinePlaySongSelect.cs | 55 ++++++++--- .../Playlists/PlaylistsSongSelect.cs | 3 +- osu.Game/Screens/Select/SongSelect.cs | 7 +- 4 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/FooterButtonFreePlay.cs diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreePlay.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreePlay.cs new file mode 100644 index 0000000000..367857e780 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreePlay.cs @@ -0,0 +1,94 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Screens.Select; + +namespace osu.Game.Screens.OnlinePlay +{ + public class FooterButtonFreePlay : FooterButton, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + private OsuSpriteText text = null!; + private Circle circle = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + ButtonContentContainer.AddRange(new[] + { + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + circle = new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colours.YellowDark, + RelativeSizeAxes = Axes.Both, + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(5), + UseFullGlyphHeight = false, + } + } + } + }); + + SelectedColour = colours.Yellow; + DeselectedColour = SelectedColour.Opacity(0.5f); + Text = @"freeplay"; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => updateDisplay(), true); + + // Overwrite any external behaviour as we delegate the main toggle action to a sub-button. + Action = () => current.Value = !current.Value; + } + + private void updateDisplay() + { + if (current.Value) + { + text.Text = "on"; + text.FadeColour(colours.Gray2, 200, Easing.OutQuint); + circle.FadeColour(colours.Yellow, 200, Easing.OutQuint); + } + else + { + text.Text = "off"; + text.FadeColour(colours.GrayF, 200, Easing.OutQuint); + circle.FadeColour(colours.Gray4, 200, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index f6b6dfd3ab..1f1d259d0a 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -41,10 +41,12 @@ namespace osu.Game.Screens.OnlinePlay protected override UserActivity InitialActivity => new UserActivity.InLobby(room); protected readonly Bindable> FreeMods = new Bindable>(Array.Empty()); + protected readonly Bindable FreePlay = new Bindable(); private readonly Room room; private readonly PlaylistItem? initialItem; - private readonly FreeModSelectOverlay freeModSelectOverlay; + private readonly FreeModSelectOverlay freeModSelect; + private FooterButton freeModsFooterButton = null!; private IDisposable? freeModSelectOverlayRegistration; @@ -61,7 +63,7 @@ namespace osu.Game.Screens.OnlinePlay Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; - freeModSelectOverlay = new FreeModSelectOverlay + freeModSelect = new FreeModSelectOverlay { SelectedMods = { BindTarget = FreeMods }, IsValidMod = IsValidFreeMod, @@ -72,7 +74,7 @@ namespace osu.Game.Screens.OnlinePlay private void load() { LeftArea.Padding = new MarginPadding { Top = Header.HEIGHT }; - LoadComponent(freeModSelectOverlay); + LoadComponent(freeModSelect); } protected override void LoadComplete() @@ -108,12 +110,36 @@ namespace osu.Game.Screens.OnlinePlay Mods.Value = initialItem.RequiredMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); FreeMods.Value = initialItem.AllowedMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); } + + if (initialItem.BeatmapSetId != null) + FreePlay.Value = true; } Mods.BindValueChanged(onModsChanged); Ruleset.BindValueChanged(onRulesetChanged); + FreePlay.BindValueChanged(onFreePlayChanged, true); - freeModSelectOverlayRegistration = OverlayManager?.RegisterBlockingOverlay(freeModSelectOverlay); + freeModSelectOverlayRegistration = OverlayManager?.RegisterBlockingOverlay(freeModSelect); + } + + private void onFreePlayChanged(ValueChangedEvent enabled) + { + if (enabled.NewValue) + { + freeModsFooterButton.Enabled.Value = false; + ModsFooterButton.Enabled.Value = false; + + ModSelect.Hide(); + freeModSelect.Hide(); + + Mods.Value = []; + FreeMods.Value = []; + } + else + { + freeModsFooterButton.Enabled.Value = true; + ModsFooterButton.Enabled.Value = true; + } } private void onModsChanged(ValueChangedEvent> mods) @@ -121,7 +147,7 @@ namespace osu.Game.Screens.OnlinePlay FreeMods.Value = FreeMods.Value.Where(checkCompatibleFreeMod).ToList(); // Reset the validity delegate to update the overlay's display. - freeModSelectOverlay.IsValidMod = IsValidFreeMod; + freeModSelect.IsValidMod = IsValidFreeMod; } private void onRulesetChanged(ValueChangedEvent ruleset) @@ -135,7 +161,8 @@ namespace osu.Game.Screens.OnlinePlay { RulesetID = Ruleset.Value.OnlineID, RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(), - AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray() + AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray(), + BeatmapSetId = FreePlay.Value ? Beatmap.Value.BeatmapSetInfo.OnlineID : null }; return SelectItem(item); @@ -150,9 +177,9 @@ namespace osu.Game.Screens.OnlinePlay public override bool OnBackButton() { - if (freeModSelectOverlay.State.Value == Visibility.Visible) + if (freeModSelect.State.Value == Visibility.Visible) { - freeModSelectOverlay.Hide(); + freeModSelect.Hide(); return true; } @@ -161,7 +188,7 @@ namespace osu.Game.Screens.OnlinePlay public override bool OnExiting(ScreenExitEvent e) { - freeModSelectOverlay.Hide(); + freeModSelect.Hide(); return base.OnExiting(e); } @@ -173,9 +200,15 @@ namespace osu.Game.Screens.OnlinePlay protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateSongSelectFooterButtons() { var baseButtons = base.CreateSongSelectFooterButtons().ToList(); - var freeModsButton = new FooterButtonFreeMods(freeModSelectOverlay) { Current = FreeMods }; - baseButtons.Insert(baseButtons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (freeModsButton, freeModSelectOverlay)); + freeModsFooterButton = new FooterButtonFreeMods(freeModSelect) { Current = FreeMods }; + var freePlayButton = new FooterButtonFreePlay { Current = FreePlay }; + + baseButtons.InsertRange(baseButtons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, new (FooterButton, OverlayContainer?)[] + { + (freeModsFooterButton, freeModSelect), + (freePlayButton, null) + }); return baseButtons; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs index 23824b6a73..f9e014a727 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs @@ -37,9 +37,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private PlaylistItem createNewItem() => new PlaylistItem(Beatmap.Value.BeatmapInfo) { ID = room.Playlist.Count == 0 ? 0 : room.Playlist.Max(p => p.ID) + 1, + BeatmapSetId = FreePlay.Value ? Beatmap.Value.BeatmapSetInfo.OnlineID : null, RulesetID = Ruleset.Value.OnlineID, RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(), - AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray() + AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray(), }; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 9f7a2c02ff..9ebd9c9846 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -82,6 +82,11 @@ namespace osu.Game.Screens.Select /// protected Container FooterPanels { get; private set; } = null!; + /// + /// The that opens the mod select dialog. + /// + protected FooterButton ModsFooterButton { get; private set; } = null!; + /// /// Whether entering editor mode should be allowed. /// @@ -407,7 +412,7 @@ namespace osu.Game.Screens.Select /// A set of and an optional which the button opens when pressed. protected virtual IEnumerable<(FooterButton, OverlayContainer?)> CreateSongSelectFooterButtons() => new (FooterButton, OverlayContainer?)[] { - (new FooterButtonMods { Current = Mods }, ModSelect), + (ModsFooterButton = new FooterButtonMods { Current = Mods }, ModSelect), (new FooterButtonRandom { NextRandom = () => Carousel.SelectNextRandom(),