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()