diff --git a/osu-framework b/osu-framework index 53dfe2a110..db310bfc10 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 53dfe2a110d732cca0e7df58c33eca368bc7ff61 +Subproject commit db310bfc10cd1c9ed12c9e19cdc0edfa53117353 diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs b/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs index 19cc32078c..da807d5e53 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs @@ -33,10 +33,10 @@ namespace osu.Desktop.VisualTests.Tests Position = new Vector2(275, 5) }); - filter.PinTab(GroupMode.All); - filter.PinTab(GroupMode.RecentlyPlayed); + filter.PinItem(GroupMode.All); + filter.PinItem(GroupMode.RecentlyPlayed); - filter.ValueChanged += (sender, mode) => + filter.ItemChanged += (sender, mode) => { text.Text = "Currently Selected: " + mode.ToString(); }; 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/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 02cd5d683a..d5699eddaf 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -28,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface throw new InvalidOperationException("OsuTabControl only supports enums as the generic type argument"); foreach (var val in (T[])Enum.GetValues(typeof(T))) - AddTab(val); + AddItem(val); } [BackgroundDependencyLoader] diff --git a/osu.Game/Modes/ScoreProcessor.cs b/osu.Game/Modes/ScoreProcessor.cs index 35e19626b2..a6e29d53e1 100644 --- a/osu.Game/Modes/ScoreProcessor.cs +++ b/osu.Game/Modes/ScoreProcessor.cs @@ -7,12 +7,64 @@ 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 { - public virtual Score GetScore() => new Score + /// + /// Invoked when the ScoreProcessor is in a failed 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(); + + /// + /// 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, @@ -21,30 +73,32 @@ 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. + /// Resets this ScoreProcessor to a default state. /// - public readonly BindableInt HighestCombo = new BindableInt(); - - /// - /// Called when we reach a failing health of zero. - /// - public event Action Failed; - - /// - /// Notifies subscribers that the score is in a failed state. - /// - protected void TriggerFailed() + protected virtual void Reset() { + TotalScore.Value = 0; + Accuracy.Value = 0; + Health.Value = 0; + Combo.Value = 0; + HighestCombo.Value = 0; + + alreadyFailed = false; + } + + /// + /// Checks if the score is in a failed state and notifies subscribers. + /// + /// This can only ever notify subscribers once. + /// + /// + protected void UpdateFailed() + { + if (alreadyFailed || !HasFailed) + return; + + alreadyFailed = true; Failed?.Invoke(); } } @@ -56,32 +110,35 @@ namespace osu.Game.Modes /// /// All judgements held by this ScoreProcessor. /// - protected List Judgements; + protected readonly List Judgements = new List(); - /// - /// Are we allowed to fail? - /// - protected bool CanFail => true; - - /// - /// Whether this ScoreProcessor has already triggered the failed event. - /// - protected bool HasFailed { get; private set; } + 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 = new List(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. + /// + /// The judgement to add. private void addJudgement(TJudgement judgement) { Judgements.Add(judgement); @@ -89,17 +146,14 @@ namespace osu.Game.Modes UpdateCalculations(judgement); judgement.ComboAtHit = (ulong)Combo.Value; - if (Health.Value == Health.MinValue && !HasFailed) - { - HasFailed = true; - TriggerFailed(); - } + + UpdateFailed(); } - /// - /// Resets this ScoreProcessor to a stale state. - /// - protected virtual void Reset() { } + protected override void Reset() + { + Judgements.Clear(); + } /// /// Update any values that potentially need post-processing on a judgement change. @@ -107,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 0faa986fc7..b82e3ada51 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; /// @@ -200,9 +203,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 a4c7e84ce7..674a741d8c 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,15 +237,19 @@ namespace osu.Game.Screens.Play }); } - private void onPass() + private void onCompletion() { + // Only show the completion screen if the player hasn't failed + if (scoreProcessor.HasFailed) + return; + Delay(1000); Schedule(delegate { ValidForResume = false; Push(new Results { - Score = scoreProcessor.GetScore() + Score = scoreProcessor.CreateScore() }); }); } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 637b6eafbc..2a25928dc7 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -141,10 +141,10 @@ namespace osu.Game.Screens.Select } }; - groupTabs.PinTab(GroupMode.All); - groupTabs.PinTab(GroupMode.RecentlyPlayed); - groupTabs.ValueChanged += (sender, value) => Group = value; - sortTabs.ValueChanged += (sender, value) => Sort = value; + groupTabs.PinItem(GroupMode.All); + groupTabs.PinItem(GroupMode.RecentlyPlayed); + groupTabs.ItemChanged += (sender, value) => Group = value; + sortTabs.ItemChanged += (sender, value) => Sort = value; } public void Deactivate()