diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index c21b9ba409..db9460c494 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -22,6 +22,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens;
+using osu.Game.Utils;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
@@ -46,9 +47,27 @@ namespace osu.Game.Overlays.Mods
protected readonly ModSettingsContainer ModSettingsContainer;
+ private bool stacked = true;
+
+ ///
+ /// Whether mod icons should be stacked, or appear as individual buttons.
+ ///
+ public bool Stacked
+ {
+ get => stacked;
+ set
+ {
+ stacked = value;
+ updateAvailableMods();
+ }
+ }
+
[NotNull]
private Func isValidMod = m => true;
+ ///
+ /// A function that checks whether a given mod is valid.
+ ///
[NotNull]
public Func IsValidMod
{
@@ -419,11 +438,18 @@ namespace osu.Game.Overlays.Mods
private void updateAvailableMods()
{
- if (availableMods.Value == null)
+ if (availableMods?.Value == null)
return;
foreach (var section in ModSectionsContainer.Children)
- section.Mods = availableMods.Value[section.ModType].Where(IsValidMod);
+ {
+ IEnumerable modEnumeration = availableMods.Value[section.ModType];
+
+ if (!stacked)
+ modEnumeration = ModValidation.FlattenMods(modEnumeration);
+
+ section.Mods = modEnumeration.Where(IsValidMod);
+ }
}
private void selectedModsChanged(ValueChangedEvent> mods)
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs
index 56e74a8460..8334df1e44 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs
@@ -14,6 +14,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
public class FreeModSelectOverlay : ModSelectOverlay
{
+ public FreeModSelectOverlay()
+ {
+ Stacked = false;
+ }
+
protected override ModSection CreateModSection(ModType type) => new FreeModSection(type);
private class FreeModSection : ModSection
diff --git a/osu.Game/Utils/ModValidation.cs b/osu.Game/Utils/ModValidation.cs
index 0c4d58ab2e..3597396ec4 100644
--- a/osu.Game/Utils/ModValidation.cs
+++ b/osu.Game/Utils/ModValidation.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Utils
var incompatibleTypes = new HashSet();
var incomingTypes = new HashSet();
- foreach (var mod in combination.SelectMany(flattenMod))
+ foreach (var mod in combination.SelectMany(FlattenMod))
{
// Add the new mod incompatibilities, checking whether any match the existing mod types.
foreach (var t in mod.IncompatibleMods)
@@ -79,7 +79,7 @@ namespace osu.Game.Utils
{
var allowedSet = new HashSet(allowedTypes);
- return combination.SelectMany(flattenMod)
+ return combination.SelectMany(FlattenMod)
.All(m => allowedSet.Contains(m.GetType()));
}
@@ -93,20 +93,27 @@ namespace osu.Game.Utils
/// The set of incompatible types.
/// Whether the given is incompatible.
private static bool isModIncompatible(Mod mod, ICollection incompatibleTypes)
- => flattenMod(mod)
+ => FlattenMod(mod)
.SelectMany(m => m.GetType().EnumerateBaseTypes())
.Any(incompatibleTypes.Contains);
+ ///
+ /// Flattens a set of s, returning a new set with all s removed.
+ ///
+ /// The set of s to flatten.
+ /// The new set, containing all s in recursively with all s removed.
+ public static IEnumerable FlattenMods(IEnumerable mods) => mods.SelectMany(FlattenMod);
+
///
/// Flattens a , returning a set of s in-place of any s.
///
/// The to flatten.
/// A set of singular "flattened" s
- private static IEnumerable flattenMod(Mod mod)
+ public static IEnumerable FlattenMod(Mod mod)
{
if (mod is MultiMod multi)
{
- foreach (var m in multi.Mods.SelectMany(flattenMod))
+ foreach (var m in multi.Mods.SelectMany(FlattenMod))
yield return m;
}
else