Initial implementation of freemod selection overlay

This commit is contained in:
smoogipoo 2021-01-27 22:15:53 +09:00
parent 4019cc38e5
commit 45e41aaeac
6 changed files with 216 additions and 28 deletions

View File

@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Mods;
using osu.Game.Screens.OnlinePlay.Multiplayer;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneFreeModSelectOverlay : MultiplayerTestScene
{
private ModSelectOverlay overlay;
[SetUp]
public new void Setup() => Schedule(() =>
{
Child = overlay = new FreeModSelectOverlay
{
State = { Value = Visibility.Visible }
};
});
}
}

View File

@ -174,7 +174,7 @@ namespace osu.Game.Overlays.Mods
switch (e.Button)
{
case MouseButton.Right:
SelectNext(-1);
OnRightClick(e);
break;
}
}
@ -183,10 +183,14 @@ namespace osu.Game.Overlays.Mods
protected override bool OnClick(ClickEvent e)
{
SelectNext(1);
return true;
}
protected virtual void OnRightClick(MouseUpEvent e)
{
SelectNext(-1);
}
/// <summary>
/// Select the next available mod in a specified direction.
/// </summary>

View File

@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Mods
{
public class ModSection : Container
{
private readonly OsuSpriteText headerLabel;
private readonly Drawable header;
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
@ -47,10 +47,7 @@ namespace osu.Game.Overlays.Mods
if (m == null)
return new ModButtonEmpty();
return new ModButton(m)
{
SelectionChanged = Action,
};
return CreateModButton(m).With(b => b.SelectionChanged = Action);
}).ToArray();
modsLoadCts?.Cancel();
@ -58,7 +55,7 @@ namespace osu.Game.Overlays.Mods
if (modContainers.Length == 0)
{
ModIconsLoaded = true;
headerLabel.Hide();
header.Hide();
Hide();
return;
}
@ -73,7 +70,7 @@ namespace osu.Game.Overlays.Mods
buttons = modContainers.OfType<ModButton>().ToArray();
headerLabel.FadeIn(200);
header.FadeIn(200);
this.FadeIn(200);
}
}
@ -160,16 +157,9 @@ namespace osu.Game.Overlays.Mods
Origin = Anchor.TopCentre;
Anchor = Anchor.TopCentre;
Children = new Drawable[]
Children = new[]
{
headerLabel = new OsuSpriteText
{
Origin = Anchor.TopLeft,
Anchor = Anchor.TopLeft,
Position = new Vector2(0f, 0f),
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = type.Humanize(LetterCasing.Title)
},
header = CreateHeader(type.Humanize(LetterCasing.Title)),
ButtonsContainer = new FillFlowContainer<ModButtonEmpty>
{
AutoSizeAxes = Axes.Y,
@ -185,5 +175,13 @@ namespace osu.Game.Overlays.Mods
},
};
}
protected virtual ModButton CreateModButton(Mod mod) => new ModButton(mod);
protected virtual Drawable CreateHeader(string text) => new OsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = text
};
}
}

View File

@ -0,0 +1,101 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
public class FreeModSelectOverlay : ModSelectOverlay
{
protected override ModSection CreateModSection(ModType type) => new FreeModSection(type);
private class FreeModSection : ModSection
{
private HeaderCheckbox checkbox;
public FreeModSection(ModType type)
: base(type)
{
}
protected override ModButton CreateModButton(Mod mod) => new FreeModButton(mod);
protected override Drawable CreateHeader(string text) => new Container
{
AutoSizeAxes = Axes.Y,
Width = 175,
Child = checkbox = new HeaderCheckbox
{
LabelText = text,
Changed = onCheckboxChanged
}
};
private void onCheckboxChanged(bool value)
{
foreach (var button in ButtonsContainer.OfType<ModButton>())
{
if (value)
// Note: Buttons where only part of the group has an implementation are not fully supported.
button.SelectAt(0);
else
button.Deselect();
}
}
protected override void Update()
{
base.Update();
// If any of the buttons aren't selected, deselect the checkbox.
foreach (var button in ButtonsContainer.OfType<ModButton>())
{
if (button.Mods.Any(m => m.HasImplementation) && !button.Selected)
checkbox.Current.Value = false;
}
}
}
private class HeaderCheckbox : OsuCheckbox
{
public Action<bool> Changed;
protected override void OnUserChange(bool value)
{
base.OnUserChange(value);
Changed?.Invoke(value);
}
}
private class FreeModButton : ModButton
{
public FreeModButton(Mod mod)
: base(mod)
{
}
protected override bool OnClick(ClickEvent e)
{
onClick();
return true;
}
protected override void OnRightClick(MouseUpEvent e) => onClick();
private void onClick()
{
if (Selected)
Deselect();
else
SelectNext(1);
}
}
}
}

View File

@ -6,17 +6,23 @@ using System.Linq;
using Humanizer;
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.UserInterface;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Select;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
@ -33,6 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private StatefulMultiplayerClient client { get; set; }
private LoadingLayer loadingLayer;
private FreeModSelectOverlay freeModSelectOverlay;
private WorkingBeatmap initialBeatmap;
private RulesetInfo initialRuleset;
@ -43,6 +50,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
public MultiplayerMatchSongSelect()
{
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
freeModSelectOverlay = new FreeModSelectOverlay();
}
[BackgroundDependencyLoader]
@ -52,6 +61,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
initialBeatmap = Beatmap.Value;
initialRuleset = Ruleset.Value;
initialMods = Mods.Value.ToList();
FooterPanels.Add(freeModSelectOverlay);
}
protected override bool OnStart()
@ -111,8 +122,61 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(isValidMod);
protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay(isValidMod);
protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons()
{
var buttons = base.CreateFooterButtons().ToList();
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods(), freeModSelectOverlay));
return buttons;
}
private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true;
}
public class FooterButtonFreeMods : FooterButton, IHasCurrentValue<IReadOnlyList<Mod>>
{
public Bindable<IReadOnlyList<Mod>> Current
{
get => modDisplay.Current;
set => modDisplay.Current = value;
}
private readonly ModDisplay modDisplay;
public FooterButtonFreeMods()
{
ButtonContentContainer.Add(modDisplay = new ModDisplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
DisplayUnrankedText = false,
Scale = new Vector2(0.8f),
ExpansionMode = ExpansionMode.AlwaysContracted,
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
SelectedColour = colours.Yellow;
DeselectedColour = SelectedColour.Opacity(0.5f);
Text = @"freemods";
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(_ => updateModDisplay(), true);
}
private void updateModDisplay()
{
if (Current.Value?.Count > 0)
modDisplay.FadeIn();
else
modDisplay.FadeOut();
}
}
}

View File

@ -28,19 +28,16 @@ namespace osu.Game.Screens.Select
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
/// <param name="button">THe button to be added.</param>
/// <param name="button">The button to be added.</param>
/// <param name="overlay">The <see cref="OverlayContainer"/> to be toggled by this button.</param>
public void AddButton(FooterButton button, OverlayContainer overlay)
{
overlays.Add(overlay);
button.Action = () => showOverlay(overlay);
if (overlay != null)
{
overlays.Add(overlay);
button.Action = () => showOverlay(overlay);
}
AddButton(button);
}
/// <param name="button">Button to be added.</param>
public void AddButton(FooterButton button)
{
button.Hovered = updateModeLight;
button.HoverLost = updateModeLight;