Merge pull request #11793 from peppy/playlists-freemod-support

Add "free mod" user mod selection support to the playlists system
This commit is contained in:
Dean Herbert
2021-02-19 16:56:21 +09:00
committed by GitHub
5 changed files with 121 additions and 59 deletions

View File

@ -8,11 +8,14 @@ using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
@ -26,6 +29,14 @@ namespace osu.Game.Screens.OnlinePlay.Match
public override bool DisallowExternalBeatmapRulesetChanges => true; public override bool DisallowExternalBeatmapRulesetChanges => true;
private readonly ModSelectOverlay userModsSelectOverlay;
/// <summary>
/// A container that provides controls for selection of user mods.
/// This will be shown/hidden automatically when applicable.
/// </summary>
protected Drawable UserModsSection;
private Sample sampleStart; private Sample sampleStart;
/// <summary> /// <summary>
@ -51,9 +62,26 @@ namespace osu.Game.Screens.OnlinePlay.Match
protected RoomSubScreen() protected RoomSubScreen()
{ {
AddInternal(BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker AddRangeInternal(new Drawable[]
{
BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker
{ {
SelectedItem = { BindTarget = SelectedItem } SelectedItem = { BindTarget = SelectedItem }
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Depth = float.MinValue,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING },
Child = userModsSelectOverlay = new UserModSelectOverlay
{
SelectedMods = { BindTarget = UserMods },
IsValidMod = _ => false
}
},
}); });
} }
@ -78,6 +106,19 @@ namespace osu.Game.Screens.OnlinePlay.Match
UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods)); UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods));
} }
public override bool OnBackButton()
{
if (userModsSelectOverlay.State.Value == Visibility.Visible)
{
userModsSelectOverlay.Hide();
return true;
}
return base.OnBackButton();
}
protected void ShowUserModSelect() => userModsSelectOverlay.Show();
public override void OnEntering(IScreen last) public override void OnEntering(IScreen last)
{ {
base.OnEntering(last); base.OnEntering(last);
@ -117,17 +158,31 @@ namespace osu.Game.Screens.OnlinePlay.Match
{ {
updateWorkingBeatmap(); updateWorkingBeatmap();
if (SelectedItem.Value == null) var selected = SelectedItem.Value;
if (selected == null)
return; return;
// Remove any user mods that are no longer allowed. // Remove any user mods that are no longer allowed.
UserMods.Value = UserMods.Value UserMods.Value = UserMods.Value
.Where(m => SelectedItem.Value.AllowedMods.Any(a => m.GetType() == a.GetType())) .Where(m => selected.AllowedMods.Any(a => m.GetType() == a.GetType()))
.ToList(); .ToList();
UpdateMods(); UpdateMods();
Ruleset.Value = SelectedItem.Value.Ruleset.Value; Ruleset.Value = selected.Ruleset.Value;
if (!selected.AllowedMods.Any())
{
UserModsSection?.Hide();
userModsSelectOverlay.Hide();
userModsSelectOverlay.IsValidMod = _ => false;
}
else
{
UserModsSection?.Show();
userModsSelectOverlay.IsValidMod = m => selected.AllowedMods.Any(a => a.GetType() == m.GetType());
}
} }
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet) => Schedule(updateWorkingBeatmap); private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet) => Schedule(updateWorkingBeatmap);
@ -182,5 +237,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (track != null) if (track != null)
track.Looping = false; track.Looping = false;
} }
private class UserModSelectOverlay : LocalPlayerModSelectOverlay
{
}
} }
} }

View File

@ -15,7 +15,6 @@ using osu.Framework.Threading;
using osu.Game.Configuration; using osu.Game.Configuration;
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.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match;
@ -42,9 +41,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[Resolved] [Resolved]
private OngoingOperationTracker ongoingOperationTracker { get; set; } private OngoingOperationTracker ongoingOperationTracker { get; set; }
private ModSelectOverlay userModsSelectOverlay;
private MultiplayerMatchSettingsOverlay settingsOverlay; private MultiplayerMatchSettingsOverlay settingsOverlay;
private Drawable userModsSection;
private readonly IBindable<bool> isConnected = new Bindable<bool>(); private readonly IBindable<bool> isConnected = new Bindable<bool>();
@ -154,7 +151,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
} }
}, },
userModsSection = new FillFlowContainer UserModsSection = new FillFlowContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
@ -175,7 +172,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Width = 90, Width = 90,
Text = "Select", Text = "Select",
Action = () => userModsSelectOverlay.Show() Action = ShowUserModSelect,
}, },
new ModDisplay new ModDisplay
{ {
@ -230,19 +227,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize),
} }
}, },
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING },
Child = userModsSelectOverlay = new UserModSelectOverlay
{
SelectedMods = { BindTarget = UserMods },
IsValidMod = _ => false
}
},
settingsOverlay = new MultiplayerMatchSettingsOverlay settingsOverlay = new MultiplayerMatchSettingsOverlay
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -269,7 +253,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
base.LoadComplete(); base.LoadComplete();
SelectedItem.BindTo(client.CurrentMatchPlayingItem); SelectedItem.BindTo(client.CurrentMatchPlayingItem);
SelectedItem.BindValueChanged(onSelectedItemChanged, true);
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true); BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
UserMods.BindValueChanged(onUserModsChanged); UserMods.BindValueChanged(onUserModsChanged);
@ -285,24 +268,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
}, true); }, true);
} }
private void onSelectedItemChanged(ValueChangedEvent<PlaylistItem> item)
{
if (client?.LocalUser == null)
return;
if (item.NewValue?.AllowedMods.Any() != true)
{
userModsSection.Hide();
userModsSelectOverlay.Hide();
userModsSelectOverlay.IsValidMod = _ => false;
}
else
{
userModsSection.Show();
userModsSelectOverlay.IsValidMod = m => item.NewValue.AllowedMods.Any(a => a.GetType() == m.GetType());
}
}
protected override void UpdateMods() protected override void UpdateMods()
{ {
if (SelectedItem.Value == null || client.LocalUser == null) if (SelectedItem.Value == null || client.LocalUser == null)
@ -322,12 +287,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
return true; return true;
} }
if (userModsSelectOverlay.State.Value == Visibility.Visible)
{
userModsSelectOverlay.Hide();
return true;
}
return base.OnBackButton(); return base.OnBackButton();
} }
@ -435,9 +394,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
modSettingChangeTracker?.Dispose(); modSettingChangeTracker?.Dispose();
} }
private class UserModSelectOverlay : LocalPlayerModSelectOverlay
{
}
} }
} }

View File

@ -32,11 +32,12 @@ namespace osu.Game.Screens.OnlinePlay
[Resolved(typeof(Room), nameof(Room.Playlist))] [Resolved(typeof(Room), nameof(Room.Playlist))]
protected BindableList<PlaylistItem> Playlist { get; private set; } protected BindableList<PlaylistItem> Playlist { get; private set; }
protected readonly Bindable<IReadOnlyList<Mod>> FreeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
[CanBeNull] [CanBeNull]
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private IBindable<PlaylistItem> selectedItem { get; set; } private IBindable<PlaylistItem> selectedItem { get; set; }
private readonly Bindable<IReadOnlyList<Mod>> freeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
private readonly FreeModSelectOverlay freeModSelectOverlay; private readonly FreeModSelectOverlay freeModSelectOverlay;
private WorkingBeatmap initialBeatmap; private WorkingBeatmap initialBeatmap;
@ -50,7 +51,7 @@ namespace osu.Game.Screens.OnlinePlay
freeModSelectOverlay = new FreeModSelectOverlay freeModSelectOverlay = new FreeModSelectOverlay
{ {
SelectedMods = { BindTarget = freeMods }, SelectedMods = { BindTarget = FreeMods },
IsValidMod = IsValidFreeMod, IsValidMod = IsValidFreeMod,
}; };
} }
@ -72,14 +73,14 @@ namespace osu.Game.Screens.OnlinePlay
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods. // At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
// Similarly, freeMods is currently empty but should only contain the allowed mods. // Similarly, freeMods is currently empty but should only contain the allowed mods.
Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>(); Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
freeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>(); FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
Ruleset.BindValueChanged(onRulesetChanged); Ruleset.BindValueChanged(onRulesetChanged);
} }
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset) private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
{ {
freeMods.Value = Array.Empty<Mod>(); FreeMods.Value = Array.Empty<Mod>();
} }
protected sealed override bool OnStart() protected sealed override bool OnStart()
@ -95,7 +96,7 @@ namespace osu.Game.Screens.OnlinePlay
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
item.AllowedMods.Clear(); item.AllowedMods.Clear();
item.AllowedMods.AddRange(freeMods.Value.Select(m => m.CreateCopy())); item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy()));
SelectItem(item); SelectItem(item);
return true; return true;
@ -138,7 +139,7 @@ namespace osu.Game.Screens.OnlinePlay
protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons()
{ {
var buttons = base.CreateFooterButtons().ToList(); var buttons = base.CreateFooterButtons().ToList();
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = freeMods }, freeModSelectOverlay)); buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = FreeMods }, freeModSelectOverlay));
return buttons; return buttons;
} }

View File

@ -13,7 +13,9 @@ using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Screens.Play.HUD;
using osu.Game.Users; using osu.Game.Users;
using osuTK;
using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer; using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer;
namespace osu.Game.Screens.OnlinePlay.Playlists namespace osu.Game.Screens.OnlinePlay.Playlists
@ -143,13 +145,55 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Content = new[] Content = new[]
{ {
new Drawable[] { new OverlinedHeader("Leaderboard"), }, new[]
{
UserModsSection = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Bottom = 10 },
Children = new Drawable[]
{
new OverlinedHeader("Extra mods"),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new PurpleTriangleButton
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Width = 90,
Text = "Select",
Action = ShowUserModSelect,
},
new ModDisplay
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
DisplayUnrankedText = false,
Current = UserMods,
Scale = new Vector2(0.8f),
},
}
}
}
},
},
new Drawable[]
{
new OverlinedHeader("Leaderboard")
},
new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, }, new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
new Drawable[] { new OverlinedHeader("Chat"), }, new Drawable[] { new OverlinedHeader("Chat"), },
new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } }
}, },
RowDimensions = new[] RowDimensions = new[]
{ {
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize),
new Dimension(), new Dimension(),
new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize),

View File

@ -56,6 +56,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
item.RequiredMods.Clear(); item.RequiredMods.Clear();
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
item.AllowedMods.Clear();
item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy()));
} }
} }
} }