diff --git a/osu-resources b/osu-resources index ce76f812d3..b90c4ed490 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit ce76f812d3e059233e1dea395b125352f638b2da +Subproject commit b90c4ed490f76f2995662b3a8af3a32b8756a012 diff --git a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs new file mode 100644 index 0000000000..93e0646255 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Ranking; +using osu.Game.Users; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseResults : TestCase + { + private BeatmapDatabase db; + + public override string Description => @"Results after playing."; + + [BackgroundDependencyLoader] + private void load(BeatmapDatabase db) + { + this.db = db; + } + + private WorkingBeatmap beatmap; + + public override void Reset() + { + base.Reset(); + + if (beatmap == null) + { + var beatmapInfo = db.Query().FirstOrDefault(b => b.RulesetID == 0); + if (beatmapInfo != null) + beatmap = db.GetWorkingBeatmap(beatmapInfo); + } + + base.Reset(); + + Add(new Results(new Score + { + TotalScore = 2845370, + Accuracy = 0.98, + MaxCombo = 123, + Rank = ScoreRank.A, + Date = DateTime.Now, + User = new User + { + Username = "peppy", + } + }) + { + Beatmap = beatmap + }); + } + } + } diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 80b18c4a9b..10b0d4c491 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -198,6 +198,7 @@ + diff --git a/osu.Game.Rulesets.Osu/OsuAutoReplay.cs b/osu.Game.Rulesets.Osu/OsuAutoReplay.cs index d6eab9eb56..4e10179819 100644 --- a/osu.Game.Rulesets.Osu/OsuAutoReplay.cs +++ b/osu.Game.Rulesets.Osu/OsuAutoReplay.cs @@ -12,6 +12,7 @@ using System.Diagnostics; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; +using osu.Game.Users; namespace osu.Game.Rulesets.Osu { @@ -27,6 +28,11 @@ namespace osu.Game.Rulesets.Osu { this.beatmap = beatmap; + User = new User + { + Username = @"Autoplay", + }; + createAutoReplay(); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 371699eab3..efda7ff7a0 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -116,6 +116,7 @@ namespace osu.Game Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BlackItalic")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera")); + Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light")); OszArchiveReader.Register(); diff --git a/osu.Game/Rulesets/Replays/Replay.cs b/osu.Game/Rulesets/Replays/Replay.cs index c903c2f56d..36e1b24e73 100644 --- a/osu.Game/Rulesets/Replays/Replay.cs +++ b/osu.Game/Rulesets/Replays/Replay.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Game.Users; namespace osu.Game.Rulesets.Replays { @@ -9,6 +10,8 @@ namespace osu.Game.Rulesets.Replays { protected const double KEY_UP_DELAY = 50; + public User User; + public List Frames = new List(); } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index b2c5d8319e..39008c5889 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -71,9 +71,26 @@ namespace osu.Game.Rulesets.Scoring Combo = Combo, MaxCombo = HighestCombo, Accuracy = Accuracy, + Rank = rankFrom(Accuracy), + Date = DateTime.Now, Health = Health, }; + private ScoreRank rankFrom(double acc) + { + if (acc == 1) + return ScoreRank.X; + if (acc > 0.95) + return ScoreRank.S; + if (acc > 0.9) + return ScoreRank.A; + if (acc > 0.8) + return ScoreRank.B; + if (acc > 0.7) + return ScoreRank.C; + return ScoreRank.D; + } + /// /// Resets this ScoreProcessor to a default state. /// diff --git a/osu.Game/Rulesets/UI/HitRenderer.cs b/osu.Game/Rulesets/UI/HitRenderer.cs index 098a4d5f03..021de37aae 100644 --- a/osu.Game/Rulesets/UI/HitRenderer.cs +++ b/osu.Game/Rulesets/UI/HitRenderer.cs @@ -91,11 +91,17 @@ namespace osu.Game.Rulesets.UI protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new FramedReplayInputHandler(replay); + public Replay Replay { get; private set; } + /// /// Sets a replay to be used, overriding local input. /// /// The replay, null for local input. - public void SetReplay(Replay replay) => InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; + public void SetReplay(Replay replay) + { + Replay = replay; + InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; + } } /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f831387626..52518180d9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -17,11 +17,11 @@ using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.UI; using osu.Game.Screens.Backgrounds; -using osu.Game.Screens.Ranking; using System; using System.Linq; using osu.Framework.Threading; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Play { @@ -266,10 +266,9 @@ namespace osu.Game.Screens.Play Delay(1000); onCompletionEvent = Schedule(delegate { - Push(new Results - { - Score = scoreProcessor.CreateScore() - }); + var score = scoreProcessor.CreateScore(); + score.User = HitRenderer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value; + Push(new Results(score)); }); } diff --git a/osu.Game/Screens/Ranking/AspectContainer.cs b/osu.Game/Screens/Ranking/AspectContainer.cs new file mode 100644 index 0000000000..4699b4ab92 --- /dev/null +++ b/osu.Game/Screens/Ranking/AspectContainer.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Screens.Ranking +{ + public class AspectContainer : Container + { + protected override void Update() + { + base.Update(); + if (RelativeSizeAxes == Axes.X) + Height = DrawWidth; + else + Width = DrawHeight; + } + } +} \ No newline at end of file diff --git a/osu.Game/Screens/Ranking/ResultMode.cs b/osu.Game/Screens/Ranking/ResultMode.cs new file mode 100644 index 0000000000..eeb04033ea --- /dev/null +++ b/osu.Game/Screens/Ranking/ResultMode.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Ranking +{ + public enum ResultMode + { + Summary, + Ranking, + Share + } +} \ No newline at end of file diff --git a/osu.Game/Screens/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs new file mode 100644 index 0000000000..fc62342860 --- /dev/null +++ b/osu.Game/Screens/Ranking/ResultModeButton.cs @@ -0,0 +1,108 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Ranking +{ + public class ResultModeButton : TabItem + { + private readonly FontAwesome icon; + private Color4 activeColour; + private Color4 inactiveColour; + private CircularContainer colouredPart; + + public ResultModeButton(ResultMode mode) : base(mode) + { + switch (mode) + { + case ResultMode.Summary: + icon = FontAwesome.fa_asterisk; + break; + case ResultMode.Ranking: + icon = FontAwesome.fa_list; + break; + case ResultMode.Share: + icon = FontAwesome.fa_camera; + break; + } + } + + public override bool Active + { + get + { + return base.Active; + } + set + { + base.Active = value; + colouredPart.FadeColour(Active ? activeColour : inactiveColour, 200, EasingTypes.OutQuint); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Size = new Vector2(50); + + Masking = true; + CornerRadius = 25; + + activeColour = colours.PinkDarker; + inactiveColour = OsuColour.Gray(0.8f); + + EdgeEffect = new EdgeEffect + { + Colour = Color4.Black.Opacity(0.4f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + colouredPart = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.8f), + BorderThickness = 4, + BorderColour = Color4.White, + Colour = inactiveColour, + Children = new Drawable[] + { + new Box + { + AlwaysPresent = true, //for border rendering + RelativeSizeAxes = Axes.Both, + Colour = Color4.Transparent, + }, + new TextAwesome + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shadow = false, + Colour = OsuColour.Gray(0.95f), + Icon = icon, + TextSize = 20, + } + } + } + }; + } + } +} diff --git a/osu.Game/Screens/Ranking/ResultModeTabControl.cs b/osu.Game/Screens/Ranking/ResultModeTabControl.cs new file mode 100644 index 0000000000..346bff5720 --- /dev/null +++ b/osu.Game/Screens/Ranking/ResultModeTabControl.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.UserInterface; +using OpenTK; + +namespace osu.Game.Screens.Ranking +{ + public class ResultModeTabControl : TabControl + { + public ResultModeTabControl() + { + TabContainer.Anchor = Anchor.BottomCentre; + TabContainer.Origin = Anchor.BottomCentre; + TabContainer.Spacing = new Vector2(15); + + TabContainer.Masking = false; + TabContainer.Padding = new MarginPadding(5); + } + + protected override Dropdown CreateDropdown() => null; + + protected override TabItem CreateTabItem(ResultMode value) => new ResultModeButton(value) + { + Anchor = TabContainer.Anchor, + Origin = TabContainer.Origin + }; + } +} \ No newline at end of file diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 3db070d33c..f4edc11436 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -1,93 +1,281 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Screens; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Scoring; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Screens; +using osu.Game.Graphics.Containers; using osu.Game.Screens.Backgrounds; using OpenTK; using OpenTK.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Ranking { - internal class Results : OsuScreen + public class Results : OsuScreen { - protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); + private readonly Score score; + private Container circleOuterBackground; + private Container circleOuter; + private Container circleInner; + + private ParallaxContainer backgroundParallax; + + private ResultModeTabControl modeChangeButtons; + + private Container currentPage; private static readonly Vector2 background_blur = new Vector2(20); - private ScoreDisplay scoreDisplay; + protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); + + private const float overscan = 1.3f; + + private const float circle_outer_scale = 0.96f; + + public Results(Score score) + { + this.score = score; + } + + private const float transition_time = 800; + + private IEnumerable allCircles => new Drawable[] { circleOuterBackground, circleInner, circleOuter }; protected override void OnEntering(Screen last) { base.OnEntering(last); - Background.Schedule(() => (Background as BackgroundScreenBeatmap)?.BlurTo(background_blur, 1000)); + (Background as BackgroundScreenBeatmap)?.BlurTo(background_blur, 2500, EasingTypes.OutQuint); + + allCircles.ForEach(c => + { + c.FadeOut(); + c.ScaleTo(0); + }); + + backgroundParallax.FadeOut(); + modeChangeButtons.FadeOut(); + currentPage.FadeOut(); + + circleOuterBackground.ScaleTo(1, transition_time, EasingTypes.OutQuint); + circleOuterBackground.FadeTo(1, transition_time, EasingTypes.OutQuint); + + Content.Delay(transition_time * 0.25f, true); + + circleOuter.ScaleTo(1, transition_time, EasingTypes.OutQuint); + circleOuter.FadeTo(1, transition_time, EasingTypes.OutQuint); + + Content.Delay(transition_time * 0.3f, true); + + backgroundParallax.FadeIn(transition_time, EasingTypes.OutQuint); + + circleInner.ScaleTo(1, transition_time, EasingTypes.OutQuint); + circleInner.FadeTo(1, transition_time, EasingTypes.OutQuint); + + Content.Delay(transition_time * 0.4f, true); + + modeChangeButtons.FadeIn(transition_time, EasingTypes.OutQuint); + currentPage.FadeIn(transition_time, EasingTypes.OutQuint); + + Content.DelayReset(); } protected override bool OnExiting(Screen next) { - Background.Schedule(() => Background.FadeColour(Color4.White, 500)); + allCircles.ForEach(c => + { + c.ScaleTo(0, transition_time, EasingTypes.OutSine); + }); + + Content.FadeOut(transition_time / 4); + return base.OnExiting(next); } - public Score Score + [BackgroundDependencyLoader] + private void load(OsuColour colours) { - 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); - - } - } - } - - internal class ScoreDisplay : Container - { - public ScoreDisplay(Score s) - { - AutoSizeAxes = Axes.Both; - Children = new Drawable[] { - new FillFlowContainer + new AspectContainer { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Height = overscan, Children = new Drawable[] { - new OsuSpriteText + circleOuterBackground = new CircularContainer { - TextSize = 40, - Text = $@"Accuracy: {s.Accuracy:#0.00%}", + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + Children = new Drawable[] + { + new Box + { + Alpha = 0.2f, + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + } + } }, - new OsuSpriteText + circleOuter = new CircularContainer { - TextSize = 40, - Text = $@"Score: {s.TotalScore}", + Size = new Vector2(circle_outer_scale), + EdgeEffect = new EdgeEffect + { + Colour = Color4.Black.Opacity(0.4f), + Type = EdgeEffectType.Shadow, + Radius = 15, + }, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + backgroundParallax = new ParallaxContainer + { + RelativeSizeAxes = Axes.Both, + ParallaxAmount = 0.01f, + Scale = new Vector2(1 / circle_outer_scale / overscan), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Sprite + { + Alpha = 0.2f, + Texture = Beatmap?.Background, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill + } + } + }, + modeChangeButtons = new ResultModeTabControl + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 50, + Margin = new MarginPadding { Bottom = 110 }, + }, + new SpriteText + { + Text = $"{score.MaxCombo}x", + TextSize = 40, + RelativePositionAxes = Axes.X, + Font = @"Exo2.0-Bold", + X = 0.1f, + Colour = colours.BlueDarker, + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomCentre, + }, + new SpriteText + { + Text = "max combo", + TextSize = 20, + RelativePositionAxes = Axes.X, + X = 0.1f, + Colour = colours.Gray6, + Anchor = Anchor.CentreLeft, + Origin = Anchor.TopCentre, + }, + new SpriteText + { + Text = $"{score.Accuracy:P2}", + TextSize = 40, + RelativePositionAxes = Axes.X, + Font = @"Exo2.0-Bold", + X = 0.9f, + Colour = colours.BlueDarker, + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomCentre, + }, + new SpriteText + { + Text = "accuracy", + TextSize = 20, + RelativePositionAxes = Axes.X, + X = 0.9f, + Colour = colours.Gray6, + Anchor = Anchor.CentreLeft, + Origin = Anchor.TopCentre, + }, + } }, - new OsuSpriteText + circleInner = new CircularContainer { - TextSize = 40, - Text = $@"MaxCombo: {s.MaxCombo}", + Size = new Vector2(0.6f), + EdgeEffect = new EdgeEffect + { + Colour = Color4.Black.Opacity(0.4f), + Type = EdgeEffectType.Shadow, + Radius = 15, + }, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + } } } - } + }, + new BackButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Action = Exit + }, }; + + modeChangeButtons.AddItem(ResultMode.Summary); + modeChangeButtons.AddItem(ResultMode.Ranking); + //modeChangeButtons.AddItem(ResultMode.Share); + + modeChangeButtons.Current.ValueChanged += mode => + { + currentPage?.FadeOut(); + currentPage?.Expire(); + + switch (mode) + { + case ResultMode.Summary: + currentPage = new ResultsPageScore(score, Beatmap); + break; + case ResultMode.Ranking: + currentPage = new ResultsPageRanking(score, Beatmap); + break; + } + + if (currentPage != null) + circleInner.Add(currentPage); + }; + + modeChangeButtons.Current.TriggerChange(); } } } diff --git a/osu.Game/Screens/Ranking/ResultsPage.cs b/osu.Game/Screens/Ranking/ResultsPage.cs new file mode 100644 index 0000000000..02eae3643b --- /dev/null +++ b/osu.Game/Screens/Ranking/ResultsPage.cs @@ -0,0 +1,92 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Ranking +{ + internal class ResultsPage : Container + { + protected readonly Score Score; + protected readonly WorkingBeatmap Beatmap; + private CircularContainer content; + private Box fill; + + protected override Container Content => content; + + public ResultsPage(Score score, WorkingBeatmap beatmap) + { + Score = score; + Beatmap = beatmap; + RelativeSizeAxes = Axes.Both; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + fill.Delay(400); + fill.FadeInFromZero(600); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AddInternal(new Drawable[] + { + fill = new Box + { + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray6 + }, + new CircularContainer + { + EdgeEffect = new EdgeEffect + { + Colour = colours.GrayF.Opacity(0.8f), + Type = EdgeEffectType.Shadow, + Radius = 1, + }, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = 20, + BorderColour = Color4.White, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Box{ + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + }, + } + }, + content = new CircularContainer + { + EdgeEffect = new EdgeEffect + { + Colour = Color4.Black.Opacity(0.2f), + Type = EdgeEffectType.Shadow, + Radius = 15, + }, + RelativeSizeAxes = Axes.Both, + Masking = true, + Size = new Vector2(0.88f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }); + } + + } +} diff --git a/osu.Game/Screens/Ranking/ResultsPageRanking.cs b/osu.Game/Screens/Ranking/ResultsPageRanking.cs new file mode 100644 index 0000000000..827abd556e --- /dev/null +++ b/osu.Game/Screens/Ranking/ResultsPageRanking.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Select.Leaderboards; +using OpenTK; + +namespace osu.Game.Screens.Ranking +{ + internal class ResultsPageRanking : ResultsPage + { + public ResultsPageRanking(Score score, WorkingBeatmap beatmap = null) : base(score, beatmap) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + new Box + { + Colour = colours.GrayE, + RelativeSizeAxes = Axes.Both, + }, + new Leaderboard + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Beatmap = Beatmap.BeatmapInfo ?? Score.Beatmap, + Scale = new Vector2(0.7f) + } + }; + } + } +} \ No newline at end of file diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs new file mode 100644 index 0000000000..2e04e8ffc3 --- /dev/null +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -0,0 +1,330 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Configuration; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Users; +using OpenTK; +using OpenTK.Graphics; +using System; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Beatmaps; +using osu.Game.Screens.Play; +using osu.Game.Rulesets.Scoring; +using osu.Framework.Graphics.Colour; + +namespace osu.Game.Screens.Ranking +{ + internal class ResultsPageScore : ResultsPage + { + private ScoreCounter scoreCounter; + + public ResultsPageScore(Score score, WorkingBeatmap beatmap) : base(score, beatmap) { } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + const float user_header_height = 120; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = user_header_height }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + } + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new UserHeader(Score.User) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = user_header_height, + }, + new DrawableRank(Score.Rank) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(150, 60), + Margin = new MarginPadding(20), + }, + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = 60, + Children = new Drawable[] + { + new SongProgressGraph + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.5f, + Objects = Beatmap.Beatmap.HitObjects, + }, + scoreCounter = new SlowScoreCounter(6) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Colour = colours.PinkDarker, + Y = 10, + TextSize = 56, + }, + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Colour = colours.PinkDarker, + Shadow = false, + Font = @"Exo2.0-Bold", + TextSize = 16, + Text = "total score", + Margin = new MarginPadding { Bottom = 15 }, + }, + new BeatmapDetails(Beatmap.BeatmapInfo) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Bottom = 10 }, + }, + new DateDisplay(Score.Date) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + new Container + { + RelativeSizeAxes = Axes.X, + Size = new Vector2(0.75f, 1), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = 10, Bottom = 10 }, + Children = new Drawable[] + { + new Box + { + ColourInfo = ColourInfo.GradientHorizontal( + colours.GrayC.Opacity(0), + colours.GrayC.Opacity(0.9f)), + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f, 1), + }, + new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + ColourInfo = ColourInfo.GradientHorizontal( + colours.GrayC.Opacity(0.9f), + colours.GrayC.Opacity(0)), + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f, 1), + }, + } + }, + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Schedule(() => scoreCounter.Increment(Score.TotalScore)); + } + + private class DateDisplay : Container + { + private DateTime date; + + public DateDisplay(DateTime date) + { + this.date = date; + + AutoSizeAxes = Axes.Y; + + Width = 140; + + Masking = true; + CornerRadius = 5; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray6, + }, + new OsuSpriteText + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = date.ToString("HH:mm"), + Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 }, + Colour = Color4.White, + }, + new OsuSpriteText + { + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Text = date.ToString("yyyy/MM/dd"), + Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 }, + Colour = Color4.White, + } + }; + } + } + + private class BeatmapDetails : Container + { + private readonly BeatmapInfo beatmap; + + private Bindable preferUnicode; + + private readonly OsuSpriteText title; + private readonly OsuSpriteText artist; + private readonly OsuSpriteText versionMapper; + + public BeatmapDetails(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + title = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Shadow = false, + TextSize = 24, + Font = @"Exo2.0-BoldItalic", + }, + artist = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Shadow = false, + TextSize = 20, + Font = @"Exo2.0-BoldItalic", + }, + versionMapper = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Shadow = false, + TextSize = 16, + Font = @"Exo2.0-Bold", + }, + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, OsuConfigManager config) + { + title.Colour = artist.Colour = colours.BlueDarker; + versionMapper.Colour = colours.Gray8; + + versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author}"; + + preferUnicode = config.GetBindable(OsuConfig.ShowUnicode); + preferUnicode.ValueChanged += unicode => + { + title.Text = unicode ? beatmap.Metadata.TitleUnicode : beatmap.Metadata.Title; + artist.Text = unicode ? beatmap.Metadata.ArtistUnicode : beatmap.Metadata.Artist; + }; + preferUnicode.TriggerChange(); + } + } + + private class UserHeader : Container + { + private readonly User user; + private readonly Sprite cover; + + public UserHeader(User user) + { + this.user = user; + Children = new Drawable[] + { + cover = new Sprite + { + FillMode = FillMode.Fill, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new OsuSpriteText + { + Font = @"Exo2.0-RegularItalic", + Text = user.Username, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + TextSize = 30, + Padding = new MarginPadding { Bottom = 10 }, + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + if (user.Cover?.Url != null) + cover.Texture = textures.Get(user.Cover?.Url); + } + } + + private class SlowScoreCounter : ScoreCounter + { + protected override double RollingDuration => 3000; + + protected override EasingTypes RollingEasing => EasingTypes.OutPow10; + + public SlowScoreCounter(uint leading = 0) : base(leading) + { + DisplayedCountSpriteText.Shadow = false; + DisplayedCountSpriteText.Font = @"Venera-Light"; + UseCommaSeparator = true; + } + } + } +} diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 2abf3c3175..2c51429d4c 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -23,6 +23,8 @@ namespace osu.Game.Screens.Select.Leaderboards private readonly ScrollContainer scrollContainer; private readonly FillFlowContainer scrollFlow; + public Action ScoreSelected; + private IEnumerable scores; public IEnumerable Scores { @@ -52,6 +54,7 @@ namespace osu.Game.Screens.Select.Leaderboards var ls = new LeaderboardScore(s, 1 + i) { AlwaysPresent = true, + Action = () => ScoreSelected?.Invoke(s), State = Visibility.Hidden, }; scrollFlow.Add(ls); diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index b211b781ae..8df95f6913 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -17,7 +17,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Select.Leaderboards { - public class LeaderboardScore : Container, IStateful + public class LeaderboardScore : ClickableContainer, IStateful { public static readonly float HEIGHT = 60; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 78a8e4c177..c15900eb6d 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Overlays.Mods; using osu.Game.Screens.Edit; using osu.Game.Screens.Play; +using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Select { @@ -35,6 +36,8 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 10, Right = 5 }, }); + + beatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s)); } [BackgroundDependencyLoader] diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b17823759d..54fde88b4f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -204,6 +204,9 @@ + + + @@ -227,6 +230,10 @@ + + + +