mirror of
https://github.com/ppy/osu.git
synced 2025-02-22 18:12:56 +08:00
Initial implementation of freemod selection overlay
This commit is contained in:
parent
4019cc38e5
commit
45e41aaeac
@ -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 }
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -174,7 +174,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
switch (e.Button)
|
switch (e.Button)
|
||||||
{
|
{
|
||||||
case MouseButton.Right:
|
case MouseButton.Right:
|
||||||
SelectNext(-1);
|
OnRightClick(e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,10 +183,14 @@ namespace osu.Game.Overlays.Mods
|
|||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
SelectNext(1);
|
SelectNext(1);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void OnRightClick(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
SelectNext(-1);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Select the next available mod in a specified direction.
|
/// Select the next available mod in a specified direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
public class ModSection : Container
|
public class ModSection : Container
|
||||||
{
|
{
|
||||||
private readonly OsuSpriteText headerLabel;
|
private readonly Drawable header;
|
||||||
|
|
||||||
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
|
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
|
||||||
|
|
||||||
@ -47,10 +47,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
if (m == null)
|
if (m == null)
|
||||||
return new ModButtonEmpty();
|
return new ModButtonEmpty();
|
||||||
|
|
||||||
return new ModButton(m)
|
return CreateModButton(m).With(b => b.SelectionChanged = Action);
|
||||||
{
|
|
||||||
SelectionChanged = Action,
|
|
||||||
};
|
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
modsLoadCts?.Cancel();
|
modsLoadCts?.Cancel();
|
||||||
@ -58,7 +55,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
if (modContainers.Length == 0)
|
if (modContainers.Length == 0)
|
||||||
{
|
{
|
||||||
ModIconsLoaded = true;
|
ModIconsLoaded = true;
|
||||||
headerLabel.Hide();
|
header.Hide();
|
||||||
Hide();
|
Hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -73,7 +70,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
buttons = modContainers.OfType<ModButton>().ToArray();
|
buttons = modContainers.OfType<ModButton>().ToArray();
|
||||||
|
|
||||||
headerLabel.FadeIn(200);
|
header.FadeIn(200);
|
||||||
this.FadeIn(200);
|
this.FadeIn(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,16 +157,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
Anchor = Anchor.TopCentre;
|
Anchor = Anchor.TopCentre;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
headerLabel = new OsuSpriteText
|
header = CreateHeader(type.Humanize(LetterCasing.Title)),
|
||||||
{
|
|
||||||
Origin = Anchor.TopLeft,
|
|
||||||
Anchor = Anchor.TopLeft,
|
|
||||||
Position = new Vector2(0f, 0f),
|
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Bold),
|
|
||||||
Text = type.Humanize(LetterCasing.Title)
|
|
||||||
},
|
|
||||||
ButtonsContainer = new FillFlowContainer<ModButtonEmpty>
|
ButtonsContainer = new FillFlowContainer<ModButtonEmpty>
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y,
|
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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
101
osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs
Normal file
101
osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,17 +6,23 @@ using System.Linq;
|
|||||||
using Humanizer;
|
using Humanizer;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||||
{
|
{
|
||||||
@ -33,6 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
private StatefulMultiplayerClient client { get; set; }
|
private StatefulMultiplayerClient client { get; set; }
|
||||||
|
|
||||||
private LoadingLayer loadingLayer;
|
private LoadingLayer loadingLayer;
|
||||||
|
private FreeModSelectOverlay freeModSelectOverlay;
|
||||||
|
|
||||||
private WorkingBeatmap initialBeatmap;
|
private WorkingBeatmap initialBeatmap;
|
||||||
private RulesetInfo initialRuleset;
|
private RulesetInfo initialRuleset;
|
||||||
@ -43,6 +50,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
public MultiplayerMatchSongSelect()
|
public MultiplayerMatchSongSelect()
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
|
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
|
||||||
|
|
||||||
|
freeModSelectOverlay = new FreeModSelectOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -52,6 +61,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
initialBeatmap = Beatmap.Value;
|
initialBeatmap = Beatmap.Value;
|
||||||
initialRuleset = Ruleset.Value;
|
initialRuleset = Ruleset.Value;
|
||||||
initialMods = Mods.Value.ToList();
|
initialMods = Mods.Value.ToList();
|
||||||
|
|
||||||
|
FooterPanels.Add(freeModSelectOverlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnStart()
|
protected override bool OnStart()
|
||||||
@ -111,8 +122,61 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
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;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,19 +28,16 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
|
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>
|
/// <param name="overlay">The <see cref="OverlayContainer"/> to be toggled by this button.</param>
|
||||||
public void AddButton(FooterButton button, OverlayContainer overlay)
|
public void AddButton(FooterButton button, OverlayContainer overlay)
|
||||||
|
{
|
||||||
|
if (overlay != null)
|
||||||
{
|
{
|
||||||
overlays.Add(overlay);
|
overlays.Add(overlay);
|
||||||
button.Action = () => showOverlay(overlay);
|
button.Action = () => showOverlay(overlay);
|
||||||
|
|
||||||
AddButton(button);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <param name="button">Button to be added.</param>
|
|
||||||
public void AddButton(FooterButton button)
|
|
||||||
{
|
|
||||||
button.Hovered = updateModeLight;
|
button.Hovered = updateModeLight;
|
||||||
button.HoverLost = updateModeLight;
|
button.HoverLost = updateModeLight;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user