diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs
index 25aed4c980..97b89c6f74 100644
--- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs
+++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet
return;
modsContainer.Add(new ModButton(new ModNoMod()));
- modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.UserPlayable).Select(m => new ModButton(m)));
+ modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.IsPlayable(ModUsage.Solo)).Select(m => new ModButton(m)));
modsContainer.ForEach(button =>
{
diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs
index 5f4fecb649..bdfb273b13 100644
--- a/osu.Game/Rulesets/Mods/IMod.cs
+++ b/osu.Game/Rulesets/Mods/IMod.cs
@@ -33,24 +33,26 @@ namespace osu.Game.Rulesets.Mods
///
IconUsage? Icon { get; }
+ ///
+ /// Whether this mod is playable for the given usage.
+ ///
+ ///
+ ///
+ /// - Should be always false for cases where the user is not interacting with the game.
+ /// - Should be false in for mods that make gameplay duration dependent on user input (e.g. ).
+ /// - Should be false in for mods that affect the gameplay duration (e.g. and ).
+ ///
+ ///
+ /// The mod usage.
+ bool IsPlayable(ModUsage usage);
+
///
/// Whether this mod is playable by an end user.
/// Should be false for cases where the user is not interacting with the game (so it can be excluded from multiplayer selection, for example).
///
+ [Obsolete("Override IsPlayable instead.")] // Can be removed 20220918
bool UserPlayable { get; }
- ///
- /// Whether this mod is playable in a multiplayer match.
- /// Should be false for mods that make gameplay duration dependent on user input (e.g. ).
- ///
- bool PlayableInMultiplayer { get; }
-
- ///
- /// Whether this mod is valid to be a "free mod" in a multiplayer match.
- /// Should be false for mods that affect the gameplay duration (e.g. and ).
- ///
- bool ValidFreeModInMultiplayer { get; }
-
///
/// Create a fresh instance based on this mod.
///
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index a6562b4f4c..00aef1a598 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -91,16 +91,13 @@ namespace osu.Game.Rulesets.Mods
[JsonIgnore]
public virtual bool HasImplementation => this is IApplicableMod;
+ public virtual bool IsPlayable(ModUsage usage) => true;
+
[JsonIgnore]
+ [Obsolete("Override IsPlayable instead.")] // Can be removed 20220918
public virtual bool UserPlayable => true;
- [JsonIgnore]
- public virtual bool PlayableInMultiplayer => UserPlayable;
-
- [JsonIgnore]
- public virtual bool ValidFreeModInMultiplayer => PlayableInMultiplayer;
-
- [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override and set UserPlayable to false.")] // Can be removed 20211009
+ [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override IsPlayable to false.")] // Can be removed 20211009
public virtual bool Ranked => false;
///
diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs
new file mode 100644
index 0000000000..82ff6bc418
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/ModUsage.cs
@@ -0,0 +1,26 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Rulesets.Mods
+{
+ ///
+ /// The usage of this mod to determine its playability.
+ ///
+ public enum ModUsage
+ {
+ ///
+ /// In a solo gameplay session.
+ ///
+ Solo,
+
+ ///
+ /// In a multiplayer match, as a required mod.
+ ///
+ MultiplayerRequired,
+
+ ///
+ /// In a multiplayer match, as a "free" mod.
+ ///
+ MultiplayerFree,
+ }
+}