diff --git a/osu-framework b/osu-framework index 1490f00327..2234013e59 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 1490f003273d7aab6589e489f6e4b02d204c9f27 +Subproject commit 2234013e59a99116ee9f9e56a95ff8a6667db2a7 diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config index d1da144f50..45685a74a8 100644 --- a/osu.Desktop.Deploy/App.config +++ b/osu.Desktop.Deploy/App.config @@ -21,4 +21,16 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste + + + + + + + + + + + + \ No newline at end of file diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index 7a3719a25b..901117b026 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -68,9 +68,8 @@ $(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll True - - $(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True + + $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll @@ -120,7 +119,7 @@ - - - + + diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs new file mode 100644 index 0000000000..4a59ad9534 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetails.cs @@ -0,0 +1,65 @@ +// 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.Testing; +using osu.Game.Database; +using osu.Game.Screens.Select; +using System.Linq; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseBeatmapDetails : TestCase + { + public override string Description => "BeatmapDetails tab of BeatmapDetailArea"; + + private BeatmapDetails details; + + public override void Reset() + { + base.Reset(); + + Add(details = new BeatmapDetails + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(150), + Beatmap = new BeatmapInfo + { + Version = "VisualTest", + Metadata = new BeatmapMetadata + { + Source = "Some guy", + Tags = "beatmap metadata example with a very very long list of tags and not much creativity", + }, + Difficulty = new BeatmapDifficulty + { + CircleSize = 7, + ApproachRate = 3.5f, + OverallDifficulty = 5.7f, + DrainRate = 1, + }, + StarDifficulty = 5.3f, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0,10), + Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6), + }, + }, + }); + + AddRepeatStep("fail values", newRetryAndFailValues, 10); + } + + private int lastRange = 1; + + private void newRetryAndFailValues() + { + details.Beatmap.Metrics.Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6); + details.Beatmap.Metrics.Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6); + details.Beatmap = details.Beatmap; + lastRange += 100; + } + } +} \ No newline at end of file diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs b/osu.Desktop.VisualTests/Tests/TestCaseGraph.cs new file mode 100644 index 0000000000..7ac795f6f9 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseGraph.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 OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using System.Linq; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseGraph : TestCase + { + public override string Description => "graph"; + + private BarGraph graph; + + public override void Reset() + { + base.Reset(); + + Children = new[] + { + graph = new BarGraph + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.5f), + }, + }; + + AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1,10).Select(i => (float)i)); + AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i)); + AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i)); + AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop); + AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom); + AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight); + AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft); + } + } +} \ No newline at end of file diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs index f36889b02a..624723ed35 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs @@ -83,10 +83,7 @@ namespace osu.Desktop.VisualTests.Tests Colour = Color4.Black, }); - Add(new PlayerLoader(Player = CreatePlayer(beatmap)) - { - Beatmap = beatmap - }); + Add(Player = CreatePlayer(beatmap)); } protected virtual Player CreatePlayer(WorkingBeatmap beatmap) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs index a8e4382ebb..4e9ff4980e 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs @@ -27,6 +27,7 @@ namespace osu.Desktop.VisualTests.Tests private readonly Random rng = new Random(1337); private TaikoPlayfield playfield; + private Container playfieldContainer; public override void Reset() { @@ -48,14 +49,17 @@ namespace osu.Desktop.VisualTests.Tests AddStep("Height test 3", () => changePlayfieldSize(3)); AddStep("Height test 4", () => changePlayfieldSize(4)); AddStep("Height test 5", () => changePlayfieldSize(5)); + AddStep("Reset height", () => changePlayfieldSize(6)); var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; - Add(new Container + Add(playfieldContainer = new Container { - Clock = new FramedClock(rateAdjustClock), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, - Y = 200, + Height = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT, + Clock = new FramedClock(rateAdjustClock), Children = new[] { playfield = new TaikoPlayfield() @@ -65,6 +69,7 @@ namespace osu.Desktop.VisualTests.Tests private void changePlayfieldSize(int step) { + // Add new hits switch (step) { case 1: @@ -81,11 +86,20 @@ namespace osu.Desktop.VisualTests.Tests break; case 5: addSwell(1000); - playfield.Delay(scroll_time - 100); + playfieldContainer.Delay(scroll_time - 100); break; } - playfield.ResizeTo(new Vector2(1, rng.Next(25, 400)), 500); + // Tween playfield height + switch (step) + { + default: + playfieldContainer.ResizeTo(new Vector2(1, rng.Next(25, 400)), 500); + break; + case 6: + playfieldContainer.ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT), 500); + break; + } } private void addHitJudgement() diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index da068c5557..ffafa219a8 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -83,22 +83,20 @@ - - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll - True + + $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - - ..\packages\SharpCompress.0.15.1\lib\net45\SharpCompress.dll - True + + $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll + + + $(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll False $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll - - $(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - $(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll @@ -187,8 +185,10 @@ + + diff --git a/osu.Desktop.VisualTests/packages.config b/osu.Desktop.VisualTests/packages.config index 5a30c50600..cad2ffff0d 100644 --- a/osu.Desktop.VisualTests/packages.config +++ b/osu.Desktop.VisualTests/packages.config @@ -4,9 +4,9 @@ Copyright (c) 2007-2017 ppy Pty Ltd . Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE --> - - - + + + diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 70925f6cf4..9532652bfe 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -189,19 +189,24 @@ namespace osu.Desktop.Overlays private class UpdateProgressNotification : ProgressNotification { + private OsuGame game; + protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification() { Text = @"Update ready to install. Click to restart!", Activated = () => { - UpdateManager.RestartApp(); + UpdateManager.RestartAppWhenExited(); + game.GracefullyExit(); return true; } }; [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, OsuGame game) { + this.game = game; + IconContent.Add(new Drawable[] { new Box diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index fbc342d695..dbd26b4640 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -124,8 +124,7 @@ True - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll - True + $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll $(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config index be9b65f0c6..60e8182c82 100644 --- a/osu.Desktop/packages.config +++ b/osu.Desktop/packages.config @@ -7,7 +7,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - + \ No newline at end of file diff --git a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj index 593d8db4f6..50b1a095af 100644 --- a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj +++ b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj @@ -33,8 +33,7 @@ - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll - True + $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll @@ -84,7 +83,7 @@ - - + \ No newline at end of file diff --git a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj index cc925d417a..896e9c68c6 100644 --- a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj +++ b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj @@ -33,8 +33,7 @@ - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll - True + $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll @@ -89,7 +88,7 @@ - - + \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs index 00c797e97d..3b798a2fad 100644 --- a/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs @@ -30,6 +30,19 @@ namespace osu.Game.Modes.Osu.Scoring protected override void OnNewJudgement(OsuJudgement judgement) { + if (judgement != null) + { + switch (judgement.Result) + { + case HitResult.Hit: + Health.Value += 0.1f; + break; + case HitResult.Miss: + Health.Value -= 0.2f; + break; + } + } + int score = 0; int maxScore = 0; diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index 55322e855e..21f0f03d8c 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -34,8 +34,7 @@ - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll - True + $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll @@ -104,7 +103,7 @@ - - + \ No newline at end of file diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index d0981c2500..19ba5c77e4 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -33,8 +33,7 @@ - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll - True + $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll @@ -112,7 +111,7 @@ - - + \ No newline at end of file diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index d01aa77e02..2844528d0c 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -29,13 +29,11 @@ false - - $(SolutionDir)\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll - True + + $(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll - True + $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config index ca53ef08b0..9972fb41a1 100644 --- a/osu.Game.Tests/packages.config +++ b/osu.Game.Tests/packages.config @@ -1,12 +1,11 @@  - - - + + \ No newline at end of file diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs index bc6e077633..3e84825919 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Database/BeatmapInfo.cs @@ -41,8 +41,12 @@ namespace osu.Game.Database [OneToOne(CascadeOperations = CascadeOperation.All)] public BeatmapDifficulty Difficulty { get; set; } + [Ignore] + public BeatmapMetrics Metrics { get; set; } + public string Path { get; set; } + [JsonProperty("file_md5")] public string Hash { get; set; } // General diff --git a/osu.Game/Database/BeatmapMetrics.cs b/osu.Game/Database/BeatmapMetrics.cs new file mode 100644 index 0000000000..91320110d0 --- /dev/null +++ b/osu.Game/Database/BeatmapMetrics.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; + +namespace osu.Game.Database +{ + /// + /// Beatmap metrics based on acculumated online data from community plays. + /// + public class BeatmapMetrics + { + /// + /// Total vote counts of user ratings on a scale of 0..length. + /// + public IEnumerable Ratings { get; set; } + + /// + /// Points of failure on a relative time scale (usually 0..100). + /// + public IEnumerable Fails { get; set; } + + /// + /// Points of retry on a relative time scale (usually 0..100). + /// + public IEnumerable Retries { get; set; } + } +} diff --git a/osu.Game/Graphics/Cursor/CursorTrail.cs b/osu.Game/Graphics/Cursor/CursorTrail.cs index 4b5610e840..09d1b99d13 100644 --- a/osu.Game/Graphics/Cursor/CursorTrail.cs +++ b/osu.Game/Graphics/Cursor/CursorTrail.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.OpenGL.Buffers; using OpenTK.Graphics.ES30; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Colour; +using osu.Framework.Timing; namespace osu.Game.Graphics.Cursor { @@ -58,6 +59,9 @@ namespace osu.Game.Graphics.Cursor public CursorTrail() { + // as we are currently very dependent on having a running clock, let's make our own clock for the time being. + Clock = new FramedClock(); + AlwaysReceiveInput = true; RelativeSizeAxes = Axes.Both; @@ -231,4 +235,4 @@ namespace osu.Game.Graphics.Cursor } } } -} \ No newline at end of file +} diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 0fb7f59212..ceb3296bdf 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -80,14 +80,12 @@ namespace osu.Game.Graphics.Cursor protected override void PopIn() { ActiveCursor.FadeTo(1, 250, EasingTypes.OutQuint); - ActiveCursor.ScaleTo(1, 1000, EasingTypes.OutElastic); + ActiveCursor.ScaleTo(1, 400, EasingTypes.OutQuint); } protected override void PopOut() { - ActiveCursor.FadeTo(0, 1400, EasingTypes.OutQuint); - ActiveCursor.ScaleTo(1.1f, 100, EasingTypes.Out); - ActiveCursor.Delay(100); + ActiveCursor.FadeTo(0, 900, EasingTypes.OutQuint); ActiveCursor.ScaleTo(0, 500, EasingTypes.In); } diff --git a/osu.Game/Graphics/IHasAccentColour.cs b/osu.Game/Graphics/IHasAccentColour.cs index f959bc8760..e4647f22fd 100644 --- a/osu.Game/Graphics/IHasAccentColour.cs +++ b/osu.Game/Graphics/IHasAccentColour.cs @@ -28,7 +28,7 @@ namespace osu.Game.Graphics /// The tween easing. public static void FadeAccent(this IHasAccentColour accentedDrawable, Color4 newColour, double duration = 0, EasingTypes easing = EasingTypes.None) { - accentedDrawable.TransformTo(accentedDrawable.AccentColour, newColour, duration, easing, new TransformAccent()); + accentedDrawable.TransformTo(() => accentedDrawable.AccentColour, newColour, duration, easing, new TransformAccent()); } } } diff --git a/osu.Game/Graphics/UserInterface/Bar.cs b/osu.Game/Graphics/UserInterface/Bar.cs new file mode 100644 index 0000000000..76b75f1084 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/Bar.cs @@ -0,0 +1,137 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using System; + +namespace osu.Game.Graphics.UserInterface +{ + public class Bar : Container, IHasAccentColour + { + private readonly Box background; + private readonly Box bar; + + private const int resize_duration = 250; + + private const EasingTypes easing = EasingTypes.InOutCubic; + + private float length; + /// + /// Length of the bar, ranges from 0 to 1 + /// + public float Length + { + get + { + return length; + } + set + { + length = MathHelper.Clamp(value, 0, 1); + updateBarLength(); + } + } + + public Color4 BackgroundColour + { + get + { + return background.Colour; + } + set + { + background.Colour = value; + } + } + + public Color4 AccentColour + { + get + { + return bar.Colour; + } + set + { + bar.Colour = value; + } + } + + private BarDirection direction = BarDirection.LeftToRight; + public BarDirection Direction + { + get + { + return direction; + } + set + { + direction = value; + updateBarLength(); + } + } + + public Bar() + { + Children = new[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(0,0,0,0) + }, + bar = new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0, + }, + }; + } + + private void updateBarLength() + { + switch (direction) + { + case BarDirection.LeftToRight: + case BarDirection.RightToLeft: + bar.ResizeTo(new Vector2(length, 1), resize_duration, easing); + break; + + case BarDirection.TopToBottom: + case BarDirection.BottomToTop: + bar.ResizeTo(new Vector2(1, length), resize_duration, easing); + break; + } + + switch (direction) + { + case BarDirection.LeftToRight: + case BarDirection.TopToBottom: + bar.Anchor = Anchor.TopLeft; + bar.Origin = Anchor.TopLeft; + break; + + case BarDirection.RightToLeft: + case BarDirection.BottomToTop: + bar.Anchor = Anchor.BottomRight; + bar.Origin = Anchor.BottomRight; + break; + } + } + } + + [Flags] + public enum BarDirection + { + LeftToRight = 1 << 0, + RightToLeft = 1 << 1, + TopToBottom = 1 << 2, + BottomToTop = 1 << 3, + + Vertical = TopToBottom | BottomToTop, + Horizontal = LeftToRight | RightToLeft, + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/BarGraph.cs b/osu.Game/Graphics/UserInterface/BarGraph.cs new file mode 100644 index 0000000000..d0965a1861 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/BarGraph.cs @@ -0,0 +1,65 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using System.Collections.Generic; +using System.Linq; + +namespace osu.Game.Graphics.UserInterface +{ + public class BarGraph : FillFlowContainer + { + /// + /// Manually sets the max value, if null is instead used + /// + public float? MaxValue { get; set; } + + private BarDirection direction = BarDirection.BottomToTop; + public new BarDirection Direction + { + get + { + return direction; + } + set + { + direction = value; + base.Direction = (direction & BarDirection.Horizontal) > 0 ? FillDirection.Vertical : FillDirection.Horizontal; + foreach (var bar in Children) + { + bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, 1.0f / Children.Count()) : new Vector2(1.0f / Children.Count(), 1); + bar.Direction = direction; + } + } + } + + /// + /// A list of floats that defines the length of each + /// + public IEnumerable Values + { + set + { + List bars = Children.ToList(); + foreach (var bar in value.Select((length, index) => new { Value = length, Bar = bars.Count > index ? bars[index] : null })) + if (bar.Bar != null) + { + bar.Bar.Length = bar.Value / (MaxValue ?? value.Max()); + bar.Bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, 1.0f / value.Count()) : new Vector2(1.0f / value.Count(), 1); + } + else + Add(new Bar + { + RelativeSizeAxes = Axes.Both, + Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, 1.0f / value.Count()) : new Vector2(1.0f / value.Count(), 1), + Length = bar.Value / (MaxValue ?? value.Max()), + Direction = Direction, + }); + //I'm using ToList() here because Where() returns an Enumerable which can change it's elements afterwards + Remove(Children.Where((bar, index) => index >= value.Count()).ToList()); + } + } + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 91fb1c672a..180cb88707 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK; -using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -63,15 +62,6 @@ namespace osu.Game.Graphics.UserInterface rightBox.Colour = colours.Pink; } - private void playSample() - { - if (Clock == null || Clock.CurrentTime - lastSampleTime <= 50) - return; - lastSampleTime = Clock.CurrentTime; - sample.Frequency.Value = 1 + NormalizedValue * 0.2f; - sample.Play(); - } - protected override bool OnHover(InputState state) { nub.Glowing = true; @@ -84,11 +74,25 @@ namespace osu.Game.Graphics.UserInterface base.OnHoverLost(state); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override void OnUserChange() { - if (args.Key == Key.Left || args.Key == Key.Right) - playSample(); - return base.OnKeyDown(state, args); + base.OnUserChange(); + playSample(); + } + + private void playSample() + { + if (Clock == null || Clock.CurrentTime - lastSampleTime <= 50) + return; + lastSampleTime = Clock.CurrentTime; + sample.Frequency.Value = 1 + NormalizedValue * 0.2f; + + if (NormalizedValue == 0) + sample.Frequency.Value -= 0.4f; + else if (NormalizedValue == 1) + sample.Frequency.Value += 0.4f; + + sample.Play(); } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) @@ -103,18 +107,6 @@ namespace osu.Game.Graphics.UserInterface return base.OnMouseUp(state, args); } - protected override bool OnClick(InputState state) - { - playSample(); - return base.OnClick(state); - } - - protected override bool OnDrag(InputState state) - { - playSample(); - return base.OnDrag(state); - } - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index f8ab9f0ff3..e98867277a 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -115,8 +115,6 @@ namespace osu.Game.Graphics.UserInterface { base.LoadComplete(); - Flush(false, TransformType); - DisplayedCountSpriteText.Text = FormatCount(Current); } @@ -210,8 +208,8 @@ namespace osu.Game.Graphics.UserInterface ? GetProportionalDuration(currentValue, newValue) : RollingDuration; - transform.StartTime = Time.Current; - transform.EndTime = Time.Current + rollingTotalDuration; + transform.StartTime = TransformStartTime; + transform.EndTime = TransformStartTime + rollingTotalDuration; transform.StartValue = currentValue; transform.EndValue = newValue; transform.Easing = RollingEasing; diff --git a/osu.Game/Modes/Scoring/Score.cs b/osu.Game/Modes/Scoring/Score.cs index c998b11f77..b0c123f438 100644 --- a/osu.Game/Modes/Scoring/Score.cs +++ b/osu.Game/Modes/Scoring/Score.cs @@ -27,7 +27,24 @@ namespace osu.Game.Modes.Scoring public int Combo { get; set; } public Mod[] Mods { get; set; } - public User User { get; set; } + private User user; + + public User User + { + get + { + return user ?? new User + { + Username = LegacyUsername, + Id = LegacyUserID + }; + } + + set + { + user = value; + } + } [JsonProperty(@"replay_data")] public Replay Replay; @@ -38,10 +55,10 @@ namespace osu.Game.Modes.Scoring public long OnlineScoreID; [JsonProperty(@"username")] - public string Username; + public string LegacyUsername; [JsonProperty(@"user_id")] - public long UserID; + public long LegacyUserID; [JsonProperty(@"date")] public DateTime Date; diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index afc525d686..dd5eff5a95 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -33,6 +33,11 @@ namespace osu.Game.Modes.UI /// public event Action OnAllJudged; + /// + /// Whether to apply adjustments to the child based on our own size. + /// + public bool AspectAdjust = true; + /// /// The input manager for this HitRenderer. /// @@ -168,11 +173,6 @@ namespace osu.Game.Modes.UI { public event Action OnJudgement; - /// - /// Whether to apply adjustments to the child based on our own size. - /// - public bool AspectAdjust = true; - public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor; protected override Container Content => content; diff --git a/osu.Game/Modes/UI/Playfield.cs b/osu.Game/Modes/UI/Playfield.cs index bf5f0acde5..1e7cf6579c 100644 --- a/osu.Game/Modes/UI/Playfield.cs +++ b/osu.Game/Modes/UI/Playfield.cs @@ -39,6 +39,9 @@ namespace osu.Game.Modes.UI { AlwaysReceiveInput = true; + // Default height since we force relative size axes + Size = Vector2.One; + AddInternal(ScaledContent = new ScaledContainer { CustomWidth = customWidth, diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7172aba3be..ccea6ef458 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -307,6 +307,18 @@ namespace osu.Game return base.OnExiting(); } + /// + /// Use to programatically exit the game as if the user was triggering via alt-f4. + /// Will keep persisting until an exit occurs (exit may be blocked multiple times). + /// + public void GracefullyExit() + { + if (!OnExiting()) + Exit(); + else + Scheduler.AddDelayed(GracefullyExit, 2000); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 0bb3d3dc71..fc12789b05 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -93,7 +93,7 @@ namespace osu.Game.Overlays { var postText = sender.Text; - if (!string.IsNullOrEmpty(postText)) + if (!string.IsNullOrEmpty(postText) && api.LocalUser.Value != null) { //todo: actually send to server careChannels.FirstOrDefault()?.AddNewMessages(new[] diff --git a/osu.Game/Overlays/DragBar.cs b/osu.Game/Overlays/DragBar.cs index 90991bb195..53a01c9e9c 100644 --- a/osu.Game/Overlays/DragBar.cs +++ b/osu.Game/Overlays/DragBar.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays private void updatePosition(float position) { position = MathHelper.Clamp(position, 0, 1); - fill.TransformTo(fill.Width, position, 200, EasingTypes.OutQuint, new TransformSeek()); + fill.TransformTo(() => fill.Width, position, 200, EasingTypes.OutQuint, new TransformSeek()); } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 7a32e19338..faff687ddb 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -1,31 +1,19 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Containers; using osu.Framework.Input; using OpenTK.Input; using osu.Game.Graphics; using OpenTK.Graphics; using osu.Framework.Allocation; +using System.Linq; namespace osu.Game.Screens.Play { public class FailOverlay : MenuOverlay { - public override string Header => "failed"; public override string Description => "you're dead, try again?"; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (args.Key == Key.Escape) - { - if (State == Visibility.Hidden) return false; - OnQuit(); - return true; - } - - return base.OnKeyDown(state, args); - } [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -33,5 +21,16 @@ namespace osu.Game.Screens.Play AddButton("Retry", colours.YellowDark, OnRetry); AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit); } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!args.Repeat && args.Key == Key.Escape) + { + Buttons.Children.Last().TriggerClick(); + return true; + } + + return base.OnKeyDown(state, args); + } } } diff --git a/osu.Game/Screens/Play/MenuOverlay.cs b/osu.Game/Screens/Play/MenuOverlay.cs index ede49065a7..738e5cc35d 100644 --- a/osu.Game/Screens/Play/MenuOverlay.cs +++ b/osu.Game/Screens/Play/MenuOverlay.cs @@ -13,10 +13,11 @@ using OpenTK; using OpenTK.Graphics; using osu.Game.Graphics; using osu.Framework.Allocation; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Play { - public abstract class MenuOverlay : OverlayContainer + public abstract class MenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition { private const int transition_duration = 200; private const int button_height = 70; @@ -30,7 +31,7 @@ namespace osu.Game.Screens.Play public abstract string Header { get; } public abstract string Description { get; } - private FillFlowContainer buttons; + protected FillFlowContainer Buttons; public int Retries { @@ -80,11 +81,13 @@ namespace osu.Game.Screens.Play // Don't let mouse down events through the overlay or people can click circles while paused. protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => true; + protected override bool OnMouseMove(InputState state) => true; protected void AddButton(string text, Color4 colour, Action action) { - buttons.Add(new PauseButton + Buttons.Add(new PauseButton { Text = text, ButtonColour = colour, @@ -151,7 +154,7 @@ namespace osu.Game.Screens.Play } } }, - buttons = new FillFlowContainer + Buttons = new FillFlowContainer { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index f9706d263e..9561979751 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -2,10 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Linq; using osu.Framework.Input; using osu.Game.Graphics; using OpenTK.Input; -using osu.Framework.Graphics.Containers; using OpenTK.Graphics; using osu.Framework.Allocation; @@ -20,10 +20,9 @@ namespace osu.Game.Screens.Play protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - if (args.Key == Key.Escape) + if (!args.Repeat && args.Key == Key.Escape) { - if (State == Visibility.Hidden) return false; - OnResume(); + Buttons.Children.First().TriggerClick(); return true; } @@ -39,4 +38,3 @@ namespace osu.Game.Screens.Play } } } - \ No newline at end of file diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a7108eda1b..01d5d0770a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play public BeatmapInfo BeatmapInfo; - public bool IsPaused { get; private set; } + public bool IsPaused => !interpolatedSourceClock.IsRunning; public bool HasFailed { get; private set; } @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Play private const double pause_cooldown = 1000; private double lastPauseActionTime; - private bool canPause => Time.Current >= lastPauseActionTime + pause_cooldown; + private bool canPause => ValidForResume && !HasFailed && Time.Current >= lastPauseActionTime + pause_cooldown; private IAdjustableClock sourceClock; private IFrameBasedClock interpolatedSourceClock; @@ -116,7 +116,12 @@ namespace osu.Game.Screens.Play scoreProcessor = HitRenderer.CreateScoreProcessor(); - hudOverlay = new StandardHudOverlay(); + hudOverlay = new StandardHudOverlay() + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + hudOverlay.KeyCounter.Add(ruleset.CreateGameplayKeys()); hudOverlay.BindProcessor(scoreProcessor); hudOverlay.BindHitRenderer(HitRenderer); @@ -160,7 +165,12 @@ namespace osu.Game.Screens.Play }, new HotkeyRetryOverlay { - Action = Restart, + Action = () => { + //we want to hide the hitrenderer immediately (looks better). + //we may be able to remove this once the mouse cursor trail is improved. + HitRenderer?.Hide(); + Restart(); + }, } }; } @@ -194,19 +204,30 @@ namespace osu.Game.Screens.Play public void Pause(bool force = false) { - if (canPause || force) + if (!canPause && !force) return; + + // the actual pausing is potentially happening on a different thread. + // we want to wait for the source clock to stop so we can be sure all components are in a stable state. + if (!IsPaused) { + sourceClock.Stop(); + + Schedule(() => Pause(force)); + return; + } + + // we need to do a final check after all of our children have processed up to the paused clock time. + // this is to cover cases where, for instance, the player fails in the last processed frame (which would change canPause). + // as the scheduler runs before children updates, let's schedule for the next frame. + Schedule(() => + { + if (!canPause) return; + lastPauseActionTime = Time.Current; hudOverlay.KeyCounter.IsCounting = false; pauseOverlay.Retries = RestartCount; pauseOverlay.Show(); - sourceClock.Stop(); - IsPaused = true; - } - else - { - IsPaused = false; - } + }); } public void Resume() @@ -215,13 +236,6 @@ namespace osu.Game.Screens.Play hudOverlay.KeyCounter.IsCounting = true; pauseOverlay.Hide(); sourceClock.Start(); - IsPaused = false; - } - - public void TogglePaused() - { - IsPaused = !IsPaused; - if (IsPaused) Pause(); else Resume(); } public void Restart() @@ -230,11 +244,11 @@ namespace osu.Game.Screens.Play var newPlayer = new Player(); + ValidForResume = false; + LoadComponentAsync(newPlayer, delegate { newPlayer.RestartCount = RestartCount + 1; - ValidForResume = false; - if (!Push(newPlayer)) { // Error(?) @@ -250,10 +264,11 @@ namespace osu.Game.Screens.Play if (scoreProcessor.HasFailed || onCompletionEvent != null) return; + ValidForResume = false; + Delay(1000); onCompletionEvent = Schedule(delegate { - ValidForResume = false; Push(new Results { Score = scoreProcessor.CreateScore() @@ -265,8 +280,6 @@ namespace osu.Game.Screens.Play { sourceClock.Stop(); - Delay(500); - HasFailed = true; failOverlay.Retries = RestartCount; failOverlay.Show(); @@ -304,36 +317,46 @@ namespace osu.Game.Screens.Play protected override void OnSuspending(Screen next) { - Content.FadeOut(350); - Content.ScaleTo(0.7f, 750, EasingTypes.InQuint); + fadeOut(); base.OnSuspending(next); } protected override bool OnExiting(Screen next) { + if (HasFailed || !ValidForResume) + return false; + if (pauseOverlay != null && !HitRenderer.HasReplayLoaded) { //pause screen override logic. if (pauseOverlay?.State == Visibility.Hidden && !canPause) return true; - if (!IsPaused && sourceClock.IsRunning) // For if the user presses escape quickly when entering the map + if (!IsPaused) // For if the user presses escape quickly when entering the map { Pause(); return true; } } - HitRenderer?.FadeOut(60); - - FadeOut(250); - Content.ScaleTo(0.7f, 750, EasingTypes.InQuint); - Background?.FadeTo(1f, 200); + fadeOut(); return base.OnExiting(next); } + private void fadeOut() + { + const float fade_out_duration = 250; + + HitRenderer?.FadeOut(fade_out_duration); + Content.FadeOut(fade_out_duration); + + hudOverlay.ScaleTo(0.7f, fade_out_duration * 3, EasingTypes.In); + + Background?.FadeTo(1f, fade_out_duration); + } + private Bindable mouseWheelDisabled; protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !IsPaused; } -} \ No newline at end of file +} diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index dae909f2b7..ae117254fa 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -1,13 +1,10 @@ // 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.Containers; using osu.Framework.Graphics.Primitives; using osu.Game.Beatmaps; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Screens.Select.Leaderboards; namespace osu.Game.Screens.Select @@ -17,11 +14,9 @@ namespace osu.Game.Screens.Select private readonly Container content; protected override Container Content => content; - public readonly Container Details; //todo: replace with a real details view when added + public readonly BeatmapDetails Details; public readonly Leaderboard Leaderboard; - private APIAccess api; - private WorkingBeatmap beatmap; public WorkingBeatmap Beatmap { @@ -32,7 +27,8 @@ namespace osu.Game.Screens.Select set { beatmap = value; - if (IsLoaded) Schedule(updateScores); + Leaderboard.Beatmap = beatmap?.BeatmapInfo; + Details.Beatmap = beatmap?.Beatmap.BeatmapInfo; } } @@ -51,14 +47,12 @@ namespace osu.Game.Screens.Select Details.Show(); Leaderboard.Hide(); break; + default: Details.Hide(); Leaderboard.Show(); break; } - - //for now let's always update scores. - updateScores(); }, }, content = new Container @@ -70,42 +64,18 @@ namespace osu.Game.Screens.Select Add(new Drawable[] { - Details = new Container + Details = new BeatmapDetails { RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(5), + Alpha = 0, }, Leaderboard = new Leaderboard { RelativeSizeAxes = Axes.Both, + } }); } - - protected override void LoadComplete() - { - base.LoadComplete(); - updateScores(); - } - - [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api) - { - this.api = api; - } - - private GetScoresRequest getScoresRequest; - private void updateScores() - { - if (!IsLoaded) return; - - Leaderboard.Scores = null; - getScoresRequest?.Cancel(); - - if (api == null || beatmap?.BeatmapInfo == null || !Leaderboard.IsPresent) return; - - getScoresRequest = new GetScoresRequest(beatmap.BeatmapInfo); - getScoresRequest.Success += r => Leaderboard.Scores = r.Scores; - api.Queue(getScoresRequest); - } } -} +} \ No newline at end of file diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs new file mode 100644 index 0000000000..a0d15101e0 --- /dev/null +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -0,0 +1,434 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using System.Globalization; +using System.Linq; + +namespace osu.Game.Screens.Select +{ + public class BeatmapDetails : Container + { + private readonly MetadataSegment description; + private readonly MetadataSegment source; + private readonly MetadataSegment tags; + + private readonly DifficultyRow circleSize; + private readonly DifficultyRow drainRate; + private readonly DifficultyRow overallDifficulty; + private readonly DifficultyRow approachRate; + private readonly DifficultyRow stars; + + private readonly Container ratingsContainer; + private readonly Bar ratingsBar; + private readonly OsuSpriteText negativeRatings; + private readonly OsuSpriteText positiveRatings; + private readonly BarGraph ratingsGraph; + + private readonly FillFlowContainer retryFailContainer; + private readonly BarGraph retryGraph; + private readonly BarGraph failGraph; + + private BeatmapInfo beatmap; + public BeatmapInfo Beatmap + { + get + { + return beatmap; + } + set + { + beatmap = value; + if (beatmap == null) return; + + description.Text = beatmap.Version; + source.Text = beatmap.Metadata.Source; + tags.Text = beatmap.Metadata.Tags; + + circleSize.Value = beatmap.Difficulty.CircleSize; + drainRate.Value = beatmap.Difficulty.DrainRate; + overallDifficulty.Value = beatmap.Difficulty.OverallDifficulty; + approachRate.Value = beatmap.Difficulty.ApproachRate; + stars.Value = (float)beatmap.StarDifficulty; + + if (beatmap.Metrics?.Ratings.Any() ?? false) + { + var ratings = beatmap.Metrics.Ratings.ToList(); + ratingsContainer.Show(); + + negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString(); + positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString(); + ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum(); + + ratingsGraph.Values = ratings.Select(rating => (float)rating); + } + else + ratingsContainer.Hide(); + + if ((beatmap.Metrics?.Retries.Any() ?? false) && beatmap.Metrics.Fails.Any()) + { + var retries = beatmap.Metrics.Retries; + var fails = beatmap.Metrics.Fails; + retryFailContainer.Show(); + + float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max(); + failGraph.MaxValue = maxValue; + retryGraph.MaxValue = maxValue; + + failGraph.Values = fails.Select(fail => (float)fail); + retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue)); + } + else + retryFailContainer.Hide(); + } + } + + public BeatmapDetails() + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new FillFlowContainer() + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.4f, + Direction = FillDirection.Vertical, + LayoutDuration = 200, + LayoutEasing = EasingTypes.OutQuint, + Padding = new MarginPadding(10) { Top = 25 }, + Children = new [] + { + description = new MetadataSegment("Description"), + source = new MetadataSegment("Source"), + tags = new MetadataSegment("Tags") + }, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.6f, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 15), + Padding = new MarginPadding(10) { Top = 0 }, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0,10), + Padding = new MarginPadding(15) { Top = 25 }, + Children = new [] + { + circleSize = new DifficultyRow("Circle Size", 7), + drainRate = new DifficultyRow("HP Drain"), + overallDifficulty = new DifficultyRow("Accuracy"), + approachRate = new DifficultyRow("Approach Rate"), + stars = new DifficultyRow("Star Diffculty"), + }, + }, + }, + }, + ratingsContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0, + AlwaysPresent = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding + { + Top = 25, + Left = 15, + Right = 15, + }, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "User Rating", + Font = @"Exo2.0-Medium", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + ratingsBar = new Bar + { + RelativeSizeAxes = Axes.X, + Height = 5, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + negativeRatings = new OsuSpriteText + { + Font = @"Exo2.0-Regular", + Text = "0", + }, + positiveRatings = new OsuSpriteText + { + Font = @"Exo2.0-Regular", + Text = "0", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + }, + }, + new OsuSpriteText + { + Text = "Rating Spread", + TextSize = 14, + Font = @"Exo2.0-Regular", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + ratingsGraph = new BarGraph + { + RelativeSizeAxes = Axes.X, + Height = 50, + }, + }, + }, + }, + }, + retryFailContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Points of Failure", + Font = @"Exo2.0-Regular", + }, + new Container + { + RelativeSizeAxes = Axes.X, + Size = new Vector2(1/0.6f, 50), + Children = new[] + { + retryGraph = new BarGraph + { + RelativeSizeAxes = Axes.Both, + }, + failGraph = new BarGraph + { + RelativeSizeAxes = Axes.Both, + }, + }, + }, + } + }, + }, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + description.AccentColour = colour.GrayB; + source.AccentColour = colour.GrayB; + tags.AccentColour = colour.YellowLight; + + stars.AccentColour = colour.Yellow; + + ratingsBar.BackgroundColour = colour.Green; + ratingsBar.AccentColour = colour.YellowDark; + ratingsGraph.Colour = colour.BlueDark; + + failGraph.Colour = colour.YellowDarker; + retryGraph.Colour = colour.Yellow; + } + + private class DifficultyRow : Container, IHasAccentColour + { + private readonly OsuSpriteText name; + private readonly Bar bar; + private readonly OsuSpriteText valueText; + + private readonly float maxValue; + + private float difficultyValue; + public float Value + { + get + { + return difficultyValue; + } + set + { + difficultyValue = value; + bar.Length = value / maxValue; + valueText.Text = value.ToString(CultureInfo.InvariantCulture); + } + } + + public Color4 AccentColour + { + get + { + return bar.AccentColour; + } + set + { + bar.AccentColour = value; + } + } + + public DifficultyRow(string difficultyName, float maxValue = 10) + { + this.maxValue = maxValue; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Children = new Drawable[] + { + name = new OsuSpriteText + { + Font = @"Exo2.0-Regular", + Text = difficultyName, + }, + bar = new Bar + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(1, 0.35f), + Padding = new MarginPadding { Left = 100, Right = 25 }, + }, + valueText = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Font = @"Exo2.0-Regular", + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + name.Colour = colour.GrayB; + bar.BackgroundColour = colour.Gray7; + valueText.Colour = colour.GrayB; + } + } + + private class MetadataSegment : Container, IHasAccentColour + { + private readonly OsuSpriteText header; + private readonly FillFlowContainer content; + + public string Text + { + set + { + if (string.IsNullOrEmpty(value)) + Hide(); + else + { + Show(); + if (header.Text == "Tags") + content.Children = value.Split(' ').Select(text => new OsuSpriteText + { + Text = text, + Font = "Exo2.0-Regular", + }); + else + content.Children = new[] + { + new OsuSpriteText + { + Text = value, + Font = "Exo2.0-Regular", + } + }; + } + } + } + + public Color4 AccentColour + { + get + { + return content.Colour; + } + set + { + content.Colour = value; + } + } + + public MetadataSegment(string headerText) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Margin = new MarginPadding { Top = 10 }; + Children = new Drawable[] + { + header = new OsuSpriteText + { + Font = @"Exo2.0-Bold", + Text = headerText, + }, + content = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Full, + Spacing = new Vector2(5,0), + Margin = new MarginPadding { Top = header.TextSize } + } + }; + } + } + } +} \ No newline at end of file diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index acf0954418..2654129a44 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -31,7 +31,9 @@ namespace osu.Game.Screens.Select || (set.Metadata.Artist ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1 || (set.Metadata.ArtistUnicode ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1 || (set.Metadata.Title ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1 - || (set.Metadata.TitleUnicode ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1; + || (set.Metadata.TitleUnicode ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1 + || (set.Metadata.Tags ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1 + || (set.Metadata.Source ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1; switch (g.State) { diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 12ff096d16..315611a60c 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -10,7 +10,11 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using System; +using osu.Framework.Allocation; +using osu.Game.Database; using osu.Game.Modes.Scoring; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; namespace osu.Game.Screens.Select.Leaderboards { @@ -26,6 +30,7 @@ namespace osu.Game.Screens.Select.Leaderboards set { scores = value; + getScoresRequest?.Cancel(); int i = 150; if (scores == null) @@ -81,6 +86,41 @@ namespace osu.Game.Screens.Select.Leaderboards }; } + private APIAccess api; + + private BeatmapInfo beatmap; + + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + beatmap = value; + Schedule(updateScores); + } + } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(APIAccess api) + { + this.api = api; + } + + private GetScoresRequest getScoresRequest; + private void updateScores() + { + if (!IsLoaded) return; + + Scores = null; + getScoresRequest?.Cancel(); + + if (api == null || Beatmap == null) return; + + getScoresRequest = new GetScoresRequest(Beatmap); + getScoresRequest.Success += r => Scores = r.Scores; + api.Queue(getScoresRequest); + } + protected override void Update() { base.Update(); diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index 2bac387c5c..493f351b75 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -142,7 +142,7 @@ namespace osu.Game.Screens.Select.Leaderboards Children = new Drawable[] { avatar = new DelayedLoadWrapper( - new Avatar(Score.User ?? new User { Id = Score.UserID }) + new Avatar(Score.User) { RelativeSizeAxes = Axes.Both, CornerRadius = corner_radius, @@ -169,7 +169,7 @@ namespace osu.Game.Screens.Select.Leaderboards { nameLabel = new OsuSpriteText { - Text = Score.User?.Username ?? Score.Username, + Text = Score.User.Username, Font = @"Exo2.0-BoldItalic", TextSize = 23, }, diff --git a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs index b80f76d281..08f270741c 100644 --- a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs +++ b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs @@ -298,7 +298,7 @@ namespace osu.Game.Screens.Tournament private void speedTo(float value, double duration = 0, EasingTypes easing = EasingTypes.None) { DelayReset(); - TransformTo(speed, value, duration, easing, new TransformScrollSpeed()); + TransformTo(() => speed, value, duration, easing, new TransformScrollSpeed()); } private enum ScrollState diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7b9d41a4e1..b9b80ac5b9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,17 +35,14 @@ false - - $(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True + + $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll - True + $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll - - ..\packages\SharpCompress.0.15.1\lib\net45\SharpCompress.dll - True + + $(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll @@ -79,6 +76,7 @@ + @@ -87,6 +85,7 @@ + @@ -206,6 +205,8 @@ + + @@ -390,7 +391,7 @@ - - - - + + +