From 987196011230f95b7c50f29eba4108283bd41d4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 15:41:48 +0900 Subject: [PATCH 01/12] Basic score flow. --- osu.Game.Mode.Osu/OsuRuleset.cs | 2 ++ osu.Game.Mode.Osu/OsuScore.cs | 12 ++++++++++++ osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj | 1 + osu.Game.Modes.Catch/CatchRuleset.cs | 2 ++ osu.Game.Modes.Mania/ManiaRuleset.cs | 2 ++ osu.Game.Modes.Taiko/TaikoRuleset.cs | 3 +++ osu.Game/Modes/Ruleset.cs | 2 ++ osu.Game/Modes/Score.cs | 17 +++++++++++++++++ osu.Game/Modes/UI/ScoreOverlay.cs | 1 + osu.Game/Screens/Play/Player.cs | 11 +++++++++++ osu.Game/Screens/Ranking/Results.cs | 3 +++ osu.Game/osu.Game.csproj | 1 + 12 files changed, 57 insertions(+) create mode 100644 osu.Game.Mode.Osu/OsuScore.cs create mode 100644 osu.Game/Modes/Score.cs diff --git a/osu.Game.Mode.Osu/OsuRuleset.cs b/osu.Game.Mode.Osu/OsuRuleset.cs index ac43e5501c..c7cfd63297 100644 --- a/osu.Game.Mode.Osu/OsuRuleset.cs +++ b/osu.Game.Mode.Osu/OsuRuleset.cs @@ -17,6 +17,8 @@ namespace osu.Game.Modes.Osu public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); + public override Score CreateScore() => new OsuScore(); + protected override PlayMode PlayMode => PlayMode.Osu; } } diff --git a/osu.Game.Mode.Osu/OsuScore.cs b/osu.Game.Mode.Osu/OsuScore.cs new file mode 100644 index 0000000000..5a70cea434 --- /dev/null +++ b/osu.Game.Mode.Osu/OsuScore.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Modes.Osu +{ + class OsuScore : Score + { + } +} diff --git a/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj index 97b4ebcb99..c27f75f9c5 100644 --- a/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj @@ -53,6 +53,7 @@ + diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index 0195859cb7..08b472f1e2 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -18,6 +18,8 @@ namespace osu.Game.Modes.Catch protected override PlayMode PlayMode => PlayMode.Catch; + public override Score CreateScore() => new Score(); + public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } } diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index cb122084df..85e50b661e 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -19,6 +19,8 @@ namespace osu.Game.Modes.Mania protected override PlayMode PlayMode => PlayMode.Mania; + public override Score CreateScore() => new Score(); + public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } } diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index fa9d0862c7..51be717e04 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -1,6 +1,7 @@ //Copyright (c) 2007-2016 ppy Pty Ltd . //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using osu.Game.Modes.Objects; using osu.Game.Modes.Osu.Objects; @@ -18,6 +19,8 @@ namespace osu.Game.Modes.Taiko protected override PlayMode PlayMode => PlayMode.Taiko; + public override Score CreateScore() => new Score(); + public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } } diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index d35aab6568..2b52bbc1b1 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -18,6 +18,8 @@ namespace osu.Game.Modes public abstract ScoreOverlay CreateScoreOverlay(); + public abstract Score CreateScore(); + public abstract HitRenderer CreateHitRendererWith(List objects); public abstract HitObjectParser CreateHitObjectParser(); diff --git a/osu.Game/Modes/Score.cs b/osu.Game/Modes/Score.cs new file mode 100644 index 0000000000..88d79f94dd --- /dev/null +++ b/osu.Game/Modes/Score.cs @@ -0,0 +1,17 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Modes +{ + public class Score + { + public double TotalScore { get; } + public double Accuracy { get; } + } +} diff --git a/osu.Game/Modes/UI/ScoreOverlay.cs b/osu.Game/Modes/UI/ScoreOverlay.cs index 2270533d47..31f6ebdcca 100644 --- a/osu.Game/Modes/UI/ScoreOverlay.cs +++ b/osu.Game/Modes/UI/ScoreOverlay.cs @@ -15,6 +15,7 @@ namespace osu.Game.Modes.UI public ComboCounter ComboCounter; public ScoreCounter ScoreCounter; public PercentageCounter AccuracyCounter; + public Score Score { get; set; } protected abstract KeyCounterCollection CreateKeyCounter(); protected abstract ComboCounter CreateComboCounter(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 66cec7405e..590412b83a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -19,6 +19,7 @@ using OpenTK.Input; using MouseState = osu.Framework.Input.MouseState; using OpenTK; using osu.Framework.GameModes; +using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Play { @@ -36,6 +37,8 @@ namespace osu.Game.Screens.Play private IAdjustableClock sourceClock; + private Score score; + private Ruleset ruleset; [BackgroundDependencyLoader] @@ -84,6 +87,8 @@ namespace osu.Game.Screens.Play ruleset = Ruleset.GetRuleset(usablePlayMode); var scoreOverlay = ruleset.CreateScoreOverlay(); + scoreOverlay.Score = (score = ruleset.CreateScore()); + var hitRenderer = ruleset.CreateHitRendererWith(beatmap.HitObjects); hitRenderer.OnHit += delegate (HitObject h) { scoreOverlay.OnHit(h); }; @@ -117,6 +122,12 @@ namespace osu.Game.Screens.Play { base.Update(); Clock.ProcessFrame(); + + if (Beatmap.Track.HasCompleted) + Push(new Results + { + Score = score + }); } class PlayerInputManager : UserInputManager diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index b1d55ab54a..55f2d4fe2b 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -2,6 +2,7 @@ //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.GameModes; +using osu.Game.Modes; using osu.Game.Screens.Backgrounds; using OpenTK.Graphics; @@ -22,5 +23,7 @@ namespace osu.Game.Screens.Ranking Background.Schedule(() => Background.FadeColour(Color4.White, 500)); return base.OnExiting(next); } + + public Score Score { get; set; } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c35bafb251..90bea8c033 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -66,6 +66,7 @@ + From 9d2a69f1e2139bd61a5750b9b9a83a9d99db1f9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 20:30:16 +0900 Subject: [PATCH 02/12] Add concept of ScoreProcessor. --- osu.Game.Mode.Osu/OsuRuleset.cs | 2 +- osu.Game.Mode.Osu/OsuScoreProcessor.cs | 12 ++++++++++++ osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj | 1 + osu.Game.Modes.Catch/CatchRuleset.cs | 2 +- osu.Game.Modes.Mania/ManiaRuleset.cs | 2 +- osu.Game.Modes.Taiko/TaikoRuleset.cs | 2 +- osu.Game/Modes/Ruleset.cs | 2 +- osu.Game/Modes/ScoreProcesssor.cs | 16 ++++++++++++++++ osu.Game/Modes/UI/ScoreOverlay.cs | 5 +++++ osu.Game/Screens/Play/Player.cs | 8 ++++---- osu.Game/osu.Game.csproj | 1 + 11 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Mode.Osu/OsuScoreProcessor.cs create mode 100644 osu.Game/Modes/ScoreProcesssor.cs diff --git a/osu.Game.Mode.Osu/OsuRuleset.cs b/osu.Game.Mode.Osu/OsuRuleset.cs index c7cfd63297..15799b134a 100644 --- a/osu.Game.Mode.Osu/OsuRuleset.cs +++ b/osu.Game.Mode.Osu/OsuRuleset.cs @@ -17,7 +17,7 @@ namespace osu.Game.Modes.Osu public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); - public override Score CreateScore() => new OsuScore(); + public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(); protected override PlayMode PlayMode => PlayMode.Osu; } diff --git a/osu.Game.Mode.Osu/OsuScoreProcessor.cs b/osu.Game.Mode.Osu/OsuScoreProcessor.cs new file mode 100644 index 0000000000..8ff053f96f --- /dev/null +++ b/osu.Game.Mode.Osu/OsuScoreProcessor.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Modes.Osu +{ + class OsuScoreProcessor : ScoreProcessor + { + } +} diff --git a/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj index c27f75f9c5..cca903643b 100644 --- a/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj @@ -54,6 +54,7 @@ + diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index 08b472f1e2..695bca2593 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -18,7 +18,7 @@ namespace osu.Game.Modes.Catch protected override PlayMode PlayMode => PlayMode.Catch; - public override Score CreateScore() => new Score(); + public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(); public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index 85e50b661e..0e7761e8fb 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -19,7 +19,7 @@ namespace osu.Game.Modes.Mania protected override PlayMode PlayMode => PlayMode.Mania; - public override Score CreateScore() => new Score(); + public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(); public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index 51be717e04..3e697e48ea 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -19,7 +19,7 @@ namespace osu.Game.Modes.Taiko protected override PlayMode PlayMode => PlayMode.Taiko; - public override Score CreateScore() => new Score(); + public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(); public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index 2b52bbc1b1..b0304f6576 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -18,7 +18,7 @@ namespace osu.Game.Modes public abstract ScoreOverlay CreateScoreOverlay(); - public abstract Score CreateScore(); + public abstract ScoreProcessor CreateScoreProcessor(); public abstract HitRenderer CreateHitRendererWith(List objects); diff --git a/osu.Game/Modes/ScoreProcesssor.cs b/osu.Game/Modes/ScoreProcesssor.cs new file mode 100644 index 0000000000..9d7eace60e --- /dev/null +++ b/osu.Game/Modes/ScoreProcesssor.cs @@ -0,0 +1,16 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Modes +{ + public class ScoreProcessor + { + public virtual Score GetScore() => new Score(); + } +} diff --git a/osu.Game/Modes/UI/ScoreOverlay.cs b/osu.Game/Modes/UI/ScoreOverlay.cs index 31f6ebdcca..9f29ff63ff 100644 --- a/osu.Game/Modes/UI/ScoreOverlay.cs +++ b/osu.Game/Modes/UI/ScoreOverlay.cs @@ -46,5 +46,10 @@ namespace osu.Game.Modes.UI AccuracyCounter = CreateAccuracyCounter(), }; } + + public void BindProcessor(ScoreProcessor processor) + { + + } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 590412b83a..262e5c5fbc 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -37,10 +37,10 @@ namespace osu.Game.Screens.Play private IAdjustableClock sourceClock; - private Score score; - private Ruleset ruleset; + private ScoreProcessor scoreProcessor; + [BackgroundDependencyLoader] private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuGameBase game) { @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Play ruleset = Ruleset.GetRuleset(usablePlayMode); var scoreOverlay = ruleset.CreateScoreOverlay(); - scoreOverlay.Score = (score = ruleset.CreateScore()); + scoreOverlay.BindProcessor(scoreProcessor = ruleset.CreateScoreProcessor()); var hitRenderer = ruleset.CreateHitRendererWith(beatmap.HitObjects); @@ -126,7 +126,7 @@ namespace osu.Game.Screens.Play if (Beatmap.Track.HasCompleted) Push(new Results { - Score = score + Score = scoreProcessor.GetScore() }); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 90bea8c033..af8feebe9a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -67,6 +67,7 @@ + From 9b243ccc2346dfc134d6dbf62251765e3471dbc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 21:28:22 +0900 Subject: [PATCH 03/12] Remove Hit500. --- osu.Game.Mode.Osu/Objects/Drawables/DrawableOsuHitObject.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Mode.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Mode.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 0e3a03665c..80e1f2bb7f 100644 --- a/osu.Game.Mode.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Mode.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -74,7 +74,5 @@ namespace osu.Game.Modes.Osu.Objects.Drawables Hit100, [Description(@"300")] Hit300, - [Description(@"500")] - Hit500 } } From 2947121e482c035cb7c2bdd6e24abc659b93d8d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 21:28:43 +0900 Subject: [PATCH 04/12] Hook up ScoreProcessor to Scoreoverlay etc. --- osu.Game.Mode.Osu/OsuScoreProcessor.cs | 50 +++++++++++++++++++ .../Objects/Drawables/DrawableHitObject.cs | 8 ++- osu.Game/Modes/ScoreProcesssor.cs | 24 +++++++++ osu.Game/Modes/UI/ComboCounter.cs | 8 +++ osu.Game/Modes/UI/HitRenderer.cs | 18 ++----- osu.Game/Modes/UI/ScoreOverlay.cs | 5 +- osu.Game/Screens/Play/Player.cs | 3 +- 7 files changed, 95 insertions(+), 21 deletions(-) diff --git a/osu.Game.Mode.Osu/OsuScoreProcessor.cs b/osu.Game.Mode.Osu/OsuScoreProcessor.cs index 8ff053f96f..177844297e 100644 --- a/osu.Game.Mode.Osu/OsuScoreProcessor.cs +++ b/osu.Game.Mode.Osu/OsuScoreProcessor.cs @@ -3,10 +3,60 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Osu.Objects.Drawables; namespace osu.Game.Modes.Osu { class OsuScoreProcessor : ScoreProcessor { + public override void AddJudgement(JudgementInfo judgement) + { + base.AddJudgement(judgement); + + switch (judgement.Result) + { + case HitResult.Hit: + Combo.Value++; + break; + case HitResult.Miss: + Combo.Value = 0; + break; + } + } + protected override void UpdateCalculations() + { + base.UpdateCalculations(); + + int score = 0; + int maxScore = 0; + + foreach (OsuJudgementInfo j in Judgements) + { + switch (j.Score) + { + case OsuScoreResult.Miss: + maxScore += 300; + break; + case OsuScoreResult.Hit50: + score += 50; + maxScore += 300; + break; + case OsuScoreResult.Hit100: + score += 100; + maxScore += 300; + break; + case OsuScoreResult.Hit300: + score += 300; + maxScore += 300; + break; + } + + + } + + TotalScore.Value = score; + Accuracy.Value = (double)score / maxScore; + } } } diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index 3508d6c3a6..f00a24d79e 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -13,9 +13,7 @@ namespace osu.Game.Modes.Objects.Drawables { public abstract class DrawableHitObject : Container, IStateful { - //todo: move to a more central implementation. this logic should not be at a drawable level. - public Action OnHit; - public Action OnMiss; + public event Action OnJudgement; public Container ChildObjects; @@ -73,14 +71,14 @@ namespace osu.Game.Modes.Objects.Drawables { default: State = ArmedState.Hit; - OnHit?.Invoke(this, Judgement); break; case HitResult.Miss: State = ArmedState.Miss; - OnMiss?.Invoke(this, Judgement); break; } + OnJudgement?.Invoke(this, Judgement); + return true; } diff --git a/osu.Game/Modes/ScoreProcesssor.cs b/osu.Game/Modes/ScoreProcesssor.cs index 9d7eace60e..efae0bd40b 100644 --- a/osu.Game/Modes/ScoreProcesssor.cs +++ b/osu.Game/Modes/ScoreProcesssor.cs @@ -6,11 +6,35 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using osu.Framework.Configuration; +using osu.Game.Modes.Objects.Drawables; namespace osu.Game.Modes { public class ScoreProcessor { public virtual Score GetScore() => new Score(); + + public BindableDouble TotalScore = new BindableDouble { MinValue = 0 }; + + public BindableDouble Accuracy = new BindableDouble { MinValue = 0, MaxValue = 1 }; + + public BindableInt Combo = new BindableInt(); + + public List Judgements = new List(); + + public virtual void AddJudgement(JudgementInfo judgement) + { + Judgements.Add(judgement); + UpdateCalculations(); + } + + /// + /// Update any values that potentially need post-processing on a judgement change. + /// + protected virtual void UpdateCalculations() + { + + } } } diff --git a/osu.Game/Modes/UI/ComboCounter.cs b/osu.Game/Modes/UI/ComboCounter.cs index 0d34d5dd9a..b150111387 100644 --- a/osu.Game/Modes/UI/ComboCounter.cs +++ b/osu.Game/Modes/UI/ComboCounter.cs @@ -262,5 +262,13 @@ namespace osu.Game.Modes.UI (d as ComboCounter).DisplayedCount = CurrentValue; } } + + public void Set(ulong value) + { + if (value == 0) + Roll(); + else + Count = value; + } } } diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index d37cf545a8..5d70fd71a0 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -14,8 +14,9 @@ namespace osu.Game.Modes.UI { public abstract class HitRenderer : Container { - public Action OnHit; - public Action OnMiss; + public event Action OnJudgement; + + protected void TriggerOnJudgement(JudgementInfo j) => OnJudgement?.Invoke(j); protected Playfield Playfield; @@ -68,22 +69,13 @@ namespace osu.Game.Modes.UI if (drawableObject == null) continue; - drawableObject.OnHit = onHit; - drawableObject.OnMiss = onMiss; + drawableObject.OnJudgement += onJudgement; Playfield.Add(drawableObject); } } - private void onMiss(DrawableHitObject obj, JudgementInfo judgement) - { - OnMiss?.Invoke(obj.HitObject); - } - - private void onHit(DrawableHitObject obj, JudgementInfo judgement) - { - OnHit?.Invoke(obj.HitObject); - } + private void onJudgement(DrawableHitObject o, JudgementInfo j) => TriggerOnJudgement(j); protected abstract DrawableHitObject GetVisualRepresentation(T h); } diff --git a/osu.Game/Modes/UI/ScoreOverlay.cs b/osu.Game/Modes/UI/ScoreOverlay.cs index 9f29ff63ff..c7441483f8 100644 --- a/osu.Game/Modes/UI/ScoreOverlay.cs +++ b/osu.Game/Modes/UI/ScoreOverlay.cs @@ -49,7 +49,10 @@ namespace osu.Game.Modes.UI public void BindProcessor(ScoreProcessor processor) { - + //bind processor bindables to combocounter, score display etc. + processor.TotalScore.ValueChanged += delegate { ScoreCounter?.Set((ulong)processor.TotalScore.Value); }; + processor.Accuracy.ValueChanged += delegate { AccuracyCounter?.Set((float)processor.Accuracy.Value); }; + processor.Combo.ValueChanged += delegate { ComboCounter?.Set((ulong)processor.Combo.Value); }; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 262e5c5fbc..1fa652afec 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -91,8 +91,7 @@ namespace osu.Game.Screens.Play var hitRenderer = ruleset.CreateHitRendererWith(beatmap.HitObjects); - hitRenderer.OnHit += delegate (HitObject h) { scoreOverlay.OnHit(h); }; - hitRenderer.OnMiss += delegate (HitObject h) { scoreOverlay.OnMiss(h); }; + hitRenderer.OnJudgement += scoreProcessor.AddJudgement; if (Autoplay) hitRenderer.Schedule(() => hitRenderer.DrawableObjects.ForEach(h => h.State = ArmedState.Hit)); From a0503bd13fac41f8ddb4348b295a1abbc148f8aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 21:29:53 +0900 Subject: [PATCH 05/12] Tidy up Slider class. --- .../Objects/BezierApproximator.cs | 148 ++++++++++++++ osu.Game.Mode.Osu/Objects/Slider.cs | 184 ------------------ osu.Game.Mode.Osu/Objects/SliderCurve.cs | 44 +++++ osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj | 2 + 4 files changed, 194 insertions(+), 184 deletions(-) create mode 100644 osu.Game.Mode.Osu/Objects/BezierApproximator.cs create mode 100644 osu.Game.Mode.Osu/Objects/SliderCurve.cs diff --git a/osu.Game.Mode.Osu/Objects/BezierApproximator.cs b/osu.Game.Mode.Osu/Objects/BezierApproximator.cs new file mode 100644 index 0000000000..f08b7aa377 --- /dev/null +++ b/osu.Game.Mode.Osu/Objects/BezierApproximator.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using OpenTK; + +namespace osu.Game.Modes.Osu.Objects +{ + public class BezierApproximator + { + private int count; + private List controlPoints; + private Vector2[] subdivisionBuffer1; + private Vector2[] subdivisionBuffer2; + + private const float TOLERANCE = 0.5f; + private const float TOLERANCE_SQ = TOLERANCE * TOLERANCE; + + public BezierApproximator(List controlPoints) + { + this.controlPoints = controlPoints; + count = controlPoints.Count; + + subdivisionBuffer1 = new Vector2[count]; + subdivisionBuffer2 = new Vector2[count * 2 - 1]; + } + + /// + /// Make sure the 2nd order derivative (approximated using finite elements) is within tolerable bounds. + /// NOTE: The 2nd order derivative of a 2d curve represents its curvature, so intuitively this function + /// checks (as the name suggests) whether our approximation is _locally_ "flat". More curvy parts + /// need to have a denser approximation to be more "flat". + /// + /// The control points to check for flatness. + /// Whether the control points are flat enough. + private static bool IsFlatEnough(Vector2[] controlPoints) + { + for (int i = 1; i < controlPoints.Length - 1; i++) + if ((controlPoints[i - 1] - 2 * controlPoints[i] + controlPoints[i + 1]).LengthSquared > TOLERANCE_SQ) + return false; + + return true; + } + + /// + /// Subdivides n control points representing a bezier curve into 2 sets of n control points, each + /// describing a bezier curve equivalent to a half of the original curve. Effectively this splits + /// the original curve into 2 curves which result in the original curve when pieced back together. + /// + /// The control points to split. + /// Output: The control points corresponding to the left half of the curve. + /// Output: The control points corresponding to the right half of the curve. + private void Subdivide(Vector2[] controlPoints, Vector2[] l, Vector2[] r) + { + Vector2[] midpoints = subdivisionBuffer1; + + for (int i = 0; i < count; ++i) + midpoints[i] = controlPoints[i]; + + for (int i = 0; i < count; i++) + { + l[i] = midpoints[0]; + r[count - i - 1] = midpoints[count - i - 1]; + + for (int j = 0; j < count - i - 1; j++) + midpoints[j] = (midpoints[j] + midpoints[j + 1]) / 2; + } + } + + /// + /// This uses De Casteljau's algorithm to obtain an optimal + /// piecewise-linear approximation of the bezier curve with the same amount of points as there are control points. + /// + /// The control points describing the bezier curve to be approximated. + /// The points representing the resulting piecewise-linear approximation. + private void Approximate(Vector2[] controlPoints, List output) + { + Vector2[] l = subdivisionBuffer2; + Vector2[] r = subdivisionBuffer1; + + Subdivide(controlPoints, l, r); + + for (int i = 0; i < count - 1; ++i) + l[count + i] = r[i + 1]; + + output.Add(controlPoints[0]); + for (int i = 1; i < count - 1; ++i) + { + int index = 2 * i; + Vector2 p = 0.25f * (l[index - 1] + 2 * l[index] + l[index + 1]); + output.Add(p); + } + } + + /// + /// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing + /// the control points until their approximation error vanishes below a given threshold. + /// + /// The control points describing the curve. + /// A list of vectors representing the piecewise-linear approximation. + public List CreateBezier() + { + List output = new List(); + + if (count == 0) + return output; + + Stack toFlatten = new Stack(); + Stack freeBuffers = new Stack(); + + // "toFlatten" contains all the curves which are not yet approximated well enough. + // We use a stack to emulate recursion without the risk of running into a stack overflow. + // (More specifically, we iteratively and adaptively refine our curve with a + // Depth-first search + // over the tree resulting from the subdivisions we make.) + toFlatten.Push(controlPoints.ToArray()); + + Vector2[] leftChild = subdivisionBuffer2; + + while (toFlatten.Count > 0) + { + Vector2[] parent = toFlatten.Pop(); + if (IsFlatEnough(parent)) + { + // If the control points we currently operate on are sufficiently "flat", we use + // an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation + // of the bezier curve represented by our control points, consisting of the same amount + // of points as there are control points. + Approximate(parent, output); + freeBuffers.Push(parent); + continue; + } + + // If we do not yet have a sufficiently "flat" (in other words, detailed) approximation we keep + // subdividing the curve we are currently operating on. + Vector2[] rightChild = freeBuffers.Count > 0 ? freeBuffers.Pop() : new Vector2[count]; + Subdivide(parent, leftChild, rightChild); + + // We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration. + for (int i = 0; i < count; ++i) + parent[i] = leftChild[i]; + + toFlatten.Push(rightChild); + toFlatten.Push(parent); + } + + output.Add(controlPoints[count - 1]); + return output; + } + } +} \ No newline at end of file diff --git a/osu.Game.Mode.Osu/Objects/Slider.cs b/osu.Game.Mode.Osu/Objects/Slider.cs index 7ee131659a..a0cdbeae7c 100644 --- a/osu.Game.Mode.Osu/Objects/Slider.cs +++ b/osu.Game.Mode.Osu/Objects/Slider.cs @@ -1,9 +1,7 @@ //Copyright (c) 2007-2016 ppy Pty Ltd . //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using osu.Game.Database; -using OpenTK; using osu.Game.Beatmaps; using System; @@ -25,188 +23,6 @@ namespace osu.Game.Modes.Osu.Objects public SliderCurve Curve; } - public class SliderCurve - { - public double Length; - - public List Path; - - public CurveTypes CurveType; - - private List calculatedPath; - - public void Calculate() - { - switch (CurveType) - { - case CurveTypes.Linear: - calculatedPath = Path; - break; - default: - var bezier = new BezierApproximator(Path); - calculatedPath = bezier.CreateBezier(); - break; - } - } - - public Vector2 PositionAt(double progress) - { - progress = MathHelper.Clamp(progress, 0, 1); - - double index = progress * (calculatedPath.Count - 1); - int flooredIndex = (int)index; - - Vector2 pos = calculatedPath[flooredIndex]; - if (index != flooredIndex) - pos += (calculatedPath[flooredIndex + 1] - pos) * (float)(index - flooredIndex); - - return pos; - } - } - - public class BezierApproximator - { - private int count; - private List controlPoints; - private Vector2[] subdivisionBuffer1; - private Vector2[] subdivisionBuffer2; - - private const float TOLERANCE = 0.5f; - private const float TOLERANCE_SQ = TOLERANCE * TOLERANCE; - - public BezierApproximator(List controlPoints) - { - this.controlPoints = controlPoints; - count = controlPoints.Count; - - subdivisionBuffer1 = new Vector2[count]; - subdivisionBuffer2 = new Vector2[count * 2 - 1]; - } - - /// - /// Make sure the 2nd order derivative (approximated using finite elements) is within tolerable bounds. - /// NOTE: The 2nd order derivative of a 2d curve represents its curvature, so intuitively this function - /// checks (as the name suggests) whether our approximation is _locally_ "flat". More curvy parts - /// need to have a denser approximation to be more "flat". - /// - /// The control points to check for flatness. - /// Whether the control points are flat enough. - private static bool IsFlatEnough(Vector2[] controlPoints) - { - for (int i = 1; i < controlPoints.Length - 1; i++) - if ((controlPoints[i - 1] - 2 * controlPoints[i] + controlPoints[i + 1]).LengthSquared > TOLERANCE_SQ) - return false; - - return true; - } - - /// - /// Subdivides n control points representing a bezier curve into 2 sets of n control points, each - /// describing a bezier curve equivalent to a half of the original curve. Effectively this splits - /// the original curve into 2 curves which result in the original curve when pieced back together. - /// - /// The control points to split. - /// Output: The control points corresponding to the left half of the curve. - /// Output: The control points corresponding to the right half of the curve. - private void Subdivide(Vector2[] controlPoints, Vector2[] l, Vector2[] r) - { - Vector2[] midpoints = subdivisionBuffer1; - - for (int i = 0; i < count; ++i) - midpoints[i] = controlPoints[i]; - - for (int i = 0; i < count; i++) - { - l[i] = midpoints[0]; - r[count - i - 1] = midpoints[count - i - 1]; - - for (int j = 0; j < count - i - 1; j++) - midpoints[j] = (midpoints[j] + midpoints[j + 1]) / 2; - } - } - - /// - /// This uses De Casteljau's algorithm to obtain an optimal - /// piecewise-linear approximation of the bezier curve with the same amount of points as there are control points. - /// - /// The control points describing the bezier curve to be approximated. - /// The points representing the resulting piecewise-linear approximation. - private void Approximate(Vector2[] controlPoints, List output) - { - Vector2[] l = subdivisionBuffer2; - Vector2[] r = subdivisionBuffer1; - - Subdivide(controlPoints, l, r); - - for (int i = 0; i < count - 1; ++i) - l[count + i] = r[i + 1]; - - output.Add(controlPoints[0]); - for (int i = 1; i < count - 1; ++i) - { - int index = 2 * i; - Vector2 p = 0.25f * (l[index - 1] + 2 * l[index] + l[index + 1]); - output.Add(p); - } - } - - /// - /// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing - /// the control points until their approximation error vanishes below a given threshold. - /// - /// The control points describing the curve. - /// A list of vectors representing the piecewise-linear approximation. - public List CreateBezier() - { - List output = new List(); - - if (count == 0) - return output; - - Stack toFlatten = new Stack(); - Stack freeBuffers = new Stack(); - - // "toFlatten" contains all the curves which are not yet approximated well enough. - // We use a stack to emulate recursion without the risk of running into a stack overflow. - // (More specifically, we iteratively and adaptively refine our curve with a - // Depth-first search - // over the tree resulting from the subdivisions we make.) - toFlatten.Push(controlPoints.ToArray()); - - Vector2[] leftChild = subdivisionBuffer2; - - while (toFlatten.Count > 0) - { - Vector2[] parent = toFlatten.Pop(); - if (IsFlatEnough(parent)) - { - // If the control points we currently operate on are sufficiently "flat", we use - // an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation - // of the bezier curve represented by our control points, consisting of the same amount - // of points as there are control points. - Approximate(parent, output); - freeBuffers.Push(parent); - continue; - } - - // If we do not yet have a sufficiently "flat" (in other words, detailed) approximation we keep - // subdividing the curve we are currently operating on. - Vector2[] rightChild = freeBuffers.Count > 0 ? freeBuffers.Pop() : new Vector2[count]; - Subdivide(parent, leftChild, rightChild); - - // We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration. - for (int i = 0; i < count; ++i) - parent[i] = leftChild[i]; - - toFlatten.Push(rightChild); - toFlatten.Push(parent); - } - - output.Add(controlPoints[count - 1]); - return output; - } - } - public enum CurveTypes { Catmull, diff --git a/osu.Game.Mode.Osu/Objects/SliderCurve.cs b/osu.Game.Mode.Osu/Objects/SliderCurve.cs new file mode 100644 index 0000000000..98e25524da --- /dev/null +++ b/osu.Game.Mode.Osu/Objects/SliderCurve.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using OpenTK; + +namespace osu.Game.Modes.Osu.Objects +{ + public class SliderCurve + { + public double Length; + + public List Path; + + public CurveTypes CurveType; + + private List calculatedPath; + + public void Calculate() + { + switch (CurveType) + { + case CurveTypes.Linear: + calculatedPath = Path; + break; + default: + var bezier = new BezierApproximator(Path); + calculatedPath = bezier.CreateBezier(); + break; + } + } + + public Vector2 PositionAt(double progress) + { + progress = MathHelper.Clamp(progress, 0, 1); + + double index = progress * (calculatedPath.Count - 1); + int flooredIndex = (int)index; + + Vector2 pos = calculatedPath[flooredIndex]; + if (index != flooredIndex) + pos += (calculatedPath[flooredIndex + 1] - pos) * (float)(index - flooredIndex); + + return pos; + } + } +} \ No newline at end of file diff --git a/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj index cca903643b..ba6e714e0a 100644 --- a/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj @@ -41,6 +41,7 @@ + @@ -53,6 +54,7 @@ + From f8c1f4dd582ea573060fee5fbb6e66cc1a9bb531 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 21:40:24 +0900 Subject: [PATCH 06/12] Add basic slider judgement handling. --- .../Objects/Drawables/DrawableSlider.cs | 12 ++++++++++++ .../Modes/Objects/Drawables/DrawableHitObject.cs | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs index 1d55d704c4..df79953eae 100644 --- a/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs @@ -75,6 +75,18 @@ namespace osu.Game.Modes.Osu.Objects.Drawables ball.Position = slider.Curve.PositionAt(t); } + protected override void CheckJudgement(bool userTriggered) + { + var j = Judgement as OsuJudgementInfo; + var sc = startCircle.Judgement as OsuJudgementInfo; + + if (!userTriggered && Time.Current >= HitObject.EndTime) + { + j.Score = sc.Score; + j.Result = sc.Result; + } + } + protected override void UpdateState(ArmedState state) { base.UpdateState(state); diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index f00a24d79e..53d3b1bafc 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -17,7 +17,7 @@ namespace osu.Game.Modes.Objects.Drawables public Container ChildObjects; - protected JudgementInfo Judgement; + public JudgementInfo Judgement; public abstract JudgementInfo CreateJudgementInfo(); @@ -84,7 +84,7 @@ namespace osu.Game.Modes.Objects.Drawables protected virtual void CheckJudgement(bool userTriggered) { - + //todo: consider making abstract. } protected override void Update() From 18d331f869026557df46ad9dfc6e1ac36f5924b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 21:46:30 +0900 Subject: [PATCH 07/12] Add combo info to JudgementInfo. --- osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs | 1 + osu.Game/Modes/ScoreProcesssor.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index 53d3b1bafc..a10f8df27a 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -111,6 +111,7 @@ namespace osu.Game.Modes.Objects.Drawables public class JudgementInfo { + public ulong? ComboAtHit; public HitResult? Result; public double TimeOffset; } diff --git a/osu.Game/Modes/ScoreProcesssor.cs b/osu.Game/Modes/ScoreProcesssor.cs index efae0bd40b..fd9b5ba0fe 100644 --- a/osu.Game/Modes/ScoreProcesssor.cs +++ b/osu.Game/Modes/ScoreProcesssor.cs @@ -27,6 +27,8 @@ namespace osu.Game.Modes { Judgements.Add(judgement); UpdateCalculations(); + + judgement.ComboAtHit = (ulong)Combo.Value; } /// From eb70ae788c9e037edca47f767265621d2af5b80e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 21:57:53 +0900 Subject: [PATCH 08/12] Store max combo in ScoreProcessor. --- osu.Game/Modes/ScoreProcesssor.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Modes/ScoreProcesssor.cs b/osu.Game/Modes/ScoreProcesssor.cs index fd9b5ba0fe..7dfd7d04ce 100644 --- a/osu.Game/Modes/ScoreProcesssor.cs +++ b/osu.Game/Modes/ScoreProcesssor.cs @@ -21,6 +21,8 @@ namespace osu.Game.Modes public BindableInt Combo = new BindableInt(); + public BindableInt MaximumCombo = new BindableInt(); + public List Judgements = new List(); public virtual void AddJudgement(JudgementInfo judgement) @@ -29,6 +31,9 @@ namespace osu.Game.Modes UpdateCalculations(); judgement.ComboAtHit = (ulong)Combo.Value; + + if (Combo.Value > MaximumCombo.Value) + MaximumCombo.Value = Combo.Value; } /// From d71b284642cb018be767116492036e02063bda21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 22:02:37 +0900 Subject: [PATCH 09/12] Make ScoreProcessor abstract and avoid making AddJudgement virtual. --- osu.Game.Mode.Osu/OsuScoreProcessor.cs | 27 +++++++++------------- osu.Game.Modes.Catch/CatchRuleset.cs | 2 +- osu.Game.Modes.Mania/ManiaRuleset.cs | 2 +- osu.Game.Modes.Taiko/TaikoRuleset.cs | 2 +- osu.Game/Modes/ScoreProcesssor.cs | 31 +++++++++++++------------- 5 files changed, 30 insertions(+), 34 deletions(-) diff --git a/osu.Game.Mode.Osu/OsuScoreProcessor.cs b/osu.Game.Mode.Osu/OsuScoreProcessor.cs index 177844297e..a5ebda6834 100644 --- a/osu.Game.Mode.Osu/OsuScoreProcessor.cs +++ b/osu.Game.Mode.Osu/OsuScoreProcessor.cs @@ -10,23 +10,20 @@ namespace osu.Game.Modes.Osu { class OsuScoreProcessor : ScoreProcessor { - public override void AddJudgement(JudgementInfo judgement) + protected override void UpdateCalculations(JudgementInfo judgement) { - base.AddJudgement(judgement); - - switch (judgement.Result) + if (judgement != null) { - case HitResult.Hit: - Combo.Value++; - break; - case HitResult.Miss: - Combo.Value = 0; - break; + switch (judgement.Result) + { + case HitResult.Hit: + Combo.Value++; + break; + case HitResult.Miss: + Combo.Value = 0; + break; + } } - } - protected override void UpdateCalculations() - { - base.UpdateCalculations(); int score = 0; int maxScore = 0; @@ -51,8 +48,6 @@ namespace osu.Game.Modes.Osu maxScore += 300; break; } - - } TotalScore.Value = score; diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index 695bca2593..eac762b4a0 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -18,7 +18,7 @@ namespace osu.Game.Modes.Catch protected override PlayMode PlayMode => PlayMode.Catch; - public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(); + public override ScoreProcessor CreateScoreProcessor() => null; public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index 0e7761e8fb..e91b2ed02e 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -19,7 +19,7 @@ namespace osu.Game.Modes.Mania protected override PlayMode PlayMode => PlayMode.Mania; - public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(); + public override ScoreProcessor CreateScoreProcessor() => null; public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index 3e697e48ea..e706387aaa 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -19,7 +19,7 @@ namespace osu.Game.Modes.Taiko protected override PlayMode PlayMode => PlayMode.Taiko; - public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(); + public override ScoreProcessor CreateScoreProcessor() => null; public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); } diff --git a/osu.Game/Modes/ScoreProcesssor.cs b/osu.Game/Modes/ScoreProcesssor.cs index 7dfd7d04ce..63a1ed1528 100644 --- a/osu.Game/Modes/ScoreProcesssor.cs +++ b/osu.Game/Modes/ScoreProcesssor.cs @@ -11,37 +11,38 @@ using osu.Game.Modes.Objects.Drawables; namespace osu.Game.Modes { - public class ScoreProcessor + public abstract class ScoreProcessor { public virtual Score GetScore() => new Score(); - public BindableDouble TotalScore = new BindableDouble { MinValue = 0 }; + public readonly BindableDouble TotalScore = new BindableDouble { MinValue = 0 }; - public BindableDouble Accuracy = new BindableDouble { MinValue = 0, MaxValue = 1 }; + public readonly BindableDouble Accuracy = new BindableDouble { MinValue = 0, MaxValue = 1 }; - public BindableInt Combo = new BindableInt(); + public readonly BindableInt Combo = new BindableInt(); - public BindableInt MaximumCombo = new BindableInt(); + public readonly BindableInt MaximumCombo = new BindableInt(); - public List Judgements = new List(); + public readonly List Judgements = new List(); - public virtual void AddJudgement(JudgementInfo judgement) + public ScoreProcessor() + { + Combo.ValueChanged += delegate { MaximumCombo.Value = Math.Max(MaximumCombo.Value, Combo.Value); }; + } + + public void AddJudgement(JudgementInfo judgement) { Judgements.Add(judgement); - UpdateCalculations(); + + UpdateCalculations(judgement); judgement.ComboAtHit = (ulong)Combo.Value; - - if (Combo.Value > MaximumCombo.Value) - MaximumCombo.Value = Combo.Value; } /// /// Update any values that potentially need post-processing on a judgement change. /// - protected virtual void UpdateCalculations() - { - - } + /// A new JudgementInfo that triggered this calculation. May be null. + protected abstract void UpdateCalculations(JudgementInfo newJudgement); } } From ca0bea753439ce81ddb5eb86ef32fb6cc369c110 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 22:05:21 +0900 Subject: [PATCH 10/12] Rename MaximumCombo to HighestCombo. --- osu.Game/Modes/ScoreProcesssor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Modes/ScoreProcesssor.cs b/osu.Game/Modes/ScoreProcesssor.cs index 63a1ed1528..d94a424098 100644 --- a/osu.Game/Modes/ScoreProcesssor.cs +++ b/osu.Game/Modes/ScoreProcesssor.cs @@ -21,13 +21,13 @@ namespace osu.Game.Modes public readonly BindableInt Combo = new BindableInt(); - public readonly BindableInt MaximumCombo = new BindableInt(); + public readonly BindableInt HighestCombo = new BindableInt(); public readonly List Judgements = new List(); public ScoreProcessor() { - Combo.ValueChanged += delegate { MaximumCombo.Value = Math.Max(MaximumCombo.Value, Combo.Value); }; + Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; } public void AddJudgement(JudgementInfo judgement) From 30e62eb53c652da524cee8fa0d2ee2a1562b5faf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 22:14:56 +0900 Subject: [PATCH 11/12] Adjust slider fadeout slightly. --- osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs index df79953eae..c87bceb1d0 100644 --- a/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs @@ -92,7 +92,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables base.UpdateState(state); Delay(HitObject.Duration); - FadeOut(100); + FadeOut(300); } private class Ball : Container From 74e3a87f7874d2d3e90fe0622eb9a308c44dc10e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Nov 2016 23:59:56 +0900 Subject: [PATCH 12/12] Add *very* basic ranking screen. --- osu.Game/Modes/Score.cs | 6 ++- osu.Game/Modes/ScoreProcesssor.cs | 8 +++- osu.Game/Modes/UI/HitRenderer.cs | 11 ++++- osu.Game/Screens/Play/Player.cs | 23 ++++++--- osu.Game/Screens/Ranking/Results.cs | 73 +++++++++++++++++++++++++++-- 5 files changed, 106 insertions(+), 15 deletions(-) diff --git a/osu.Game/Modes/Score.cs b/osu.Game/Modes/Score.cs index 88d79f94dd..ee90a9a0f9 100644 --- a/osu.Game/Modes/Score.cs +++ b/osu.Game/Modes/Score.cs @@ -11,7 +11,9 @@ namespace osu.Game.Modes { public class Score { - public double TotalScore { get; } - public double Accuracy { get; } + public double TotalScore { get; set; } + public double Accuracy { get; set; } + public double Combo { get; set; } + public double MaxCombo { get; set; } } } diff --git a/osu.Game/Modes/ScoreProcesssor.cs b/osu.Game/Modes/ScoreProcesssor.cs index d94a424098..a34915eaef 100644 --- a/osu.Game/Modes/ScoreProcesssor.cs +++ b/osu.Game/Modes/ScoreProcesssor.cs @@ -13,7 +13,13 @@ namespace osu.Game.Modes { public abstract class ScoreProcessor { - public virtual Score GetScore() => new Score(); + public virtual Score GetScore() => new Score() + { + TotalScore = TotalScore, + Combo = Combo, + MaxCombo = HighestCombo, + Accuracy = Accuracy + }; public readonly BindableDouble TotalScore = new BindableDouble { MinValue = 0 }; diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index 5d70fd71a0..bd191a47a3 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -16,10 +16,19 @@ namespace osu.Game.Modes.UI { public event Action OnJudgement; - protected void TriggerOnJudgement(JudgementInfo j) => OnJudgement?.Invoke(j); + public event Action OnAllJudged; + + protected void TriggerOnJudgement(JudgementInfo j) + { + OnJudgement?.Invoke(j); + if (AllObjectsJudged) + OnAllJudged?.Invoke(); + } protected Playfield Playfield; + public bool AllObjectsJudged => Playfield.HitObjects.Children.First()?.Judgement.Result != null; //reverse depth sort means First() instead of Last(). + public IEnumerable DrawableObjects => Playfield.HitObjects.Children; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1fa652afec..2f35f1b5d5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -19,6 +19,7 @@ using OpenTK.Input; using MouseState = osu.Framework.Input.MouseState; using OpenTK; using osu.Framework.GameModes; +using osu.Game.Modes.UI; using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Play @@ -40,6 +41,7 @@ namespace osu.Game.Screens.Play private Ruleset ruleset; private ScoreProcessor scoreProcessor; + private HitRenderer hitRenderer; [BackgroundDependencyLoader] private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuGameBase game) @@ -89,9 +91,10 @@ namespace osu.Game.Screens.Play var scoreOverlay = ruleset.CreateScoreOverlay(); scoreOverlay.BindProcessor(scoreProcessor = ruleset.CreateScoreProcessor()); - var hitRenderer = ruleset.CreateHitRendererWith(beatmap.HitObjects); + hitRenderer = ruleset.CreateHitRendererWith(beatmap.HitObjects); hitRenderer.OnJudgement += scoreProcessor.AddJudgement; + hitRenderer.OnAllJudged += hitRenderer_OnAllJudged; if (Autoplay) hitRenderer.Schedule(() => hitRenderer.DrawableObjects.ForEach(h => h.State = ArmedState.Hit)); @@ -110,6 +113,18 @@ namespace osu.Game.Screens.Play }; } + private void hitRenderer_OnAllJudged() + { + Delay(1000); + Schedule(delegate + { + Push(new Results + { + Score = scoreProcessor.GetScore() + }); + }); + } + protected override void OnEntering(GameMode last) { base.OnEntering(last); @@ -121,12 +136,6 @@ namespace osu.Game.Screens.Play { base.Update(); Clock.ProcessFrame(); - - if (Beatmap.Track.HasCompleted) - Push(new Results - { - Score = scoreProcessor.GetScore() - }); } class PlayerInputManager : UserInputManager diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 55f2d4fe2b..a4219daf54 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -2,20 +2,29 @@ //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.GameModes; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transformations; using osu.Game.Modes; using osu.Game.Screens.Backgrounds; +using OpenTK; using OpenTK.Graphics; namespace osu.Game.Screens.Ranking { - class Results : GameModeWhiteBox + class Results : OsuGameMode { - protected override BackgroundMode CreateBackground() => new BackgroundModeCustom(@"Backgrounds/bg4"); + protected override BackgroundMode CreateBackground() => new BackgroundModeBeatmap(Beatmap); + + private static readonly Vector2 BACKGROUND_BLUR = new Vector2(20); + + ScoreDisplay scoreDisplay; protected override void OnEntering(GameMode last) { base.OnEntering(last); - Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500)); + Background.Schedule(() => (Background as BackgroundModeBeatmap)?.BlurTo(BACKGROUND_BLUR, 1000)); } protected override bool OnExiting(GameMode next) @@ -24,6 +33,62 @@ namespace osu.Game.Screens.Ranking return base.OnExiting(next); } - public Score Score { get; set; } + public Score Score + { + set + { + scoreDisplay?.FadeOut(500); + scoreDisplay?.Expire(); + + scoreDisplay = new ScoreDisplay(value) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + Add(scoreDisplay); + + scoreDisplay.FadeIn(500); + scoreDisplay.ScaleTo(0.1f); + scoreDisplay.ScaleTo(1, 1000, EasingTypes.OutElastic); + scoreDisplay.RotateTo(360 * 5, 1000, EasingTypes.OutElastic); + + } + } + } + + class ScoreDisplay : Container + { + public ScoreDisplay(Score s) + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new FlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FlowDirection.VerticalOnly, + Children = new Drawable[] + { + new SpriteText + { + TextSize = 40, + Text = $@"Accuracy: {s.Accuracy:#0.00%}", + }, + new SpriteText + { + TextSize = 40, + Text = $@"Score: {s.TotalScore}", + }, + new SpriteText + { + TextSize = 40, + Text = $@"MaxCombo: {s.MaxCombo}", + } + } + } + }; + } } }