diff --git a/osu.Game/Modes/ScoreProcessor.cs b/osu.Game/Modes/ScoreProcessor.cs index 35e19626b2..3900a79485 100644 --- a/osu.Game/Modes/ScoreProcessor.cs +++ b/osu.Game/Modes/ScoreProcessor.cs @@ -12,6 +12,36 @@ namespace osu.Game.Modes { public abstract class ScoreProcessor { + /// + /// Invoked when the score is in a failing state. + /// + public event Action Failed; + + /// + /// The current total score. + /// + public readonly BindableDouble TotalScore = new BindableDouble { MinValue = 0 }; + + /// + /// The current accuracy. + /// + public readonly BindableDouble Accuracy = new BindableDouble { MinValue = 0, MaxValue = 1 }; + + /// + /// The current health. + /// + public readonly BindableDouble Health = new BindableDouble { MinValue = 0, MaxValue = 1 }; + + /// + /// The current combo. + /// + public readonly BindableInt Combo = new BindableInt(); + + /// + /// THe highest combo achieved by this score. + /// + public readonly BindableInt HighestCombo = new BindableInt(); + public virtual Score GetScore() => new Score { TotalScore = TotalScore, @@ -21,24 +51,11 @@ namespace osu.Game.Modes Health = Health, }; - public readonly BindableDouble TotalScore = new BindableDouble { MinValue = 0 }; - - public readonly BindableDouble Accuracy = new BindableDouble { MinValue = 0, MaxValue = 1 }; - - public readonly BindableDouble Health = new BindableDouble { MinValue = 0, MaxValue = 1 }; - - public readonly BindableInt Combo = new BindableInt(); - /// - /// Keeps track of the highest combo ever achieved in this play. - /// This is handled automatically by ScoreProcessor. + /// Checks if the score is in a failing state. /// - public readonly BindableInt HighestCombo = new BindableInt(); - - /// - /// Called when we reach a failing health of zero. - /// - public event Action Failed; + /// Whether the score is in a failing state. + public abstract bool CheckFailed(); /// /// Notifies subscribers that the score is in a failed state. @@ -56,17 +73,17 @@ namespace osu.Game.Modes /// /// All judgements held by this ScoreProcessor. /// - protected List Judgements; + protected readonly List Judgements = new List(); /// - /// Are we allowed to fail? + /// Whether the score is in a failable state. /// - protected bool CanFail => true; + protected virtual bool IsFailable => Health.Value == Health.MinValue; /// - /// Whether this ScoreProcessor has already triggered the failed event. + /// Whether this ScoreProcessor has already failed. /// - protected bool HasFailed { get; private set; } + private bool hasFailed; protected ScoreProcessor() { @@ -78,10 +95,14 @@ namespace osu.Game.Modes protected ScoreProcessor(HitRenderer hitRenderer) : this() { - Judgements = new List(hitRenderer.Beatmap.HitObjects.Count); + Judgements.Capacity = hitRenderer.Beatmap.HitObjects.Count; hitRenderer.OnJudgement += addJudgement; } + /// + /// Adds a judgement to this ScoreProcessor. + /// + /// The judgement to add. private void addJudgement(TJudgement judgement) { Judgements.Add(judgement); @@ -89,17 +110,35 @@ namespace osu.Game.Modes UpdateCalculations(judgement); judgement.ComboAtHit = (ulong)Combo.Value; - if (Health.Value == Health.MinValue && !HasFailed) + + CheckFailed(); + } + + public override bool CheckFailed() + { + if (!hasFailed && IsFailable) { - HasFailed = true; + hasFailed = true; TriggerFailed(); } + + return hasFailed; } /// /// Resets this ScoreProcessor to a stale state. /// - protected virtual void Reset() { } + protected virtual void Reset() + { + Judgements.Clear(); + + hasFailed = false; + TotalScore.Value = 0; + Accuracy.Value = 0; + Health.Value = 0; + Combo.Value = 0; + HighestCombo.Value = 0; + } /// /// Update any values that potentially need post-processing on a judgement change. diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a4c7e84ce7..ec8cbb1438 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Play hudOverlay.BindHitRenderer(hitRenderer); //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) - hitRenderer.OnAllJudged += onPass; + hitRenderer.OnAllJudged += onCompletion; //bind ScoreProcessor to ourselves (for a fail situation) scoreProcessor.Failed += onFail; @@ -237,8 +237,17 @@ namespace osu.Game.Screens.Play }); } - private void onPass() + private void onCompletion() { + // Force a final check to see if the player has failed + // Some game modes (e.g. taiko) fail at the end of the map + if (scoreProcessor.CheckFailed()) + { + // If failed, onFail will be invoked which will push a new screen. + // Let's not push the completion screen in this case + return; + } + Delay(1000); Schedule(delegate {