From 3b95fbab7db0761dc0177512f311c953c34ab213 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Apr 2017 15:40:10 +0900 Subject: [PATCH] Add score statistic tracking (osu!). --- .../Tests/TestCaseResults.cs | 7 +- osu.Game.Rulesets.Osu/Scoring/OsuScore.cs | 14 +++- .../Scoring/OsuScoreProcessor.cs | 36 +++++++++++ osu.Game/Database/ScoreDatabase.cs | 2 +- osu.Game/Rulesets/Scoring/Score.cs | 17 +---- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 29 ++++++--- osu.Game/Rulesets/Scoring/ScoreStatistic.cs | 17 +++++ osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Ranking/ResultsPageScore.cs | 64 ++++++++++++++++++- osu.Game/osu.Game.csproj | 1 + 10 files changed, 158 insertions(+), 31 deletions(-) create mode 100644 osu.Game/Rulesets/Scoring/ScoreStatistic.cs diff --git a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs index 93e0646255..e023b54abe 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Users; @@ -40,13 +41,17 @@ namespace osu.Desktop.VisualTests.Tests base.Reset(); - Add(new Results(new Score + Add(new Results(new OsuScore { TotalScore = 2845370, Accuracy = 0.98, MaxCombo = 123, Rank = ScoreRank.A, Date = DateTime.Now, + Count300 = 100, + Count100 = 10, + Count50 = 1, + CountMiss = 2, User = new User { Username = "peppy", diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScore.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScore.cs index c73cfe3338..248576b21d 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScore.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScore.cs @@ -1,11 +1,23 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring { - internal class OsuScore : Score + public class OsuScore : Score { + public int Count300; + public int Count100; + public int Count50; + public int CountMiss; + + public override IEnumerable Statistics => new[] { + new ScoreStatistic(@"300", Count300), + new ScoreStatistic(@"100", Count100), + new ScoreStatistic(@"50", Count50), + new ScoreStatistic(@"x", CountMiss), + }; } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 0c38f66abe..45253e0793 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -1,9 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -26,12 +28,46 @@ namespace osu.Game.Rulesets.Osu.Scoring Health.Value = 1; Accuracy.Value = 1; + + scoreResultCounts.Clear(); + comboResultCounts.Clear(); + } + + private readonly Dictionary scoreResultCounts = new Dictionary(); + private readonly Dictionary comboResultCounts = new Dictionary(); + + public override Score CreateEmptyScore() => new OsuScore(); + + public override Score GetPopulatedScore() + { + var score = (OsuScore)base.GetPopulatedScore(); + + scoreResultCounts.TryGetValue(OsuScoreResult.Hit300, out score.Count300); + scoreResultCounts.TryGetValue(OsuScoreResult.Hit100, out score.Count100); + scoreResultCounts.TryGetValue(OsuScoreResult.Hit50, out score.Count50); + scoreResultCounts.TryGetValue(OsuScoreResult.Miss, out score.CountMiss); + + return score; } protected override void OnNewJudgement(OsuJudgement judgement) { if (judgement != null) { + if (judgement.Result != HitResult.None) + { + int count; + if (scoreResultCounts.TryGetValue(judgement.Score, out count)) + scoreResultCounts[judgement.Score] = count + 1; + else + scoreResultCounts[judgement.Score] = 0; + + if (comboResultCounts.TryGetValue(judgement.Combo, out count)) + comboResultCounts[judgement.Combo] = count + 1; + else + comboResultCounts[judgement.Combo] = 0; + } + switch (judgement.Result) { case HitResult.Hit: diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Database/ScoreDatabase.cs index 359728070f..9dacf26e33 100644 --- a/osu.Game/Database/ScoreDatabase.cs +++ b/osu.Game/Database/ScoreDatabase.cs @@ -43,7 +43,7 @@ namespace osu.Game.Database using (SerializationReader sr = new SerializationReader(s)) { var ruleset = rulesets.GetRuleset(sr.ReadByte()).CreateInstance(); - score = ruleset.CreateScoreProcessor().CreateScore(); + score = ruleset.CreateScoreProcessor().CreateEmptyScore(); /* score.Pass = true;*/ var version = sr.ReadInt32(); diff --git a/osu.Game/Rulesets/Scoring/Score.cs b/osu.Game/Rulesets/Scoring/Score.cs index cb7831b04a..bb72237524 100644 --- a/osu.Game/Rulesets/Scoring/Score.cs +++ b/osu.Game/Rulesets/Scoring/Score.cs @@ -93,21 +93,6 @@ namespace osu.Game.Rulesets.Scoring return new Replay { Frames = frames }; } - // [JsonProperty(@"count50")] 0, - //[JsonProperty(@"count100")] 0, - //[JsonProperty(@"count300")] 100, - //[JsonProperty(@"countmiss")] 0, - //[JsonProperty(@"countkatu")] 0, - //[JsonProperty(@"countgeki")] 31, - //[JsonProperty(@"perfect")] true, - //[JsonProperty(@"enabled_mods")] [ - // "DT", - // "FL", - // "HD", - // "HR" - //], - //[JsonProperty(@"rank")] "XH", - //[JsonProperty(@"pp")] 26.1816, - //[JsonProperty(@"replay")] true + public virtual IEnumerable Statistics => new ScoreStatistic[] { }; } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 39008c5889..1fe21c8724 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -65,16 +65,7 @@ namespace osu.Game.Rulesets.Scoring /// Creates a Score applicable to the ruleset in which this ScoreProcessor resides. /// /// The Score. - public virtual Score CreateScore() => new Score - { - TotalScore = TotalScore, - Combo = Combo, - MaxCombo = HighestCombo, - Accuracy = Accuracy, - Rank = rankFrom(Accuracy), - Date = DateTime.Now, - Health = Health, - }; + public virtual Score CreateEmptyScore() => new Score(); private ScoreRank rankFrom(double acc) { @@ -119,6 +110,24 @@ namespace osu.Game.Rulesets.Scoring alreadyFailed = true; Failed?.Invoke(); } + + /// + /// Retrieve a score populated with data for the current play this processor is responsible for. + /// + public virtual Score GetPopulatedScore() + { + var score = CreateEmptyScore(); + + score.TotalScore = TotalScore; + score.Combo = Combo; + score.MaxCombo = HighestCombo; + score.Accuracy = Accuracy; + score.Rank = rankFrom(Accuracy); + score.Date = DateTime.Now; + score.Health = Health; + + return score; + } } public abstract class ScoreProcessor : ScoreProcessor diff --git a/osu.Game/Rulesets/Scoring/ScoreStatistic.cs b/osu.Game/Rulesets/Scoring/ScoreStatistic.cs new file mode 100644 index 0000000000..5d1011e829 --- /dev/null +++ b/osu.Game/Rulesets/Scoring/ScoreStatistic.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Scoring +{ + public class ScoreStatistic + { + public readonly string Name; + public readonly object Value; + + public ScoreStatistic(string name, object value) + { + Name = name; + Value = value; + } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 52518180d9..8c3f3da58a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -266,7 +266,7 @@ namespace osu.Game.Screens.Play Delay(1000); onCompletionEvent = Schedule(delegate { - var score = scoreProcessor.CreateScore(); + var score = scoreProcessor.GetPopulatedScore(); score.User = HitRenderer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value; Push(new Results(score)); }); diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 0874767f05..0d485121ed 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -23,6 +23,7 @@ using osu.Game.Beatmaps; using osu.Game.Screens.Play; using osu.Game.Rulesets.Scoring; using osu.Framework.Graphics.Colour; +using System.Linq; namespace osu.Game.Screens.Ranking { @@ -32,6 +33,8 @@ namespace osu.Game.Screens.Ranking public ResultsPageScore(Score score, WorkingBeatmap beatmap) : base(score, beatmap) { } + private FillFlowContainer statisticsContainer; + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -148,15 +151,74 @@ namespace osu.Game.Screens.Ranking }, } }, + statisticsContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Horizontal, + LayoutDuration = 200, + LayoutEasing = EasingTypes.OutQuint + } } } }; + + statisticsContainer.Children = Score.Statistics.Select(s => new DrawableScoreStatistic(s)); } protected override void LoadComplete() { base.LoadComplete(); - Schedule(() => scoreCounter.Increment(Score.TotalScore)); + + Schedule(() => + { + scoreCounter.Increment(Score.TotalScore); + + int delay = 0; + foreach (var s in statisticsContainer.Children) + { + s.FadeOut(); + s.Delay(delay += 200); + s.FadeIn(300 + delay, EasingTypes.Out); + } + }); + } + + private class DrawableScoreStatistic : Container + { + private readonly ScoreStatistic statistic; + + public DrawableScoreStatistic(ScoreStatistic statistic) + { + this.statistic = statistic; + + AutoSizeAxes = Axes.Both; + Margin = new MarginPadding { Left = 5, Right = 5 }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + new SpriteText { + Text = statistic.Value.ToString().PadLeft(4, '0'), + Colour = colours.Gray7, + TextSize = 30, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + new SpriteText { + Text = statistic.Name, + Colour = colours.Gray7, + Font = @"Exo2.0-Bold", + Y = 26, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + }; + } } private class DateDisplay : Container diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fe331c6356..4ee1aa0b32 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -153,6 +153,7 @@ +