diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Database/ScoreDatabase.cs index a8eb5e1886..cfa8e6ac7e 100644 --- a/osu.Game/Database/ScoreDatabase.cs +++ b/osu.Game/Database/ScoreDatabase.cs @@ -39,7 +39,7 @@ namespace osu.Game.Database using (SerializationReader sr = new SerializationReader(s)) { var ruleset = Ruleset.GetRuleset((PlayMode)sr.ReadByte()); - score = ruleset.CreateScoreProcessor().GetScore(); + score = ruleset.CreateScoreProcessor().CreateScore(); /* score.Pass = true;*/ var version = sr.ReadInt32(); diff --git a/osu.Game/Modes/ScoreProcessor.cs b/osu.Game/Modes/ScoreProcessor.cs index 3900a79485..a6e29d53e1 100644 --- a/osu.Game/Modes/ScoreProcessor.cs +++ b/osu.Game/Modes/ScoreProcessor.cs @@ -7,13 +7,14 @@ using System.Collections.Generic; using osu.Game.Modes.Judgements; using osu.Game.Modes.UI; using osu.Game.Modes.Objects; +using osu.Game.Beatmaps; namespace osu.Game.Modes { public abstract class ScoreProcessor { /// - /// Invoked when the score is in a failing state. + /// Invoked when the ScoreProcessor is in a failed state. /// public event Action Failed; @@ -42,7 +43,28 @@ namespace osu.Game.Modes /// public readonly BindableInt HighestCombo = new BindableInt(); - public virtual Score GetScore() => new Score + /// + /// Whether the score is in a failed state. + /// + public virtual bool HasFailed => false; + + /// + /// Whether this ScoreProcessor has already triggered the failed state. + /// + private bool alreadyFailed; + + protected ScoreProcessor() + { + Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; + + Reset(); + } + + /// + /// Creates a Score applicable to the game mode in which this ScoreProcessor resides. + /// + /// The Score. + public virtual Score CreateScore() => new Score { TotalScore = TotalScore, Combo = Combo, @@ -52,16 +74,31 @@ namespace osu.Game.Modes }; /// - /// Checks if the score is in a failing state. + /// Resets this ScoreProcessor to a default state. /// - /// Whether the score is in a failing state. - public abstract bool CheckFailed(); + protected virtual void Reset() + { + TotalScore.Value = 0; + Accuracy.Value = 0; + Health.Value = 0; + Combo.Value = 0; + HighestCombo.Value = 0; + + alreadyFailed = false; + } /// - /// Notifies subscribers that the score is in a failed state. + /// Checks if the score is in a failed state and notifies subscribers. + /// + /// This can only ever notify subscribers once. + /// /// - protected void TriggerFailed() + protected void UpdateFailed() { + if (alreadyFailed || !HasFailed) + return; + + alreadyFailed = true; Failed?.Invoke(); } } @@ -75,29 +112,28 @@ namespace osu.Game.Modes /// protected readonly List Judgements = new List(); - /// - /// Whether the score is in a failable state. - /// - protected virtual bool IsFailable => Health.Value == Health.MinValue; - - /// - /// Whether this ScoreProcessor has already failed. - /// - private bool hasFailed; + public override bool HasFailed => Health.Value == Health.MinValue; protected ScoreProcessor() { - Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; + } + + protected ScoreProcessor(HitRenderer hitRenderer) + { + Judgements.Capacity = hitRenderer.Beatmap.HitObjects.Count; + + hitRenderer.OnJudgement += addJudgement; + + ComputeTargets(hitRenderer.Beatmap); Reset(); } - protected ScoreProcessor(HitRenderer hitRenderer) - : this() - { - Judgements.Capacity = hitRenderer.Beatmap.HitObjects.Count; - hitRenderer.OnJudgement += addJudgement; - } + /// + /// Computes target scoring values for this ScoreProcessor. This is equivalent to performing an auto-play of the score to find the values. + /// + /// The Beatmap containing the objects that will be judged by this ScoreProcessor. + protected virtual void ComputeTargets(Beatmap beatmap) { } /// /// Adds a judgement to this ScoreProcessor. @@ -111,33 +147,12 @@ namespace osu.Game.Modes judgement.ComboAtHit = (ulong)Combo.Value; - CheckFailed(); + UpdateFailed(); } - public override bool CheckFailed() - { - if (!hasFailed && IsFailable) - { - hasFailed = true; - TriggerFailed(); - } - - return hasFailed; - } - - /// - /// Resets this ScoreProcessor to a stale state. - /// - protected virtual void Reset() + protected override void Reset() { Judgements.Clear(); - - hasFailed = false; - TotalScore.Value = 0; - Accuracy.Value = 0; - Health.Value = 0; - Combo.Value = 0; - HighestCombo.Value = 0; } /// @@ -146,4 +161,4 @@ namespace osu.Game.Modes /// A new JudgementInfo that triggered this calculation. May be null. protected abstract void UpdateCalculations(TJudgement newJudgement); } -} +} \ No newline at end of file diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index a0ca2549c0..1971559f49 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -25,6 +25,9 @@ namespace osu.Game.Modes.UI /// public abstract class HitRenderer : Container { + /// + /// Invoked when all the judgeable HitObjects have been judged. + /// public event Action OnAllJudged; /// @@ -190,9 +193,10 @@ namespace osu.Game.Modes.UI /// The object that Judgement has been updated for. private void onJudgement(DrawableHitObject judgedObject) { - OnJudgement?.Invoke(judgedObject.Judgement); Playfield.OnJudgement(judgedObject); + OnJudgement?.Invoke(judgedObject.Judgement); + CheckAllJudged(); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ec8cbb1438..674a741d8c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -239,14 +239,9 @@ namespace osu.Game.Screens.Play 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 + // Only show the completion screen if the player hasn't failed + if (scoreProcessor.HasFailed) return; - } Delay(1000); Schedule(delegate @@ -254,7 +249,7 @@ namespace osu.Game.Screens.Play ValidForResume = false; Push(new Results { - Score = scoreProcessor.GetScore() + Score = scoreProcessor.CreateScore() }); }); }