// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Configuration; using System; 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. /// 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(); protected ScoreProcessor() { Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; Reset(); } public virtual Score GetScore() => new Score { TotalScore = TotalScore, Combo = Combo, MaxCombo = HighestCombo, Accuracy = Accuracy, Health = Health, }; /// /// Resets this ScoreProcessor to a default state. /// protected virtual void Reset() { TotalScore.Value = 0; Accuracy.Value = 0; Health.Value = 0; Combo.Value = 0; HighestCombo.Value = 0; } /// /// Notifies subscribers that the score is in a failed state. /// protected void TriggerFailed() { Failed?.Invoke(); } /// /// Checks if the score is in a failing state. /// /// Whether the score is in a failing state. public abstract bool CheckFailed(); } public abstract class ScoreProcessor : ScoreProcessor where TObject : HitObject where TJudgement : JudgementInfo { /// /// All judgements held by this ScoreProcessor. /// 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; protected ScoreProcessor() { } protected ScoreProcessor(HitRenderer hitRenderer) { Judgements.Capacity = hitRenderer.Beatmap.HitObjects.Count; hitRenderer.OnJudgement += addJudgement; ComputeTargets(hitRenderer.Beatmap); Reset(); } public override bool CheckFailed() { if (!hasFailed && IsFailable) { hasFailed = true; TriggerFailed(); } return hasFailed; } /// /// 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. /// /// The judgement to add. private void addJudgement(TJudgement judgement) { Judgements.Add(judgement); UpdateCalculations(judgement); judgement.ComboAtHit = (ulong)Combo.Value; CheckFailed(); } protected override void Reset() { Judgements.Clear(); hasFailed = false; } /// /// Update any values that potentially need post-processing on a judgement change. /// /// A new JudgementInfo that triggered this calculation. May be null. protected abstract void UpdateCalculations(TJudgement newJudgement); } }