diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index 0e5df329d8..0048566b15 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
///
/// Taiko fails at the end of the map if the player has not half-filled their HP bar.
///
- protected override bool FailCondition => Hits == MaxHits && Health.Value <= 0.5;
+ protected override bool DefaultFailCondition => Hits == MaxHits && Health.Value <= 0.5;
private double hpIncreaseTick;
private double hpIncreaseGreat;
diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs
new file mode 100644
index 0000000000..db9b713c59
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs
@@ -0,0 +1,15 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Mods
+{
+ ///
+ /// An interface for mods that make general adjustments to score processor.
+ ///
+ public interface IApplicableToScoreProcessor
+ {
+ void ApplyToScoreProcessor(ScoreProcessor scoreProcessor);
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs
index 082370ea5d..59539d2b2c 100644
--- a/osu.Game/Rulesets/Mods/ModPerfect.cs
+++ b/osu.Game/Rulesets/Mods/ModPerfect.cs
@@ -1,6 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Scoring;
+
namespace osu.Game.Rulesets.Mods
{
public abstract class ModPerfect : ModSuddenDeath
@@ -8,5 +10,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Perfect";
public override string ShortenedName => "PF";
public override string Description => "SS or quit.";
+
+ protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1;
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
index 999cb40f89..bc42c69cbe 100644
--- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
+++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
@@ -3,10 +3,11 @@
using System;
using osu.Game.Graphics;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModSuddenDeath : Mod
+ public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor
{
public override string Name => "Sudden Death";
public override string ShortenedName => "SD";
@@ -16,5 +17,12 @@ namespace osu.Game.Rulesets.Mods
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
+
+ public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
+ {
+ scoreProcessor.FailConditions += FailCondition;
+ }
+
+ protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0;
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 4dd88600b2..7b26e50dd8 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Scoring
///
public event Action NewJudgement;
+ ///
+ /// Additional conditions on top of that cause a failing state.
+ ///
+ public event Func FailConditions;
+
///
/// The current total score.
///
@@ -72,9 +77,9 @@ namespace osu.Game.Rulesets.Scoring
public virtual bool HasFailed { get; private set; }
///
- /// The conditions for failing.
+ /// The default conditions for failing.
///
- protected virtual bool FailCondition => Health.Value == Health.MinValue;
+ protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue;
protected ScoreProcessor()
{
@@ -121,7 +126,10 @@ namespace osu.Game.Rulesets.Scoring
///
protected void UpdateFailed()
{
- if (HasFailed || !FailCondition)
+ if (HasFailed)
+ return;
+
+ if (!DefaultFailCondition && FailConditions?.Invoke(this) != true)
return;
if (Failed?.Invoke() != false)
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 3e57e18963..cd2818398d 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -227,6 +227,9 @@ namespace osu.Game.Screens.Play
// Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;
+
+ foreach (var mod in Beatmap.Value.Mods.Value.OfType())
+ mod.ApplyToScoreProcessor(scoreProcessor);
}
private void applyRateFromMods()
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 3b4434d1f4..c3af014fe1 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -298,6 +298,7 @@
+