diff --git a/osu-framework b/osu-framework index dc4ea5be42..ccf0ff40d1 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit dc4ea5be425d37f3a0dd09f6acdf6799d42e3d74 +Subproject commit ccf0ff40d1261ad328d0182467a1f0c1a858b099 diff --git a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs index 93e0646255..aa3a117667 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Testing; @@ -47,6 +48,13 @@ namespace osu.Desktop.VisualTests.Tests MaxCombo = 123, Rank = ScoreRank.A, Date = DateTime.Now, + Statistics = new Dictionary() + { + { "300", 50 }, + { "100", 20 }, + { "50", 50 }, + { "x", 1 } + }, User = new User { Username = "peppy", @@ -57,4 +65,4 @@ namespace osu.Desktop.VisualTests.Tests }); } } - } +} diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScore.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScore.cs deleted file mode 100644 index c73cfe3338..0000000000 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScore.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Scoring -{ - internal class OsuScore : Score - { - } -} diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 0c38f66abe..079ee928af 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -1,9 +1,12 @@ // 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.Framework.Extensions; 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 +29,34 @@ 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 void PopulateScore(Score score) + { + base.PopulateScore(score); + + score.Statistics[@"300"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit300); + score.Statistics[@"100"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit100); + score.Statistics[@"50"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit50); + score.Statistics[@"x"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Miss); } protected override void OnNewJudgement(OsuJudgement judgement) { if (judgement != null) { + if (judgement.Result != HitResult.None) + { + scoreResultCounts[judgement.Score] = scoreResultCounts.GetOrDefault(judgement.Score) + 1; + comboResultCounts[judgement.Combo] = comboResultCounts.GetOrDefault(judgement.Combo) + 1; + } + switch (judgement.Result) { case HitResult.Hit: diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 272a35c286..fcad0061e4 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -72,7 +72,6 @@ - diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Database/ScoreDatabase.cs index 359728070f..99fa9fbf9a 100644 --- a/osu.Game/Database/ScoreDatabase.cs +++ b/osu.Game/Database/ScoreDatabase.cs @@ -2,11 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.IO; using System.Linq; using osu.Framework.Platform; using osu.Game.IO.Legacy; using osu.Game.IPC; +using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using SharpCompress.Compressors.LZMA; using SQLite.Net; @@ -42,8 +44,10 @@ namespace osu.Game.Database using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) using (SerializationReader sr = new SerializationReader(s)) { - var ruleset = rulesets.GetRuleset(sr.ReadByte()).CreateInstance(); - score = ruleset.CreateScoreProcessor().CreateScore(); + score = new Score + { + Ruleset = rulesets.GetRuleset(sr.ReadByte()) + }; /* score.Pass = true;*/ var version = sr.ReadInt32(); @@ -104,13 +108,43 @@ namespace osu.Game.Database using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize)) using (var reader = new StreamReader(lzma)) - score.Replay = score.CreateReplay(reader); + score.Replay = createReplay(reader); } } return score; } + /// + /// Creates a replay which is read from a stream. + /// + /// The stream reader. + /// The replay. + private Replay createReplay(StreamReader reader) + { + var frames = new List(); + + float lastTime = 0; + + foreach (var l in reader.ReadToEnd().Split(',')) + { + var split = l.Split('|'); + + if (split.Length < 4 || float.Parse(split[0]) < 0) continue; + + lastTime += float.Parse(split[0]); + + frames.Add(new ReplayFrame( + lastTime, + float.Parse(split[1]), + 384 - float.Parse(split[2]), + (ReplayButtonState)int.Parse(split[3]) + )); + } + + return new Replay { Frames = frames }; + } + protected override void Prepare(bool reset = false) { } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 5e92d25297..ea35e61b36 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -17,6 +17,12 @@ namespace osu.Game.Rulesets public abstract IEnumerable GetModsFor(ModType type); + /// + /// Attempt to create a HitRenderer for the provided beatmap. + /// + /// + /// Unable to successfully load the beatmap to be usable with this ruleset. + /// public abstract HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap); public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap); diff --git a/osu.Game/Rulesets/Scoring/Score.cs b/osu.Game/Rulesets/Scoring/Score.cs index a1ff983628..5d94fde03b 100644 --- a/osu.Game/Rulesets/Scoring/Score.cs +++ b/osu.Game/Rulesets/Scoring/Score.cs @@ -7,7 +7,6 @@ using Newtonsoft.Json; using osu.Game.Database; using osu.Game.Rulesets.Mods; using osu.Game.Users; -using System.IO; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Scoring @@ -33,6 +32,8 @@ namespace osu.Game.Rulesets.Scoring [JsonProperty(@"mods")] protected string[] ModStrings { get; set; } //todo: parse to Mod objects + public RulesetInfo Ruleset { get; set; } + public Mod[] Mods { get; set; } [JsonProperty(@"user")] @@ -49,51 +50,6 @@ namespace osu.Game.Rulesets.Scoring [JsonProperty(@"created_at")] public DateTime Date; - /// - /// Creates a replay which is read from a stream. - /// - /// The stream reader. - /// The replay. - public virtual Replay CreateReplay(StreamReader reader) - { - var frames = new List(); - - float lastTime = 0; - - foreach (var l in reader.ReadToEnd().Split(',')) - { - var split = l.Split('|'); - - if (split.Length < 4 || float.Parse(split[0]) < 0) continue; - - lastTime += float.Parse(split[0]); - - frames.Add(new ReplayFrame( - lastTime, - float.Parse(split[1]), - 384 - float.Parse(split[2]), - (ReplayButtonState)int.Parse(split[3]) - )); - } - - 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 Dictionary Statistics = new Dictionary(); } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 39008c5889..f31a1a11aa 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -61,21 +61,6 @@ namespace osu.Game.Rulesets.Scoring Reset(); } - /// - /// 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, - }; - private ScoreRank rankFrom(double acc) { if (acc == 1) @@ -119,6 +104,20 @@ 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 void PopulateScore(Score score) + { + score.TotalScore = TotalScore; + score.Combo = Combo; + score.MaxCombo = HighestCombo; + score.Accuracy = Accuracy; + score.Rank = rankFrom(Accuracy); + score.Date = DateTime.Now; + score.Health = Health; + } } public abstract class ScoreProcessor : ScoreProcessor diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 52518180d9..ca27e07262 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Play private IAdjustableClock sourceClock; private IFrameBasedClock interpolatedSourceClock; - private Ruleset ruleset; + private RulesetInfo ruleset; private ScoreProcessor scoreProcessor; protected HitRenderer HitRenderer; @@ -68,6 +68,8 @@ namespace osu.Game.Screens.Play dimLevel = config.GetBindable(OsuConfig.DimLevel); mouseWheelDisabled = config.GetBindable(OsuConfig.MouseDisableWheel); + Ruleset rulesetInstance; + try { if (Beatmap == null) @@ -79,17 +81,20 @@ namespace osu.Game.Screens.Play if (Beatmap == null) throw new Exception("Beatmap was not loaded"); + ruleset = osu?.Ruleset.Value ?? Beatmap.BeatmapInfo.Ruleset; + rulesetInstance = ruleset.CreateInstance(); + try { - // Try using the preferred user ruleset - ruleset = osu == null ? Beatmap.BeatmapInfo.Ruleset.CreateInstance() : osu.Ruleset.Value.CreateInstance(); - HitRenderer = ruleset.CreateHitRendererWith(Beatmap); + HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap); } catch (BeatmapInvalidForModeException) { - // Default to the beatmap ruleset - ruleset = Beatmap.BeatmapInfo.Ruleset.CreateInstance(); - HitRenderer = ruleset.CreateHitRendererWith(Beatmap); + // we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset + // let's try again forcing the beatmap's ruleset. + ruleset = Beatmap.BeatmapInfo.Ruleset; + rulesetInstance = ruleset.CreateInstance(); + HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap); } } catch (Exception e) @@ -125,7 +130,7 @@ namespace osu.Game.Screens.Play Origin = Anchor.Centre }; - hudOverlay.KeyCounter.Add(ruleset.CreateGameplayKeys()); + hudOverlay.KeyCounter.Add(rulesetInstance.CreateGameplayKeys()); hudOverlay.BindProcessor(scoreProcessor); hudOverlay.BindHitRenderer(HitRenderer); @@ -266,7 +271,12 @@ namespace osu.Game.Screens.Play Delay(1000); onCompletionEvent = Schedule(delegate { - var score = scoreProcessor.CreateScore(); + var score = new Score + { + Beatmap = Beatmap.BeatmapInfo, + Ruleset = ruleset + }; + scoreProcessor.PopulateScore(score); 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 2e04e8ffc3..88c9da802e 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -18,11 +18,13 @@ using osu.Game.Users; using OpenTK; using OpenTK.Graphics; using System; +using System.Collections.Generic; using osu.Framework.Extensions.Color4Extensions; 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 +34,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 +152,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 KeyValuePair statistic; + + public DrawableScoreStatistic(KeyValuePair 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.Key, + Colour = colours.Gray7, + Font = @"Exo2.0-Bold", + Y = 26, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + }; + } } private class DateDisplay : Container diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 35d791ef51..4ce8781964 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -40,6 +40,4 @@ namespace osu.Game.Users public int? Id; } } - - } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 54fde88b4f..752290769d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -57,6 +57,7 @@ $(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll True +