diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index c00847b7d8..69daedb7a7 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -45,5 +45,10 @@ public abstract class Mod /// The mods this mod cannot be enabled with. /// public virtual Type[] IncompatibleMods => new Type[] { }; + + /// + /// Whether we should allow fails at the + /// + public virtual bool AllowFail => true; } } diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 613e1e1d4d..d41c4e3956 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -15,5 +15,10 @@ public abstract class ModNoFail : Mod public override double ScoreMultiplier => 0.5; public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) }; + + /// + /// We never fail, 'yo. + /// + public override bool AllowFail => false; } } \ No newline at end of file diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 79fb34a523..cd9089c859 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -16,8 +16,9 @@ public abstract class ScoreProcessor { /// /// Invoked when the ScoreProcessor is in a failed state. + /// Return true if the fail was permitted. /// - public event Action Failed; + public event Func Failed; /// /// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the . @@ -106,8 +107,8 @@ protected void UpdateFailed() if (alreadyFailed || !HasFailed) return; - alreadyFailed = true; - Failed?.Invoke(); + if (Failed?.Invoke() != false) + alreadyFailed = true; } /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 39128eb85e..345ad60d47 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -238,13 +238,17 @@ private void onCompletion() } } - private void onFail() + private bool onFail() { + if (Beatmap.Value.Mods.Value.Any(m => !m.AllowFail)) + return false; + decoupledClock.Stop(); HasFailed = true; failOverlay.Retries = RestartCount; failOverlay.Show(); + return true; } protected override void OnEntering(Screen last)