From 0ec7c11a90f6ec343752ea786a5adf4078c06f7c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 May 2019 19:44:22 +0200 Subject: [PATCH 001/178] implement CatchModRelax --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 0454bc969d..47e32b1292 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -1,12 +1,48 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using osuTK; +using System; -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModRelax : ModRelax - { +namespace osu.Game.Rulesets.Catch.Mods { + public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset { public override string Description => @"Use the mouse to control the catcher."; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) => + (drawableRuleset.Playfield.Parent as Container).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); + + private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { + private CatcherArea.Catcher catcher; + + public CatchModRelaxHelper(CatchPlayfield catchPlayfield) { + catcher = catchPlayfield.CatcherArea.MovableCatcher; + RelativeSizeAxes = Axes.Both; + } + + //disable keyboard controls + public bool OnPressed(CatchAction action) => true; + public bool OnReleased(CatchAction action) => true; + + protected override bool OnMouseMove(MouseMoveEvent e) { + //lock catcher to mouse position horizontally + catcher.X = e.MousePosition.X / DrawSize.X; + + //make Yuzu face the direction he's moving + var direction = Math.Sign(e.Delta.X); + if (direction != 0) + catcher.Scale = new Vector2(Math.Abs(catcher.Scale.X) * direction, catcher.Scale.Y); + + return base.OnMouseMove(e); + } + } } } From 6739238a0dd3620805943806f99637210f9138ca Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 May 2019 10:03:59 +0200 Subject: [PATCH 002/178] formatting --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 47e32b1292..5fa22f8bdb 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -13,17 +13,21 @@ using osu.Game.Rulesets.UI; using osuTK; using System; -namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset { +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset + { public override string Description => @"Use the mouse to control the catcher."; public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) => (drawableRuleset.Playfield.Parent as Container).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); - private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { + private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition + { private CatcherArea.Catcher catcher; - public CatchModRelaxHelper(CatchPlayfield catchPlayfield) { + public CatchModRelaxHelper(CatchPlayfield catchPlayfield) + { catcher = catchPlayfield.CatcherArea.MovableCatcher; RelativeSizeAxes = Axes.Both; } @@ -32,7 +36,8 @@ namespace osu.Game.Rulesets.Catch.Mods { public bool OnPressed(CatchAction action) => true; public bool OnReleased(CatchAction action) => true; - protected override bool OnMouseMove(MouseMoveEvent e) { + protected override bool OnMouseMove(MouseMoveEvent e) + { //lock catcher to mouse position horizontally catcher.X = e.MousePosition.X / DrawSize.X; From cf192c84a8d528f23c949aa70210e1c300fa53e2 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 May 2019 10:26:24 +0200 Subject: [PATCH 003/178] make catcher field readonly --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 5fa22f8bdb..9852e753c5 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Mods private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { - private CatcherArea.Catcher catcher; + private readonly CatcherArea.Catcher catcher; public CatchModRelaxHelper(CatchPlayfield catchPlayfield) { From b87478f6e2c3f245c34a5c11e714a74d4c90e9e9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 May 2019 11:25:29 +0200 Subject: [PATCH 004/178] replace 'as' with direct cast to avoid possible nullref --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 9852e753c5..062c0f8b26 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Mods public override string Description => @"Use the mouse to control the catcher."; public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) => - (drawableRuleset.Playfield.Parent as Container).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); + ((Container)drawableRuleset.Playfield.Parent).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { From b3a95cac84e2f30bcc4acfb5eb8e4b030f9ad7d3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 20:20:11 +0200 Subject: [PATCH 005/178] add invisible cursor for ctb --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 14 ++++++++++++++ osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 3 +++ 2 files changed, 17 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs new file mode 100644 index 0000000000..073f2e05b2 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -0,0 +1,14 @@ +using osu.Framework.Graphics; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Catch.UI { + class CatchCursorContainer : GameplayCursorContainer + { + protected override Drawable CreateCursor() => new InvisibleCursor(); + + private class InvisibleCursor : Drawable + { + + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index b6d8cf9cbe..7741096da2 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Catch.UI @@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.UI internal readonly CatcherArea CatcherArea; + protected override GameplayCursorContainer CreateCursor() => new CatchCursorContainer(); + public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation) { Container explodingFruitContainer; From 9079d7a7d83962ae058e33ad75f4ba19bab02f53 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 21:01:51 +0200 Subject: [PATCH 006/178] add license header --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index 073f2e05b2..072c5d4fdf 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -1,4 +1,7 @@ -using osu.Framework.Graphics; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.UI { From cadf0eb228d41b1ed941aef9d6ec11963a625639 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 21:14:27 +0200 Subject: [PATCH 007/178] formatting --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index 072c5d4fdf..642e0bb9de 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -4,7 +4,8 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.UI; -namespace osu.Game.Rulesets.Catch.UI { +namespace osu.Game.Rulesets.Catch.UI +{ class CatchCursorContainer : GameplayCursorContainer { protected override Drawable CreateCursor() => new InvisibleCursor(); From 3aa95f216284ddcc3f39c31dac000aba559903d8 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 21:17:06 +0200 Subject: [PATCH 008/178] add missing access modifier --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index 642e0bb9de..d4ac4b2f2e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -6,7 +6,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.UI { - class CatchCursorContainer : GameplayCursorContainer + public class CatchCursorContainer : GameplayCursorContainer { protected override Drawable CreateCursor() => new InvisibleCursor(); From 69f9d003829c98fd858f9dc0ac458dc64e7c3697 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 21:23:59 +0200 Subject: [PATCH 009/178] more formatting --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index d4ac4b2f2e..03bfad7e8c 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -10,9 +10,6 @@ namespace osu.Game.Rulesets.Catch.UI { protected override Drawable CreateCursor() => new InvisibleCursor(); - private class InvisibleCursor : Drawable - { - - } + private class InvisibleCursor : Drawable { } } } From 2a395956aa4278f7eca532049c88947599f8171e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 27 Sep 2019 19:00:17 +0300 Subject: [PATCH 010/178] Merge dependencies --- .../UserInterface/TestSceneMetricNumbers.cs | 58 +++++++++++++++++++ .../API/Requests/GetCountryRankingsRequest.cs | 18 ++++++ .../Online/API/Requests/GetRankingsRequest.cs | 34 +++++++++++ .../API/Requests/GetUserRankingsRequest.cs | 40 +++++++++++++ .../Requests/Responses/APICountryRankings.cs | 14 +++++ .../API/Requests/Responses/APIUserRankings.cs | 14 +++++ osu.Game/Users/CountryStatistics.cs | 25 ++++++++ osu.Game/Utils/HumanizerUtils.cs | 27 +++++++++ 8 files changed, 230 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs create mode 100644 osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs create mode 100644 osu.Game/Online/API/Requests/GetRankingsRequest.cs create mode 100644 osu.Game/Online/API/Requests/GetUserRankingsRequest.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APICountryRankings.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIUserRankings.cs create mode 100644 osu.Game/Users/CountryStatistics.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs new file mode 100644 index 0000000000..74470f22fc --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; +using osu.Game.Utils; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneMetricNumbers : OsuTestScene + { + public TestSceneMetricNumbers() + { + Child = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new DrawableNumber(0), + new DrawableNumber(1001), + new DrawableNumber(999_999), + new DrawableNumber(1_000_000), + new DrawableNumber(845_006_456), + new DrawableNumber(999_999_999), + new DrawableNumber(1_000_000_000), + new DrawableNumber(7_875_454_545), + new DrawableNumber(999_999_999_999), + new DrawableNumber(1_000_000_000_000), + new DrawableNumber(687_545_454_554_545), + new DrawableNumber(999_999_999_999_999), + new DrawableNumber(1_000_000_000_000_000), + new DrawableNumber(587_545_454_554_545_455), + new DrawableNumber(999_999_999_999_999_999), + new DrawableNumber(1_000_000_000_000_000_000), + new DrawableNumber(long.MaxValue), + } + }; + } + + private class DrawableNumber : SpriteText, IHasTooltip + { + public string TooltipText => value.ToString("F0"); + + private readonly long value; + + public DrawableNumber(long value) + { + this.value = value; + Text = HumanizerUtils.ToMetric(value); + } + } + } +} diff --git a/osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs b/osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs new file mode 100644 index 0000000000..3bd772732f --- /dev/null +++ b/osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; + +namespace osu.Game.Online.API.Requests +{ + public class GetCountryRankingsRequest : GetRankingsRequest + { + public GetCountryRankingsRequest(RulesetInfo ruleset, int page = 1) + : base(ruleset, page) + { + } + + protected override string TargetPostfix() => "country"; + } +} diff --git a/osu.Game/Online/API/Requests/GetRankingsRequest.cs b/osu.Game/Online/API/Requests/GetRankingsRequest.cs new file mode 100644 index 0000000000..efaaa2cb40 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetRankingsRequest.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.IO.Network; +using osu.Game.Rulesets; +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public abstract class GetRankingsRequest : APIRequest> + { + private readonly RulesetInfo ruleset; + private readonly int page; + + protected GetRankingsRequest(RulesetInfo ruleset, int page = 1) + { + this.ruleset = ruleset; + this.page = page; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + req.AddParameter("page", page.ToString()); + + return req; + } + + protected override string Target => $"rankings/{ruleset.ShortName}/{TargetPostfix()}"; + + protected abstract string TargetPostfix(); + } +} diff --git a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs new file mode 100644 index 0000000000..bbba6a210d --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.IO.Network; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserRankingsRequest : GetRankingsRequest + { + private readonly string country; + private readonly UserRankingsType type; + + public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, string country = null) + : base(ruleset, page) + { + this.type = type; + this.country = country; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + if (country != null) + req.AddParameter("country", country); + + return req; + } + + protected override string TargetPostfix() => type.ToString().ToLowerInvariant(); + } + + public enum UserRankingsType + { + Performance, + Score + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APICountryRankings.cs b/osu.Game/Online/API/Requests/Responses/APICountryRankings.cs new file mode 100644 index 0000000000..91086876a9 --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APICountryRankings.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APICountryRankings : CountryStatistics + { + [JsonProperty] + public Country Country; + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIUserRankings.cs b/osu.Game/Online/API/Requests/Responses/APIUserRankings.cs new file mode 100644 index 0000000000..1cdb6ecb8c --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIUserRankings.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIUserRankings : UserStatistics + { + [JsonProperty] + public User User; + } +} diff --git a/osu.Game/Users/CountryStatistics.cs b/osu.Game/Users/CountryStatistics.cs new file mode 100644 index 0000000000..53fa70f0d4 --- /dev/null +++ b/osu.Game/Users/CountryStatistics.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; + +namespace osu.Game.Users +{ + public class CountryStatistics + { + [JsonProperty(@"code")] + public string FlagName; + + [JsonProperty(@"active_users")] + public long ActiveUsers; + + [JsonProperty(@"play_count")] + public long PlayCount; + + [JsonProperty(@"ranked_score")] + public long RankedScore; + + [JsonProperty(@"performance")] + public long Performance; + } +} diff --git a/osu.Game/Utils/HumanizerUtils.cs b/osu.Game/Utils/HumanizerUtils.cs index 5b7c3630d9..ee6f6f95a3 100644 --- a/osu.Game/Utils/HumanizerUtils.cs +++ b/osu.Game/Utils/HumanizerUtils.cs @@ -26,5 +26,32 @@ namespace osu.Game.Utils return input.Humanize(culture: new CultureInfo("en-US")); } } + + /// + /// Turns the current or provided big number into a readable string. + /// + /// The number to be humanized. + /// Simplified number with a suffix. + public static string ToMetric(long input) + { + const int k = 1000; + + if (input < k) + return input.ToString(); + + int i = (int)Math.Floor(Math.Round(Math.Log(input, k))); + return $"{input / Math.Pow(k, i):F} {suffixes[i]}"; + } + + private static readonly string[] suffixes = new[] + { + "", + "k", + "million", + "billion", + "trillion", + "quadrillion", + "quintillion", + }; } } From bbaf21a69d0d025019e8cd9035485e473e0006d4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 27 Sep 2019 19:33:52 +0300 Subject: [PATCH 011/178] Tables implementation --- .../Visual/Online/TestSceneRankingsTables.cs | 148 ++++++++++++++++++ .../Rankings/Tables/CountriesTable.cs | 88 +++++++++++ .../Rankings/Tables/PerformanceTable.cs | 102 ++++++++++++ .../Overlays/Rankings/Tables/RankingsTable.cs | 128 +++++++++++++++ .../Overlays/Rankings/Tables/ScoresTable.cs | 108 +++++++++++++ .../Rankings/Tables/TableRowBackground.cs | 56 +++++++ 6 files changed, 630 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs create mode 100644 osu.Game/Overlays/Rankings/Tables/CountriesTable.cs create mode 100644 osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs create mode 100644 osu.Game/Overlays/Rankings/Tables/RankingsTable.cs create mode 100644 osu.Game/Overlays/Rankings/Tables/ScoresTable.cs create mode 100644 osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs new file mode 100644 index 0000000000..08a6da734f --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs @@ -0,0 +1,148 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Rankings.Tables; +using osu.Framework.Graphics; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; +using osu.Game.Graphics.UserInterface; +using System.Threading; +using osu.Game.Online.API; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Taiko; +using osu.Game.Rulesets.Catch; +using osu.Framework.Allocation; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsTables : OsuTestScene + { + protected override bool UseOnlineAPI => true; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(PerformanceTable), + typeof(ScoresTable), + typeof(CountriesTable), + typeof(TableRowBackground), + }; + + [Resolved] + private IAPIProvider api { get; set; } + + private readonly BasicScrollContainer scrollFlow; + private readonly DimmedLoadingLayer loading; + private CancellationTokenSource cancellationToken; + private APIRequest request; + + public TestSceneRankingsTables() + { + Children = new Drawable[] + { + scrollFlow = new BasicScrollContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Width = 0.8f, + }, + loading = new DimmedLoadingLayer(), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep("Osu performance", () => createPerformanceTable(new OsuRuleset().RulesetInfo, null)); + AddStep("Mania scores", () => createScoreTable(new ManiaRuleset().RulesetInfo)); + AddStep("Taiko country scores", () => createCountryTable(new TaikoRuleset().RulesetInfo)); + AddStep("Catch US performance page 10", () => createPerformanceTable(new CatchRuleset().RulesetInfo, "US", 10)); + } + + private void createCountryTable(RulesetInfo ruleset, int page = 1) + { + loading.Show(); + + request?.Cancel(); + cancellationToken?.Cancel(); + cancellationToken = new CancellationTokenSource(); + + request = new GetCountryRankingsRequest(ruleset, page); + ((GetCountryRankingsRequest)request).Success += rankings => Schedule(() => + { + var table = new CountriesTable(page) + { + Rankings = rankings, + }; + + LoadComponentAsync(table, t => + { + scrollFlow.Clear(); + scrollFlow.Add(t); + loading.Hide(); + }, cancellationToken.Token); + }); + + api.Queue(request); + } + + private void createPerformanceTable(RulesetInfo ruleset, string country, int page = 1) + { + loading.Show(); + + request?.Cancel(); + cancellationToken?.Cancel(); + cancellationToken = new CancellationTokenSource(); + + request = new GetUserRankingsRequest(ruleset, country: country, page: page); + ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => + { + var table = new PerformanceTable(page) + { + Rankings = rankings, + }; + + LoadComponentAsync(table, t => + { + scrollFlow.Clear(); + scrollFlow.Add(t); + loading.Hide(); + }, cancellationToken.Token); + }); + + api.Queue(request); + } + + private void createScoreTable(RulesetInfo ruleset, int page = 1) + { + loading.Show(); + + request?.Cancel(); + cancellationToken?.Cancel(); + cancellationToken = new CancellationTokenSource(); + + request = new GetUserRankingsRequest(ruleset, UserRankingsType.Score, page); + ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => + { + var table = new ScoresTable(page) + { + Rankings = rankings, + }; + + LoadComponentAsync(table, t => + { + scrollFlow.Clear(); + scrollFlow.Add(t); + loading.Hide(); + }, cancellationToken.Token); + }); + + api.Queue(request); + } + } +} diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs new file mode 100644 index 0000000000..cfd404ff07 --- /dev/null +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -0,0 +1,88 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Users.Drawables; +using osuTK; +using osu.Game.Online.API.Requests.Responses; +using System; + +namespace osu.Game.Overlays.Rankings.Tables +{ + public class CountriesTable : RankingsTable + { + public CountriesTable(int page = 1) + : base(page) + { + } + + protected override TableColumn[] CreateHeaders() => new[] + { + new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place + new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and country name + new TableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), + new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), + new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), + new TableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), + new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), + new TableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), + }; + + protected override Drawable[] CreateContent(int index, APICountryRankings item) => new Drawable[] + { + new OsuSpriteText + { + Text = $"#{index + 1}", + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7, 0), + Children = new Drawable[] + { + new UpdateableFlag(item.Country) + { + Size = new Vector2(20, 13), + ShowPlaceholderOnNull = false, + }, + new OsuSpriteText + { + Text = $@"{item.Country.FullName}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + } + } + }, + new ColoredText + { + Text = $@"{item.ActiveUsers:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredMetricNumber(item.PlayCount) + { + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredMetricNumber(item.RankedScore) + { + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredMetricNumber(item.RankedScore / Math.Max(item.ActiveUsers, 1)) + { + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new MetricNumber(item.Performance) + { + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.Performance / Math.Max(item.ActiveUsers, 1):N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + } + }; + } +} diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs new file mode 100644 index 0000000000..eab3e67f39 --- /dev/null +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -0,0 +1,102 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using System.Collections.Generic; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Users.Drawables; +using osuTK; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Overlays.Rankings.Tables +{ + public class PerformanceTable : RankingsTable + { + public PerformanceTable(int page = 1) + : base(page) + { + } + + protected override TableColumn[] CreateHeaders() => new[] + { + new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place + new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username + new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), + new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), + new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), + new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), + new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), + new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), + }; + + protected override Drawable[] CreateContent(int index, APIUserRankings item) + { + var content = new List + { + new OsuSpriteText + { + Text = $"#{index + 1}", + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) + }, + }; + + var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both }; + username.AddUserLink(item.User); + + content.Add(new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7, 0), + Children = new Drawable[] + { + new UpdateableFlag(item.User.Country) + { + Size = new Vector2(20, 13), + ShowPlaceholderOnNull = false, + }, + username + } + }); + + content.AddRange(new Drawable[] + { + new ColoredText + { + Text = $@"{item.Accuracy:F2}%", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.PlayCount:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new OsuSpriteText + { + Text = $@"{item.PP:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.GradesCount.A:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + }); + + return content.ToArray(); + } + } +} diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs new file mode 100644 index 0000000000..0734004913 --- /dev/null +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -0,0 +1,128 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Cursor; +using osu.Game.Utils; + +namespace osu.Game.Overlays.Rankings.Tables +{ + public abstract class RankingsTable : TableContainer + { + protected const int TEXT_SIZE = 14; + private const float horizontal_inset = 20; + private const float row_height = 25; + private const int items_per_page = 50; + + private readonly int page; + private readonly FillFlowContainer backgroundFlow; + + public IReadOnlyList Rankings + { + set + { + Content = null; + backgroundFlow.Clear(); + + if (value?.Any() != true) + return; + + value.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); + + Columns = CreateHeaders(); + Content = value.Select((s, i) => CreateContent(page * items_per_page - (items_per_page - i), s)).ToArray().ToRectangular(); + } + } + + protected RankingsTable(int page) + { + this.page = page; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding { Horizontal = horizontal_inset }; + RowSize = new Dimension(GridSizeMode.Absolute, row_height); + + AddInternal(backgroundFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Depth = 1f, + Margin = new MarginPadding { Top = row_height } + }); + } + + protected abstract TableColumn[] CreateHeaders(); + + protected abstract Drawable[] CreateContent(int index, TModel item); + + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty, HighlightedColumn()); + + protected virtual string HighlightedColumn() => @"Performance"; + + private class HeaderText : OsuSpriteText + { + private readonly string highlighted; + + public HeaderText(string text, string highlighted) + { + this.highlighted = highlighted; + + Text = text; + Font = OsuFont.GetFont(size: 12); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (Text != highlighted) + Colour = colours.GreySeafoamLighter; + } + } + + protected class MetricNumber : OsuSpriteText, IHasTooltip + { + public string TooltipText => $"{value:N0}"; + + private readonly long value; + + public MetricNumber(long value) + { + this.value = value; + + Text = HumanizerUtils.ToMetric(value); + } + } + + protected class ColoredMetricNumber : MetricNumber + { + public ColoredMetricNumber(long value) + : base(value) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.GreySeafoamLighter; + } + } + + protected class ColoredText : OsuSpriteText + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.GreySeafoamLighter; + } + } + } +} diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs new file mode 100644 index 0000000000..561b08472b --- /dev/null +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -0,0 +1,108 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Users.Drawables; +using osuTK; +using osu.Game.Online.API.Requests.Responses; +using System.Collections.Generic; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Rankings.Tables +{ + public class ScoresTable : RankingsTable + { + public ScoresTable(int page = 1) + : base(page) + { + } + + protected override TableColumn[] CreateHeaders() => new[] + { + new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place + new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username + new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), + new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), + new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), + new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), + new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), + new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), + new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), + }; + + protected override Drawable[] CreateContent(int index, APIUserRankings item) + { + var content = new List + { + new OsuSpriteText + { + Text = $"#{index + 1}", + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) + }, + }; + + var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both }; + username.AddUserLink(item.User); + + content.Add(new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7, 0), + Children = new Drawable[] + { + new UpdateableFlag(item.User.Country) + { + Size = new Vector2(20, 13), + ShowPlaceholderOnNull = false, + }, + username + } + }); + + content.AddRange(new Drawable[] + { + new ColoredText + { + Text = $@"{item.Accuracy:F2}%", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.PlayCount:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredMetricNumber(item.TotalScore) + { + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new MetricNumber(item.RankedScore) + { + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + new ColoredText + { + Text = $@"{item.GradesCount.A:N0}", + Font = OsuFont.GetFont(size: TEXT_SIZE), + }, + }); + + return content.ToArray(); + } + + protected override string HighlightedColumn() => @"Ranked Score"; + } +} diff --git a/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs b/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs new file mode 100644 index 0000000000..04e1c22dae --- /dev/null +++ b/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Rankings.Tables +{ + public class TableRowBackground : CompositeDrawable + { + private const int fade_duration = 100; + + private readonly Box background; + + private Color4 idleColour; + private Color4 hoverColour; + + public TableRowBackground() + { + RelativeSizeAxes = Axes.X; + Height = 25; + + CornerRadius = 3; + Masking = true; + + InternalChild = background = new Box + { + RelativeSizeAxes = Axes.Both, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = idleColour = colours.GreySeafoam; + hoverColour = colours.GreySeafoamLight; + } + + protected override bool OnHover(HoverEvent e) + { + background.FadeColour(hoverColour, fade_duration, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + background.FadeColour(idleColour, fade_duration, Easing.OutQuint); + base.OnHoverLost(e); + } + } +} From 883ee9851af74ae0695e9731df269685f9a7ad70 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 29 Sep 2019 15:02:33 +0300 Subject: [PATCH 012/178] Update dependencies --- .../Visual/UserInterface/TestSceneMetricNumbers.cs | 2 +- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 2 +- osu.Game/Utils/HumanizerUtils.cs | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs index 74470f22fc..8e3924e1fb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.UserInterface public DrawableNumber(long value) { this.value = value; - Text = HumanizerUtils.ToMetric(value); + Text = HumanizerUtils.ToReadableString(value); } } } diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 0734004913..d05d642051 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -98,7 +98,7 @@ namespace osu.Game.Overlays.Rankings.Tables { this.value = value; - Text = HumanizerUtils.ToMetric(value); + Text = HumanizerUtils.ToReadableString(value); } } diff --git a/osu.Game/Utils/HumanizerUtils.cs b/osu.Game/Utils/HumanizerUtils.cs index ee6f6f95a3..bc710a799d 100644 --- a/osu.Game/Utils/HumanizerUtils.cs +++ b/osu.Game/Utils/HumanizerUtils.cs @@ -32,7 +32,7 @@ namespace osu.Game.Utils /// /// The number to be humanized. /// Simplified number with a suffix. - public static string ToMetric(long input) + public static string ToReadableString(long input) { const int k = 1000; @@ -42,8 +42,7 @@ namespace osu.Game.Utils int i = (int)Math.Floor(Math.Round(Math.Log(input, k))); return $"{input / Math.Pow(k, i):F} {suffixes[i]}"; } - - private static readonly string[] suffixes = new[] + private static readonly string[] suffixes = { "", "k", From 138e65d7a49c0d9df0c5648148bc8e490b40542f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 29 Sep 2019 15:15:41 +0300 Subject: [PATCH 013/178] Add missing blank line --- osu.Game/Utils/HumanizerUtils.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Utils/HumanizerUtils.cs b/osu.Game/Utils/HumanizerUtils.cs index bc710a799d..9920f7d127 100644 --- a/osu.Game/Utils/HumanizerUtils.cs +++ b/osu.Game/Utils/HumanizerUtils.cs @@ -42,6 +42,7 @@ namespace osu.Game.Utils int i = (int)Math.Floor(Math.Round(Math.Log(input, k))); return $"{input / Math.Pow(k, i):F} {suffixes[i]}"; } + private static readonly string[] suffixes = { "", From febe0175cd6f6c3b594e2cf2b8dd2e1b1c59f0e0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 29 Sep 2019 15:27:29 +0300 Subject: [PATCH 014/178] Small cleanups --- .../Rankings/Tables/CountriesTable.cs | 22 ++------- .../Rankings/Tables/PerformanceTable.cs | 36 ++++++--------- .../Overlays/Rankings/Tables/RankingsTable.cs | 6 +++ .../Overlays/Rankings/Tables/ScoresTable.cs | 46 +++++++------------ 4 files changed, 42 insertions(+), 68 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index cfd404ff07..96ff506814 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -60,28 +60,14 @@ namespace osu.Game.Overlays.Rankings.Tables new ColoredText { Text = $@"{item.ActiveUsers:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), - }, - new ColoredMetricNumber(item.PlayCount) - { - Font = OsuFont.GetFont(size: TEXT_SIZE), - }, - new ColoredMetricNumber(item.RankedScore) - { - Font = OsuFont.GetFont(size: TEXT_SIZE), - }, - new ColoredMetricNumber(item.RankedScore / Math.Max(item.ActiveUsers, 1)) - { - Font = OsuFont.GetFont(size: TEXT_SIZE), - }, - new MetricNumber(item.Performance) - { - Font = OsuFont.GetFont(size: TEXT_SIZE), }, + new ColoredMetricNumber(item.PlayCount), + new ColoredMetricNumber(item.RankedScore), + new ColoredMetricNumber(item.RankedScore / Math.Max(item.ActiveUsers, 1)), + new MetricNumber(item.Performance), new ColoredText { Text = $@"{item.Performance / Math.Max(item.ActiveUsers, 1):N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), } }; } diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index eab3e67f39..c79553a3be 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -46,33 +46,30 @@ namespace osu.Game.Overlays.Rankings.Tables var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both }; username.AddUserLink(item.User); - content.Add(new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(7, 0), - Children = new Drawable[] - { - new UpdateableFlag(item.User.Country) - { - Size = new Vector2(20, 13), - ShowPlaceholderOnNull = false, - }, - username - } - }); - content.AddRange(new Drawable[] { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7, 0), + Children = new Drawable[] + { + new UpdateableFlag(item.User.Country) + { + Size = new Vector2(20, 13), + ShowPlaceholderOnNull = false, + }, + username + } + }, new ColoredText { Text = $@"{item.Accuracy:F2}%", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, new ColoredText { Text = $@"{item.PlayCount:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, new OsuSpriteText { @@ -82,17 +79,14 @@ namespace osu.Game.Overlays.Rankings.Tables new ColoredText { Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, new ColoredText { Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, new ColoredText { Text = $@"{item.GradesCount.A:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, }); diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index d05d642051..6c53a9fa74 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -99,6 +99,7 @@ namespace osu.Game.Overlays.Rankings.Tables this.value = value; Text = HumanizerUtils.ToReadableString(value); + Font = OsuFont.GetFont(size: TEXT_SIZE); } } @@ -118,6 +119,11 @@ namespace osu.Game.Overlays.Rankings.Tables protected class ColoredText : OsuSpriteText { + public ColoredText() + { + Font = OsuFont.GetFont(size: TEXT_SIZE); + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 561b08472b..1c8b474689 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -47,56 +47,44 @@ namespace osu.Game.Overlays.Rankings.Tables var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both }; username.AddUserLink(item.User); - content.Add(new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(7, 0), - Children = new Drawable[] - { - new UpdateableFlag(item.User.Country) - { - Size = new Vector2(20, 13), - ShowPlaceholderOnNull = false, - }, - username - } - }); - content.AddRange(new Drawable[] { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7, 0), + Children = new Drawable[] + { + new UpdateableFlag(item.User.Country) + { + Size = new Vector2(20, 13), + ShowPlaceholderOnNull = false, + }, + username + } + }, new ColoredText { Text = $@"{item.Accuracy:F2}%", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, new ColoredText { Text = $@"{item.PlayCount:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), - }, - new ColoredMetricNumber(item.TotalScore) - { - Font = OsuFont.GetFont(size: TEXT_SIZE), - }, - new MetricNumber(item.RankedScore) - { - Font = OsuFont.GetFont(size: TEXT_SIZE), }, + new ColoredMetricNumber(item.TotalScore), + new MetricNumber(item.RankedScore), new ColoredText { Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, new ColoredText { Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, new ColoredText { Text = $@"{item.GradesCount.A:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, }); From ff477cd56c36eb99a8e238dd0de607fc5f3a6222 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 1 Oct 2019 14:12:03 +0300 Subject: [PATCH 015/178] Remove humanized number dependency --- .../UserInterface/TestSceneMetricNumbers.cs | 58 ------------------- .../Rankings/Tables/CountriesTable.cs | 27 ++++++--- .../Rankings/Tables/PerformanceTable.cs | 13 ++--- .../Overlays/Rankings/Tables/RankingsTable.cs | 34 +---------- .../Overlays/Rankings/Tables/ScoresTable.cs | 20 ++++--- osu.Game/Utils/HumanizerUtils.cs | 27 --------- 6 files changed, 41 insertions(+), 138 deletions(-) delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs deleted file mode 100644 index 8e3924e1fb..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMetricNumbers.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Sprites; -using osu.Game.Utils; - -namespace osu.Game.Tests.Visual.UserInterface -{ - public class TestSceneMetricNumbers : OsuTestScene - { - public TestSceneMetricNumbers() - { - Child = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new DrawableNumber(0), - new DrawableNumber(1001), - new DrawableNumber(999_999), - new DrawableNumber(1_000_000), - new DrawableNumber(845_006_456), - new DrawableNumber(999_999_999), - new DrawableNumber(1_000_000_000), - new DrawableNumber(7_875_454_545), - new DrawableNumber(999_999_999_999), - new DrawableNumber(1_000_000_000_000), - new DrawableNumber(687_545_454_554_545), - new DrawableNumber(999_999_999_999_999), - new DrawableNumber(1_000_000_000_000_000), - new DrawableNumber(587_545_454_554_545_455), - new DrawableNumber(999_999_999_999_999_999), - new DrawableNumber(1_000_000_000_000_000_000), - new DrawableNumber(long.MaxValue), - } - }; - } - - private class DrawableNumber : SpriteText, IHasTooltip - { - public string TooltipText => value.ToString("F0"); - - private readonly long value; - - public DrawableNumber(long value) - { - this.value = value; - Text = HumanizerUtils.ToReadableString(value); - } - } - } -} diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 96ff506814..8e0e55595d 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -50,22 +50,33 @@ namespace osu.Game.Overlays.Rankings.Tables Size = new Vector2(20, 13), ShowPlaceholderOnNull = false, }, - new OsuSpriteText + new RowText { Text = $@"{item.Country.FullName}", - Font = OsuFont.GetFont(size: TEXT_SIZE), } } }, - new ColoredText + new ColoredRowText { Text = $@"{item.ActiveUsers:N0}", }, - new ColoredMetricNumber(item.PlayCount), - new ColoredMetricNumber(item.RankedScore), - new ColoredMetricNumber(item.RankedScore / Math.Max(item.ActiveUsers, 1)), - new MetricNumber(item.Performance), - new ColoredText + new ColoredRowText + { + Text = $@"{item.PlayCount:N0}", + }, + new ColoredRowText + { + Text = $@"{item.RankedScore:N0}", + }, + new ColoredRowText + { + Text = $@"{item.RankedScore / Math.Max(item.ActiveUsers, 1):N0}", + }, + new RowText + { + Text = $@"{item.Performance:N0}", + }, + new ColoredRowText { Text = $@"{item.Performance / Math.Max(item.ActiveUsers, 1):N0}", } diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index c79553a3be..2d582f4321 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -63,28 +63,27 @@ namespace osu.Game.Overlays.Rankings.Tables username } }, - new ColoredText + new ColoredRowText { Text = $@"{item.Accuracy:F2}%", }, - new ColoredText + new ColoredRowText { Text = $@"{item.PlayCount:N0}", }, - new OsuSpriteText + new RowText { Text = $@"{item.PP:N0}", - Font = OsuFont.GetFont(size: TEXT_SIZE), }, - new ColoredText + new ColoredRowText { Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", }, - new ColoredText + new ColoredRowText { Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", }, - new ColoredText + new ColoredRowText { Text = $@"{item.GradesCount.A:N0}", }, diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 6c53a9fa74..a04e8be9d0 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -10,8 +10,6 @@ using osu.Framework.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics.Cursor; -using osu.Game.Utils; namespace osu.Game.Overlays.Rankings.Tables { @@ -88,42 +86,16 @@ namespace osu.Game.Overlays.Rankings.Tables } } - protected class MetricNumber : OsuSpriteText, IHasTooltip + protected class RowText : OsuSpriteText { - public string TooltipText => $"{value:N0}"; - - private readonly long value; - - public MetricNumber(long value) + public RowText() { - this.value = value; - - Text = HumanizerUtils.ToReadableString(value); Font = OsuFont.GetFont(size: TEXT_SIZE); } } - protected class ColoredMetricNumber : MetricNumber + protected class ColoredRowText : RowText { - public ColoredMetricNumber(long value) - : base(value) - { - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = colours.GreySeafoamLighter; - } - } - - protected class ColoredText : OsuSpriteText - { - public ColoredText() - { - Font = OsuFont.GetFont(size: TEXT_SIZE); - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 1c8b474689..5d572a6af0 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -64,25 +64,31 @@ namespace osu.Game.Overlays.Rankings.Tables username } }, - new ColoredText + new ColoredRowText { Text = $@"{item.Accuracy:F2}%", }, - new ColoredText + new ColoredRowText { Text = $@"{item.PlayCount:N0}", }, - new ColoredMetricNumber(item.TotalScore), - new MetricNumber(item.RankedScore), - new ColoredText + new ColoredRowText + { + Text = $@"{item.TotalScore:N0}", + }, + new RowText + { + Text = $@"{item.RankedScore:N0}", + }, + new ColoredRowText { Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", }, - new ColoredText + new ColoredRowText { Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", }, - new ColoredText + new ColoredRowText { Text = $@"{item.GradesCount.A:N0}", }, diff --git a/osu.Game/Utils/HumanizerUtils.cs b/osu.Game/Utils/HumanizerUtils.cs index 9920f7d127..5b7c3630d9 100644 --- a/osu.Game/Utils/HumanizerUtils.cs +++ b/osu.Game/Utils/HumanizerUtils.cs @@ -26,32 +26,5 @@ namespace osu.Game.Utils return input.Humanize(culture: new CultureInfo("en-US")); } } - - /// - /// Turns the current or provided big number into a readable string. - /// - /// The number to be humanized. - /// Simplified number with a suffix. - public static string ToReadableString(long input) - { - const int k = 1000; - - if (input < k) - return input.ToString(); - - int i = (int)Math.Floor(Math.Round(Math.Log(input, k))); - return $"{input / Math.Pow(k, i):F} {suffixes[i]}"; - } - - private static readonly string[] suffixes = - { - "", - "k", - "million", - "billion", - "trillion", - "quadrillion", - "quintillion", - }; } } From 2f703090ef1996c34eb4db392a894f015cdb0130 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 1 Nov 2019 21:49:50 +0300 Subject: [PATCH 016/178] Update API with latest web changes --- .../Visual/Online/TestSceneRankingsTables.cs | 6 +++--- .../Online/API/Requests/GetCountriesResponse.cs | 15 +++++++++++++++ .../API/Requests/GetCountryRankingsRequest.cs | 3 +-- .../Online/API/Requests/GetRankingsRequest.cs | 3 +-- .../Online/API/Requests/GetUserRankingsRequest.cs | 3 +-- osu.Game/Online/API/Requests/GetUsersResponse.cs | 2 +- 6 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Online/API/Requests/GetCountriesResponse.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs index 08a6da734f..49656900cc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Online { var table = new CountriesTable(page) { - Rankings = rankings, + Rankings = rankings.Countries, }; LoadComponentAsync(table, t => @@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual.Online { var table = new PerformanceTable(page) { - Rankings = rankings, + Rankings = rankings.Users, }; LoadComponentAsync(table, t => @@ -131,7 +131,7 @@ namespace osu.Game.Tests.Visual.Online { var table = new ScoresTable(page) { - Rankings = rankings, + Rankings = rankings.Users, }; LoadComponentAsync(table, t => diff --git a/osu.Game/Online/API/Requests/GetCountriesResponse.cs b/osu.Game/Online/API/Requests/GetCountriesResponse.cs new file mode 100644 index 0000000000..7b65c6fadd --- /dev/null +++ b/osu.Game/Online/API/Requests/GetCountriesResponse.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using Newtonsoft.Json; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class GetCountriesResponse : ResponseWithCursor + { + [JsonProperty("ranking")] + public List Countries; + } +} diff --git a/osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs b/osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs index 3bd772732f..d8a1198627 100644 --- a/osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs +++ b/osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs @@ -1,12 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { - public class GetCountryRankingsRequest : GetRankingsRequest + public class GetCountryRankingsRequest : GetRankingsRequest { public GetCountryRankingsRequest(RulesetInfo ruleset, int page = 1) : base(ruleset, page) diff --git a/osu.Game/Online/API/Requests/GetRankingsRequest.cs b/osu.Game/Online/API/Requests/GetRankingsRequest.cs index efaaa2cb40..941691c4c1 100644 --- a/osu.Game/Online/API/Requests/GetRankingsRequest.cs +++ b/osu.Game/Online/API/Requests/GetRankingsRequest.cs @@ -3,11 +3,10 @@ using osu.Framework.IO.Network; using osu.Game.Rulesets; -using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public abstract class GetRankingsRequest : APIRequest> + public abstract class GetRankingsRequest : APIRequest { private readonly RulesetInfo ruleset; private readonly int page; diff --git a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs index bbba6a210d..9c3eba9fdc 100644 --- a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs @@ -2,12 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.IO.Network; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { - public class GetUserRankingsRequest : GetRankingsRequest + public class GetUserRankingsRequest : GetRankingsRequest { private readonly string country; private readonly UserRankingsType type; diff --git a/osu.Game/Online/API/Requests/GetUsersResponse.cs b/osu.Game/Online/API/Requests/GetUsersResponse.cs index 860785875a..c60bc71096 100644 --- a/osu.Game/Online/API/Requests/GetUsersResponse.cs +++ b/osu.Game/Online/API/Requests/GetUsersResponse.cs @@ -10,6 +10,6 @@ namespace osu.Game.Online.API.Requests public class GetUsersResponse : ResponseWithCursor { [JsonProperty("ranking")] - public List Users; + public List Users; } } From 48385dbdfe2dfbe685fbd03a8ca09206a794c4e1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Nov 2019 14:31:23 +0300 Subject: [PATCH 017/178] Layout adjustments --- .../Overlays/Rankings/Tables/CountriesTable.cs | 12 ++++++------ .../Overlays/Rankings/Tables/PerformanceTable.cs | 12 ++++++------ osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 1 + osu.Game/Overlays/Rankings/Tables/ScoresTable.cs | 14 +++++++------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 8e0e55595d..386225a606 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -23,12 +23,12 @@ namespace osu.Game.Overlays.Rankings.Tables { new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and country name - new TableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), - new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), - new TableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), - new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), - new TableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), + new TableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; protected override Drawable[] CreateContent(int index, APICountryRankings item) => new Drawable[] diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 2d582f4321..11357a82a1 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -24,12 +24,12 @@ namespace osu.Game.Overlays.Rankings.Tables { new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username - new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), - new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), - new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), - new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), - new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), + new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; protected override Drawable[] CreateContent(int index, APIUserRankings item) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index a04e8be9d0..16dca06c44 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -91,6 +91,7 @@ namespace osu.Game.Overlays.Rankings.Tables public RowText() { Font = OsuFont.GetFont(size: TEXT_SIZE); + Margin = new MarginPadding { Horizontal = 10 }; } } diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 5d572a6af0..0a8ec50e30 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -24,13 +24,13 @@ namespace osu.Game.Overlays.Rankings.Tables { new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username - new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 80)), - new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), - new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 100)), - new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), - new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), - new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), + new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; protected override Drawable[] CreateContent(int index, APIUserRankings item) From 5761cc673f09c2826f909164e9bf7767664bab50 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 10 May 2019 19:44:22 +0200 Subject: [PATCH 018/178] implement CatchModRelax --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 0454bc969d..47e32b1292 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -1,12 +1,48 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using osuTK; +using System; -namespace osu.Game.Rulesets.Catch.Mods -{ - public class CatchModRelax : ModRelax - { +namespace osu.Game.Rulesets.Catch.Mods { + public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset { public override string Description => @"Use the mouse to control the catcher."; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) => + (drawableRuleset.Playfield.Parent as Container).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); + + private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { + private CatcherArea.Catcher catcher; + + public CatchModRelaxHelper(CatchPlayfield catchPlayfield) { + catcher = catchPlayfield.CatcherArea.MovableCatcher; + RelativeSizeAxes = Axes.Both; + } + + //disable keyboard controls + public bool OnPressed(CatchAction action) => true; + public bool OnReleased(CatchAction action) => true; + + protected override bool OnMouseMove(MouseMoveEvent e) { + //lock catcher to mouse position horizontally + catcher.X = e.MousePosition.X / DrawSize.X; + + //make Yuzu face the direction he's moving + var direction = Math.Sign(e.Delta.X); + if (direction != 0) + catcher.Scale = new Vector2(Math.Abs(catcher.Scale.X) * direction, catcher.Scale.Y); + + return base.OnMouseMove(e); + } + } } } From a842d727c7905af23bb411121ec513292fe240ae Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 May 2019 10:03:59 +0200 Subject: [PATCH 019/178] formatting --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 47e32b1292..5fa22f8bdb 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -13,17 +13,21 @@ using osu.Game.Rulesets.UI; using osuTK; using System; -namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset { +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset + { public override string Description => @"Use the mouse to control the catcher."; public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) => (drawableRuleset.Playfield.Parent as Container).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); - private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { + private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition + { private CatcherArea.Catcher catcher; - public CatchModRelaxHelper(CatchPlayfield catchPlayfield) { + public CatchModRelaxHelper(CatchPlayfield catchPlayfield) + { catcher = catchPlayfield.CatcherArea.MovableCatcher; RelativeSizeAxes = Axes.Both; } @@ -32,7 +36,8 @@ namespace osu.Game.Rulesets.Catch.Mods { public bool OnPressed(CatchAction action) => true; public bool OnReleased(CatchAction action) => true; - protected override bool OnMouseMove(MouseMoveEvent e) { + protected override bool OnMouseMove(MouseMoveEvent e) + { //lock catcher to mouse position horizontally catcher.X = e.MousePosition.X / DrawSize.X; From a8fab4cba1deacc0d154546f2d6fd7bdc3457056 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 May 2019 10:26:24 +0200 Subject: [PATCH 020/178] make catcher field readonly --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 5fa22f8bdb..9852e753c5 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Mods private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { - private CatcherArea.Catcher catcher; + private readonly CatcherArea.Catcher catcher; public CatchModRelaxHelper(CatchPlayfield catchPlayfield) { From 750e3c4aafa1d65e8aed975505fef3ded1c2eeee Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 May 2019 11:25:29 +0200 Subject: [PATCH 021/178] replace 'as' with direct cast to avoid possible nullref --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 9852e753c5..062c0f8b26 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Mods public override string Description => @"Use the mouse to control the catcher."; public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) => - (drawableRuleset.Playfield.Parent as Container).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); + ((Container)drawableRuleset.Playfield.Parent).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { From 8e248d5cfa6f0bd1560f67f2ceeb94a60734800c Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 20:20:11 +0200 Subject: [PATCH 022/178] add invisible cursor for ctb --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 14 ++++++++++++++ osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 3 +++ 2 files changed, 17 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs new file mode 100644 index 0000000000..073f2e05b2 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -0,0 +1,14 @@ +using osu.Framework.Graphics; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Catch.UI { + class CatchCursorContainer : GameplayCursorContainer + { + protected override Drawable CreateCursor() => new InvisibleCursor(); + + private class InvisibleCursor : Drawable + { + + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index b6d8cf9cbe..7741096da2 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Catch.UI @@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.UI internal readonly CatcherArea CatcherArea; + protected override GameplayCursorContainer CreateCursor() => new CatchCursorContainer(); + public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation) { Container explodingFruitContainer; From bd5cb86b1546e9c9c7fe6d64f373f5b9766e0500 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 21:01:51 +0200 Subject: [PATCH 023/178] add license header --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index 073f2e05b2..072c5d4fdf 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -1,4 +1,7 @@ -using osu.Framework.Graphics; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.UI { From 883d5ed58edfb1922df3f00e42f5821efdeff15b Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 21:14:27 +0200 Subject: [PATCH 024/178] formatting --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index 072c5d4fdf..642e0bb9de 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -4,7 +4,8 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.UI; -namespace osu.Game.Rulesets.Catch.UI { +namespace osu.Game.Rulesets.Catch.UI +{ class CatchCursorContainer : GameplayCursorContainer { protected override Drawable CreateCursor() => new InvisibleCursor(); From 2a0db8aeac74ba0dc41d7ee53c3cf96c10be8829 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 21:17:06 +0200 Subject: [PATCH 025/178] add missing access modifier --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index 642e0bb9de..d4ac4b2f2e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -6,7 +6,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.UI { - class CatchCursorContainer : GameplayCursorContainer + public class CatchCursorContainer : GameplayCursorContainer { protected override Drawable CreateCursor() => new InvisibleCursor(); From 9570f7c04bc8a4653ee30f82d1abbcb11c0ca661 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 May 2019 21:23:59 +0200 Subject: [PATCH 026/178] more formatting --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index d4ac4b2f2e..03bfad7e8c 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -10,9 +10,6 @@ namespace osu.Game.Rulesets.Catch.UI { protected override Drawable CreateCursor() => new InvisibleCursor(); - private class InvisibleCursor : Drawable - { - - } + private class InvisibleCursor : Drawable { } } } From c180a71afe4e788a6d66043df3b9267ad2dec2ab Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 3 Nov 2019 15:57:52 +0100 Subject: [PATCH 027/178] even more formatting --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index 03bfad7e8c..c596217196 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Catch.UI { protected override Drawable CreateCursor() => new InvisibleCursor(); - private class InvisibleCursor : Drawable { } + private class InvisibleCursor : Drawable { + } } } From 131809838f7cab4413713cb7f2c99a2af9f590aa Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 3 Nov 2019 16:22:07 +0100 Subject: [PATCH 028/178] pleasing CI --- osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs index c596217196..163718f202 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs @@ -10,7 +10,8 @@ namespace osu.Game.Rulesets.Catch.UI { protected override Drawable CreateCursor() => new InvisibleCursor(); - private class InvisibleCursor : Drawable { + private class InvisibleCursor : Drawable + { } } } From 423e26a4d147cef6525e9e88b3e86497e3978189 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 6 Nov 2019 13:07:25 +0300 Subject: [PATCH 029/178] Fix header text could be sticked together --- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 16dca06c44..07e2257e8f 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -76,6 +76,7 @@ namespace osu.Game.Overlays.Rankings.Tables Text = text; Font = OsuFont.GetFont(size: 12); + Margin = new MarginPadding { Horizontal = 10 }; } [BackgroundDependencyLoader] From d1e3718038f806afff93aaebd9bf8a1481312f34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Nov 2019 15:04:09 +0900 Subject: [PATCH 030/178] Reduce the scale of background blurs --- osu.Game/Graphics/Backgrounds/Background.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 0f923c3a28..21fca5d5da 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -16,6 +16,8 @@ namespace osu.Game.Graphics.Backgrounds /// public class Background : CompositeDrawable { + private const float blur_scale = 0.5f; + public readonly Sprite Sprite; private readonly string textureName; @@ -64,7 +66,10 @@ namespace osu.Game.Graphics.Backgrounds }); } - bufferedContainer?.BlurTo(newBlurSigma, duration, easing); + if (bufferedContainer != null) + bufferedContainer.FrameBufferScale = newBlurSigma == Vector2.Zero ? Vector2.One : new Vector2(blur_scale); + + bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing); } } } From e1302d84ddd51f71bcb3b8e85c241323a9552e8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Nov 2019 00:26:10 +0900 Subject: [PATCH 031/178] Use string.Empty --- osu.Game/Overlays/Rankings/Tables/CountriesTable.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 386225a606..ae686cd4ad 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -21,8 +21,8 @@ namespace osu.Game.Overlays.Rankings.Tables protected override TableColumn[] CreateHeaders() => new[] { - new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place - new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and country name + new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place + new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and country name new TableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), From 724fcecbc572f803478c12b9132296f28425fb01 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Nov 2019 00:32:31 +0900 Subject: [PATCH 032/178] Fix test scene not actually covering the class it's testing --- osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs index 49656900cc..2c76b1937b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs @@ -29,6 +29,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ScoresTable), typeof(CountriesTable), typeof(TableRowBackground), + typeof(RankingsTable<>) }; [Resolved] From bb3152ac8b58def442b9a3ea996ea73c9d694bbf Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 27 Nov 2019 21:35:03 +0300 Subject: [PATCH 033/178] Use string.empty for empty headers --- osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs | 4 ++-- osu.Game/Overlays/Rankings/Tables/ScoresTable.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 11357a82a1..2a658ec0de 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -22,8 +22,8 @@ namespace osu.Game.Overlays.Rankings.Tables protected override TableColumn[] CreateHeaders() => new[] { - new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place - new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username + new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place + new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 0a8ec50e30..b644edefd1 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -22,8 +22,8 @@ namespace osu.Game.Overlays.Rankings.Tables protected override TableColumn[] CreateHeaders() => new[] { - new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place - new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username + new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place + new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), From 14c471fe5d8a4391be57546530d916338e3de71f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 27 Nov 2019 21:37:34 +0300 Subject: [PATCH 034/178] Move Rankings property below ctor --- .../Overlays/Rankings/Tables/RankingsTable.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 07e2257e8f..58363929b3 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -23,23 +23,6 @@ namespace osu.Game.Overlays.Rankings.Tables private readonly int page; private readonly FillFlowContainer backgroundFlow; - public IReadOnlyList Rankings - { - set - { - Content = null; - backgroundFlow.Clear(); - - if (value?.Any() != true) - return; - - value.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); - - Columns = CreateHeaders(); - Content = value.Select((s, i) => CreateContent(page * items_per_page - (items_per_page - i), s)).ToArray().ToRectangular(); - } - } - protected RankingsTable(int page) { this.page = page; @@ -58,6 +41,23 @@ namespace osu.Game.Overlays.Rankings.Tables }); } + public IReadOnlyList Rankings + { + set + { + Content = null; + backgroundFlow.Clear(); + + if (value?.Any() != true) + return; + + value.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); + + Columns = CreateHeaders(); + Content = value.Select((s, i) => CreateContent(page * items_per_page - (items_per_page - i), s)).ToArray().ToRectangular(); + } + } + protected abstract TableColumn[] CreateHeaders(); protected abstract Drawable[] CreateContent(int index, TModel item); From 4cf59680263a2aa6f2883426a00d385cd72b6500 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 27 Nov 2019 21:39:38 +0300 Subject: [PATCH 035/178] Simplify place calculation --- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 58363929b3..98a6cc1caf 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Rankings.Tables value.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); Columns = CreateHeaders(); - Content = value.Select((s, i) => CreateContent(page * items_per_page - (items_per_page - i), s)).ToArray().ToRectangular(); + Content = value.Select((s, i) => CreateContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); } } From 2135a7fd7b32b8763cbe950a32705145f144ed54 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 27 Nov 2019 21:46:41 +0300 Subject: [PATCH 036/178] Don't use repetitive headers in each class --- osu.Game/Overlays/Rankings/Tables/CountriesTable.cs | 4 +--- osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs | 4 +--- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 10 ++++++++-- osu.Game/Overlays/Rankings/Tables/ScoresTable.cs | 4 +--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index ae686cd4ad..f5e95f316e 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -19,10 +19,8 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateHeaders() => new[] + protected override TableColumn[] CreateAdditionalHeaders() => new[] { - new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and country name new TableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 2a658ec0de..7d4bd71526 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -20,10 +20,8 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateHeaders() => new[] + protected override TableColumn[] CreateAdditionalHeaders() => new[] { - new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 98a6cc1caf..530295b039 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -53,12 +53,18 @@ namespace osu.Game.Overlays.Rankings.Tables value.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); - Columns = CreateHeaders(); + Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray(); Content = value.Select((s, i) => CreateContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); } } - protected abstract TableColumn[] CreateHeaders(); + private static TableColumn[] mainHeaders => new[] + { + new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place + new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username (country name) + }; + + protected abstract TableColumn[] CreateAdditionalHeaders(); protected abstract Drawable[] CreateContent(int index, TModel item); diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index b644edefd1..9ddd89914a 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -20,10 +20,8 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateHeaders() => new[] + protected override TableColumn[] CreateAdditionalHeaders() => new[] { - new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), From c546df8a8050e5e220c9d8c372a00b6062b38947 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 27 Nov 2019 21:56:22 +0300 Subject: [PATCH 037/178] Refactor API logic --- .../Online/API/Requests/GetCountriesResponse.cs | 4 ++-- osu.Game/Online/API/Requests/GetUsersResponse.cs | 4 ++-- .../API/Requests/Responses/APICountryRankings.cs | 14 -------------- .../API/Requests/Responses/APIUserRankings.cs | 14 -------------- .../Overlays/Rankings/Tables/CountriesTable.cs | 6 +++--- .../Overlays/Rankings/Tables/PerformanceTable.cs | 6 +++--- osu.Game/Overlays/Rankings/Tables/ScoresTable.cs | 6 +++--- osu.Game/Users/CountryStatistics.cs | 3 +++ osu.Game/Users/UserStatistics.cs | 3 +++ 9 files changed, 19 insertions(+), 41 deletions(-) delete mode 100644 osu.Game/Online/API/Requests/Responses/APICountryRankings.cs delete mode 100644 osu.Game/Online/API/Requests/Responses/APIUserRankings.cs diff --git a/osu.Game/Online/API/Requests/GetCountriesResponse.cs b/osu.Game/Online/API/Requests/GetCountriesResponse.cs index 7b65c6fadd..6624344b44 100644 --- a/osu.Game/Online/API/Requests/GetCountriesResponse.cs +++ b/osu.Game/Online/API/Requests/GetCountriesResponse.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; using Newtonsoft.Json; -using osu.Game.Online.API.Requests.Responses; +using osu.Game.Users; namespace osu.Game.Online.API.Requests { public class GetCountriesResponse : ResponseWithCursor { [JsonProperty("ranking")] - public List Countries; + public List Countries; } } diff --git a/osu.Game/Online/API/Requests/GetUsersResponse.cs b/osu.Game/Online/API/Requests/GetUsersResponse.cs index c60bc71096..b301f551e3 100644 --- a/osu.Game/Online/API/Requests/GetUsersResponse.cs +++ b/osu.Game/Online/API/Requests/GetUsersResponse.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; using Newtonsoft.Json; -using osu.Game.Online.API.Requests.Responses; +using osu.Game.Users; namespace osu.Game.Online.API.Requests { public class GetUsersResponse : ResponseWithCursor { [JsonProperty("ranking")] - public List Users; + public List Users; } } diff --git a/osu.Game/Online/API/Requests/Responses/APICountryRankings.cs b/osu.Game/Online/API/Requests/Responses/APICountryRankings.cs deleted file mode 100644 index 91086876a9..0000000000 --- a/osu.Game/Online/API/Requests/Responses/APICountryRankings.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Newtonsoft.Json; -using osu.Game.Users; - -namespace osu.Game.Online.API.Requests.Responses -{ - public class APICountryRankings : CountryStatistics - { - [JsonProperty] - public Country Country; - } -} diff --git a/osu.Game/Online/API/Requests/Responses/APIUserRankings.cs b/osu.Game/Online/API/Requests/Responses/APIUserRankings.cs deleted file mode 100644 index 1cdb6ecb8c..0000000000 --- a/osu.Game/Online/API/Requests/Responses/APIUserRankings.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Newtonsoft.Json; -using osu.Game.Users; - -namespace osu.Game.Online.API.Requests.Responses -{ - public class APIUserRankings : UserStatistics - { - [JsonProperty] - public User User; - } -} diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index f5e95f316e..878f85b346 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -7,12 +7,12 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osuTK; -using osu.Game.Online.API.Requests.Responses; using System; +using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables { - public class CountriesTable : RankingsTable + public class CountriesTable : RankingsTable { public CountriesTable(int page = 1) : base(page) @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Rankings.Tables new TableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; - protected override Drawable[] CreateContent(int index, APICountryRankings item) => new Drawable[] + protected override Drawable[] CreateContent(int index, CountryStatistics item) => new Drawable[] { new OsuSpriteText { diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 7d4bd71526..010da07858 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -9,11 +9,11 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osuTK; -using osu.Game.Online.API.Requests.Responses; +using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables { - public class PerformanceTable : RankingsTable + public class PerformanceTable : RankingsTable { public PerformanceTable(int page = 1) : base(page) @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Rankings.Tables new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; - protected override Drawable[] CreateContent(int index, APIUserRankings item) + protected override Drawable[] CreateContent(int index, UserStatistics item) { var content = new List { diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 9ddd89914a..d40da63f7f 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -7,13 +7,13 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osuTK; -using osu.Game.Online.API.Requests.Responses; using System.Collections.Generic; using osu.Game.Graphics.Containers; +using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables { - public class ScoresTable : RankingsTable + public class ScoresTable : RankingsTable { public ScoresTable(int page = 1) : base(page) @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Rankings.Tables new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; - protected override Drawable[] CreateContent(int index, APIUserRankings item) + protected override Drawable[] CreateContent(int index, UserStatistics item) { var content = new List { diff --git a/osu.Game/Users/CountryStatistics.cs b/osu.Game/Users/CountryStatistics.cs index 53fa70f0d4..000553c32b 100644 --- a/osu.Game/Users/CountryStatistics.cs +++ b/osu.Game/Users/CountryStatistics.cs @@ -7,6 +7,9 @@ namespace osu.Game.Users { public class CountryStatistics { + [JsonProperty] + public Country Country; + [JsonProperty(@"code")] public string FlagName; diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index 032ec2e05f..24f1f0b30e 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -10,6 +10,9 @@ namespace osu.Game.Users { public class UserStatistics { + [JsonProperty] + public User User; + [JsonProperty(@"level")] public LevelInfo Level; From 800bda7e815caa2ff0739642444e431acf49c1f3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 27 Nov 2019 23:35:02 +0300 Subject: [PATCH 038/178] Refactor tables to avoid code duplication --- .../Visual/Online/TestSceneRankingsTables.cs | 1 + .../Rankings/Tables/CountriesTable.cs | 39 +++------ .../Rankings/Tables/PerformanceTable.cs | 77 ++--------------- .../Overlays/Rankings/Tables/RankingsTable.cs | 35 +++++++- .../Overlays/Rankings/Tables/ScoresTable.cs | 85 +++---------------- .../Rankings/Tables/UserBasedTable.cs | 70 +++++++++++++++ 6 files changed, 133 insertions(+), 174 deletions(-) create mode 100644 osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs index 2c76b1937b..ff238803a5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs @@ -29,6 +29,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ScoresTable), typeof(CountriesTable), typeof(TableRowBackground), + typeof(UserBasedTable), typeof(RankingsTable<>) }; diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 878f85b346..208a4dbfe4 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -3,12 +3,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Users.Drawables; -using osuTK; using System; using osu.Game.Users; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics; namespace osu.Game.Overlays.Rankings.Tables { @@ -29,31 +27,16 @@ namespace osu.Game.Overlays.Rankings.Tables new TableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; - protected override Drawable[] CreateContent(int index, CountryStatistics item) => new Drawable[] + protected override Country GetCountry(CountryStatistics item) => item.Country; + + protected override Drawable CreateFlagContent(CountryStatistics item) => new OsuSpriteText + { + Font = OsuFont.GetFont(size: TEXT_SIZE), + Text = $@"{item.Country.FullName}", + }; + + protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new[] { - new OsuSpriteText - { - Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(7, 0), - Children = new Drawable[] - { - new UpdateableFlag(item.Country) - { - Size = new Vector2(20, 13), - ShowPlaceholderOnNull = false, - }, - new RowText - { - Text = $@"{item.Country.FullName}", - } - } - }, new ColoredRowText { Text = $@"{item.ActiveUsers:N0}", diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 010da07858..263f6052cc 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -3,91 +3,28 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using System.Collections.Generic; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Users.Drawables; -using osuTK; using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables { - public class PerformanceTable : RankingsTable + public class PerformanceTable : UserBasedTable { public PerformanceTable(int page = 1) : base(page) { } - protected override TableColumn[] CreateAdditionalHeaders() => new[] + protected override TableColumn[] CreateUniqueHeaders() => new[] { - new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; - protected override Drawable[] CreateContent(int index, UserStatistics item) + protected override Drawable[] CreateUniqueContent(UserStatistics item) => new[] { - var content = new List + new RowText { - new OsuSpriteText - { - Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) - }, - }; - - var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both }; - username.AddUserLink(item.User); - - content.AddRange(new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(7, 0), - Children = new Drawable[] - { - new UpdateableFlag(item.User.Country) - { - Size = new Vector2(20, 13), - ShowPlaceholderOnNull = false, - }, - username - } - }, - new ColoredRowText - { - Text = $@"{item.Accuracy:F2}%", - }, - new ColoredRowText - { - Text = $@"{item.PlayCount:N0}", - }, - new RowText - { - Text = $@"{item.PP:N0}", - }, - new ColoredRowText - { - Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", - }, - new ColoredRowText - { - Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", - }, - new ColoredRowText - { - Text = $@"{item.GradesCount.A:N0}", - }, - }); - - return content.ToArray(); - } + Text = $@"{item.PP:N0}", + } + }; } } diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 530295b039..d085b3a9a8 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -10,6 +10,9 @@ using osu.Framework.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osuTK; namespace osu.Game.Overlays.Rankings.Tables { @@ -54,10 +57,12 @@ namespace osu.Game.Overlays.Rankings.Tables value.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray(); - Content = value.Select((s, i) => CreateContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); + Content = value.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); } } + private Drawable[] createContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray(); + private static TableColumn[] mainHeaders => new[] { new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place @@ -66,10 +71,36 @@ namespace osu.Game.Overlays.Rankings.Tables protected abstract TableColumn[] CreateAdditionalHeaders(); - protected abstract Drawable[] CreateContent(int index, TModel item); + protected abstract Drawable[] CreateAdditionalContent(TModel item); protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty, HighlightedColumn()); + protected abstract Country GetCountry(TModel item); + + protected abstract Drawable CreateFlagContent(TModel item); + + private OsuSpriteText createIndexDrawable(int index) => new OsuSpriteText + { + Text = $"#{index + 1}", + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) + }; + + private FillFlowContainer createMainContent(TModel item) => new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(7, 0), + Children = new Drawable[] + { + new UpdateableFlag(GetCountry(item)) + { + Size = new Vector2(20, 13), + ShowPlaceholderOnNull = false, + }, + CreateFlagContent(item) + } + }; + protected virtual string HighlightedColumn() => @"Performance"; private class HeaderText : OsuSpriteText diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index d40da63f7f..0d4999ee8f 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -3,97 +3,34 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Users.Drawables; -using osuTK; -using System.Collections.Generic; -using osu.Game.Graphics.Containers; using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables { - public class ScoresTable : RankingsTable + public class ScoresTable : UserBasedTable { public ScoresTable(int page = 1) : base(page) { } - protected override TableColumn[] CreateAdditionalHeaders() => new[] + protected override TableColumn[] CreateUniqueHeaders() => new[] { - new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)) }; - protected override Drawable[] CreateContent(int index, UserStatistics item) + protected override Drawable[] CreateUniqueContent(UserStatistics item) => new[] { - var content = new List + new ColoredRowText { - new OsuSpriteText - { - Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) - }, - }; - - var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both }; - username.AddUserLink(item.User); - - content.AddRange(new Drawable[] + Text = $@"{item.TotalScore:N0}", + }, + new RowText { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(7, 0), - Children = new Drawable[] - { - new UpdateableFlag(item.User.Country) - { - Size = new Vector2(20, 13), - ShowPlaceholderOnNull = false, - }, - username - } - }, - new ColoredRowText - { - Text = $@"{item.Accuracy:F2}%", - }, - new ColoredRowText - { - Text = $@"{item.PlayCount:N0}", - }, - new ColoredRowText - { - Text = $@"{item.TotalScore:N0}", - }, - new RowText - { - Text = $@"{item.RankedScore:N0}", - }, - new ColoredRowText - { - Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", - }, - new ColoredRowText - { - Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", - }, - new ColoredRowText - { - Text = $@"{item.GradesCount.A:N0}", - }, - }); - - return content.ToArray(); - } + Text = $@"{item.RankedScore:N0}", + } + }; protected override string HighlightedColumn() => @"Ranked Score"; } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs new file mode 100644 index 0000000000..1283db9e32 --- /dev/null +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -0,0 +1,70 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Users; + +namespace osu.Game.Overlays.Rankings.Tables +{ + public abstract class UserBasedTable : RankingsTable + { + protected UserBasedTable(int page) + : base(page) + { + } + + protected override TableColumn[] CreateAdditionalHeaders() => new[] + { + new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + }.Concat(CreateUniqueHeaders()).Concat(new[] + { + new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + }).ToArray(); + + protected override Country GetCountry(UserStatistics item) => item.User.Country; + + protected override Drawable CreateFlagContent(UserStatistics item) + { + var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both }; + username.AddUserLink(item.User); + return username; + } + + protected override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] + { + new ColoredRowText + { + Text = $@"{item.Accuracy:F2}%", + }, + new ColoredRowText + { + Text = $@"{item.PlayCount:N0}", + }, + }.Concat(CreateUniqueContent(item)).Concat(new[] + { + new ColoredRowText + { + Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", + }, + new ColoredRowText + { + Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", + }, + new ColoredRowText + { + Text = $@"{item.GradesCount.A:N0}", + } + }).ToArray(); + + protected abstract TableColumn[] CreateUniqueHeaders(); + + protected abstract Drawable[] CreateUniqueContent(UserStatistics item); + } +} From 44cfe98278a60e0fea2ccf43caf6e4d3a52568c8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 27 Nov 2019 23:40:52 +0300 Subject: [PATCH 039/178] Simplify test scene --- .../Visual/Online/TestSceneRankingsTables.cs | 57 ++++++++----------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs index ff238803a5..5501bb54d0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs @@ -68,11 +68,7 @@ namespace osu.Game.Tests.Visual.Online private void createCountryTable(RulesetInfo ruleset, int page = 1) { - loading.Show(); - - request?.Cancel(); - cancellationToken?.Cancel(); - cancellationToken = new CancellationTokenSource(); + onLoadStarted(); request = new GetCountryRankingsRequest(ruleset, page); ((GetCountryRankingsRequest)request).Success += rankings => Schedule(() => @@ -82,12 +78,7 @@ namespace osu.Game.Tests.Visual.Online Rankings = rankings.Countries, }; - LoadComponentAsync(table, t => - { - scrollFlow.Clear(); - scrollFlow.Add(t); - loading.Hide(); - }, cancellationToken.Token); + loadTable(table); }); api.Queue(request); @@ -95,11 +86,7 @@ namespace osu.Game.Tests.Visual.Online private void createPerformanceTable(RulesetInfo ruleset, string country, int page = 1) { - loading.Show(); - - request?.Cancel(); - cancellationToken?.Cancel(); - cancellationToken = new CancellationTokenSource(); + onLoadStarted(); request = new GetUserRankingsRequest(ruleset, country: country, page: page); ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => @@ -109,12 +96,7 @@ namespace osu.Game.Tests.Visual.Online Rankings = rankings.Users, }; - LoadComponentAsync(table, t => - { - scrollFlow.Clear(); - scrollFlow.Add(t); - loading.Hide(); - }, cancellationToken.Token); + loadTable(table); }); api.Queue(request); @@ -122,11 +104,7 @@ namespace osu.Game.Tests.Visual.Online private void createScoreTable(RulesetInfo ruleset, int page = 1) { - loading.Show(); - - request?.Cancel(); - cancellationToken?.Cancel(); - cancellationToken = new CancellationTokenSource(); + onLoadStarted(); request = new GetUserRankingsRequest(ruleset, UserRankingsType.Score, page); ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => @@ -136,15 +114,28 @@ namespace osu.Game.Tests.Visual.Online Rankings = rankings.Users, }; - LoadComponentAsync(table, t => - { - scrollFlow.Clear(); - scrollFlow.Add(t); - loading.Hide(); - }, cancellationToken.Token); + loadTable(table); }); api.Queue(request); } + + private void onLoadStarted() + { + loading.Show(); + request?.Cancel(); + cancellationToken?.Cancel(); + cancellationToken = new CancellationTokenSource(); + } + + private void loadTable(Drawable table) + { + LoadComponentAsync(table, t => + { + scrollFlow.Clear(); + scrollFlow.Add(t); + loading.Hide(); + }, cancellationToken.Token); + } } } From 8077c86d13265e2d58677269d3ae457fc9ce3f86 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 27 Nov 2019 23:58:31 +0300 Subject: [PATCH 040/178] CI fixes --- osu.Game/Overlays/Rankings/Tables/CountriesTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/ScoresTable.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 208a4dbfe4..b49583f0e2 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Rankings.Tables Text = $@"{item.Country.FullName}", }; - protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new[] + protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new Drawable[] { new ColoredRowText { diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 263f6052cc..ec3252b22a 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Rankings.Tables new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; - protected override Drawable[] CreateUniqueContent(UserStatistics item) => new[] + protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { new RowText { diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index d085b3a9a8..7a20d60f6e 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -90,7 +90,7 @@ namespace osu.Game.Overlays.Rankings.Tables AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(7, 0), - Children = new Drawable[] + Children = new[] { new UpdateableFlag(GetCountry(item)) { diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 0d4999ee8f..cc3c5ccf94 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Rankings.Tables new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)) }; - protected override Drawable[] CreateUniqueContent(UserStatistics item) => new[] + protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { new ColoredRowText { From f504370867a1f221444c743a2520a686dc5459c2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Nov 2019 15:58:26 +0900 Subject: [PATCH 041/178] Make player block exit if pausable --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 7 ++++++- osu.Game/Screens/Play/Player.cs | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 6e8975f11b..803cab9325 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -115,8 +115,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestExitTooSoon() { - pauseAndConfirm(); + AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000)); + pauseAndConfirm(); resume(); AddStep("exit too soon", () => Player.Exit()); @@ -176,7 +177,9 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestExitFromGameplay() { AddStep("exit", () => Player.Exit()); + confirmPaused(); + AddStep("exit", () => Player.Exit()); confirmExited(); } @@ -214,6 +217,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestRestartAfterResume() { + AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000)); + pauseAndConfirm(); resumeAndConfirm(); restart(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7f32db2ebf..d40c448452 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -536,6 +536,12 @@ namespace osu.Game.Screens.Play return true; } + if (canPause) + { + Pause(); + return true; + } + // GameplayClockContainer performs seeks / start / stop operations on the beatmap's track. // as we are no longer the current screen, we cannot guarantee the track is still usable. GameplayClockContainer.StopUsingBeatmapClock(); From aa9776d51ad200634d87e38fea50946552bebbe6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Nov 2019 19:07:43 +0900 Subject: [PATCH 042/178] seal and compact rows --- .../Rankings/Tables/PerformanceTable.cs | 5 +-- .../Rankings/Tables/UserBasedTable.cs | 31 +++++-------------- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index ec3252b22a..f604efb8d5 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -21,10 +21,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText - { - Text = $@"{item.PP:N0}", - } + new RowText { Text = $@"{item.PP:N0}", } }; } } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 1283db9e32..6ea145be9c 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -28,39 +28,24 @@ namespace osu.Game.Overlays.Rankings.Tables new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }).ToArray(); - protected override Country GetCountry(UserStatistics item) => item.User.Country; + protected sealed override Country GetCountry(UserStatistics item) => item.User.Country; - protected override Drawable CreateFlagContent(UserStatistics item) + protected sealed override Drawable CreateFlagContent(UserStatistics item) { var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both }; username.AddUserLink(item.User); return username; } - protected override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] + protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] { - new ColoredRowText - { - Text = $@"{item.Accuracy:F2}%", - }, - new ColoredRowText - { - Text = $@"{item.PlayCount:N0}", - }, + new ColoredRowText { Text = $@"{item.Accuracy:F2}%", }, + new ColoredRowText { Text = $@"{item.PlayCount:N0}", }, }.Concat(CreateUniqueContent(item)).Concat(new[] { - new ColoredRowText - { - Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", - }, - new ColoredRowText - { - Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", - }, - new ColoredRowText - { - Text = $@"{item.GradesCount.A:N0}", - } + new ColoredRowText { Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", }, + new ColoredRowText { Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", }, + new ColoredRowText { Text = $@"{item.GradesCount.A:N0}", } }).ToArray(); protected abstract TableColumn[] CreateUniqueHeaders(); From a4f584c6a4771c0f8c40bafaa544d4e83e8a571e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Nov 2019 22:00:01 +0900 Subject: [PATCH 043/178] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f2d2af5678..b5e8c62123 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -53,6 +53,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 449b4dc4e3..143ad48c7f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,7 +21,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ff6839f5ca..105ef08c7a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -73,7 +73,7 @@ - + @@ -81,7 +81,7 @@ - + From b9a8a36e60e633e94042734d2a7dc8b81b74bfe3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 00:44:32 +0900 Subject: [PATCH 044/178] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3a6a6e9832..997bd32bac 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ef873bd3ac..3f056cacfb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 1723a6210c..b231e924ef 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From 1c4063677ddd33d04dc4f9fd34a95f4af1d3d461 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 00:45:35 +0900 Subject: [PATCH 045/178] Ignore test temporarily --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 6e8975f11b..1c9132edef 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -113,6 +113,7 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] + [Ignore("Will be resolved with merge of https://github.com/ppy/osu/pull/6992")] public void TestExitTooSoon() { pauseAndConfirm(); From bb0a4db847ae8e084233576cd39845252f331dac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 01:16:26 +0900 Subject: [PATCH 046/178] Fix failing tests --- osu.Game/Graphics/Backgrounds/Background.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 21fca5d5da..c90b1e0e98 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics.Backgrounds Sprite.Texture = textures.Get(textureName); } - public Vector2 BlurSigma => bufferedContainer?.BlurSigma ?? Vector2.Zero; + public Vector2 BlurSigma => bufferedContainer?.BlurSigma / blur_scale ?? Vector2.Zero; /// /// Smoothly adjusts over time. From e7ef919b0b12b31706f1eb9036259c663c706bb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 01:47:52 +0900 Subject: [PATCH 047/178] Ignore another failing test --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 1c9132edef..e3f1694ee5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -213,6 +213,7 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] + [Ignore("Will be resolved with merge of https://github.com/ppy/osu/pull/6992")] public void TestRestartAfterResume() { pauseAndConfirm(); From 83e3ad9e696bcbaf2dcee329efbde84c92d11f5f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 28 Nov 2019 20:09:05 +0300 Subject: [PATCH 048/178] Make Rankings a ctor variable --- .../Visual/Online/TestSceneRankingsTables.cs | 18 +++---------- .../Rankings/Tables/CountriesTable.cs | 5 ++-- .../Rankings/Tables/PerformanceTable.cs | 5 ++-- .../Overlays/Rankings/Tables/RankingsTable.cs | 25 ++++--------------- .../Overlays/Rankings/Tables/ScoresTable.cs | 5 ++-- .../Rankings/Tables/UserBasedTable.cs | 5 ++-- 6 files changed, 20 insertions(+), 43 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs index 5501bb54d0..93da2a439e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs @@ -73,11 +73,7 @@ namespace osu.Game.Tests.Visual.Online request = new GetCountryRankingsRequest(ruleset, page); ((GetCountryRankingsRequest)request).Success += rankings => Schedule(() => { - var table = new CountriesTable(page) - { - Rankings = rankings.Countries, - }; - + var table = new CountriesTable(page, rankings.Countries); loadTable(table); }); @@ -91,11 +87,7 @@ namespace osu.Game.Tests.Visual.Online request = new GetUserRankingsRequest(ruleset, country: country, page: page); ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => { - var table = new PerformanceTable(page) - { - Rankings = rankings.Users, - }; - + var table = new PerformanceTable(page, rankings.Users); loadTable(table); }); @@ -109,11 +101,7 @@ namespace osu.Game.Tests.Visual.Online request = new GetUserRankingsRequest(ruleset, UserRankingsType.Score, page); ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => { - var table = new ScoresTable(page) - { - Rankings = rankings.Users, - }; - + var table = new ScoresTable(page, rankings.Users); loadTable(table); }); diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index b49583f0e2..a0e4f694bd 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -7,13 +7,14 @@ using System; using osu.Game.Users; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; +using System.Collections.Generic; namespace osu.Game.Overlays.Rankings.Tables { public class CountriesTable : RankingsTable { - public CountriesTable(int page = 1) - : base(page) + public CountriesTable(int page, IReadOnlyList rankings) + : base(page, rankings) { } diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index f604efb8d5..1e6b2307e0 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Users; @@ -9,8 +10,8 @@ namespace osu.Game.Overlays.Rankings.Tables { public class PerformanceTable : UserBasedTable { - public PerformanceTable(int page = 1) - : base(page) + public PerformanceTable(int page, IReadOnlyList rankings) + : base(page, rankings) { } diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 7a20d60f6e..08d396d88e 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -23,12 +23,9 @@ namespace osu.Game.Overlays.Rankings.Tables private const float row_height = 25; private const int items_per_page = 50; - private readonly int page; - private readonly FillFlowContainer backgroundFlow; - - protected RankingsTable(int page) + protected RankingsTable(int page, IReadOnlyList rankings) { - this.page = page; + FillFlowContainer backgroundFlow; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -42,23 +39,11 @@ namespace osu.Game.Overlays.Rankings.Tables Depth = 1f, Margin = new MarginPadding { Top = row_height } }); - } - public IReadOnlyList Rankings - { - set - { - Content = null; - backgroundFlow.Clear(); + rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); - if (value?.Any() != true) - return; - - value.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); - - Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray(); - Content = value.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); - } + Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray(); + Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); } private Drawable[] createContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray(); diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index cc3c5ccf94..370ee506c2 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Users; @@ -9,8 +10,8 @@ namespace osu.Game.Overlays.Rankings.Tables { public class ScoresTable : UserBasedTable { - public ScoresTable(int page = 1) - : base(page) + public ScoresTable(int page, IReadOnlyList rankings) + : base(page, rankings) { } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 6ea145be9c..019a278771 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,8 +13,8 @@ namespace osu.Game.Overlays.Rankings.Tables { public abstract class UserBasedTable : RankingsTable { - protected UserBasedTable(int page) - : base(page) + protected UserBasedTable(int page, IReadOnlyList rankings) + : base(page, rankings) { } From 9a941c4f941c7b0072b0bbe46379530dace1c6c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 02:15:13 +0900 Subject: [PATCH 049/178] Update font loading to use new method --- osu.Game.Tournament/TournamentGameBase.cs | 4 +-- osu.Game/OsuGameBase.cs | 40 +++++++++++------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 6bff3fb725..6456ce2d58 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -54,8 +54,8 @@ namespace osu.Game.Tournament { Resources.AddStore(new DllResourceStore(@"osu.Game.Tournament.dll")); - Fonts.AddStore(new GlyphStore(Resources, @"Resources/Fonts/Aquatico-Regular")); - Fonts.AddStore(new GlyphStore(Resources, @"Resources/Fonts/Aquatico-Light")); + AddFont(Resources, @"Resources/Fonts/Aquatico-Regular"); + AddFont(Resources, @"Resources/Fonts/Aquatico-Light"); Textures.AddStore(new TextureLoaderStore(new ResourceStore(new StorageBackedResourceStore(storage)))); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 0845a33639..f310da3883 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -131,29 +131,29 @@ namespace osu.Game dependencies.CacheAs(this); dependencies.Cache(LocalConfig); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Medium")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-MediumItalic")); + AddFont(Resources, @"Fonts/osuFont"); + AddFont(Resources, @"Fonts/Exo2.0-Medium"); + AddFont(Resources, @"Fonts/Exo2.0-MediumItalic"); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Basic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Hangul")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Basic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Compatibility")); + AddFont(Resources, @"Fonts/Noto-Basic"); + AddFont(Resources, @"Fonts/Noto-Hangul"); + AddFont(Resources, @"Fonts/Noto-CJK-Basic"); + AddFont(Resources, @"Fonts/Noto-CJK-Compatibility"); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Regular")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-RegularItalic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBold")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBoldItalic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Bold")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BoldItalic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Light")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-LightItalic")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Black")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BlackItalic")); + AddFont(Resources, @"Fonts/Exo2.0-Regular"); + AddFont(Resources, @"Fonts/Exo2.0-RegularItalic"); + AddFont(Resources, @"Fonts/Exo2.0-SemiBold"); + AddFont(Resources, @"Fonts/Exo2.0-SemiBoldItalic"); + AddFont(Resources, @"Fonts/Exo2.0-Bold"); + AddFont(Resources, @"Fonts/Exo2.0-BoldItalic"); + AddFont(Resources, @"Fonts/Exo2.0-Light"); + AddFont(Resources, @"Fonts/Exo2.0-LightItalic"); + AddFont(Resources, @"Fonts/Exo2.0-Black"); + AddFont(Resources, @"Fonts/Exo2.0-BlackItalic"); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light")); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Medium")); + AddFont(Resources, @"Fonts/Venera"); + AddFont(Resources, @"Fonts/Venera-Light"); + AddFont(Resources, @"Fonts/Venera-Medium"); runMigrations(); From f4f54bc46b6e7dc64a7e0f651ef0fb86f0f85621 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 13:02:15 +0900 Subject: [PATCH 050/178] Fix social browser calling game-wide load stalls --- osu.Game/Users/UserCoverBackground.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Users/UserCoverBackground.cs b/osu.Game/Users/UserCoverBackground.cs index a45fd85901..748d9bd939 100644 --- a/osu.Game/Users/UserCoverBackground.cs +++ b/osu.Game/Users/UserCoverBackground.cs @@ -23,6 +23,7 @@ namespace osu.Game.Users protected override Drawable CreateDrawable(User user) => new Cover(user); + [LongRunningLoad] private class Cover : CompositeDrawable { private readonly User user; From 2663e5d756854a043ef091c66a8973f7ba9b3432 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 13:52:23 +0900 Subject: [PATCH 051/178] Add some more missing LongRunningLoad flags --- osu.Game/Overlays/MedalSplash/DrawableMedal.cs | 1 + osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs | 1 + osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs | 1 + osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs index f1ae5d64f5..a9b4bed334 100644 --- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs +++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs @@ -16,6 +16,7 @@ using osu.Game.Users; namespace osu.Game.Overlays.MedalSplash { + [LongRunningLoad] public class DrawableMedal : Container, IStateful { private const float scale_when_unlocked = 0.76f; diff --git a/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs b/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs index ea259fe49a..7eed4d3b6b 100644 --- a/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs +++ b/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs @@ -12,6 +12,7 @@ using osuTK; namespace osu.Game.Overlays.Profile.Header.Components { + [LongRunningLoad] public class DrawableBadge : CompositeDrawable, IHasTooltip { public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40); diff --git a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs index 56ff4d4dec..4563510046 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Textures; namespace osu.Game.Overlays.Profile.Sections.Recent { + [LongRunningLoad] public class MedalIcon : Container { private readonly string slug; diff --git a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs index 27cea99f1c..027c53bf58 100644 --- a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs +++ b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs @@ -370,6 +370,7 @@ namespace osu.Game.Screens.Ranking.Pages } } + [LongRunningLoad] private class UserHeader : Container { private readonly User user; From e678fe7a773336e5ee2b8b7ed2bb0579101b9748 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 15:35:33 +0900 Subject: [PATCH 052/178] Move potentially expensive load to BDL --- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 08d396d88e..f947c5585c 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -23,15 +23,25 @@ namespace osu.Game.Overlays.Rankings.Tables private const float row_height = 25; private const int items_per_page = 50; + private readonly int page; + private readonly IReadOnlyList rankings; + protected RankingsTable(int page, IReadOnlyList rankings) { - FillFlowContainer backgroundFlow; + this.page = page; + this.rankings = rankings; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Horizontal = horizontal_inset }; RowSize = new Dimension(GridSizeMode.Absolute, row_height); + } + + [BackgroundDependencyLoader] + private void load() + { + FillFlowContainer backgroundFlow; AddInternal(backgroundFlow = new FillFlowContainer { From b51ebe443199a19806a8d8a51d03d9169fc33a44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 15:50:50 +0900 Subject: [PATCH 053/178] Fix ScoreResultsPage usage --- osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs index 027c53bf58..43234c0b29 100644 --- a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs +++ b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs @@ -70,7 +70,10 @@ namespace osu.Game.Screens.Ranking.Pages Direction = FillDirection.Vertical, Children = new Drawable[] { - new UserHeader(Score.User) + new DelayedLoadWrapper(new UserHeader(Score.User) + { + RelativeSizeAxes = Axes.Both, + }) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, From 3fc2afeb2629061b887220fff116def08f7343b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 15:51:00 +0900 Subject: [PATCH 054/178] Fix UserDimContainer test failing on subsequent runs --- .../Visual/Background/TestSceneUserDimContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index f858174ff2..8f71584b4d 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -209,9 +209,10 @@ namespace osu.Game.Tests.Visual.Background public void TransitionTest() { performFullSetup(); - var results = new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } }); - AddStep("Transition to Results", () => player.Push(results)); - AddUntilStep("Wait for results is current", results.IsCurrentScreen); + FadeAccessibleResults results = null; + AddStep("Transition to Results", () => player.Push(results = + new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } }))); + AddUntilStep("Wait for results is current", () => results.IsCurrentScreen()); waitForDim(); AddAssert("Screen is undimmed, original background retained", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect()); From 5dbc32f49be13313bdc37835fbd38e6cca956113 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 16:19:57 +0900 Subject: [PATCH 055/178] Fix MedalIcon usage --- .../Profile/Sections/DrawableProfileRow.cs | 35 +++++++++---------- .../Sections/Recent/DrawableRecentActivity.cs | 7 ++-- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs index 23fe6e9cd5..03ee29d0c2 100644 --- a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs +++ b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs @@ -19,8 +19,8 @@ namespace osu.Game.Overlays.Profile.Sections private const int fade_duration = 200; private Box underscoreLine; - private readonly Box coloredBackground; - private readonly Container background; + private Box coloredBackground; + private Container background; /// /// A visual element displayed to the left of content. @@ -36,6 +36,19 @@ namespace osu.Game.Overlays.Profile.Sections { RelativeSizeAxes = Axes.X; Height = 60; + + Content = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 0.97f, + }; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colour) + { InternalChildren = new Drawable[] { background = new Container @@ -53,21 +66,7 @@ namespace osu.Game.Overlays.Profile.Sections }, Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both } }, - Content = new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Width = 0.97f, - }, - }; - } - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colour) - { - AddRange(new Drawable[] - { + Content, underscoreLine = new Box { Anchor = Anchor.BottomCentre, @@ -101,7 +100,7 @@ namespace osu.Game.Overlays.Profile.Sections Origin = Anchor.CentreRight, Direction = FillDirection.Vertical, }, - }); + }; coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; } diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index b5a508bff7..4e856845ac 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -66,11 +66,14 @@ namespace osu.Game.Overlays.Profile.Sections.Recent }; case RecentActivityType.Achievement: - return new MedalIcon(activity.Achievement.Slug) + return new DelayedLoadWrapper(new MedalIcon(activity.Achievement.Slug) + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + }) { RelativeSizeAxes = Axes.Y, Width = 60, - FillMode = FillMode.Fit, }; default: From f181ee18434cea3c22f09ae470a3bc651cb6e8d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 17:35:11 +0900 Subject: [PATCH 056/178] Hide the menu cursor while inside the playfield by default --- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 3 +++ osu.Game/Rulesets/UI/DrawableRuleset.cs | 4 ++++ osu.Game/Rulesets/UI/Playfield.cs | 18 +++++++++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index aa61fb6922..49aea52902 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; +using osuTK; namespace osu.Game.Rulesets.Osu.UI { @@ -30,6 +31,8 @@ namespace osu.Game.Rulesets.Osu.UI { } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor + public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this); protected override Playfield CreatePlayfield() => new OsuPlayfield(); diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5ed1e5a368..96275c1274 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -34,6 +34,7 @@ using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; +using osuTK; namespace osu.Game.Rulesets.UI { @@ -331,6 +332,9 @@ namespace osu.Game.Rulesets.UI protected override bool OnHover(HoverEvent e) => true; // required for IProvideCursor + // only show the cursor when within the playfield, by default. + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Playfield.ReceivePositionalInputAt(screenSpacePos); + CursorContainer IProvideCursor.Cursor => Playfield.Cursor; public override GameplayCursorContainer Cursor => Playfield.Cursor; diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index ca4983e3d7..047047ccfd 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -100,10 +100,13 @@ namespace osu.Game.Rulesets.UI public GameplayCursorContainer Cursor { get; private set; } /// - /// Provide an optional cursor which is to be used for gameplay. + /// Provide a cursor which is to be used for gameplay. /// - /// The cursor, or null if a cursor is not rqeuired. - protected virtual GameplayCursorContainer CreateCursor() => null; + /// + /// The default provided cursor is invisible when inside the bounds of the . + /// + /// The cursor, or null to show the menu cursor. + protected virtual GameplayCursorContainer CreateCursor() => new InvisibleCursorContainer(); /// /// Registers a as a nested . @@ -143,5 +146,14 @@ namespace osu.Game.Rulesets.UI /// Creates the container that will be used to contain the s. /// protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); + + public class InvisibleCursorContainer : GameplayCursorContainer + { + protected override Drawable CreateCursor() => new InvisibleCursor(); + + private class InvisibleCursor : Drawable + { + } + } } } From 4f081f2fe897ca4700696ab3e5d26a94aefb77c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 17:38:28 +0900 Subject: [PATCH 057/178] Remove catch-specific cursor provider --- .../UI/CatchCursorContainer.cs | 17 ----------------- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 3 --- 2 files changed, 20 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs diff --git a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs deleted file mode 100644 index 163718f202..0000000000 --- a/osu.Game.Rulesets.Catch/UI/CatchCursorContainer.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Catch.UI -{ - public class CatchCursorContainer : GameplayCursorContainer - { - protected override Drawable CreateCursor() => new InvisibleCursor(); - - private class InvisibleCursor : Drawable - { - } - } -} diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 7741096da2..b6d8cf9cbe 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -9,7 +9,6 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Catch.UI @@ -20,8 +19,6 @@ namespace osu.Game.Rulesets.Catch.UI internal readonly CatcherArea CatcherArea; - protected override GameplayCursorContainer CreateCursor() => new CatchCursorContainer(); - public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation) { Container explodingFruitContainer; From 3864ea6ca3ddf8d59568558448fde9597af1a44a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 18:24:46 +0900 Subject: [PATCH 058/178] Move position updating logic back to CatcherArea --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 11 ++--------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 062c0f8b26..a2554850c0 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -38,14 +38,7 @@ namespace osu.Game.Rulesets.Catch.Mods protected override bool OnMouseMove(MouseMoveEvent e) { - //lock catcher to mouse position horizontally - catcher.X = e.MousePosition.X / DrawSize.X; - - //make Yuzu face the direction he's moving - var direction = Math.Sign(e.Delta.X); - if (direction != 0) - catcher.Scale = new Vector2(Math.Abs(catcher.Scale.X) * direction, catcher.Scale.Y); - + catcher.UpdatePosition(e.MousePosition.X / DrawSize.X); return base.OnMouseMove(e); } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 49393a836f..2d6ce02e45 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -377,8 +377,7 @@ namespace osu.Game.Rulesets.Catch.UI double dashModifier = Dashing ? 1 : 0.5; double speed = BASE_SPEED * dashModifier * hyperDashModifier; - Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y); - X = (float)Math.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1); + UpdatePosition((float)(X + direction * Clock.ElapsedFrameTime * speed)); // Correct overshooting. if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) || @@ -452,6 +451,17 @@ namespace osu.Game.Rulesets.Catch.UI fruit.LifetimeStart = Time.Current; fruit.Expire(); } + + public void UpdatePosition(float position) + { + position = Math.Clamp(position, 0, 1); + + if (position == X) + return; + + Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y); + X = position; + } } } } From 63128c9465669cda7e699d608cc33aeb46b8c844 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 18:25:11 +0900 Subject: [PATCH 059/178] Extend mouse hiding in catch to include catcher area --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index b6d8cf9cbe..589503c35b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; +using osuTK; namespace osu.Game.Rulesets.Catch.UI { @@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.UI internal readonly CatcherArea CatcherArea; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || CatcherArea.ReceivePositionalInputAt(screenSpacePos); + public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation) { Container explodingFruitContainer; From c7305f0b4462cf77440d9f74dc42e37f9e7fe6ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 18:25:24 +0900 Subject: [PATCH 060/178] Simplify implementation structure --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index a2554850c0..a47efcc10a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -1,8 +1,7 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -11,7 +10,6 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osuTK; -using System; namespace osu.Game.Rulesets.Catch.Mods { @@ -19,16 +17,20 @@ namespace osu.Game.Rulesets.Catch.Mods { public override string Description => @"Use the mouse to control the catcher."; - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) => - ((Container)drawableRuleset.Playfield.Parent).Add(new CatchModRelaxHelper(drawableRuleset.Playfield as CatchPlayfield)); + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield)); + } - private class CatchModRelaxHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition + private class MouseInputHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { private readonly CatcherArea.Catcher catcher; - public CatchModRelaxHelper(CatchPlayfield catchPlayfield) + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + + public MouseInputHelper(CatchPlayfield playfield) { - catcher = catchPlayfield.CatcherArea.MovableCatcher; + catcher = playfield.CatcherArea.MovableCatcher; RelativeSizeAxes = Axes.Both; } From c49aeb08c4f2a135831f245eac39c2d6cdf976e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Nov 2019 20:03:14 +0900 Subject: [PATCH 061/178] Add API methods to perform requests out-of-queue --- osu.Game.Tournament/TournamentGameBase.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- .../DownloadableArchiveModelManager.cs | 12 +----------- osu.Game/Online/API/APIAccess.cs | 17 +++++++++++++++++ osu.Game/Online/API/DummyAPIAccess.cs | 5 +++++ osu.Game/Online/API/IAPIProvider.cs | 19 +++++++++++++++++++ .../Changelog/ChangelogSingleBuild.cs | 14 +------------- osu.Game/Overlays/ChangelogOverlay.cs | 10 +--------- 8 files changed, 46 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 6456ce2d58..4d7abfe272 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -228,7 +228,7 @@ namespace osu.Game.Tournament if (b.BeatmapInfo == null && b.ID > 0) { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); - req.Perform(API); + API.Perform(req); b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore); addedInfo = true; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index bc78e50f5d..a2e750cac5 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -398,7 +398,7 @@ namespace osu.Game.Beatmaps try { // intentionally blocking to limit web request concurrency - req.Perform(api); + api.Perform(req); var res = req.Result; diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 0b7d63f469..243060388f 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -99,17 +99,7 @@ namespace osu.Game.Database currentDownloads.Add(request); PostNotification?.Invoke(notification); - Task.Factory.StartNew(() => - { - try - { - request.Perform(api); - } - catch (Exception error) - { - triggerFailure(error); - } - }, TaskCreationOptions.LongRunning); + api.PerformAsync(request); DownloadBegan?.Invoke(request); return true; diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index b6127027cc..1c45d26afd 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Net; using System.Net.Http; using System.Threading; +using System.Threading.Tasks; using Newtonsoft.Json.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -198,6 +199,22 @@ namespace osu.Game.Online.API } } + public void Perform(APIRequest request) + { + try + { + request.Perform(this); + } + catch (Exception e) + { + // todo: fix exception handling + request.Fail(e); + } + } + + public Task PerformAsync(APIRequest request) => + Task.Factory.StartNew(() => Perform(request), TaskCreationOptions.LongRunning); + public void Login(string username, string password) { Debug.Assert(State == APIState.Offline); diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 28132765d3..7f23f9b5d5 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Users; @@ -56,6 +57,10 @@ namespace osu.Game.Online.API { } + public void Perform(APIRequest request) { } + + public Task PerformAsync(APIRequest request) => Task.CompletedTask; + public void Register(IOnlineComponent component) { Scheduler.Add(delegate { components.Add(component); }); diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 0cd41aee26..dff6d0b2ce 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading.Tasks; using osu.Framework.Bindables; using osu.Game.Users; @@ -42,6 +43,24 @@ namespace osu.Game.Online.API /// The request to perform. void Queue(APIRequest request); + /// + /// Perform a request immediately, bypassing any API state checks. + /// + /// + /// Can be used to run requests as a guest user. + /// + /// The request to perform. + void Perform(APIRequest request); + + /// + /// Perform a request immediately, bypassing any API state checks. + /// + /// + /// Can be used to run requests as a guest user. + /// + /// The request to perform. + Task PerformAsync(APIRequest request); + /// /// Register a component to receive state changes. /// diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 3297b00322..67bcb6f558 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using System.Threading; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -43,18 +42,7 @@ namespace osu.Game.Overlays.Changelog }; req.Failure += _ => complete = true; - // This is done on a separate thread to support cancellation below - Task.Run(() => - { - try - { - req.Perform(api); - } - catch - { - complete = true; - } - }); + api.PerformAsync(req); while (!complete) { diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 559989af5c..fbc9dfcbd9 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -191,15 +191,7 @@ namespace osu.Game.Overlays tcs.SetResult(false); }; - try - { - req.Perform(API); - } - catch - { - initialFetchTask = null; - tcs.SetResult(false); - } + await API.PerformAsync(req); await tcs.Task; }); From 3b88afd069fd179195d4593db439e9da9247cdaf Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 1 Dec 2019 12:02:47 -0800 Subject: [PATCH 062/178] Fix overlays closing when dragging from in/out or out/in --- .../Containers/OsuFocusedOverlayContainer.cs | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index b117d71006..4977b6b70a 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -67,33 +67,21 @@ namespace osu.Game.Graphics.Containers // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceivePositionalInputAt(screenSpacePos); - protected override bool OnClick(ClickEvent e) - { - if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) - Hide(); + private bool closeOnMouseUp; - return base.OnClick(e); + protected override bool OnMouseDown(MouseDownEvent e) + { + closeOnMouseUp = !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition) ? true : false; + + return base.OnMouseDown(e); } - private bool closeOnDragEnd; - - protected override bool OnDragStart(DragStartEvent e) + protected override bool OnMouseUp(MouseUpEvent e) { - if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) - closeOnDragEnd = true; - - return base.OnDragStart(e); - } - - protected override bool OnDragEnd(DragEndEvent e) - { - if (closeOnDragEnd) - { + if (closeOnMouseUp && !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) Hide(); - closeOnDragEnd = false; - } - return base.OnDragEnd(e); + return base.OnMouseUp(e); } public virtual bool OnPressed(GlobalAction action) From 74176a69545d569895914ab03cfa359370408850 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 1 Dec 2019 12:54:37 -0800 Subject: [PATCH 063/178] Remove redundant ternary expression --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 4977b6b70a..facf70b47a 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -71,7 +71,7 @@ namespace osu.Game.Graphics.Containers protected override bool OnMouseDown(MouseDownEvent e) { - closeOnMouseUp = !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition) ? true : false; + closeOnMouseUp = !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition); return base.OnMouseDown(e); } From 59f956bb01e550e0723355467275b17348e32fe6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2019 07:18:54 +0000 Subject: [PATCH 064/178] Bump Microsoft.CodeAnalysis.BannedApiAnalyzers from 2.9.7 to 2.9.8 Bumps [Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 2.9.7 to 2.9.8. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v2.9.7...v2.9.8) Signed-off-by: dependabot-preview[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index c8d441a78c..c0d740bac1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ - + From 6893ec22bbf7610c2736e7ba485650c5bef4f722 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Dec 2019 16:14:52 +0900 Subject: [PATCH 065/178] Fix currenTrackCompleted not being run on main thread --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e978df404f..c7c746bed3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -406,11 +406,11 @@ namespace osu.Game nextBeatmap?.LoadBeatmapAsync(); } - private void currentTrackCompleted() + private void currentTrackCompleted() => Schedule(() => { if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled) musicController.NextTrack(); - } + }); #endregion From 1ec11946c63f56467de94a303ed1be07272a1a44 Mon Sep 17 00:00:00 2001 From: recapitalverb <41869184+recapitalverb@users.noreply.github.com> Date: Mon, 2 Dec 2019 18:45:57 +0700 Subject: [PATCH 066/178] Fix spelling in comment on SearchTextBox.OnPressed --- osu.Game/Graphics/UserInterface/SearchTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index e2b0e1b425..ff3618b263 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -35,7 +35,7 @@ namespace osu.Game.Graphics.UserInterface public override bool OnPressed(PlatformAction action) { // Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox - // as we do not allow arrow key navigation in the first place (ie. the care should always be at the end of text) + // as we do not allow arrow key navigation in the first place (ie. the caret should always be at the end of text) // Avoid handling it here to allow other components to potentially consume the shortcut. if (action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete) return false; From aadbbb1af3c100e8145332384b8b07bb5c4bcff9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Dec 2019 13:33:42 +0900 Subject: [PATCH 067/178] Fix replay download button not working --- osu.Game.Tests/Scores/IO/TestScoreEquality.cs | 73 +++++++++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 21 ++++-- 2 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 osu.Game.Tests/Scores/IO/TestScoreEquality.cs diff --git a/osu.Game.Tests/Scores/IO/TestScoreEquality.cs b/osu.Game.Tests/Scores/IO/TestScoreEquality.cs new file mode 100644 index 0000000000..d1374eb6e5 --- /dev/null +++ b/osu.Game.Tests/Scores/IO/TestScoreEquality.cs @@ -0,0 +1,73 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Scoring; + +namespace osu.Game.Tests.Scores.IO +{ + [TestFixture] + public class TestScoreEquality + { + [Test] + public void TestNonMatchingByReference() + { + ScoreInfo score1 = new ScoreInfo(); + ScoreInfo score2 = new ScoreInfo(); + + Assert.That(score1, Is.Not.EqualTo(score2)); + } + + [Test] + public void TestMatchingByReference() + { + ScoreInfo score = new ScoreInfo(); + + Assert.That(score, Is.EqualTo(score)); + } + + [Test] + public void TestNonMatchingByPrimaryKey() + { + ScoreInfo score1 = new ScoreInfo { ID = 1 }; + ScoreInfo score2 = new ScoreInfo { ID = 2 }; + + Assert.That(score1, Is.Not.EqualTo(score2)); + } + + [Test] + public void TestMatchingByPrimaryKey() + { + ScoreInfo score1 = new ScoreInfo { ID = 1 }; + ScoreInfo score2 = new ScoreInfo { ID = 1 }; + + Assert.That(score1, Is.EqualTo(score2)); + } + + [Test] + public void TestNonMatchingByHash() + { + ScoreInfo score1 = new ScoreInfo { Hash = "a" }; + ScoreInfo score2 = new ScoreInfo { Hash = "b" }; + + Assert.That(score1, Is.Not.EqualTo(score2)); + } + + [Test] + public void TestMatchingByHash() + { + ScoreInfo score1 = new ScoreInfo { Hash = "a" }; + ScoreInfo score2 = new ScoreInfo { Hash = "a" }; + + Assert.That(score1, Is.EqualTo(score2)); + } + + [Test] + public void TestNonMatchingByNull() + { + ScoreInfo score = new ScoreInfo(); + + Assert.That(score, Is.Not.EqualTo(null)); + } + } +} diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index f7bac82e74..c7609e8a0b 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -183,10 +183,21 @@ namespace osu.Game.Scoring public override string ToString() => $"{User} playing {Beatmap}"; - public bool Equals(ScoreInfo other) => - other != null - && other.OnlineScoreID == OnlineScoreID - && other.BeatmapInfoID == BeatmapInfoID - && other.Hash == Hash; + public bool Equals(ScoreInfo other) + { + if (other == null) + return false; + + if (ID != 0 && other.ID != 0) + return ID == other.ID; + + if (OnlineScoreID.HasValue && other.OnlineScoreID.HasValue) + return OnlineScoreID == other.OnlineScoreID; + + if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash)) + return Hash == other.Hash; + + return ReferenceEquals(this, other); + } } } From f0d49d0cdf3283f8d1680ab118adc3f0f5e4315d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Dec 2019 15:28:10 +0900 Subject: [PATCH 068/178] Decouple APILegacyScoreInfo from ScoreInfo --- .../Difficulty/CatchPerformanceCalculator.cs | 10 +- .../Gameplay/TestSceneReplayDownloadButton.cs | 11 +- .../Visual/Online/TestSceneScoresContainer.cs | 50 +++-- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 3 +- .../TestSceneUserTopScoreContainer.cs | 3 +- .../Online/API/Requests/GetScoresRequest.cs | 7 +- .../Requests/Responses/APILegacyScoreInfo.cs | 161 ++++++++-------- .../BeatmapSet/Scores/ScoresContainer.cs | 14 +- .../Sections/Ranks/PaginatedScoreContainer.cs | 12 +- osu.Game/Scoring/Legacy/LegacyScoreInfo.cs | 177 ++++++++++-------- osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 14 +- .../Ranking/Pages/ReplayDownloadButton.cs | 3 +- .../Select/Leaderboards/BeatmapLeaderboard.cs | 5 +- .../Leaderboards/UserTopScoreContainer.cs | 11 +- 14 files changed, 254 insertions(+), 227 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 3c237c86be..a6283eb7c4 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -34,12 +34,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty { mods = Score.Mods; - var legacyScore = Score as LegacyScoreInfo; - - fruitsHit = legacyScore?.Count300 ?? Score.Statistics[HitResult.Perfect]; - ticksHit = legacyScore?.Count100 ?? 0; - tinyTicksHit = legacyScore?.Count50 ?? 0; - tinyTicksMissed = legacyScore?.CountKatu ?? 0; + fruitsHit = Score?.GetCount300() ?? Score.Statistics[HitResult.Perfect]; + ticksHit = Score?.GetCount100() ?? 0; + tinyTicksHit = Score?.GetCount50() ?? 0; + tinyTicksMissed = Score?.GetCountKatu() ?? 0; misses = Score.Statistics[HitResult.Miss]; // Don't count scores made with supposedly unranked mods diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 7b22fedbd5..8cb44de8cb 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -5,11 +5,12 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Users; using System; using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Game.Rulesets; using osu.Game.Screens.Ranking.Pages; namespace osu.Game.Tests.Visual.Gameplay @@ -17,6 +18,9 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneReplayDownloadButton : OsuTestScene { + [Resolved] + private RulesetStore rulesets { get; set; } + public override IReadOnlyList RequiredTypes => new[] { typeof(ReplayDownloadButton) @@ -49,16 +53,15 @@ namespace osu.Game.Tests.Visual.Gameplay { return new APILegacyScoreInfo { - ID = 1, OnlineScoreID = 2553163309, - Ruleset = new OsuRuleset().RulesetInfo, + OnlineRulesetID = 0, Replay = replayAvailable, User = new User { Id = 39828, Username = @"WubWoofWolf", } - }; + }.CreateScoreInfo(rulesets); } private class TestReplayDownloadButton : ReplayDownloadButton diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index b26de1984a..3386cc41e7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -9,9 +9,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.MathUtils; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet.Scores; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Users; using osuTK.Graphics; @@ -66,12 +64,12 @@ namespace osu.Game.Tests.Visual.Online FlagName = @"ES", }, }, - Mods = new Mod[] + Mods = new[] { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), - new OsuModHardRock(), + new OsuModDoubleTime().Acronym, + new OsuModHidden().Acronym, + new OsuModFlashlight().Acronym, + new OsuModHardRock().Acronym, }, Rank = ScoreRank.XH, PP = 200, @@ -91,11 +89,11 @@ namespace osu.Game.Tests.Visual.Online FlagName = @"BR", }, }, - Mods = new Mod[] + Mods = new[] { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), + new OsuModDoubleTime().Acronym, + new OsuModHidden().Acronym, + new OsuModFlashlight().Acronym, }, Rank = ScoreRank.S, PP = 190, @@ -115,10 +113,10 @@ namespace osu.Game.Tests.Visual.Online FlagName = @"JP", }, }, - Mods = new Mod[] + Mods = new[] { - new OsuModDoubleTime(), - new OsuModHidden(), + new OsuModDoubleTime().Acronym, + new OsuModHidden().Acronym, }, Rank = ScoreRank.B, PP = 180, @@ -138,9 +136,9 @@ namespace osu.Game.Tests.Visual.Online FlagName = @"CA", }, }, - Mods = new Mod[] + Mods = new[] { - new OsuModDoubleTime(), + new OsuModDoubleTime().Acronym, }, Rank = ScoreRank.C, PP = 170, @@ -208,12 +206,12 @@ namespace osu.Game.Tests.Visual.Online FlagName = @"ES", }, }, - Mods = new Mod[] + Mods = new[] { - new OsuModDoubleTime(), - new OsuModHidden(), - new OsuModFlashlight(), - new OsuModHardRock(), + new OsuModDoubleTime().Acronym, + new OsuModHidden().Acronym, + new OsuModFlashlight().Acronym, + new OsuModHardRock().Acronym, }, Rank = ScoreRank.XH, PP = 200, @@ -226,10 +224,10 @@ namespace osu.Game.Tests.Visual.Online foreach (var s in allScores.Scores) { - s.Statistics.Add(HitResult.Great, RNG.Next(2000)); - s.Statistics.Add(HitResult.Good, RNG.Next(2000)); - s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); - s.Statistics.Add(HitResult.Miss, RNG.Next(2000)); + s.Statistics.Add("count_300", RNG.Next(2000)); + s.Statistics.Add("count_100", RNG.Next(2000)); + s.Statistics.Add("count_50", RNG.Next(2000)); + s.Statistics.Add("count_miss", RNG.Next(2000)); } AddStep("Load all scores", () => diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index fb27ec7654..57e297bcd5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; @@ -62,7 +61,7 @@ namespace osu.Game.Tests.Visual.SongSelect Accuracy = 1, MaxCombo = 244, TotalScore = 1707827, - Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + Mods = new[] { new OsuModHidden().Acronym, new OsuModHardRock().Acronym, }, User = new User { Id = 6602580, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs index 7fac45e0f1..e34e1844ce 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Shapes; using osuTK.Graphics; using osu.Game.Online.API.Requests.Responses; using osu.Game.Scoring; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; @@ -52,7 +51,7 @@ namespace osu.Game.Tests.Visual.SongSelect Accuracy = 1, MaxCombo = 244, TotalScore = 1707827, - Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, + Mods = new[] { new OsuModHidden().Acronym, new OsuModHardRock().Acronym, }, User = new User { Id = 6602580, diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 50844fa256..bf3441d2a0 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -9,6 +9,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets.Mods; using System.Text; using System.Collections.Generic; +using System.Diagnostics; namespace osu.Game.Online.API.Requests { @@ -37,10 +38,12 @@ namespace osu.Game.Online.API.Requests private void onSuccess(APILegacyScores r) { + Debug.Assert(ruleset.ID != null, "ruleset.ID != null"); + foreach (APILegacyScoreInfo score in r.Scores) { score.Beatmap = beatmap; - score.Ruleset = ruleset; + score.OnlineRulesetID = ruleset.ID.Value; } var userScore = r.UserScore; @@ -48,7 +51,7 @@ namespace osu.Game.Online.API.Requests if (userScore != null) { userScore.Score.Beatmap = beatmap; - userScore.Score.Ruleset = ruleset; + userScore.Score.OnlineRulesetID = ruleset.ID.Value; } } diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 17da255873..d231b8a5df 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -5,56 +5,103 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Users; namespace osu.Game.Online.API.Requests.Responses { - public class APILegacyScoreInfo : LegacyScoreInfo + public class APILegacyScoreInfo { - [JsonProperty(@"score")] - private int totalScore + public ScoreInfo CreateScoreInfo(RulesetStore rulesets) { - set => TotalScore = value; + var ruleset = rulesets.GetRuleset(OnlineRulesetID); + + var mods = Mods != null ? ruleset.CreateInstance().GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty(); + + var scoreInfo = new ScoreInfo + { + TotalScore = TotalScore, + MaxCombo = MaxCombo, + User = User, + Accuracy = Accuracy, + OnlineScoreID = OnlineScoreID, + Date = Date, + PP = PP, + Beatmap = Beatmap, + RulesetID = OnlineRulesetID, + Hash = "online", // todo: temporary? + Rank = Rank, + Ruleset = ruleset, + Mods = mods, + }; + + foreach (var kvp in Statistics) + { + switch (kvp.Key) + { + case @"count_geki": + scoreInfo.SetCountGeki(kvp.Value); + break; + + case @"count_300": + scoreInfo.SetCount300(kvp.Value); + break; + + case @"count_katu": + scoreInfo.SetCountKatu(kvp.Value); + break; + + case @"count_100": + scoreInfo.SetCount100(kvp.Value); + break; + + case @"count_50": + scoreInfo.SetCount50(kvp.Value); + break; + + case @"count_miss": + scoreInfo.SetCountMiss(kvp.Value); + break; + } + } + + return scoreInfo; } + [JsonProperty(@"score")] + public int TotalScore { get; set; } + [JsonProperty(@"max_combo")] - private int maxCombo - { - set => MaxCombo = value; - } + public int MaxCombo { get; set; } [JsonProperty(@"user")] - private User user - { - set => User = value; - } + public User User { get; set; } [JsonProperty(@"id")] - private long onlineScoreID - { - set => OnlineScoreID = value; - } + public long OnlineScoreID { get; set; } [JsonProperty(@"replay")] public bool Replay { get; set; } [JsonProperty(@"created_at")] - private DateTimeOffset date - { - set => Date = value; - } + public DateTimeOffset Date { get; set; } [JsonProperty(@"beatmap")] - private BeatmapInfo beatmap - { - set => Beatmap = value; - } + public BeatmapInfo Beatmap { get; set; } + + [JsonProperty("accuracy")] + public double Accuracy { get; set; } + + [JsonProperty(@"pp")] + public double? PP { get; set; } [JsonProperty(@"beatmapset")] - private BeatmapMetadata metadata + public BeatmapMetadata Metadata { set { @@ -67,68 +114,16 @@ namespace osu.Game.Online.API.Requests.Responses } [JsonProperty(@"statistics")] - private Dictionary jsonStats - { - set - { - foreach (var kvp in value) - { - switch (kvp.Key) - { - case @"count_geki": - CountGeki = kvp.Value; - break; - - case @"count_300": - Count300 = kvp.Value; - break; - - case @"count_katu": - CountKatu = kvp.Value; - break; - - case @"count_100": - Count100 = kvp.Value; - break; - - case @"count_50": - Count50 = kvp.Value; - break; - - case @"count_miss": - CountMiss = kvp.Value; - break; - - default: - continue; - } - } - } - } + public Dictionary Statistics { get; set; } [JsonProperty(@"mode_int")] - public int OnlineRulesetID - { - get => RulesetID; - set => RulesetID = value; - } + public int OnlineRulesetID { get; set; } [JsonProperty(@"mods")] - private string[] modStrings { get; set; } + public string[] Mods { get; set; } - public override RulesetInfo Ruleset - { - get => base.Ruleset; - set - { - base.Ruleset = value; - - if (modStrings != null) - { - // Evaluate the mod string - Mods = Ruleset.CreateInstance().GetAllMods().Where(mod => modStrings.Contains(mod.Acronym)).ToArray(); - } - } - } + [JsonProperty("rank")] + [JsonConverter(typeof(StringEnumConverter))] + public ScoreRank Rank { get; set; } } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index f06f2ac802..0378d364b8 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -41,6 +41,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores [Resolved] private IAPIProvider api { get; set; } + [Resolved] + private RulesetStore rulesets { get; set; } + private GetScoresRequest getScoresRequest; protected APILegacyScores Scores @@ -56,16 +59,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores return; } - scoreTable.Scores = value.Scores; + var scoreInfos = value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToList(); + + scoreTable.Scores = scoreInfos; scoreTable.Show(); - var topScore = value.Scores.First(); + var topScore = scoreInfos.First(); var userScore = value.UserScore; + var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets); topScoresContainer.Add(new DrawableTopScore(topScore)); - if (userScore != null && userScore.Score.OnlineScoreID != topScore.OnlineScoreID) - topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position)); + if (userScoreInfo != null && userScoreInfo.OnlineScoreID != topScore.OnlineScoreID) + topScoresContainer.Add(new DrawableTopScore(userScoreInfo, userScore.Position)); }); } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 853b9db0a7..5b58fc0930 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -29,14 +29,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks ItemsContainer.Direction = FillDirection.Vertical; } - protected override void UpdateItems(List items) - { - foreach (var item in items) - item.Ruleset = Rulesets.GetRuleset(item.RulesetID); - - base.UpdateItems(items); - } - protected override APIRequest> CreateRequest() => new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); @@ -45,10 +37,10 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks switch (type) { default: - return new DrawablePerformanceScore(model, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); + return new DrawablePerformanceScore(model.CreateScoreInfo(Rulesets), includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); case ScoreType.Recent: - return new DrawableTotalScore(model); + return new DrawableTotalScore(model.CreateScoreInfo(Rulesets)); } } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs b/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs index e66f93ec8d..2ee450b501 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs @@ -5,114 +5,139 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Scoring.Legacy { - public class LegacyScoreInfo : ScoreInfo + public static class ScoreInfoLegacyExtensions { - private int countGeki; - - public int CountGeki + public static int? GetCountGeki(this ScoreInfo scoreInfo) { - get => countGeki; - set + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { - countGeki = value; + case 3: + return scoreInfo.Statistics[HitResult.Perfect]; + } - switch (Ruleset?.ID ?? RulesetID) - { - case 3: - Statistics[HitResult.Perfect] = value; - break; - } + return null; + } + + public static void SetCountGeki(this ScoreInfo scoreInfo, int value) + { + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) + { + case 3: + scoreInfo.Statistics[HitResult.Perfect] = value; + break; } } - private int count300; - - public int Count300 + public static int? GetCount300(this ScoreInfo scoreInfo) { - get => count300; - set + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { - count300 = value; + case 0: + case 1: + case 3: + return scoreInfo.Statistics[HitResult.Great]; - switch (Ruleset?.ID ?? RulesetID) - { - case 0: - case 1: - case 3: - Statistics[HitResult.Great] = value; - break; + case 2: + return scoreInfo.Statistics[HitResult.Perfect]; + } - case 2: - Statistics[HitResult.Perfect] = value; - break; - } + return null; + } + + public static void SetCount300(this ScoreInfo scoreInfo, int value) + { + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) + { + case 0: + case 1: + case 3: + scoreInfo.Statistics[HitResult.Great] = value; + break; + + case 2: + scoreInfo.Statistics[HitResult.Perfect] = value; + break; } } - private int countKatu; - - public int CountKatu + public static int? GetCountKatu(this ScoreInfo scoreInfo) { - get => countKatu; - set + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { - countKatu = value; + case 3: + return scoreInfo.Statistics[HitResult.Good]; + } - switch (Ruleset?.ID ?? RulesetID) - { - case 3: - Statistics[HitResult.Good] = value; - break; - } + return null; + } + + public static void SetCountKatu(this ScoreInfo scoreInfo, int value) + { + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) + { + case 3: + scoreInfo.Statistics[HitResult.Good] = value; + break; } } - private int count100; - - public int Count100 + public static int? GetCount100(this ScoreInfo scoreInfo) { - get => count100; - set + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { - count100 = value; + case 0: + case 1: + return scoreInfo.Statistics[HitResult.Good]; - switch (Ruleset?.ID ?? RulesetID) - { - case 0: - case 1: - Statistics[HitResult.Good] = value; - break; + case 3: + return scoreInfo.Statistics[HitResult.Ok]; + } - case 3: - Statistics[HitResult.Ok] = value; - break; - } + return null; + } + + public static void SetCount100(this ScoreInfo scoreInfo, int value) + { + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) + { + case 0: + case 1: + scoreInfo.Statistics[HitResult.Good] = value; + break; + + case 3: + scoreInfo.Statistics[HitResult.Ok] = value; + break; } } - private int count50; - - public int Count50 + public static int? GetCount50(this ScoreInfo scoreInfo) { - get => count50; - set + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) { - count50 = value; + case 0: + case 3: + return scoreInfo.Statistics[HitResult.Meh]; + } - switch (Ruleset?.ID ?? RulesetID) - { - case 0: - case 3: - Statistics[HitResult.Meh] = value; - break; - } + return null; + } + + public static void SetCount50(this ScoreInfo scoreInfo, int value) + { + switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID) + { + case 0: + case 3: + scoreInfo.Statistics[HitResult.Meh] = value; + break; } } - public int CountMiss - { - get => Statistics[HitResult.Miss]; - set => Statistics[HitResult.Miss] = value; - } + public static int? GetCountMiss(this ScoreInfo scoreInfo) => + scoreInfo.Statistics[HitResult.Miss]; + + public static void SetCountMiss(this ScoreInfo scoreInfo, int value) => + scoreInfo.Statistics[HitResult.Miss] = value; } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index 2cdd0c4b5e..0029c843b4 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -35,7 +35,7 @@ namespace osu.Game.Scoring.Legacy using (SerializationReader sr = new SerializationReader(stream)) { currentRuleset = GetRuleset(sr.ReadByte()); - var scoreInfo = new LegacyScoreInfo { Ruleset = currentRuleset.RulesetInfo }; + var scoreInfo = new ScoreInfo { Ruleset = currentRuleset.RulesetInfo }; score.ScoreInfo = scoreInfo; @@ -53,12 +53,12 @@ namespace osu.Game.Scoring.Legacy // MD5Hash sr.ReadString(); - scoreInfo.Count300 = sr.ReadUInt16(); - scoreInfo.Count100 = sr.ReadUInt16(); - scoreInfo.Count50 = sr.ReadUInt16(); - scoreInfo.CountGeki = sr.ReadUInt16(); - scoreInfo.CountKatu = sr.ReadUInt16(); - scoreInfo.CountMiss = sr.ReadUInt16(); + scoreInfo.SetCount300(sr.ReadUInt16()); + scoreInfo.SetCount100(sr.ReadUInt16()); + scoreInfo.SetCount50(sr.ReadUInt16()); + scoreInfo.SetCountGeki(sr.ReadUInt16()); + scoreInfo.SetCountKatu(sr.ReadUInt16()); + scoreInfo.SetCountMiss(sr.ReadUInt16()); scoreInfo.TotalScore = sr.ReadInt32(); scoreInfo.MaxCombo = sr.ReadUInt16(); diff --git a/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs index 73c647d6fa..9cc6ea2628 100644 --- a/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Scoring; using osuTK; @@ -24,7 +23,7 @@ namespace osu.Game.Screens.Ranking.Pages if (State.Value == DownloadState.LocallyAvailable) return ReplayAvailability.Local; - if (Model.Value is APILegacyScoreInfo apiScore && apiScore.Replay) + if (!string.IsNullOrEmpty(Model.Value.Hash)) return ReplayAvailability.Online; return ReplayAvailability.NotAvailable; diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 3ef1fe5bc5..1b45a9d270 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -21,6 +21,9 @@ namespace osu.Game.Screens.Select.Leaderboards { public Action ScoreSelected; + [Resolved] + private RulesetStore rulesets { get; set; } + private BeatmapInfo beatmap; public BeatmapInfo Beatmap @@ -172,7 +175,7 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { - scoresCallback?.Invoke(r.Scores); + scoresCallback?.Invoke(r.Scores.Select(s => s.CreateScoreInfo(rulesets))); TopScore = r.UserScore; }; diff --git a/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs b/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs index a787eb5629..8e10734454 100644 --- a/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs +++ b/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs @@ -3,6 +3,7 @@ using System; using System.Threading; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -10,6 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; +using osu.Game.Rulesets; using osu.Game.Scoring; using osuTK; @@ -27,6 +29,9 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool StartHidden => true; + [Resolved] + private RulesetStore rulesets { get; set; } + public UserTopScoreContainer() { RelativeSizeAxes = Axes.X; @@ -77,9 +82,11 @@ namespace osu.Game.Screens.Select.Leaderboards if (newScore == null) return; - LoadComponentAsync(new LeaderboardScore(newScore.Score, newScore.Position, false) + var scoreInfo = newScore.Score.CreateScoreInfo(rulesets); + + LoadComponentAsync(new LeaderboardScore(scoreInfo, newScore.Position, false) { - Action = () => ScoreSelected?.Invoke(newScore.Score) + Action = () => ScoreSelected?.Invoke(scoreInfo) }, drawableScore => { scoreContainer.Child = drawableScore; From 1ce6a5ceb36bf0a9d412c8bca374eeddafff3fef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Dec 2019 15:38:57 +0900 Subject: [PATCH 069/178] Rename class --- .../Legacy/{LegacyScoreInfo.cs => ScoreInfoExtensions.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game/Scoring/Legacy/{LegacyScoreInfo.cs => ScoreInfoExtensions.cs} (98%) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs similarity index 98% rename from osu.Game/Scoring/Legacy/LegacyScoreInfo.cs rename to osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index 2ee450b501..66b1acf591 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreInfo.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -5,7 +5,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Scoring.Legacy { - public static class ScoreInfoLegacyExtensions + public static class ScoreInfoExtensions { public static int? GetCountGeki(this ScoreInfo scoreInfo) { From e2591f154ba7b8d821fb6186da44d17506c91a16 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Dec 2019 16:16:27 +0900 Subject: [PATCH 070/178] Only parse statistics when not null --- .../Requests/Responses/APILegacyScoreInfo.cs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index d231b8a5df..79ce04ed66 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -40,33 +40,36 @@ namespace osu.Game.Online.API.Requests.Responses Mods = mods, }; - foreach (var kvp in Statistics) + if (Statistics != null) { - switch (kvp.Key) + foreach (var kvp in Statistics) { - case @"count_geki": - scoreInfo.SetCountGeki(kvp.Value); - break; + switch (kvp.Key) + { + case @"count_geki": + scoreInfo.SetCountGeki(kvp.Value); + break; - case @"count_300": - scoreInfo.SetCount300(kvp.Value); - break; + case @"count_300": + scoreInfo.SetCount300(kvp.Value); + break; - case @"count_katu": - scoreInfo.SetCountKatu(kvp.Value); - break; + case @"count_katu": + scoreInfo.SetCountKatu(kvp.Value); + break; - case @"count_100": - scoreInfo.SetCount100(kvp.Value); - break; + case @"count_100": + scoreInfo.SetCount100(kvp.Value); + break; - case @"count_50": - scoreInfo.SetCount50(kvp.Value); - break; + case @"count_50": + scoreInfo.SetCount50(kvp.Value); + break; - case @"count_miss": - scoreInfo.SetCountMiss(kvp.Value); - break; + case @"count_miss": + scoreInfo.SetCountMiss(kvp.Value); + break; + } } } From a225a35f91cd8030234a72dd33b37d4585c180bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Dec 2019 16:18:36 +0900 Subject: [PATCH 071/178] Fix failing tests --- .../Visual/Online/TestSceneScoresContainer.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 3386cc41e7..b19f2dbf31 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -224,10 +224,13 @@ namespace osu.Game.Tests.Visual.Online foreach (var s in allScores.Scores) { - s.Statistics.Add("count_300", RNG.Next(2000)); - s.Statistics.Add("count_100", RNG.Next(2000)); - s.Statistics.Add("count_50", RNG.Next(2000)); - s.Statistics.Add("count_miss", RNG.Next(2000)); + s.Statistics = new Dictionary + { + { "count_300", RNG.Next(2000) }, + { "count_100", RNG.Next(2000) }, + { "count_50", RNG.Next(2000) }, + { "count_miss", RNG.Next(2000) } + }; } AddStep("Load all scores", () => From a42f9447e629f81ec61d2d05e52a1f3e6e97ac07 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 Dec 2019 20:07:22 +0800 Subject: [PATCH 072/178] Don't use Equals(object) on T?. --- CodeAnalysis/BannedSymbols.txt | 1 + osu.Game/Rulesets/Objects/SliderPath.cs | 2 +- osu.Game/Screens/Select/FilterCriteria.cs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 5d86b99fd7..a92191a439 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -1,5 +1,6 @@ M:System.Object.Equals(System.Object,System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable or EqualityComparer.Default instead. M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable or EqualityComparer.Default instead. M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable or EqualityComparer.Default instead. +M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 8161baba70..bd234675cb 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -280,7 +280,7 @@ namespace osu.Game.Rulesets.Objects if (other.ControlPoints == null && ControlPoints != null) return false; - return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance.Equals(other.ExpectedDistance) && Type == other.Type; + return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance == other.ExpectedDistance && Type == other.Type; } } } diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index c2cbac905e..7971506fa3 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -79,8 +79,8 @@ namespace osu.Game.Screens.Select public bool IsUpperInclusive; public bool Equals(OptionalRange other) - => Min.Equals(other.Min) - && Max.Equals(other.Max) + => EqualityComparer.Default.Equals(Min, other.Min) + && EqualityComparer.Default.Equals(Max, other.Max) && IsLowerInclusive.Equals(other.IsLowerInclusive) && IsUpperInclusive.Equals(other.IsUpperInclusive); } From 05cfef92f9d1827bacbaaaee21491c809518f2ef Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 Dec 2019 20:09:58 +0800 Subject: [PATCH 073/178] Don't compare spans with null. --- osu.Game/Rulesets/Objects/SliderPath.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index bd234675cb..ae6aad5b9c 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -273,14 +273,6 @@ namespace osu.Game.Rulesets.Objects return p0 + (p1 - p0) * (float)w; } - public bool Equals(SliderPath other) - { - if (ControlPoints == null && other.ControlPoints != null) - return false; - if (other.ControlPoints == null && ControlPoints != null) - return false; - - return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance == other.ExpectedDistance && Type == other.Type; - } + public bool Equals(SliderPath other) => ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance == other.ExpectedDistance && Type == other.Type; } } From 5375af782051327afd704b60447e9af974ae8e44 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 Dec 2019 20:16:41 +0800 Subject: [PATCH 074/178] Remove other Equals(object) calls. --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 2 +- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 7351187ab9..39a0e6f6d4 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -25,6 +25,6 @@ namespace osu.Game.Beatmaps.ControlPoints /// Whether equivalent. public abstract bool EquivalentTo(ControlPoint other); - public bool Equals(ControlPoint other) => Time.Equals(other?.Time) && EquivalentTo(other); + public bool Equals(ControlPoint other) => Time == other?.Time && EquivalentTo(other); } } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index b144de35c5..ef86186e41 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -240,6 +240,6 @@ namespace osu.Game.Tests.Beatmaps set => Objects = value; } - public virtual bool Equals(ConvertMapping other) => StartTime.Equals(other?.StartTime); + public virtual bool Equals(ConvertMapping other) => StartTime == other?.StartTime; } } From 9ddfdab27a7d1a2ea9e050f8cf140e83497b086a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 Dec 2019 20:47:00 +0800 Subject: [PATCH 075/178] Remove meaningless Convert calls. --- .../Difficulty/ManiaPerformanceCalculator.cs | 12 ++++++------ .../Difficulty/OsuPerformanceCalculator.cs | 8 ++++---- .../Difficulty/TaikoPerformanceCalculator.cs | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index b99bddee96..3f7a2baedd 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -37,12 +37,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty { mods = Score.Mods; scaledScore = Score.TotalScore; - countPerfect = Convert.ToInt32(Score.Statistics[HitResult.Perfect]); - countGreat = Convert.ToInt32(Score.Statistics[HitResult.Great]); - countGood = Convert.ToInt32(Score.Statistics[HitResult.Good]); - countOk = Convert.ToInt32(Score.Statistics[HitResult.Ok]); - countMeh = Convert.ToInt32(Score.Statistics[HitResult.Meh]); - countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]); + countPerfect = Score.Statistics[HitResult.Perfect]; + countGreat = Score.Statistics[HitResult.Great]; + countGood = Score.Statistics[HitResult.Good]; + countOk = Score.Statistics[HitResult.Ok]; + countMeh = Score.Statistics[HitResult.Meh]; + countMiss = Score.Statistics[HitResult.Miss]; if (mods.Any(m => !m.Ranked)) return 0; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 05c78cbc95..ce8ecf02ac 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -45,10 +45,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty mods = Score.Mods; accuracy = Score.Accuracy; scoreMaxCombo = Score.MaxCombo; - countGreat = Convert.ToInt32(Score.Statistics[HitResult.Great]); - countGood = Convert.ToInt32(Score.Statistics[HitResult.Good]); - countMeh = Convert.ToInt32(Score.Statistics[HitResult.Meh]); - countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]); + countGreat = Score.Statistics[HitResult.Great]; + countGood = Score.Statistics[HitResult.Good]; + countMeh = Score.Statistics[HitResult.Meh]; + countMiss = Score.Statistics[HitResult.Miss]; // Don't count scores made with supposedly unranked mods if (mods.Any(m => !m.Ranked)) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index c3638253e4..ead70c4b0a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -31,10 +31,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public override double Calculate(Dictionary categoryDifficulty = null) { mods = Score.Mods; - countGreat = Convert.ToInt32(Score.Statistics[HitResult.Great]); - countGood = Convert.ToInt32(Score.Statistics[HitResult.Good]); - countMeh = Convert.ToInt32(Score.Statistics[HitResult.Meh]); - countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]); + countGreat = Score.Statistics[HitResult.Great]; + countGood = Score.Statistics[HitResult.Good]; + countMeh =Score.Statistics[HitResult.Meh]; + countMiss = Score.Statistics[HitResult.Miss]; // Don't count scores made with supposedly unranked mods if (mods.Any(m => !m.Ranked)) From 46c9bdcf6289c32586f2c14f1bb192e4a9b974c9 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 Dec 2019 20:49:41 +0800 Subject: [PATCH 076/178] Replace Convert.ChangeType with IConvertible. --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 7412224f6c..563dc2dad9 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -151,18 +151,18 @@ namespace osu.Game.Graphics.UserInterface private void updateTooltipText(T value) { if (CurrentNumber.IsInteger) - TooltipText = ((int)Convert.ChangeType(value, typeof(int))).ToString("N0"); + TooltipText = value.ToInt32(NumberFormatInfo.InvariantInfo).ToString("N0"); else { - double floatValue = (double)Convert.ChangeType(value, typeof(double)); - double floatMinValue = (double)Convert.ChangeType(CurrentNumber.MinValue, typeof(double)); - double floatMaxValue = (double)Convert.ChangeType(CurrentNumber.MaxValue, typeof(double)); + double floatValue = value.ToDouble(NumberFormatInfo.InvariantInfo); + double floatMinValue = CurrentNumber.MinValue.ToDouble(NumberFormatInfo.InvariantInfo); + double floatMaxValue = CurrentNumber.MaxValue.ToDouble(NumberFormatInfo.InvariantInfo); if (floatMaxValue == 1 && floatMinValue >= -1) TooltipText = floatValue.ToString("P0"); else { - var decimalPrecision = normalise((decimal)Convert.ChangeType(CurrentNumber.Precision, typeof(decimal)), max_decimal_digits); + var decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits); // Find the number of significant digits (we could have less than 5 after normalize()) var significantDigits = findPrecision(decimalPrecision); From 6a58509f41b78d42fcbf8aed5b0d4c212aaa1996 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 Dec 2019 20:59:42 +0800 Subject: [PATCH 077/178] Fix format. --- .../Difficulty/TaikoPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index ead70c4b0a..3a0fb64622 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty mods = Score.Mods; countGreat = Score.Statistics[HitResult.Great]; countGood = Score.Statistics[HitResult.Good]; - countMeh =Score.Statistics[HitResult.Meh]; + countMeh = Score.Statistics[HitResult.Meh]; countMiss = Score.Statistics[HitResult.Miss]; // Don't count scores made with supposedly unranked mods From 0734b52483061db156eda0f491b5ff0938178413 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Dec 2019 23:33:26 +0900 Subject: [PATCH 078/178] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 997bd32bac..ff6d7396ee 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3f056cacfb..d7a34c2b1c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index b231e924ef..d2feebbfc9 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From 5b8ca8f84abce675b7021fa439ea108698ca0711 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 01:17:11 +0900 Subject: [PATCH 079/178] Remove test ignore rules --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 700c2561d5..803cab9325 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -113,7 +113,6 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] - [Ignore("Will be resolved with merge of https://github.com/ppy/osu/pull/6992")] public void TestExitTooSoon() { AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000)); @@ -216,7 +215,6 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] - [Ignore("Will be resolved with merge of https://github.com/ppy/osu/pull/6992")] public void TestRestartAfterResume() { AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000)); From 36224dca133b41a3a4b9d6028a81542add8268ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Dec 2019 16:10:25 +0900 Subject: [PATCH 080/178] Fix multiplayer aggregate score inheriting from ScoreInfo --- .../Multiplayer/TestSceneMatchResults.cs | 4 +- .../API/Requests/GetRoomScoresRequest.cs | 2 +- .../Requests/Responses/APIRoomScoreInfo.cs | 17 ------- .../Responses/APIUserScoreAggregate.cs | 45 +++++++++++++++++++ .../Match/Components/MatchLeaderboard.cs | 8 ++-- .../Match/Components/MatchLeaderboardScore.cs | 11 +++-- .../Ranking/Pages/RoomLeaderboardPage.cs | 8 ++-- 7 files changed, 63 insertions(+), 32 deletions(-) delete mode 100644 osu.Game/Online/API/Requests/Responses/APIRoomScoreInfo.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchResults.cs index 7915a981dd..58e9240026 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchResults.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard { - protected override APIRequest FetchScores(Action> scoresCallback) + protected override APIRequest FetchScores(Action> scoresCallback) { var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray(); @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return null; } - private APIRoomScoreInfo createRoomScore(int id) => new APIRoomScoreInfo + private APIUserScoreAggregate createRoomScore(int id) => new APIUserScoreAggregate { User = new User { Id = id, Username = $"User {id}" }, Accuracy = 0.98, diff --git a/osu.Game/Online/API/Requests/GetRoomScoresRequest.cs b/osu.Game/Online/API/Requests/GetRoomScoresRequest.cs index 993e49dab2..eb53369d18 100644 --- a/osu.Game/Online/API/Requests/GetRoomScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetRoomScoresRequest.cs @@ -6,7 +6,7 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class GetRoomScoresRequest : APIRequest> + public class GetRoomScoresRequest : APIRequest> { private readonly int roomId; diff --git a/osu.Game/Online/API/Requests/Responses/APIRoomScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIRoomScoreInfo.cs deleted file mode 100644 index 33467b59b2..0000000000 --- a/osu.Game/Online/API/Requests/Responses/APIRoomScoreInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Newtonsoft.Json; -using osu.Game.Scoring; - -namespace osu.Game.Online.API.Requests.Responses -{ - public class APIRoomScoreInfo : ScoreInfo - { - [JsonProperty("attempts")] - public int TotalAttempts { get; set; } - - [JsonProperty("completed")] - public int CompletedBeatmaps { get; set; } - } -} diff --git a/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs b/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs new file mode 100644 index 0000000000..0bba6a93bd --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; +using osu.Game.Scoring; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIUserScoreAggregate + { + [JsonProperty("attempts")] + public int TotalAttempts { get; set; } + + [JsonProperty("completed")] + public int CompletedBeatmaps { get; set; } + + [JsonProperty("accuracy")] + public double Accuracy { get; set; } + + [JsonProperty(@"pp")] + public double? PP { get; set; } + + [JsonProperty(@"room_id")] + public int RoomID { get; set; } + + [JsonProperty("total_score")] + public long TotalScore { get; set; } + + [JsonProperty(@"user_id")] + public long UserID { get; set; } + + [JsonProperty("user")] + public User User { get; set; } + + public ScoreInfo CreateScoreInfo() => + new ScoreInfo + { + Accuracy = Accuracy, + PP = PP, + TotalScore = TotalScore, + User = User, + }; + } +} diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs index ae27e53813..571bbde716 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs @@ -13,9 +13,9 @@ using osu.Game.Online.Multiplayer; namespace osu.Game.Screens.Multi.Match.Components { - public class MatchLeaderboard : Leaderboard + public class MatchLeaderboard : Leaderboard { - public Action> ScoresLoaded; + public Action> ScoresLoaded; [Resolved(typeof(Room), nameof(Room.RoomID))] private Bindable roomId { get; set; } @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Multi.Match.Components protected override bool IsOnlineScope => true; - protected override APIRequest FetchScores(Action> scoresCallback) + protected override APIRequest FetchScores(Action> scoresCallback) { if (roomId.Value == null) return null; @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Multi.Match.Components return req; } - protected override LeaderboardScore CreateDrawableScore(APIRoomScoreInfo model, int index) => new MatchLeaderboardScore(model, index); + protected override LeaderboardScore CreateDrawableScore(APIUserScoreAggregate model, int index) => new MatchLeaderboardScore(model, index); } public enum MatchLeaderboardScope diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboardScore.cs b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboardScore.cs index 92074abe4b..aa92451c77 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboardScore.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboardScore.cs @@ -12,9 +12,12 @@ namespace osu.Game.Screens.Multi.Match.Components { public class MatchLeaderboardScore : LeaderboardScore { - public MatchLeaderboardScore(APIRoomScoreInfo score, int rank) - : base(score, rank) + private readonly APIUserScoreAggregate score; + + public MatchLeaderboardScore(APIUserScoreAggregate score, int rank) + : base(score.CreateScoreInfo(), rank) { + this.score = score; } [BackgroundDependencyLoader] @@ -26,8 +29,8 @@ namespace osu.Game.Screens.Multi.Match.Components protected override IEnumerable GetStatistics(ScoreInfo model) => new[] { new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy)), - new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, "Total Attempts", ((APIRoomScoreInfo)model).TotalAttempts.ToString()), - new LeaderboardScoreStatistic(FontAwesome.Solid.Check, "Completed Beatmaps", ((APIRoomScoreInfo)model).CompletedBeatmaps.ToString()), + new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, "Total Attempts", score.TotalAttempts.ToString()), + new LeaderboardScoreStatistic(FontAwesome.Solid.Check, "Completed Beatmaps", score.CompletedBeatmaps.ToString()), }; } } diff --git a/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs b/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs index e389611caf..ff5471cf4a 100644 --- a/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs +++ b/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages leaderboard.ScoresLoaded = scoresLoaded; } - private void scoresLoaded(IEnumerable scores) + private void scoresLoaded(IEnumerable scores) { void gray(SpriteText s) => s.Colour = colours.GrayC; @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages rankText.AddText(name + "\n", white); rankText.AddText("You are placed ", gray); - int index = scores.IndexOf(new APIRoomScoreInfo { User = Score.User }, new FuncEqualityComparer((s1, s2) => s1.User.Id.Equals(s2.User.Id))); + int index = scores.IndexOf(new APIUserScoreAggregate { User = Score.User }, new FuncEqualityComparer((s1, s2) => s1.User.Id.Equals(s2.User.Id))); rankText.AddText($"#{index + 1} ", s => { @@ -104,7 +104,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages { protected override bool FadeTop => true; - protected override LeaderboardScore CreateDrawableScore(APIRoomScoreInfo model, int index) + protected override LeaderboardScore CreateDrawableScore(APIUserScoreAggregate model, int index) => new ResultsMatchLeaderboardScore(model, index); protected override FillFlowContainer CreateScoreFlow() @@ -120,7 +120,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages private class ResultsMatchLeaderboardScore : MatchLeaderboardScore { - public ResultsMatchLeaderboardScore(APIRoomScoreInfo score, int rank) + public ResultsMatchLeaderboardScore(APIUserScoreAggregate score, int rank) : base(score, rank) { } From 48287459a0aa6136e711eb7bc07df187338a7876 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 01:52:22 +0900 Subject: [PATCH 081/178] Optimise filters to avoid property retrieval Also reduces number of instantiations of SearchableTerms array in the case of multiple criteria terms. --- .../Select/Carousel/CarouselBeatmap.cs | 32 ++++++++++--------- osu.Game/Screens/Select/FilterCriteria.cs | 6 +++- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index afd6211dec..68a6ad8845 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Screens.Select.Filter; @@ -29,28 +30,29 @@ namespace osu.Game.Screens.Select.Carousel Beatmap.RulesetID == criteria.Ruleset.ID || (Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps); - match &= criteria.StarDifficulty.IsInRange(Beatmap.StarDifficulty); - match &= criteria.ApproachRate.IsInRange(Beatmap.BaseDifficulty.ApproachRate); - match &= criteria.DrainRate.IsInRange(Beatmap.BaseDifficulty.DrainRate); - match &= criteria.CircleSize.IsInRange(Beatmap.BaseDifficulty.CircleSize); - match &= criteria.Length.IsInRange(Beatmap.Length); - match &= criteria.BPM.IsInRange(Beatmap.BPM); + match &= !criteria.StarDifficulty.HasFilter || criteria.StarDifficulty.IsInRange(Beatmap.StarDifficulty); + match &= !criteria.ApproachRate.HasFilter || criteria.ApproachRate.IsInRange(Beatmap.BaseDifficulty.ApproachRate); + match &= !criteria.DrainRate.HasFilter || criteria.DrainRate.IsInRange(Beatmap.BaseDifficulty.DrainRate); + match &= !criteria.CircleSize.HasFilter || criteria.CircleSize.IsInRange(Beatmap.BaseDifficulty.CircleSize); + match &= !criteria.Length.HasFilter || criteria.Length.IsInRange(Beatmap.Length); + match &= !criteria.BPM.HasFilter || criteria.BPM.IsInRange(Beatmap.BPM); - match &= criteria.BeatDivisor.IsInRange(Beatmap.BeatDivisor); - match &= criteria.OnlineStatus.IsInRange(Beatmap.Status); + match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(Beatmap.BeatDivisor); + match &= !criteria.OnlineStatus.HasFilter || criteria.OnlineStatus.IsInRange(Beatmap.Status); - match &= criteria.Creator.Matches(Beatmap.Metadata.AuthorString); - match &= criteria.Artist.Matches(Beatmap.Metadata.Artist) || + match &= !criteria.Creator.HasFilter || criteria.Creator.Matches(Beatmap.Metadata.AuthorString); + match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(Beatmap.Metadata.Artist) || criteria.Artist.Matches(Beatmap.Metadata.ArtistUnicode); if (match) { + var terms = new List(); + + terms.AddRange(Beatmap.Metadata.SearchableTerms); + terms.Add(Beatmap.Version); + foreach (var criteriaTerm in criteria.SearchTerms) - { - match &= - Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0) || - Beatmap.Version.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0; - } + match &= terms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0); } Filtered.Value = !match; diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 7971506fa3..abcb1f2171 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -46,6 +46,8 @@ namespace osu.Game.Screens.Select public struct OptionalRange : IEquatable> where T : struct, IComparable { + public bool HasFilter => Max != null || Min != null; + public bool IsInRange(T value) { if (Min != null) @@ -87,9 +89,11 @@ namespace osu.Game.Screens.Select public struct OptionalTextFilter : IEquatable { + public bool HasFilter => !string.IsNullOrEmpty(SearchTerm); + public bool Matches(string value) { - if (string.IsNullOrEmpty(SearchTerm)) + if (!HasFilter) return true; // search term is guaranteed to be non-empty, so if the string we're comparing is empty, it's not matching From 40d0700fa514da9d4df1abd78fbe3a74a053d985 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 13:43:38 +0900 Subject: [PATCH 082/178] Add structure for path control points --- osu.Game/Rulesets/Objects/PathControlPoint.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 osu.Game/Rulesets/Objects/PathControlPoint.cs diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs new file mode 100644 index 0000000000..40a8b0251b --- /dev/null +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -0,0 +1,16 @@ +using System; +using osu.Framework.Bindables; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +namespace osu.Game.Rulesets.Objects +{ + public class PathControlPoint : IEquatable + { + public readonly Bindable Position = new Bindable(); + + public readonly Bindable Type = new Bindable(); + + public bool Equals(PathControlPoint other) => Position.Value == other.Position.Value && Type.Value == other.Type.Value; + } +} From 3e0f499e72a3149920ea13f16fd859a3e35fea6d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 14:38:21 +0900 Subject: [PATCH 083/178] Add xmldocs --- osu.Game/Rulesets/Objects/PathControlPoint.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index 40a8b0251b..fe66fb79cb 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -7,8 +7,15 @@ namespace osu.Game.Rulesets.Objects { public class PathControlPoint : IEquatable { + /// + /// The position of this . + /// public readonly Bindable Position = new Bindable(); + /// + /// The type of path segment starting at this . + /// If null, this will be a part of the previous path segment. + /// public readonly Bindable Type = new Bindable(); public bool Equals(PathControlPoint other) => Position.Value == other.Position.Value && Type.Value == other.Type.Value; From 0149e476739c1e86ddd3b4c32d7ec4292fe37355 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 14:38:32 +0900 Subject: [PATCH 084/178] Expose general control point change event --- osu.Game/Rulesets/Objects/PathControlPoint.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index fe66fb79cb..d68ef4112b 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -18,6 +18,17 @@ namespace osu.Game.Rulesets.Objects /// public readonly Bindable Type = new Bindable(); + /// + /// Invoked when any property of this is changed. + /// + internal event Action Changed; + + public PathControlPoint() + { + Position.ValueChanged += _ => Changed?.Invoke(); + Type.ValueChanged += _ => Changed?.Invoke(); + } + public bool Equals(PathControlPoint other) => Position.Value == other.Position.Value && Type.Value == other.Type.Value; } } From 5e9b739b6718c44b643679fdb72b94f0ee378d91 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 16:45:02 +0900 Subject: [PATCH 085/178] Re-implement slider paths to support multiple segments --- .../Visual/Gameplay/TestSceneSliderPath2.cs | 159 ++++++++++ osu.Game/Rulesets/Objects/SliderPath2.cs | 274 ++++++++++++++++++ 2 files changed, 433 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath2.cs create mode 100644 osu.Game/Rulesets/Objects/SliderPath2.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath2.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath2.cs new file mode 100644 index 0000000000..08d54fcdda --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath2.cs @@ -0,0 +1,159 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Lines; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSliderPath2 : OsuTestScene + { + private readonly SmoothPath drawablePath; + private SliderPath2 path; + + public TestSceneSliderPath2() + { + Child = drawablePath = new SmoothPath + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + } + + [SetUp] + public void Setup() => Schedule(() => + { + path = new SliderPath2(); + }); + + protected override void Update() + { + base.Update(); + + if (path != null) + { + List vertices = new List(); + path.GetPathToProgress(vertices, 0, 1); + + drawablePath.Vertices = vertices; + } + } + + [Test] + public void TestEmptyPath() + { + } + + [TestCase(PathType.Linear)] + [TestCase(PathType.Bezier)] + [TestCase(PathType.Catmull)] + [TestCase(PathType.PerfectCurve)] + public void TestSingleSegment(PathType type) + => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + + [TestCase(PathType.Linear)] + [TestCase(PathType.Bezier)] + [TestCase(PathType.Catmull)] + [TestCase(PathType.PerfectCurve)] + public void TestMultipleSegment(PathType type) + { + AddStep("create path", () => + { + path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + }); + } + + [Test] + public void TestAddControlPoint() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100)))); + AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = { Value = new Vector2(100) } })); + } + + [Test] + public void TestInsertControlPoint() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100)))); + AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = { Value = new Vector2(0, 100) } })); + } + + [Test] + public void TestRemoveControlPoint() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("remove second point", () => path.ControlPoints.RemoveAt(1)); + } + + [Test] + public void TestChangePathType() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("change type to bezier", () => path.ControlPoints[0].Type.Value = PathType.Bezier); + } + + [Test] + public void TestAddSegmentByChangingType() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)))); + AddStep("change second point type to bezier", () => path.ControlPoints[1].Type.Value = PathType.Bezier); + } + + [Test] + public void TestRemoveSegmentByChangingType() + { + AddStep("create path", () => + { + path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints[1].Type.Value = PathType.Bezier; + }); + + AddStep("change second point type to null", () => path.ControlPoints[1].Type.Value = null); + } + + [Test] + public void TestRemoveSegmentByRemovingControlPoint() + { + AddStep("create path", () => + { + path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints[1].Type.Value = PathType.Bezier; + }); + + AddStep("remove second point", () => path.ControlPoints.RemoveAt(1)); + } + + [TestCase(2)] + [TestCase(4)] + public void TestPerfectCurveFallbackScenarios(int points) + { + AddStep("create path", () => + { + switch (points) + { + case 2: + path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100))); + break; + case 4: + path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + break; + } + }); + + } + + private List createSegment(PathType type, params Vector2[] controlPoints) + { + var points = controlPoints.Select(p => new PathControlPoint { Position = { Value = p } }).ToList(); + points[0].Type.Value = type; + return points; + } + } +} diff --git a/osu.Game/Rulesets/Objects/SliderPath2.cs b/osu.Game/Rulesets/Objects/SliderPath2.cs new file mode 100644 index 0000000000..560313a4d5 --- /dev/null +++ b/osu.Game/Rulesets/Objects/SliderPath2.cs @@ -0,0 +1,274 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Newtonsoft.Json; +using osu.Framework.Bindables; +using osu.Framework.Caching; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +namespace osu.Game.Rulesets.Objects +{ + public class SliderPath2 + { + /// + /// The user-set distance of the path. If non-null, will match this value, + /// and the path will be shortened/lengthened to match this length. + /// + public readonly double? ExpectedDistance; + + /// + /// The control points of the path. + /// + public readonly BindableList ControlPoints = new BindableList(); + + private readonly Cached pathCache = new Cached(); + + private readonly List calculatedPath = new List(); + private readonly List cumulativeLength = new List(); + + /// + /// Creates a new . + /// + /// A user-set distance of the path that may be shorter or longer than the true distance between all control points. + /// The path will be shortened/lengthened to match this length. If null, the path will use the true distance between all control points. + [JsonConstructor] + public SliderPath2(double? expectedDistance = null) + { + ExpectedDistance = expectedDistance; + + ControlPoints.ItemsAdded += items => + { + foreach (var c in items) + c.Changed += onControlPointChanged; + + onControlPointChanged(); + }; + + ControlPoints.ItemsRemoved += items => + { + foreach (var c in items) + c.Changed -= onControlPointChanged; + + onControlPointChanged(); + }; + + void onControlPointChanged() => pathCache.Invalidate(); + } + + /// + /// The distance of the path after lengthening/shortening to account for . + /// + [JsonIgnore] + public double Distance + { + get + { + ensureValid(); + return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + } + } + + /// + /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) + /// to 1 (end of the slider) and stores the generated path in the given list. + /// + /// The list to be filled with the computed path. + /// Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). + /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). + public void GetPathToProgress(List path, double p0, double p1) + { + ensureValid(); + + double d0 = progressToDistance(p0); + double d1 = progressToDistance(p1); + + path.Clear(); + + int i = 0; + + for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) + { + } + + path.Add(interpolateVertices(i, d0)); + + for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) + path.Add(calculatedPath[i]); + + path.Add(interpolateVertices(i, d1)); + } + + /// + /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path) + /// to 1 (end of the path). + /// + /// Ranges from 0 (beginning of the path) to 1 (end of the path). + /// + public Vector2 PositionAt(double progress) + { + ensureValid(); + + double d = progressToDistance(progress); + return interpolateVertices(indexOfDistance(d), d); + } + + private void ensureValid() + { + if (pathCache.IsValid) + return; + + calculatePath(); + calculateCumulativeLength(); + + pathCache.Validate(); + } + + private void calculatePath() + { + calculatedPath.Clear(); + + if (ControlPoints.Count == 0) + return; + + if (ControlPoints[0].Type.Value == null) + throw new InvalidOperationException($"The first control point in a {nameof(SliderPath2)} must have a non-null type."); + + Vector2[] vertices = new Vector2[ControlPoints.Count]; + for (int i = 0; i < ControlPoints.Count; i++) + vertices[i] = ControlPoints[i].Position.Value; + + int start = 0; + + for (int i = 0; i < ControlPoints.Count; i++) + { + if (ControlPoints[i].Type.Value == null && i < ControlPoints.Count - 1) + continue; + + Debug.Assert(ControlPoints[start].Type.Value.HasValue); + + // The current vertex ends the segment + var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); + var segmentType = ControlPoints[start].Type.Value.Value; + + foreach (Vector2 t in computeSubPath(segmentVertices, segmentType)) + { + if (calculatedPath.Count == 0 || calculatedPath.Last() != t) + calculatedPath.Add(t); + } + + // Start the new segment at the current vertex + start = i; + } + + static List computeSubPath(ReadOnlySpan subControlPoints, PathType type) + { + switch (type) + { + case PathType.Linear: + return PathApproximator.ApproximateLinear(subControlPoints); + + case PathType.PerfectCurve: + if (subControlPoints.Length != 3) + break; + + List subpath = PathApproximator.ApproximateCircularArc(subControlPoints); + + // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. + if (subpath.Count == 0) + break; + + return subpath; + + case PathType.Catmull: + return PathApproximator.ApproximateCatmull(subControlPoints); + } + + return PathApproximator.ApproximateBezier(subControlPoints); + } + } + + private void calculateCumulativeLength() + { + double l = 0; + + cumulativeLength.Clear(); + cumulativeLength.Add(l); + + for (int i = 0; i < calculatedPath.Count - 1; ++i) + { + Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; + double d = diff.Length; + + // Shorted slider paths that are too long compared to the expected distance + if (ExpectedDistance.HasValue && ExpectedDistance - l < d) + { + calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((ExpectedDistance - l) / d); + calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); + + l = ExpectedDistance.Value; + cumulativeLength.Add(l); + break; + } + + l += d; + cumulativeLength.Add(l); + } + + // Lengthen slider paths that are too short compared to the expected distance + if (ExpectedDistance.HasValue && l < ExpectedDistance && calculatedPath.Count > 1) + { + Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; + double d = diff.Length; + + if (d <= 0) + return; + + calculatedPath[calculatedPath.Count - 1] += diff * (float)((ExpectedDistance - l) / d); + cumulativeLength[calculatedPath.Count - 1] = ExpectedDistance.Value; + } + } + + private int indexOfDistance(double d) + { + int i = cumulativeLength.BinarySearch(d); + if (i < 0) i = ~i; + + return i; + } + + private double progressToDistance(double progress) + { + return Math.Clamp(progress, 0, 1) * Distance; + } + + private Vector2 interpolateVertices(int i, double d) + { + if (calculatedPath.Count == 0) + return Vector2.Zero; + + if (i <= 0) + return calculatedPath.First(); + if (i >= calculatedPath.Count) + return calculatedPath.Last(); + + Vector2 p0 = calculatedPath[i - 1]; + Vector2 p1 = calculatedPath[i]; + + double d0 = cumulativeLength[i - 1]; + double d1 = cumulativeLength[i]; + + // Avoid division by and almost-zero number in case two points are extremely close to each other. + if (Precision.AlmostEquals(d0, d1)) + return p0; + + double w = (d - d0) / (d1 - d0); + return p0 + (p1 - p0) * (float)w; + } + } +} From 0e9787146115d728eff1b04f3e79d6b6724955ed Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Thu, 5 Dec 2019 14:53:01 +0700 Subject: [PATCH 086/178] Change colour if difficulty is ExpertPlus --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 8014631eca..025b7e11fd 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -168,7 +168,7 @@ namespace osu.Game.Beatmaps.Drawables difficultyName.Text = beatmap.Version; starRating.Text = $"{beatmap.StarDifficulty:0.##}"; - difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating); + difficultyFlow.Colour = beatmap.DifficultyRating == DifficultyRating.ExpertPlus ? colours.Gray9 : colours.ForDifficultyRating(beatmap.DifficultyRating); return true; } From 2702edfa555fc98b68c9e11c45ce30b30ba02737 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 17:49:32 +0900 Subject: [PATCH 087/178] Rename new path, replace existing one --- ...eSliderPath2.cs => TestSceneSliderPath.cs} | 8 +- osu.Game/Rulesets/Objects/SliderPath.cs | 177 +++++------ osu.Game/Rulesets/Objects/SliderPath2.cs | 274 ------------------ 3 files changed, 93 insertions(+), 366 deletions(-) rename osu.Game.Tests/Visual/Gameplay/{TestSceneSliderPath2.cs => TestSceneSliderPath.cs} (97%) delete mode 100644 osu.Game/Rulesets/Objects/SliderPath2.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath2.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs similarity index 97% rename from osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath2.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index 08d54fcdda..fe2cc188a5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath2.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -13,12 +13,12 @@ using osuTK; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSliderPath2 : OsuTestScene + public class TestSceneSliderPath : OsuTestScene { private readonly SmoothPath drawablePath; - private SliderPath2 path; + private SliderPath path; - public TestSceneSliderPath2() + public TestSceneSliderPath() { Child = drawablePath = new SmoothPath { @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUp] public void Setup() => Schedule(() => { - path = new SliderPath2(); + path = new SliderPath(); }); protected override void Update() diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index ae6aad5b9c..cc2b537afc 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -1,17 +1,20 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using Newtonsoft.Json; +using osu.Framework.Bindables; +using osu.Framework.Caching; using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Types; using osuTK; namespace osu.Game.Rulesets.Objects { - public struct SliderPath : IEquatable + public class SliderPath { /// /// The user-set distance of the path. If non-null, will match this value, @@ -20,49 +23,47 @@ namespace osu.Game.Rulesets.Objects public readonly double? ExpectedDistance; /// - /// The type of path. + /// The control points of the path. /// - public readonly PathType Type; + public readonly BindableList ControlPoints = new BindableList(); - [JsonProperty] - private Vector2[] controlPoints; + public readonly List Test = new List(); - private List calculatedPath; - private List cumulativeLength; + private readonly Cached pathCache = new Cached(); - private bool isInitialised; + private readonly List calculatedPath = new List(); + private readonly List cumulativeLength = new List(); /// /// Creates a new . /// - /// The type of path. - /// The control points of the path. - /// A user-set distance of the path that may be shorter or longer than the true distance between all - /// . The path will be shortened/lengthened to match this length. - /// If null, the path will use the true distance between all . + /// An optional set of s to initialise the path with. + /// A user-set distance of the path that may be shorter or longer than the true distance between all control points. + /// The path will be shortened/lengthened to match this length. If null, the path will use the true distance between all control points. [JsonConstructor] - public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) + public SliderPath(PathControlPoint[] controlPoints = null, double? expectedDistance = null) { - this = default; - this.controlPoints = controlPoints; - - Type = type; ExpectedDistance = expectedDistance; - ensureInitialised(); - } - - /// - /// The control points of the path. - /// - [JsonIgnore] - public ReadOnlySpan ControlPoints - { - get + ControlPoints.ItemsAdded += items => { - ensureInitialised(); - return controlPoints.AsSpan(); - } + foreach (var c in items) + c.Changed += onControlPointChanged; + + onControlPointChanged(); + }; + + ControlPoints.ItemsRemoved += items => + { + foreach (var c in items) + c.Changed -= onControlPointChanged; + + onControlPointChanged(); + }; + + ControlPoints.AddRange(controlPoints); + + void onControlPointChanged() => pathCache.Invalidate(); } /// @@ -73,7 +74,7 @@ namespace osu.Game.Rulesets.Objects { get { - ensureInitialised(); + ensureValid(); return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; } } @@ -87,7 +88,7 @@ namespace osu.Game.Rulesets.Objects /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). public void GetPathToProgress(List path, double p0, double p1) { - ensureInitialised(); + ensureValid(); double d0 = progressToDistance(p0); double d1 = progressToDistance(p1); @@ -116,82 +117,84 @@ namespace osu.Game.Rulesets.Objects /// public Vector2 PositionAt(double progress) { - ensureInitialised(); + ensureValid(); double d = progressToDistance(progress); return interpolateVertices(indexOfDistance(d), d); } - private void ensureInitialised() + private void ensureValid() { - if (isInitialised) + if (pathCache.IsValid) return; - isInitialised = true; - - controlPoints ??= Array.Empty(); - calculatedPath = new List(); - cumulativeLength = new List(); - calculatePath(); calculateCumulativeLength(); - } - private List calculateSubpath(ReadOnlySpan subControlPoints) - { - switch (Type) - { - case PathType.Linear: - return PathApproximator.ApproximateLinear(subControlPoints); - - case PathType.PerfectCurve: - //we can only use CircularArc iff we have exactly three control points and no dissection. - if (ControlPoints.Length != 3 || subControlPoints.Length != 3) - break; - - // Here we have exactly 3 control points. Attempt to fit a circular arc. - List subpath = PathApproximator.ApproximateCircularArc(subControlPoints); - - // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. - if (subpath.Count == 0) - break; - - return subpath; - - case PathType.Catmull: - return PathApproximator.ApproximateCatmull(subControlPoints); - } - - return PathApproximator.ApproximateBezier(subControlPoints); + pathCache.Validate(); } private void calculatePath() { calculatedPath.Clear(); - // Sliders may consist of various subpaths separated by two consecutive vertices - // with the same position. The following loop parses these subpaths and computes - // their shape independently, consecutively appending them to calculatedPath. + if (ControlPoints.Count == 0) + return; + + if (ControlPoints[0].Type.Value == null) + throw new InvalidOperationException($"The first control point in a {nameof(SliderPath)} must have a non-null type."); + + Vector2[] vertices = new Vector2[ControlPoints.Count]; + for (int i = 0; i < ControlPoints.Count; i++) + vertices[i] = ControlPoints[i].Position.Value; int start = 0; - int end = 0; - for (int i = 0; i < ControlPoints.Length; ++i) + for (int i = 0; i < ControlPoints.Count; i++) { - end++; + if (ControlPoints[i].Type.Value == null && i < ControlPoints.Count - 1) + continue; - if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) + Debug.Assert(ControlPoints[start].Type.Value.HasValue); + + // The current vertex ends the segment + var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); + var segmentType = ControlPoints[start].Type.Value.Value; + + foreach (Vector2 t in computeSubPath(segmentVertices, segmentType)) { - ReadOnlySpan cpSpan = ControlPoints.Slice(start, end - start); - - foreach (Vector2 t in calculateSubpath(cpSpan)) - { - if (calculatedPath.Count == 0 || calculatedPath.Last() != t) - calculatedPath.Add(t); - } - - start = end; + if (calculatedPath.Count == 0 || calculatedPath.Last() != t) + calculatedPath.Add(t); } + + // Start the new segment at the current vertex + start = i; + } + + static List computeSubPath(ReadOnlySpan subControlPoints, PathType type) + { + switch (type) + { + case PathType.Linear: + return PathApproximator.ApproximateLinear(subControlPoints); + + case PathType.PerfectCurve: + if (subControlPoints.Length != 3) + break; + + List subpath = PathApproximator.ApproximateCircularArc(subControlPoints); + + // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. + if (subpath.Count == 0) + break; + + return subpath; + + case PathType.Catmull: + return PathApproximator.ApproximateCatmull(subControlPoints); + } + + return PathApproximator.ApproximateBezier(subControlPoints); } } @@ -272,7 +275,5 @@ namespace osu.Game.Rulesets.Objects double w = (d - d0) / (d1 - d0); return p0 + (p1 - p0) * (float)w; } - - public bool Equals(SliderPath other) => ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance == other.ExpectedDistance && Type == other.Type; } } diff --git a/osu.Game/Rulesets/Objects/SliderPath2.cs b/osu.Game/Rulesets/Objects/SliderPath2.cs deleted file mode 100644 index 560313a4d5..0000000000 --- a/osu.Game/Rulesets/Objects/SliderPath2.cs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Newtonsoft.Json; -using osu.Framework.Bindables; -using osu.Framework.Caching; -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Objects.Types; -using osuTK; - -namespace osu.Game.Rulesets.Objects -{ - public class SliderPath2 - { - /// - /// The user-set distance of the path. If non-null, will match this value, - /// and the path will be shortened/lengthened to match this length. - /// - public readonly double? ExpectedDistance; - - /// - /// The control points of the path. - /// - public readonly BindableList ControlPoints = new BindableList(); - - private readonly Cached pathCache = new Cached(); - - private readonly List calculatedPath = new List(); - private readonly List cumulativeLength = new List(); - - /// - /// Creates a new . - /// - /// A user-set distance of the path that may be shorter or longer than the true distance between all control points. - /// The path will be shortened/lengthened to match this length. If null, the path will use the true distance between all control points. - [JsonConstructor] - public SliderPath2(double? expectedDistance = null) - { - ExpectedDistance = expectedDistance; - - ControlPoints.ItemsAdded += items => - { - foreach (var c in items) - c.Changed += onControlPointChanged; - - onControlPointChanged(); - }; - - ControlPoints.ItemsRemoved += items => - { - foreach (var c in items) - c.Changed -= onControlPointChanged; - - onControlPointChanged(); - }; - - void onControlPointChanged() => pathCache.Invalidate(); - } - - /// - /// The distance of the path after lengthening/shortening to account for . - /// - [JsonIgnore] - public double Distance - { - get - { - ensureValid(); - return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; - } - } - - /// - /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) - /// to 1 (end of the slider) and stores the generated path in the given list. - /// - /// The list to be filled with the computed path. - /// Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). - /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). - public void GetPathToProgress(List path, double p0, double p1) - { - ensureValid(); - - double d0 = progressToDistance(p0); - double d1 = progressToDistance(p1); - - path.Clear(); - - int i = 0; - - for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) - { - } - - path.Add(interpolateVertices(i, d0)); - - for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) - path.Add(calculatedPath[i]); - - path.Add(interpolateVertices(i, d1)); - } - - /// - /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path) - /// to 1 (end of the path). - /// - /// Ranges from 0 (beginning of the path) to 1 (end of the path). - /// - public Vector2 PositionAt(double progress) - { - ensureValid(); - - double d = progressToDistance(progress); - return interpolateVertices(indexOfDistance(d), d); - } - - private void ensureValid() - { - if (pathCache.IsValid) - return; - - calculatePath(); - calculateCumulativeLength(); - - pathCache.Validate(); - } - - private void calculatePath() - { - calculatedPath.Clear(); - - if (ControlPoints.Count == 0) - return; - - if (ControlPoints[0].Type.Value == null) - throw new InvalidOperationException($"The first control point in a {nameof(SliderPath2)} must have a non-null type."); - - Vector2[] vertices = new Vector2[ControlPoints.Count]; - for (int i = 0; i < ControlPoints.Count; i++) - vertices[i] = ControlPoints[i].Position.Value; - - int start = 0; - - for (int i = 0; i < ControlPoints.Count; i++) - { - if (ControlPoints[i].Type.Value == null && i < ControlPoints.Count - 1) - continue; - - Debug.Assert(ControlPoints[start].Type.Value.HasValue); - - // The current vertex ends the segment - var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = ControlPoints[start].Type.Value.Value; - - foreach (Vector2 t in computeSubPath(segmentVertices, segmentType)) - { - if (calculatedPath.Count == 0 || calculatedPath.Last() != t) - calculatedPath.Add(t); - } - - // Start the new segment at the current vertex - start = i; - } - - static List computeSubPath(ReadOnlySpan subControlPoints, PathType type) - { - switch (type) - { - case PathType.Linear: - return PathApproximator.ApproximateLinear(subControlPoints); - - case PathType.PerfectCurve: - if (subControlPoints.Length != 3) - break; - - List subpath = PathApproximator.ApproximateCircularArc(subControlPoints); - - // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. - if (subpath.Count == 0) - break; - - return subpath; - - case PathType.Catmull: - return PathApproximator.ApproximateCatmull(subControlPoints); - } - - return PathApproximator.ApproximateBezier(subControlPoints); - } - } - - private void calculateCumulativeLength() - { - double l = 0; - - cumulativeLength.Clear(); - cumulativeLength.Add(l); - - for (int i = 0; i < calculatedPath.Count - 1; ++i) - { - Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; - double d = diff.Length; - - // Shorted slider paths that are too long compared to the expected distance - if (ExpectedDistance.HasValue && ExpectedDistance - l < d) - { - calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((ExpectedDistance - l) / d); - calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); - - l = ExpectedDistance.Value; - cumulativeLength.Add(l); - break; - } - - l += d; - cumulativeLength.Add(l); - } - - // Lengthen slider paths that are too short compared to the expected distance - if (ExpectedDistance.HasValue && l < ExpectedDistance && calculatedPath.Count > 1) - { - Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; - double d = diff.Length; - - if (d <= 0) - return; - - calculatedPath[calculatedPath.Count - 1] += diff * (float)((ExpectedDistance - l) / d); - cumulativeLength[calculatedPath.Count - 1] = ExpectedDistance.Value; - } - } - - private int indexOfDistance(double d) - { - int i = cumulativeLength.BinarySearch(d); - if (i < 0) i = ~i; - - return i; - } - - private double progressToDistance(double progress) - { - return Math.Clamp(progress, 0, 1) * Distance; - } - - private Vector2 interpolateVertices(int i, double d) - { - if (calculatedPath.Count == 0) - return Vector2.Zero; - - if (i <= 0) - return calculatedPath.First(); - if (i >= calculatedPath.Count) - return calculatedPath.Last(); - - Vector2 p0 = calculatedPath[i - 1]; - Vector2 p1 = calculatedPath[i]; - - double d0 = cumulativeLength[i - 1]; - double d1 = cumulativeLength[i]; - - // Avoid division by and almost-zero number in case two points are extremely close to each other. - if (Precision.AlmostEquals(d0, d1)) - return p0; - - double w = (d - d0) / (d1 - d0); - return p0 + (p1 - p0) * (float)w; - } - } -} From 986ac1cee4df72dc07c93f15f24032bc714a1edc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 17:49:54 +0900 Subject: [PATCH 088/178] Make expected distance a bindable --- osu.Game/Rulesets/Objects/SliderPath.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index cc2b537afc..9d68e1337a 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Objects /// The user-set distance of the path. If non-null, will match this value, /// and the path will be shortened/lengthened to match this length. /// - public readonly double? ExpectedDistance; + public readonly Bindable ExpectedDistance = new Bindable(); /// /// The control points of the path. @@ -43,7 +43,8 @@ namespace osu.Game.Rulesets.Objects [JsonConstructor] public SliderPath(PathControlPoint[] controlPoints = null, double? expectedDistance = null) { - ExpectedDistance = expectedDistance; + ExpectedDistance.Value = expectedDistance; + ExpectedDistance.ValueChanged += _ => pathCache.Invalidate(); ControlPoints.ItemsAdded += items => { @@ -205,18 +206,20 @@ namespace osu.Game.Rulesets.Objects cumulativeLength.Clear(); cumulativeLength.Add(l); + double? expectedDistance = ExpectedDistance.Value; + for (int i = 0; i < calculatedPath.Count - 1; ++i) { Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; double d = diff.Length; // Shorted slider paths that are too long compared to the expected distance - if (ExpectedDistance.HasValue && ExpectedDistance - l < d) + if (expectedDistance.HasValue && expectedDistance - l < d) { - calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((ExpectedDistance - l) / d); + calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((expectedDistance - l) / d); calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); - l = ExpectedDistance.Value; + l = expectedDistance.Value; cumulativeLength.Add(l); break; } @@ -226,7 +229,7 @@ namespace osu.Game.Rulesets.Objects } // Lengthen slider paths that are too short compared to the expected distance - if (ExpectedDistance.HasValue && l < ExpectedDistance && calculatedPath.Count > 1) + if (expectedDistance.HasValue && l < expectedDistance && calculatedPath.Count > 1) { Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; double d = diff.Length; @@ -234,8 +237,8 @@ namespace osu.Game.Rulesets.Objects if (d <= 0) return; - calculatedPath[calculatedPath.Count - 1] += diff * (float)((ExpectedDistance - l) / d); - cumulativeLength[calculatedPath.Count - 1] = ExpectedDistance.Value; + calculatedPath[calculatedPath.Count - 1] += diff * (float)((expectedDistance - l) / d); + cumulativeLength[calculatedPath.Count - 1] = expectedDistance.Value; } } From 1585d83b969016e287cb9c2af06abdbe2df431f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 18:19:42 +0900 Subject: [PATCH 089/178] Add legacy constructor --- osu.Game/Rulesets/Objects/SliderPath.cs | 33 +++++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 9d68e1337a..bbeb03992e 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -37,13 +37,8 @@ namespace osu.Game.Rulesets.Objects /// /// Creates a new . /// - /// An optional set of s to initialise the path with. - /// A user-set distance of the path that may be shorter or longer than the true distance between all control points. - /// The path will be shortened/lengthened to match this length. If null, the path will use the true distance between all control points. - [JsonConstructor] - public SliderPath(PathControlPoint[] controlPoints = null, double? expectedDistance = null) + public SliderPath() { - ExpectedDistance.Value = expectedDistance; ExpectedDistance.ValueChanged += _ => pathCache.Invalidate(); ControlPoints.ItemsAdded += items => @@ -62,11 +57,33 @@ namespace osu.Game.Rulesets.Objects onControlPointChanged(); }; - ControlPoints.AddRange(controlPoints); - void onControlPointChanged() => pathCache.Invalidate(); } + /// + /// Creates a new . + /// + /// An optional set of s to initialise the path with. + /// A user-set distance of the path that may be shorter or longer than the true distance between all control points. + /// The path will be shortened/lengthened to match this length. If null, the path will use the true distance between all control points. + [JsonConstructor] + public SliderPath(PathControlPoint[] controlPoints, double? expectedDistance = null) + : this() + { + ControlPoints.AddRange(controlPoints); + ExpectedDistance.Value = expectedDistance; + } + + public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) + : this() + { + foreach (var c in controlPoints) + ControlPoints.Add(new PathControlPoint { Position = { Value = c } }); + ControlPoints[0].Type.Value = type; + + ExpectedDistance.Value = expectedDistance; + } + /// /// The distance of the path after lengthening/shortening to account for . /// From 2419077c0763612f0cb05f8e503e7208373dfd47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 18:31:13 +0900 Subject: [PATCH 090/178] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ff6d7396ee..301c615ce4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d7a34c2b1c..086359ee41 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d2feebbfc9..0eb8d98a63 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From c9a66c0d07a8a89267355133a0eb8caead972c88 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 18:31:28 +0900 Subject: [PATCH 091/178] Expose a version to indicate path changes --- osu.Game/Rulesets/Objects/SliderPath.cs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index bbeb03992e..6ad5d2f1c3 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -16,6 +16,13 @@ namespace osu.Game.Rulesets.Objects { public class SliderPath { + /// + /// The current version of this . Updated when any change to the path occurs. + /// + public IBindable Version => version; + + private readonly Bindable version = new Bindable(); + /// /// The user-set distance of the path. If non-null, will match this value, /// and the path will be shortened/lengthened to match this length. @@ -39,25 +46,23 @@ namespace osu.Game.Rulesets.Objects /// public SliderPath() { - ExpectedDistance.ValueChanged += _ => pathCache.Invalidate(); + ExpectedDistance.ValueChanged += _ => invalidate(); ControlPoints.ItemsAdded += items => { foreach (var c in items) - c.Changed += onControlPointChanged; + c.Changed += invalidate; - onControlPointChanged(); + invalidate(); }; ControlPoints.ItemsRemoved += items => { foreach (var c in items) - c.Changed -= onControlPointChanged; + c.Changed -= invalidate; - onControlPointChanged(); + invalidate(); }; - - void onControlPointChanged() => pathCache.Invalidate(); } /// @@ -141,6 +146,12 @@ namespace osu.Game.Rulesets.Objects return interpolateVertices(indexOfDistance(d), d); } + private void invalidate() + { + pathCache.Invalidate(); + version.Value++; + } + private void ensureValid() { if (pathCache.IsValid) From e225b0032aa092c79322fb948fd7e3adb7aac2b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 19:31:40 +0900 Subject: [PATCH 092/178] Add basic hitobject display to timeline --- .../Editor/TestSceneEditorComposeTimeline.cs | 16 ++++ .../Compose/Components/Timeline/Timeline.cs | 21 +++-- .../Timeline/TimelineHitObjectDisplay.cs | 85 +++++++++++++++++++ 3 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs index 6e5b3b93e9..b67ea798a0 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs @@ -13,6 +13,10 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK; using osuTK.Graphics; @@ -25,11 +29,23 @@ namespace osu.Game.Tests.Visual.Editor public override IReadOnlyList RequiredTypes => new[] { typeof(TimelineArea), + typeof(TimelineHitObjectDisplay), typeof(Timeline), typeof(TimelineButton), typeof(CentreMarker) }; + [Cached(typeof(IEditorBeatmap))] + private readonly EditorBeatmap editorBeatmap; + + public TestSceneEditorComposeTimeline() + { + editorBeatmap = new EditorBeatmap( + (Beatmap) + CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, new Mod[] { }) + ); + } + [BackgroundDependencyLoader] private void load(AudioManager audio) { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 748c9e2ba3..1f8c134d84 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -36,14 +36,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { this.adjustableClock = adjustableClock; - Child = waveform = new WaveformGraph + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = colours.Blue.Opacity(0.2f), - LowColour = colours.BlueLighter, - MidColour = colours.BlueDark, - HighColour = colours.BlueDarker, - Depth = float.MaxValue + waveform = new WaveformGraph + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Blue.Opacity(0.2f), + LowColour = colours.BlueLighter, + MidColour = colours.BlueDark, + HighColour = colours.BlueDarker, + Depth = float.MaxValue + }, + new TimelineHitObjectDisplay + { + RelativeSizeAxes = Axes.Both, + }, }; // We don't want the centre marker to scroll diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs new file mode 100644 index 0000000000..8bc0bf1a86 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit.Compose.Components.Timeline +{ + internal class TimelineHitObjectDisplay : TimelinePart + { + [Resolved] + private IEditorBeatmap beatmap { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + foreach (var h in beatmap.HitObjects) + add(h); + + beatmap.HitObjectAdded += add; + beatmap.HitObjectRemoved += remove; + } + + private void remove(HitObject h) + { + foreach (var d in InternalChildren.OfType().Where(c => c.HitObject == h)) + d.Expire(); + } + + private void add(HitObject h) + { + Add(new TimelineHitObjectRepresentation(h)); + } + + private class TimelineHitObjectRepresentation : CompositeDrawable + { + public readonly HitObject HitObject; + + public TimelineHitObjectRepresentation(HitObject hitObject) + { + this.HitObject = hitObject; + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + + Width = (float)(hitObject.GetEndTime() - hitObject.StartTime); + + X = (float)hitObject.StartTime; + + RelativePositionAxes = Axes.X; + RelativeSizeAxes = Axes.X; + + AddInternal(new Circle + { + Size = new Vector2(16), + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.X, + AlwaysPresent = true, + Colour = Color4.White, + }); + + if (hitObject is IHasEndTime) + { + AddInternal(new Box + { + Size = new Vector2(1, 5), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativePositionAxes = Axes.X, + RelativeSizeAxes = Axes.X, + Colour = Color4.White, + }); + } + } + } + } +} From 3ebbf62b2ab11d58fd372a091fe179eb7f827188 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Dec 2019 19:53:31 +0900 Subject: [PATCH 093/178] Initial game-wide update with the new SliderPath --- .../TestSceneSliderSelectionBlueprint.cs | 2 +- .../Components/PathControlPointPiece.cs | 28 +++-------- .../Components/PathControlPointVisualiser.cs | 24 +++++---- .../Sliders/SliderPlacementBlueprint.cs | 18 +++---- .../Sliders/SliderSelectionBlueprint.cs | 25 +++------- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 7 +-- .../Objects/Drawables/DrawableSlider.cs | 6 +-- .../Objects/Drawables/DrawableSliderHead.cs | 6 +-- .../Objects/Drawables/DrawableSliderTail.cs | 6 +-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 14 +----- .../Objects/SliderTailCircle.cs | 6 +-- .../Legacy/Catch/ConvertHitObjectParser.cs | 4 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 50 +++++++++++++++---- .../Legacy/Mania/ConvertHitObjectParser.cs | 4 +- .../Legacy/Osu/ConvertHitObjectParser.cs | 4 +- .../Legacy/Taiko/ConvertHitObjectParser.cs | 4 +- 16 files changed, 97 insertions(+), 111 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs index dde2aa53e0..013920684c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs @@ -196,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Tests { AddStep($"move mouse to control point {index}", () => { - Vector2 position = slider.Position + slider.Path.ControlPoints[index]; + Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value; InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position)); }); } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 0ccf020300..159916f16f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - Position = slider.StackedPosition + slider.Path.ControlPoints[Index]; + Position = slider.StackedPosition + slider.Path.ControlPoints[Index].Position.Value; updateMarkerDisplay(); updateConnectingPath(); @@ -116,10 +116,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { path.ClearVertices(); - if (Index != slider.Path.ControlPoints.Length - 1) + if (Index != slider.Path.ControlPoints.Count - 1) { path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints[Index + 1] - slider.Path.ControlPoints[Index]); + path.AddVertex(slider.Path.ControlPoints[Index + 1].Position.Value - slider.Path.ControlPoints[Index].Position.Value); } path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); @@ -156,8 +156,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnDrag(DragEvent e) { - var newControlPoints = slider.Path.ControlPoints.ToArray(); - if (Index == 0) { // Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account @@ -168,29 +166,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components slider.StartTime = snappedTime; // Since control points are relative to the position of the slider, they all need to be offset backwards by the delta - for (int i = 1; i < newControlPoints.Length; i++) - newControlPoints[i] -= movementDelta; + for (int i = 1; i < slider.Path.ControlPoints.Count; i++) + slider.Path.ControlPoints[i].Position.Value -= movementDelta; } else - newControlPoints[Index] += e.Delta; - - if (isSegmentSeparatorWithNext) - newControlPoints[Index + 1] = newControlPoints[Index]; - - if (isSegmentSeparatorWithPrevious) - newControlPoints[Index - 1] = newControlPoints[Index]; - - ControlPointsChanged?.Invoke(newControlPoints); + slider.Path.ControlPoints[Index].Position.Value += e.Delta; return true; } protected override bool OnDragEnd(DragEndEvent e) => true; - private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious; - - private bool isSegmentSeparatorWithNext => Index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints[Index + 1] == slider.Path.ControlPoints[Index]; - - private bool isSegmentSeparatorWithPrevious => Index > 0 && slider.Path.ControlPoints[Index - 1] == slider.Path.ControlPoints[Index]; + private bool isSegmentSeparator => slider.Path.ControlPoints[Index].Type.Value.HasValue; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index cdca48490e..c47e4d7d4a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - while (slider.Path.ControlPoints.Length > Pieces.Count) + while (slider.Path.ControlPoints.Count > Pieces.Count) { var piece = new PathControlPointPiece(slider, Pieces.Count) { @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components Pieces.Add(piece); } - while (slider.Path.ControlPoints.Length < Pieces.Count) + while (slider.Path.ControlPoints.Count < Pieces.Count) Pieces.Remove(Pieces[Pieces.Count - 1]); } @@ -105,29 +105,32 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool deleteSelected() { - var newControlPoints = new List(); + int countDeleted = 0; foreach (var piece in Pieces) { - if (!piece.IsSelected.Value) - newControlPoints.Add(slider.Path.ControlPoints[piece.Index]); + if (piece.IsSelected.Value) + { + slider.Path.ControlPoints.RemoveAt(piece.Index); + countDeleted++; + } } // Ensure that there are any points to be deleted - if (newControlPoints.Count == slider.Path.ControlPoints.Length) + if (countDeleted == 0) return false; // If there are 0 remaining control points, treat the slider as being deleted - if (newControlPoints.Count == 0) + if (slider.Path.ControlPoints.Count == 0) { placementHandler?.Delete(slider); return true; } // Make control points relative - Vector2 first = newControlPoints[0]; - for (int i = 0; i < newControlPoints.Count; i++) - newControlPoints[i] = newControlPoints[i] - first; + Vector2 first = slider.Path.ControlPoints[0].Position.Value; + for (int i = 0; i < slider.Path.ControlPoints.Count; i++) + slider.Path.ControlPoints[i].Position.Value = slider.Path.ControlPoints[i].Position.Value - first; // The slider's position defines the position of the first control point, and all further control points are relative to that point slider.Position += first; @@ -136,7 +139,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components foreach (var piece in Pieces) piece.IsSelected.Value = false; - ControlPointsChanged?.Invoke(newControlPoints.ToArray()); return true; } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9c0afada29..62a22dc858 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -27,8 +27,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private HitCirclePiece headCirclePiece; private HitCirclePiece tailCirclePiece; - private readonly List segments = new List(); - private Vector2 cursor; private InputManager inputManager; private PlacementState state; @@ -40,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders : base(new Objects.Slider()) { RelativeSizeAxes = Axes.Both; - segments.Add(new Segment(Vector2.Zero)); + HitObject.Path.ControlPoints.Add(new PathControlPoint { Position = { Value = Vector2.Zero } }); } [BackgroundDependencyLoader] @@ -74,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders case PlacementState.Body: // The given screen-space position may have been externally snapped, but the unsnapped position from the input manager // is used instead since snapping control points doesn't make much sense - cursor = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; + HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1].Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; break; } } @@ -91,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders switch (e.Button) { case MouseButton.Left: - segments.Last().ControlPoints.Add(cursor); + HitObject.Path.ControlPoints.Add(new PathControlPoint { Position = { Value = HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1].Position.Value } }); break; } @@ -110,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override bool OnDoubleClick(DoubleClickEvent e) { - segments.Add(new Segment(segments[segments.Count - 1].ControlPoints.Last())); + HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 2].Type.Value = PathType.Bezier; return true; } @@ -134,12 +132,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { - Vector2[] newControlPoints = segments.SelectMany(s => s.ControlPoints).Concat(cursor.Yield()).ToArray(); - - var unsnappedPath = new SliderPath(newControlPoints.Length > 2 ? PathType.Bezier : PathType.Linear, newControlPoints); - var snappedDistance = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)unsnappedPath.Distance) ?? (float)unsnappedPath.Distance; - - HitObject.Path = new SliderPath(unsnappedPath.Type, newControlPoints, snappedDistance); + HitObject.Path.ExpectedDistance.Value = null; + HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.Distance) ?? (float)HitObject.Path.Distance; bodyPiece.UpdateFrom(HitObject); headCirclePiece.UpdateFrom(HitObject.HeadCircle); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 820d6c92d7..ab52c906e8 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -77,12 +77,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Debug.Assert(placementControlPointIndex != null); - Vector2 position = e.MousePosition - HitObject.Position; - - var controlPoints = HitObject.Path.ControlPoints.ToArray(); - controlPoints[placementControlPointIndex.Value] = position; - - onNewControlPoints(controlPoints); + HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position; return true; } @@ -97,15 +92,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { position -= HitObject.Position; - var controlPoints = new Vector2[HitObject.Path.ControlPoints.Length + 1]; - HitObject.Path.ControlPoints.CopyTo(controlPoints); - int insertionIndex = 0; float minDistance = float.MaxValue; - for (int i = 0; i < controlPoints.Length - 2; i++) + for (int i = 0; i < HitObject.Path.ControlPoints.Count - 2; i++) { - float dist = new Line(controlPoints[i], controlPoints[i + 1]).DistanceToPoint(position); + float dist = new Line(HitObject.Path.ControlPoints[i].Position.Value, HitObject.Path.ControlPoints[i + 1].Position.Value).DistanceToPoint(position); if (dist < minDistance) { @@ -115,20 +107,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Move the control points from the insertion index onwards to make room for the insertion - Array.Copy(controlPoints, insertionIndex, controlPoints, insertionIndex + 1, controlPoints.Length - insertionIndex - 1); - controlPoints[insertionIndex] = position; - - onNewControlPoints(controlPoints); + HitObject.Path.ControlPoints.Insert(insertionIndex, new PathControlPoint { Position = { Value = position } }); return insertionIndex; } private void onNewControlPoints(Vector2[] controlPoints) { - var unsnappedPath = new SliderPath(controlPoints.Length > 2 ? PathType.Bezier : PathType.Linear, controlPoints); - var snappedDistance = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)unsnappedPath.Distance) ?? (float)unsnappedPath.Distance; - - HitObject.Path = new SliderPath(unsnappedPath.Type, controlPoints, snappedDistance); + HitObject.Path.ExpectedDistance.Value = null; + HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.Distance) ?? (float)HitObject.Path.Distance; UpdateHitObject(); } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 3d566362ae..bc5f79331f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -28,11 +28,8 @@ namespace osu.Game.Rulesets.Osu.Mods slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - var newControlPoints = new Vector2[slider.Path.ControlPoints.Length]; - for (int i = 0; i < slider.Path.ControlPoints.Length; i++) - newControlPoints[i] = new Vector2(slider.Path.ControlPoints[i].X, -slider.Path.ControlPoints[i].Y); - - slider.Path = new SliderPath(slider.Path.Type, newControlPoints, slider.Path.ExpectedDistance); + foreach (var point in slider.Path.ControlPoints) + point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 69189758a6..1e0402d492 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly IBindable positionBindable = new Bindable(); private readonly IBindable stackHeightBindable = new Bindable(); private readonly IBindable scaleBindable = new Bindable(); - private readonly IBindable pathBindable = new Bindable(); + private readonly IBindable pathVersion = new Bindable(); [Resolved(CanBeNull = true)] private OsuRulesetConfigManager config { get; set; } @@ -84,9 +84,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables positionBindable.BindTo(HitObject.PositionBindable); stackHeightBindable.BindTo(HitObject.StackHeightBindable); scaleBindable.BindTo(HitObject.ScaleBindable); - pathBindable.BindTo(slider.PathBindable); + pathVersion.BindTo(slider.Path.Version); - pathBindable.BindValueChanged(_ => Body.Refresh()); + pathVersion.BindValueChanged(_ => Body.Refresh()); AccentColour.BindValueChanged(colour => { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index a10c66d1df..166defbc41 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public class DrawableSliderHead : DrawableHitCircle { private readonly IBindable positionBindable = new Bindable(); - private readonly IBindable pathBindable = new Bindable(); + private readonly IBindable pathVersion = new Bindable(); private readonly Slider slider; @@ -27,10 +27,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private void load() { positionBindable.BindTo(HitObject.PositionBindable); - pathBindable.BindTo(slider.PathBindable); + pathVersion.BindTo(slider.Path.Version); positionBindable.BindValueChanged(_ => updatePosition()); - pathBindable.BindValueChanged(_ => updatePosition(), true); + pathVersion.BindValueChanged(_ => updatePosition(), true); } protected override void Update() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 42bf5e4d21..8e2f6ffa66 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool Tracking { get; set; } private readonly IBindable positionBindable = new Bindable(); - private readonly IBindable pathBindable = new Bindable(); + private readonly IBindable pathVersion = new Bindable(); public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle) : base(hitCircle) @@ -36,10 +36,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true; positionBindable.BindTo(hitCircle.PositionBindable); - pathBindable.BindTo(slider.PathBindable); + pathVersion.BindTo(slider.Path.Version); positionBindable.BindValueChanged(_ => updatePosition()); - pathBindable.BindValueChanged(_ => updatePosition(), true); + pathVersion.BindValueChanged(_ => updatePosition(), true); // TODO: This has no drawable content. Support for skins should be added. } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index c6f5a075e0..b68595c67e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -28,19 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); - public readonly Bindable PathBindable = new Bindable(); - - public SliderPath Path - { - get => PathBindable.Value; - set - { - PathBindable.Value = value; - endPositionCache.Invalidate(); - - updateNestedPositions(); - } - } + public SliderPath Path { get; set; } = new SliderPath(new[] { new PathControlPoint { Type = { Value = PathType.Bezier } } }); public double Distance => Path.Distance; diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index 14c3369967..c17d2275b8 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -15,12 +15,12 @@ namespace osu.Game.Rulesets.Osu.Objects /// public class SliderTailCircle : SliderCircle { - private readonly IBindable pathBindable = new Bindable(); + private readonly IBindable pathVersion = new Bindable(); public SliderTailCircle(Slider slider) { - pathBindable.BindTo(slider.PathBindable); - pathBindable.BindValueChanged(_ => Position = slider.EndPosition); + pathVersion.BindTo(slider.Path.Version); + pathVersion.BindValueChanged(_ => Position = slider.EndPosition); } public override Judgement CreateJudgement() => new OsuSliderTailJudgement(); diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 545cfe07f8..afa1f2996e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, List> nodeSamples) { newCombo |= forceNewCombo; @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch X = position.X, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, - Path = new SliderPath(pathType, controlPoints, length), + Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 5348ff1f02..1ac7284772 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -115,12 +115,6 @@ namespace osu.Game.Rulesets.Objects.Legacy points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - pos; } - // osu-stable special-cased colinear perfect curves to a CurveType.Linear - static bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); - - if (points.Length == 3 && pathType == PathType.PerfectCurve && isLinear(points)) - pathType = PathType.Linear; - int repeatCount = Parsing.ParseInt(split[6]); if (repeatCount > 9000) @@ -187,7 +181,7 @@ namespace osu.Game.Rulesets.Objects.Legacy for (int i = 0; i < nodes; i++) nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); - result = CreateSlider(pos, combo, comboOffset, points, length, pathType, repeatCount, nodeSamples); + result = CreateSlider(pos, combo, comboOffset, convertControlPoints(points, pathType), length, repeatCount, nodeSamples); // The samples are played when the slider ends, which is the last node result.Samples = nodeSamples[nodeSamples.Count - 1]; @@ -259,6 +253,45 @@ namespace osu.Game.Rulesets.Objects.Legacy bankInfo.Filename = split.Length > 4 ? split[4] : null; } + private PathControlPoint[] convertControlPoints(Vector2[] vertices, PathType type) + { + if (type == PathType.PerfectCurve) + { + if (vertices.Length == 3) + { + // osu-stable special-cased colinear perfect curves to a linear path + if (isLinear(vertices)) + type = PathType.Linear; + } + else + type = PathType.Bezier; + } + + var points = new List(vertices.Length) + { + new PathControlPoint + { + Position = { Value = vertices[0] }, + Type = { Value = type } + } + }; + + for (int i = 1; i < vertices.Length; i++) + { + if (vertices[i] == vertices[i - 1]) + { + points[points.Count - 1].Type.Value = type; + continue; + } + + points.Add(new PathControlPoint { Position = { Value = vertices[i] } }); + } + + return points.ToArray(); + + static bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); + } + /// /// Creates a legacy Hit-type hit object. /// @@ -276,11 +309,10 @@ namespace osu.Game.Rulesets.Objects.Legacy /// When starting a new combo, the offset of the new combo relative to the current one. /// The slider control points. /// The slider length. - /// The slider curve type. /// The slider repeat count. /// The samples to be played when the slider nodes are hit. This includes the head and tail of the slider. /// The hit object. - protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, List> nodeSamples); /// diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 8012b4230f..d1a8adc2b7 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -26,13 +26,13 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, List> nodeSamples) { return new ConvertSlider { X = position.X, - Path = new SliderPath(pathType, controlPoints, length), + Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 99872e630d..6628f7d059 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, List> nodeSamples) { newCombo |= forceNewCombo; @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu Position = position, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, - Path = new SliderPath(pathType, controlPoints, length), + Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index 9dc0c01932..7b1d64b19f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -23,12 +23,12 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko return new ConvertHit(); } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, List> nodeSamples) { return new ConvertSlider { - Path = new SliderPath(pathType, controlPoints, length), + Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; From d8620a70fbbb7bbc9f983126a98478447fba9dc5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 20:12:25 +0900 Subject: [PATCH 094/178] Make work in editor --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 8 ++++++-- .../Timelines/Summary/Parts/TimelinePart.cs | 6 +++--- .../Compose/Components/Timeline/Timeline.cs | 4 ---- .../Components/Timeline/TimelineArea.cs | 10 +++++++--- .../Timeline/TimelineHitObjectDisplay.cs | 20 ++++++++++++------- .../Screens/Edit/Compose/ComposeScreen.cs | 13 ++++++++++-- .../Screens/Edit/EditorScreenWithTimeline.cs | 10 ++++++++-- osu.Game/Screens/Edit/IEditorBeatmap.cs | 5 +++++ 8 files changed, 53 insertions(+), 23 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 805fc2b46f..368520ba43 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -34,7 +34,9 @@ namespace osu.Game.Rulesets.Edit where TObject : HitObject { protected IRulesetConfigManager Config { get; private set; } - protected EditorBeatmap EditorBeatmap { get; private set; } + + protected new EditorBeatmap EditorBeatmap { get; private set; } + protected readonly Ruleset Ruleset; [Resolved] @@ -148,7 +150,7 @@ namespace osu.Game.Rulesets.Edit beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap); - EditorBeatmap = new EditorBeatmap(playableBeatmap); + base.EditorBeatmap = EditorBeatmap = new EditorBeatmap(playableBeatmap); EditorBeatmap.HitObjectAdded += addHitObject; EditorBeatmap.HitObjectRemoved += removeHitObject; EditorBeatmap.StartTimeChanged += UpdateHitObject; @@ -333,6 +335,8 @@ namespace osu.Game.Rulesets.Edit /// public abstract IEnumerable HitObjects { get; } + public IEditorBeatmap EditorBeatmap { get; protected set; } + /// /// Whether the user's cursor is currently in an area of the that is valid for placement. /// diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs index 26d9614631..7706e33179 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -14,12 +14,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts /// /// Represents a part of the summary timeline.. /// - public abstract class TimelinePart : CompositeDrawable + public abstract class TimelinePart : Container { protected readonly IBindable Beatmap = new Bindable(); private readonly Container timeline; + protected override Container Content => timeline; + protected TimelinePart() { AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both }); @@ -50,8 +52,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); } - protected void Add(Drawable visualisation) => timeline.Add(visualisation); - protected virtual void LoadBeatmap(WorkingBeatmap beatmap) { timeline.Clear(); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 1f8c134d84..2a565d028f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -47,10 +47,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline HighColour = colours.BlueDarker, Depth = float.MaxValue }, - new TimelineHitObjectDisplay - { - RelativeSizeAxes = Axes.Both, - }, }; // We don't want the centre marker to scroll diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 863a120fc3..93dac059e3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -11,11 +12,14 @@ using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class TimelineArea : CompositeDrawable + public class TimelineArea : Container { - private readonly Timeline timeline; + private Timeline timeline; - public TimelineArea() + protected override Container Content => timeline; + + [BackgroundDependencyLoader] + private void load() { Masking = true; CornerRadius = 5; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 8bc0bf1a86..fc16d29639 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -16,8 +16,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { internal class TimelineHitObjectDisplay : TimelinePart { - [Resolved] - private IEditorBeatmap beatmap { get; set; } + private IEditorBeatmap beatmap { get; } + + public TimelineHitObjectDisplay(IEditorBeatmap beatmap) + { + this.beatmap = beatmap; + } [BackgroundDependencyLoader] private void load() @@ -27,18 +31,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline beatmap.HitObjectAdded += add; beatmap.HitObjectRemoved += remove; + beatmap.StartTimeChanged += h => + { + remove(h); + add(h); + }; } private void remove(HitObject h) { - foreach (var d in InternalChildren.OfType().Where(c => c.HitObject == h)) + foreach (var d in Children.OfType().Where(c => c.HitObject == h)) d.Expire(); } - private void add(HitObject h) - { - Add(new TimelineHitObjectRepresentation(h)); - } + private void add(HitObject h) => Add(new TimelineHitObjectRepresentation(h)); private class TimelineHitObjectRepresentation : CompositeDrawable { diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 2e9094ebe6..8511bc3242 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -3,17 +3,21 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Edit; +using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Skinning; namespace osu.Game.Screens.Edit.Compose { public class ComposeScreen : EditorScreenWithTimeline { + private HitObjectComposer composer; + protected override Drawable CreateMainContent() { var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance(); - var composer = ruleset?.CreateHitObjectComposer(); + composer = ruleset?.CreateHitObjectComposer(); if (composer != null) { @@ -25,10 +29,15 @@ namespace osu.Game.Screens.Edit.Compose // load the skinning hierarchy first. // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources. - return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(ruleset.CreateHitObjectComposer())); + return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer)); } return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer"); } + + protected override Drawable CreateTimelineContent() => new TimelineHitObjectDisplay(composer.EditorBeatmap) + { + RelativeSizeAxes = Axes.Both, + }; } } diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 752356e8c4..1b9d1be4f8 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -20,6 +20,8 @@ namespace osu.Game.Screens.Edit private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); + private TimelineArea timelineArea; + [BackgroundDependencyLoader(true)] private void load([CanBeNull] BindableBeatDivisor beatDivisor) { @@ -64,7 +66,7 @@ namespace osu.Game.Screens.Edit { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 5 }, - Child = CreateTimeline() + Child = timelineArea = CreateTimelineArea() }, new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } }, @@ -97,11 +99,15 @@ namespace osu.Game.Screens.Edit { mainContent.Add(content); content.FadeInFromZero(300, Easing.OutQuint); + + LoadComponentAsync(CreateTimelineContent(), timelineArea.Add); }); } protected abstract Drawable CreateMainContent(); - protected virtual Drawable CreateTimeline() => new TimelineArea { RelativeSizeAxes = Axes.Both }; + protected virtual Drawable CreateTimelineContent() => new Container { }; + + protected TimelineArea CreateTimelineArea() => new TimelineArea { RelativeSizeAxes = Axes.Both }; } } diff --git a/osu.Game/Screens/Edit/IEditorBeatmap.cs b/osu.Game/Screens/Edit/IEditorBeatmap.cs index 2f250ba446..3e3418ef79 100644 --- a/osu.Game/Screens/Edit/IEditorBeatmap.cs +++ b/osu.Game/Screens/Edit/IEditorBeatmap.cs @@ -23,6 +23,11 @@ namespace osu.Game.Screens.Edit /// Invoked when a is removed from this . /// event Action HitObjectRemoved; + + /// + /// Invoked when the start time of a in this was changed. + /// + event Action StartTimeChanged; } /// From e76f8bdd6414542de669ea0301bebf921cf0be06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 23:31:21 +0900 Subject: [PATCH 095/178] Fix warnings --- .../Editor/TestSceneEditorComposeTimeline.cs | 16 +++---- .../Timeline/TimelineHitObjectDisplay.cs | 43 ++++++++++++------- .../Screens/Edit/EditorScreenWithTimeline.cs | 2 +- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs index b67ea798a0..2b46418659 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs @@ -35,20 +35,14 @@ namespace osu.Game.Tests.Visual.Editor typeof(CentreMarker) }; - [Cached(typeof(IEditorBeatmap))] - private readonly EditorBeatmap editorBeatmap; - - public TestSceneEditorComposeTimeline() - { - editorBeatmap = new EditorBeatmap( - (Beatmap) - CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, new Mod[] { }) - ); - } - [BackgroundDependencyLoader] private void load(AudioManager audio) { + var editorBeatmap = new EditorBeatmap( + (Beatmap) + CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, new Mod[] { }) + ); + Beatmap.Value = new WaveformTestBeatmap(audio); Children = new Drawable[] diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index fc16d29639..337fd9a6b2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -44,7 +44,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline d.Expire(); } - private void add(HitObject h) => Add(new TimelineHitObjectRepresentation(h)); + private void add(HitObject h) + { + var yOffset = Children.Count(d => d.X == h.StartTime); + + Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * 4 }); + } private class TimelineHitObjectRepresentation : CompositeDrawable { @@ -52,7 +57,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public TimelineHitObjectRepresentation(HitObject hitObject) { - this.HitObject = hitObject; + HitObject = hitObject; Anchor = Anchor.CentreLeft; Origin = Anchor.CentreLeft; @@ -63,6 +68,25 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.X; + if (hitObject is IHasEndTime) + { + AddInternal(new Container + { + CornerRadius = 2, + Masking = true, + Size = new Vector2(1, 3), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativePositionAxes = Axes.X, + RelativeSizeAxes = Axes.X, + Colour = Color4.Black, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + } + }); + } + AddInternal(new Circle { Size = new Vector2(16), @@ -71,20 +95,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativePositionAxes = Axes.X, AlwaysPresent = true, Colour = Color4.White, + BorderColour = Color4.Black, + BorderThickness = 3, }); - - if (hitObject is IHasEndTime) - { - AddInternal(new Box - { - Size = new Vector2(1, 5), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativePositionAxes = Axes.X, - RelativeSizeAxes = Axes.X, - Colour = Color4.White, - }); - } } } } diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 1b9d1be4f8..aa8d99b517 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Edit protected abstract Drawable CreateMainContent(); - protected virtual Drawable CreateTimelineContent() => new Container { }; + protected virtual Drawable CreateTimelineContent() => new Container(); protected TimelineArea CreateTimelineArea() => new TimelineArea { RelativeSizeAxes = Axes.Both }; } From 12a98438353fbec5966e234dad10ab8d7f4a1dc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 10:48:18 +0900 Subject: [PATCH 096/178] Move thickness to a constant --- .../Components/Timeline/TimelineHitObjectDisplay.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 337fd9a6b2..7d6d8fd729 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -48,11 +48,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { var yOffset = Children.Count(d => d.X == h.StartTime); - Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * 4 }); + Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS }); } private class TimelineHitObjectRepresentation : CompositeDrawable { + public const float THICKNESS = 3; + public readonly HitObject HitObject; public TimelineHitObjectRepresentation(HitObject hitObject) @@ -74,7 +76,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { CornerRadius = 2, Masking = true, - Size = new Vector2(1, 3), + Size = new Vector2(1, THICKNESS), Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativePositionAxes = Axes.X, @@ -96,7 +98,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline AlwaysPresent = true, Colour = Color4.White, BorderColour = Color4.Black, - BorderThickness = 3, + BorderThickness = THICKNESS, }); } } From 28400aa865d4727eb885d50ffe0b0a1a553cff6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 11:26:50 +0900 Subject: [PATCH 097/178] Update test scene --- .../Editor/TestSceneEditorComposeTimeline.cs | 12 ++++-------- .../Compose/Components/Timeline/Timeline.cs | 19 ++++++++----------- .../Components/Timeline/TimelineArea.cs | 4 ++-- .../Timeline/TimelineHitObjectDisplay.cs | 2 ++ .../Screens/Edit/Compose/ComposeScreen.cs | 5 +---- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs index 2b46418659..e618256c03 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs @@ -13,9 +13,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK; @@ -38,13 +36,10 @@ namespace osu.Game.Tests.Visual.Editor [BackgroundDependencyLoader] private void load(AudioManager audio) { - var editorBeatmap = new EditorBeatmap( - (Beatmap) - CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, new Mod[] { }) - ); - Beatmap.Value = new WaveformTestBeatmap(audio); + var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap); + Children = new Drawable[] { new FillFlowContainer @@ -60,6 +55,7 @@ namespace osu.Game.Tests.Visual.Editor }, new TimelineArea { + Child = new TimelineHitObjectDisplay(editorBeatmap), Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 2a565d028f..b4f3b1f610 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -36,18 +36,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { this.adjustableClock = adjustableClock; - Children = new Drawable[] + Add(waveform = new WaveformGraph { - waveform = new WaveformGraph - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Blue.Opacity(0.2f), - LowColour = colours.BlueLighter, - MidColour = colours.BlueDark, - HighColour = colours.BlueDarker, - Depth = float.MaxValue - }, - }; + RelativeSizeAxes = Axes.Both, + Colour = colours.Blue.Opacity(0.2f), + LowColour = colours.BlueLighter, + MidColour = colours.BlueDark, + HighColour = colours.BlueDarker, + Depth = float.MaxValue + }); // We don't want the centre marker to scroll AddInternal(new CentreMarker()); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 93dac059e3..8909b3d422 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineArea : Container { - private Timeline timeline; + private readonly Timeline timeline = new Timeline { RelativeSizeAxes = Axes.Both }; protected override Container Content => timeline; @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } }, - timeline = new Timeline { RelativeSizeAxes = Axes.Both } + timeline }, }, ColumnDimensions = new[] diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index 7d6d8fd729..db4aca75e5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -20,6 +20,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public TimelineHitObjectDisplay(IEditorBeatmap beatmap) { + RelativeSizeAxes = Axes.Both; + this.beatmap = beatmap; } diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 8511bc3242..cb8f2b2caa 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -35,9 +35,6 @@ namespace osu.Game.Screens.Edit.Compose return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer"); } - protected override Drawable CreateTimelineContent() => new TimelineHitObjectDisplay(composer.EditorBeatmap) - { - RelativeSizeAxes = Axes.Both, - }; + protected override Drawable CreateTimelineContent() => new TimelineHitObjectDisplay(composer.EditorBeatmap); } } From 23c7132c4fdc452ab2e3b1b7d60fd179f0ea4082 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 11:53:22 +0900 Subject: [PATCH 098/178] Add missing license header --- osu.Game/Rulesets/Objects/PathControlPoint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index d68ef4112b..83436b7a36 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Types; From 8be6abf6076224b1a3a5a7aa1490793911ab7426 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Fri, 6 Dec 2019 10:07:16 +0700 Subject: [PATCH 099/178] Add param to let function return lighter colour for some diff --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 +- osu.Game/Graphics/OsuColour.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 025b7e11fd..7bd40af512 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -168,7 +168,7 @@ namespace osu.Game.Beatmaps.Drawables difficultyName.Text = beatmap.Version; starRating.Text = $"{beatmap.StarDifficulty:0.##}"; - difficultyFlow.Colour = beatmap.DifficultyRating == DifficultyRating.ExpertPlus ? colours.Gray9 : colours.ForDifficultyRating(beatmap.DifficultyRating); + difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating, true); return true; } diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index af66f57f14..2dc12b3e67 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -38,7 +38,7 @@ namespace osu.Game.Graphics } } - public Color4 ForDifficultyRating(DifficultyRating difficulty) + public Color4 ForDifficultyRating(DifficultyRating difficulty, bool useLighterColour = false) { switch (difficulty) { @@ -56,10 +56,10 @@ namespace osu.Game.Graphics return Pink; case DifficultyRating.Expert: - return Purple; + return useLighterColour ? PurpleLight : Purple; case DifficultyRating.ExpertPlus: - return Gray0; + return useLighterColour ? Gray9 : Gray0; } } From 9f4c8db39508f52fe377d07d58cee38682dc1087 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 12:17:41 +0900 Subject: [PATCH 100/178] Add xmldoc --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 368520ba43..9ac967ef74 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -335,6 +335,9 @@ namespace osu.Game.Rulesets.Edit /// public abstract IEnumerable HitObjects { get; } + /// + /// An editor-specific beatmap, exposing mutation events. + /// public IEditorBeatmap EditorBeatmap { get; protected set; } /// From 9248fbe881f81a0d0d0516ad1e770179fb71a5d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 12:22:44 +0900 Subject: [PATCH 101/178] Remove extra checkboxes for now --- .../Edit/Compose/Components/Timeline/TimelineArea.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 8909b3d422..02e5db306d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -24,8 +24,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Masking = true; CornerRadius = 5; - OsuCheckbox hitObjectsCheckbox; - OsuCheckbox hitSoundsCheckbox; OsuCheckbox waveformCheckbox; InternalChildren = new Drawable[] @@ -64,8 +62,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Spacing = new Vector2(0, 4), Children = new[] { - hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hit objects" }, - hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hit sounds" }, waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" } } } @@ -123,8 +119,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } }; - hitObjectsCheckbox.Current.Value = true; - hitSoundsCheckbox.Current.Value = true; waveformCheckbox.Current.Value = true; timeline.WaveformVisible.BindTo(waveformCheckbox.Current); From 247609388ff71a2321baae9171bd29d364c3b397 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 12:31:22 +0900 Subject: [PATCH 102/178] Clean up unused/unnecessary properties --- .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 5 +---- .../Sliders/Components/PathControlPointVisualiser.cs | 7 +------ .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 159916f16f..4fe02135c4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public class PathControlPointPiece : BlueprintPiece { public Action RequestSelection; - public Action ControlPointsChanged; public readonly BindableBool IsSelected = new BindableBool(); public readonly int Index; @@ -103,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { markerRing.Alpha = IsSelected.Value ? 1 : 0; - Color4 colour = isSegmentSeparator ? colours.Red : colours.Yellow; + Color4 colour = slider.Path.ControlPoints[Index].Type.Value.HasValue ? colours.Red : colours.Yellow; if (IsHovered || IsSelected.Value) colour = Color4.White; marker.Colour = colour; @@ -176,7 +175,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } protected override bool OnDragEnd(DragEndEvent e) => true; - - private bool isSegmentSeparator => slider.Path.ControlPoints[Index].Type.Value.HasValue; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index c47e4d7d4a..d599ebd893 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -23,8 +23,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler, IHasContextMenu { - public Action ControlPointsChanged; - internal readonly Container Pieces; private readonly Slider slider; private readonly bool allowSelection; @@ -57,10 +55,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components while (slider.Path.ControlPoints.Count > Pieces.Count) { - var piece = new PathControlPointPiece(slider, Pieces.Count) - { - ControlPointsChanged = c => ControlPointsChanged?.Invoke(c), - }; + var piece = new PathControlPointPiece(slider, Pieces.Count); if (allowSelection) piece.RequestSelection = selectPiece; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 62a22dc858..341db249cc 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders bodyPiece = new SliderBodyPiece(), headCirclePiece = new HitCirclePiece(), tailCirclePiece = new HitCirclePiece(), - new PathControlPointVisualiser(HitObject, false) { ControlPointsChanged = _ => updateSlider() }, + new PathControlPointVisualiser(HitObject, false) }; setState(PlacementState.Initial); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index ab52c906e8..8388d78c42 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders BodyPiece = new SliderBodyPiece(), HeadBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.Start), TailBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.End), - ControlPointVisualiser = new PathControlPointVisualiser(sliderObject, true) { ControlPointsChanged = onNewControlPoints }, + ControlPointVisualiser = new PathControlPointVisualiser(sliderObject, true) }; } From b1426d1b22fe9789dd99df497ae2f315b1ac2c2a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 12:51:43 +0900 Subject: [PATCH 103/178] Full impossible nullref --- .../Screens/Edit/Compose/ComposeScreen.cs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index cb8f2b2caa..6984716a2c 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -16,23 +16,20 @@ namespace osu.Game.Screens.Edit.Compose protected override Drawable CreateMainContent() { var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance(); - composer = ruleset?.CreateHitObjectComposer(); - if (composer != null) - { - var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); + if (ruleset == null || composer == null) + return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer"); - // the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation - // full access to all skin sources. - var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider)); + var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); - // load the skinning hierarchy first. - // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources. - return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer)); - } + // the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation + // full access to all skin sources. + var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider)); - return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer"); + // load the skinning hierarchy first. + // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources. + return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer)); } protected override Drawable CreateTimelineContent() => new TimelineHitObjectDisplay(composer.EditorBeatmap); From a89a23fe08ea1ec85749a665717c94c6d7b7cc9b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 13:08:28 +0900 Subject: [PATCH 104/178] Use linq to simplify some expressions --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 341db249cc..6f53a9e4b1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders case PlacementState.Body: // The given screen-space position may have been externally snapped, but the unsnapped position from the input manager // is used instead since snapping control points doesn't make much sense - HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1].Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; + HitObject.Path.ControlPoints.Last().Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; break; } } @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders switch (e.Button) { case MouseButton.Left: - HitObject.Path.ControlPoints.Add(new PathControlPoint { Position = { Value = HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1].Position.Value } }); + HitObject.Path.ControlPoints.Add(new PathControlPoint { Position = { Value = HitObject.Path.ControlPoints.Last().Position.Value } }); break; } @@ -108,6 +108,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override bool OnDoubleClick(DoubleClickEvent e) { + // At the point of a double click, there's guaranteed to be at least two points - one from the click, and one from the cursor HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 2].Type.Value = PathType.Bezier; return true; } From de23364608a8f6568f7bb1c46080c8c6ef6fee60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 13:47:34 +0900 Subject: [PATCH 105/178] Add failing test --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 26 +++++++++++++++++++ osu.Game/Screens/Play/PlayerLoader.cs | 10 ++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 74ae641bfe..5142b98898 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -19,6 +20,7 @@ using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens; @@ -55,6 +57,9 @@ namespace osu.Game.Tests.Visual.Gameplay beforeLoadAction?.Invoke(); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToClock(Beatmap.Value.Track); + InputManager.Child = container = new TestPlayerLoaderContainer( loader = new TestPlayerLoader(() => { @@ -63,6 +68,25 @@ namespace osu.Game.Tests.Visual.Gameplay })); } + /// + /// When exits early, it has to wait for the player load task + /// to complete before running disposal on player. This previously caused an issue where mod + /// speed adjustments were undone too late, causing cross-screen pollution. + /// + [Test] + public void TestEarlyExit() + { + AddStep("apply mods", () => Mods.Value = new[] { new OsuModNightcore() }); + AddStep("load dummy beatmap", () => ResetPlayer(false)); + AddUntilStep("wait for current", () => loader.IsCurrentScreen()); + AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1); + AddStep("exit loader", () => loader.Exit()); + AddUntilStep("wait for not current", () => !loader.IsCurrentScreen()); + AddAssert("player did not load", () => !player.IsLoaded); + AddUntilStep("player disposed", () => loader.DisposalTask?.IsCompleted == true); + AddAssert("mod rate still applied", () => Beatmap.Value.Track.Rate != 1); + } + [Test] public void TestBlockLoadViaMouseMovement() { @@ -196,6 +220,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public new VisualSettings VisualSettings => base.VisualSettings; + public new Task DisposalTask => base.DisposalTask; + public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 87d902b547..774ca02be1 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -55,7 +55,9 @@ namespace osu.Game.Screens.Play protected override bool PlayResumeSound => false; - private Task loadTask; + protected Task LoadTask; + + protected Task DisposalTask; private InputManager inputManager; private IdleTracker idleTracker; @@ -159,7 +161,7 @@ namespace osu.Game.Screens.Play player.RestartCount = restartCount; player.RestartRequested = restartRequested; - loadTask = LoadComponentAsync(player, _ => info.Loading = false); + LoadTask = LoadComponentAsync(player, _ => info.Loading = false); } private void contentIn() @@ -250,7 +252,7 @@ namespace osu.Game.Screens.Play { if (!this.IsCurrentScreen()) return; - loadTask = null; + LoadTask = null; //By default, we want to load the player and never be returned to. //Note that this may change if the player we load requested a re-run. @@ -301,7 +303,7 @@ namespace osu.Game.Screens.Play if (isDisposing) { // if the player never got pushed, we should explicitly dispose it. - loadTask?.ContinueWith(_ => player.Dispose()); + DisposalTask = LoadTask?.ContinueWith(_ => player.Dispose()); } } From 48c6279e8bb9d9544afae76e9cb5d1598b62627b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 13:49:01 +0900 Subject: [PATCH 106/178] Only undo adjustments in GameplayClockContainer if applied at least once --- osu.Game/Screens/Play/GameplayClockContainer.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index ff78d85bf0..58c9a6a784 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -214,10 +214,13 @@ namespace osu.Game.Screens.Play base.Update(); } + private bool speedAdjustmentsApplied; + private void updateRate() { if (sourceClock == null) return; + speedAdjustmentsApplied = true; sourceClock.ResetSpeedAdjustments(); if (sourceClock is IHasTempoAdjust tempo) @@ -239,7 +242,12 @@ namespace osu.Game.Screens.Play private void removeSourceClockAdjustments() { - sourceClock.ResetSpeedAdjustments(); + if (speedAdjustmentsApplied) + { + sourceClock.ResetSpeedAdjustments(); + speedAdjustmentsApplied = false; + } + (sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); } } From 21ceb7f85dbbffb771c443f394a98385a3b75b87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 14:40:45 +0900 Subject: [PATCH 107/178] Always display skins at native sizes for now --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 4 +++- osu.Game/Skinning/SkinnableDrawable.cs | 4 ++-- osu.Game/Skinning/SkinnableSprite.cs | 2 +- osu.Game/Skinning/SkinnableSpriteText.cs | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 479c250eab..ec9792d59d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -49,7 +49,9 @@ namespace osu.Game.Rulesets.Osu.Skinning return this.GetAnimation(component.LookupName, true, false); case OsuSkinComponents.SliderFollowCircle: - return this.GetAnimation("sliderfollowcircle", true, true); + var followCircle = this.GetAnimation("sliderfollowcircle", true, true); + followCircle.Scale *= 0.5f; + return followCircle; case OsuSkinComponents.SliderBall: var sliderBallContent = this.GetAnimation("sliderb", true, true, ""); diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 9ca5d60cb0..fda031e6cb 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -29,13 +29,13 @@ namespace osu.Game.Skinning /// A function to create the default skin implementation of this element. /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. /// How (if at all) the should be resize to fit within our own bounds. - public SkinnableDrawable(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public SkinnableDrawable(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : this(component, allowFallback, confineMode) { createDefault = defaultImplementation; } - protected SkinnableDrawable(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + protected SkinnableDrawable(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(allowFallback) { this.component = component; diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index e225bfc490..5352928ec6 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -19,7 +19,7 @@ namespace osu.Game.Skinning [Resolved] private TextureStore textures { get; set; } - public SkinnableSprite(string textureName, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public SkinnableSprite(string textureName, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(new SpriteComponent(textureName), allowFallback, confineMode) { } diff --git a/osu.Game/Skinning/SkinnableSpriteText.cs b/osu.Game/Skinning/SkinnableSpriteText.cs index e72f9c9811..567dd348e1 100644 --- a/osu.Game/Skinning/SkinnableSpriteText.cs +++ b/osu.Game/Skinning/SkinnableSpriteText.cs @@ -8,7 +8,7 @@ namespace osu.Game.Skinning { public class SkinnableSpriteText : SkinnableDrawable, IHasText { - public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(component, defaultImplementation, allowFallback, confineMode) { } From 27dd12a66d885d12b74ddc1a5e0ead595b2f0d30 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 15:06:31 +0900 Subject: [PATCH 108/178] Rewrite slider length calculation for readability --- osu.Game/Rulesets/Objects/SliderPath.cs | 63 +++++++++++++------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 6ad5d2f1c3..6c24ee6878 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -40,6 +40,7 @@ namespace osu.Game.Rulesets.Objects private readonly List calculatedPath = new List(); private readonly List cumulativeLength = new List(); + private double calculatedLength; /// /// Creates a new . @@ -158,7 +159,7 @@ namespace osu.Game.Rulesets.Objects return; calculatePath(); - calculateCumulativeLength(); + calculateLength(); pathCache.Validate(); } @@ -227,46 +228,48 @@ namespace osu.Game.Rulesets.Objects } } - private void calculateCumulativeLength() + private void calculateLength() { - double l = 0; - + calculatedLength = 0; cumulativeLength.Clear(); - cumulativeLength.Add(l); + cumulativeLength.Add(0); - double? expectedDistance = ExpectedDistance.Value; - - for (int i = 0; i < calculatedPath.Count - 1; ++i) + for (int i = 0; i < calculatedPath.Count - 1; i++) { Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; - double d = diff.Length; - - // Shorted slider paths that are too long compared to the expected distance - if (expectedDistance.HasValue && expectedDistance - l < d) - { - calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((expectedDistance - l) / d); - calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); - - l = expectedDistance.Value; - cumulativeLength.Add(l); - break; - } - - l += d; - cumulativeLength.Add(l); + calculatedLength += diff.Length; + cumulativeLength.Add(calculatedLength); } - // Lengthen slider paths that are too short compared to the expected distance - if (expectedDistance.HasValue && l < expectedDistance && calculatedPath.Count > 1) + if (ExpectedDistance.Value is double expectedDistance && calculatedLength != expectedDistance) { - Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; - double d = diff.Length; + // The last length is always incorrect + cumulativeLength.RemoveAt(cumulativeLength.Count - 1); - if (d <= 0) + int pathEndIndex = calculatedPath.Count - 1; + + if (calculatedLength > expectedDistance) + { + // The path will be shortened further, in which case we should trim any more unnecessary lengths and their associated path segments + while (cumulativeLength.Count > 0 && cumulativeLength[cumulativeLength.Count - 1] > expectedDistance) + { + cumulativeLength.RemoveAt(cumulativeLength.Count - 1); + calculatedPath.RemoveAt(pathEndIndex--); + } + } + + if (pathEndIndex <= 0) + { + // The expected distance is negative or zero + // TODO: Perhaps negative path lengths should be disallowed altogether return; + } - calculatedPath[calculatedPath.Count - 1] += diff * (float)((expectedDistance - l) / d); - cumulativeLength[calculatedPath.Count - 1] = expectedDistance.Value; + // The direction of the segment to shorten or lengthen + Vector2 dir = (calculatedPath[pathEndIndex] - calculatedPath[pathEndIndex - 1]).Normalized(); + + calculatedPath[pathEndIndex] = calculatedPath[pathEndIndex - 1] + dir * (float)(expectedDistance - cumulativeLength[cumulativeLength.Count - 1]); + cumulativeLength.Add(expectedDistance); } } From 12f1c9e088c307ac86a1411fd7e80d6feba7fefd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 15:29:02 +0900 Subject: [PATCH 109/178] Fix test failure --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 5142b98898..dbea8d28a6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -76,8 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestEarlyExit() { - AddStep("apply mods", () => Mods.Value = new[] { new OsuModNightcore() }); - AddStep("load dummy beatmap", () => ResetPlayer(false)); + AddStep("load dummy beatmap", () => ResetPlayer(false, () => Mods.Value = new[] { new OsuModNightcore() })); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1); AddStep("exit loader", () => loader.Exit()); From 2654710d917e47b44f80f38d3600203d14355a0c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 15:37:00 +0900 Subject: [PATCH 110/178] Add tests and fix negative expected distances --- .../Visual/Gameplay/TestSceneSliderPath.cs | 34 +++++++++++++++++++ osu.Game/Rulesets/Objects/SliderPath.cs | 1 + 2 files changed, 35 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index fe2cc188a5..27d39d0f17 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -146,7 +146,41 @@ namespace osu.Game.Tests.Visual.Gameplay break; } }); + } + [Test] + public void TestLengthenLastSegment() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("lengthen last segment", () => path.ExpectedDistance.Value = 300); + } + + [Test] + public void TestShortenLastSegment() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150); + } + + [Test] + public void TestShortenFirstSegment() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("shorten first segment", () => path.ExpectedDistance.Value = 50); + } + + [Test] + public void TestShortenToZeroLength() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("shorten to 0 length", () => path.ExpectedDistance.Value = 0); + } + + [Test] + public void TestShortenToNegativeLength() + { + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("shorten to -10 length", () => path.ExpectedDistance.Value = -10); } private List createSegment(PathType type, params Vector2[] controlPoints) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 6c24ee6878..095353e3f3 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -262,6 +262,7 @@ namespace osu.Game.Rulesets.Objects { // The expected distance is negative or zero // TODO: Perhaps negative path lengths should be disallowed altogether + cumulativeLength.Add(0); return; } From 3358ab9f8abde79becfd4bca2a56361170aef2e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 15:53:01 +0900 Subject: [PATCH 111/178] Adjust diffcalc test expected value The difference is caused by the reworked calculateLength() of SliderPath. This comes as a result of the increased accuracy of path lengthenings due to calculating the final position relative to the second-to-last point, rather than relative to the last point. --- osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 693faee3b7..85a41137d4 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.931145117263422, "diffcalc-test")] + [TestCase(6.9311451172608853d, "diffcalc-test")] [TestCase(1.0736587013228804d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); From b4e1b5fa983fdb826ec8693b3fabdcd66e03e2c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 15:53:19 +0900 Subject: [PATCH 112/178] Explose + use the full calculated length of the path --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 3 +-- .../Blueprints/Sliders/SliderSelectionBlueprint.cs | 3 +-- osu.Game/Rulesets/Objects/SliderPath.cs | 14 +++++++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 6f53a9e4b1..bbdc43e16f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -133,8 +133,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { - HitObject.Path.ExpectedDistance.Value = null; - HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.Distance) ?? (float)HitObject.Path.Distance; + HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; bodyPiece.UpdateFrom(HitObject); headCirclePiece.UpdateFrom(HitObject.HeadCircle); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 8388d78c42..7b3ca29e35 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -114,8 +114,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void onNewControlPoints(Vector2[] controlPoints) { - HitObject.Path.ExpectedDistance.Value = null; - HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.Distance) ?? (float)HitObject.Path.Distance; + HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; UpdateHitObject(); } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 095353e3f3..f6e7c40e12 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -103,6 +103,18 @@ namespace osu.Game.Rulesets.Objects } } + /// + /// The distance of the path prior to lengthening/shortening to account for . + /// + public double CalculatedDistance + { + get + { + ensureValid(); + return calculatedLength; + } + } + /// /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) /// to 1 (end of the slider) and stores the generated path in the given list. @@ -251,7 +263,7 @@ namespace osu.Game.Rulesets.Objects if (calculatedLength > expectedDistance) { // The path will be shortened further, in which case we should trim any more unnecessary lengths and their associated path segments - while (cumulativeLength.Count > 0 && cumulativeLength[cumulativeLength.Count - 1] > expectedDistance) + while (cumulativeLength.Count > 0 && cumulativeLength[cumulativeLength.Count - 1] >= expectedDistance) { cumulativeLength.RemoveAt(cumulativeLength.Count - 1); calculatedPath.RemoveAt(pathEndIndex--); From d29ccdc25e4ea515c54a3de945fd9724271bf3e7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 16:36:08 +0900 Subject: [PATCH 113/178] Fix selection blueprint not re-snapping the path --- .../Blueprints/Sliders/SliderSelectionBlueprint.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 7b3ca29e35..9a504445f4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; @@ -44,6 +45,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders }; } + private IBindable pathVersion; + + protected override void LoadComplete() + { + base.LoadComplete(); + + pathVersion = HitObject.Path.Version.GetBoundCopy(); + pathVersion.BindValueChanged(_ => updatePath()); + } + protected override void Update() { base.Update(); @@ -112,10 +123,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return insertionIndex; } - private void onNewControlPoints(Vector2[] controlPoints) + private void updatePath() { HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; - UpdateHitObject(); } From 2b5f9515de1b8fe386a76e6f2c1398912005dd5c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 17:03:54 +0900 Subject: [PATCH 114/178] Fix multiple control point deletions --- .../Components/PathControlPointVisualiser.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index d599ebd893..cbc2a20328 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -14,6 +14,7 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Compose; using osuTK; @@ -100,21 +101,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool deleteSelected() { - int countDeleted = 0; - - foreach (var piece in Pieces) - { - if (piece.IsSelected.Value) - { - slider.Path.ControlPoints.RemoveAt(piece.Index); - countDeleted++; - } - } + List toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.Index).Select(i => slider.Path.ControlPoints[i]).ToList(); // Ensure that there are any points to be deleted - if (countDeleted == 0) + if (toRemove.Count == 0) return false; + foreach (var c in toRemove) + slider.Path.ControlPoints.Remove(c); + // If there are 0 remaining control points, treat the slider as being deleted if (slider.Path.ControlPoints.Count == 0) { From 9de032e35f1394571e3fcc05b4caa94a81b3e27d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:10:06 +0900 Subject: [PATCH 115/178] Fix SettingsItem bindable logic --- osu.Game/Overlays/Settings/SettingsItem.cs | 29 ++++++++-------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 8863e43cca..212ef5545d 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -53,27 +53,10 @@ namespace osu.Game.Overlays.Settings } } - // hold a reference to the provided bindable so we don't have to in every settings section. - private Bindable bindable; - public virtual Bindable Bindable { - get => bindable; - - set - { - if (bindable != null) - controlWithCurrent?.Current.UnbindFrom(bindable); - - bindable = value; - controlWithCurrent?.Current.BindTo(bindable); - - if (ShowsDefaultIndicator) - { - restoreDefaultButton.Bindable = bindable.GetBoundCopy(); - restoreDefaultButton.Bindable.TriggerChange(); - } - } + get => controlWithCurrent.Current; + set => controlWithCurrent.Current = value; } public virtual IEnumerable FilterTerms => Keywords == null ? new[] { LabelText } : new List(Keywords) { LabelText }.ToArray(); @@ -110,7 +93,15 @@ namespace osu.Game.Overlays.Settings private void load() { if (controlWithCurrent != null) + { controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; }; + + if (ShowsDefaultIndicator) + { + restoreDefaultButton.Bindable = controlWithCurrent.Current; + restoreDefaultButton.Bindable.TriggerChange(); + } + } } private class RestoreDefaultValueButton : Container, IHasTooltip From 901eb5d99639edc7a21caa1f2c7577507415e42a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 18:03:50 +0900 Subject: [PATCH 116/178] Fix incorrect trigger logic --- osu.Game/Overlays/Settings/SettingsItem.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 212ef5545d..9c390c34ec 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -97,10 +97,7 @@ namespace osu.Game.Overlays.Settings controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; }; if (ShowsDefaultIndicator) - { restoreDefaultButton.Bindable = controlWithCurrent.Current; - restoreDefaultButton.Bindable.TriggerChange(); - } } } @@ -116,6 +113,7 @@ namespace osu.Game.Overlays.Settings bindable = value; bindable.ValueChanged += _ => UpdateState(); bindable.DisabledChanged += _ => UpdateState(); + UpdateState(); } } From ed6d1ccd958622bbca51c8cab54f0c3dfd4a6296 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:09:24 +0900 Subject: [PATCH 117/178] Add a method for getting settings UI components automatically from a target class --- .../Configuration/SettingSourceAttribute.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 osu.Game/Configuration/SettingSourceAttribute.cs diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs new file mode 100644 index 0000000000..fe582b1461 --- /dev/null +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Configuration +{ + /// + /// An attribute to mark a bindable as being exposed to the user via settings controls. + /// Can be used in conjunction with to automatically create UI controls. + /// + [MeansImplicitUse] + [AttributeUsage(AttributeTargets.Property)] + public class SettingSourceAttribute : Attribute + { + public string Label { get; } + + public string Description { get; } + + public SettingSourceAttribute(string label, string description = null) + { + Label = label ?? string.Empty; + Description = description ?? string.Empty; + } + } + + public static class SettingSourceExtensions + { + public static IEnumerable CreateSettingsControls(this object obj) + { + var configProperties = obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute(true) != null); + + foreach (var property in configProperties) + { + var attr = property.GetCustomAttribute(true); + + switch (property.GetValue(obj)) + { + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case Bindable bBool: + yield return new SettingsCheckbox + { + LabelText = attr.Label, + Bindable = bBool + }; + + break; + } + } + } + } +} From 2fa0b30fa27801b00927b49025134cf8c8ee8ce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:49:41 +0900 Subject: [PATCH 118/178] Add textbox and dropdown support --- .../Configuration/SettingSourceAttribute.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index fe582b1461..dceef1cb7d 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -8,6 +8,7 @@ using System.Reflection; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; using osu.Game.Overlays.Settings; namespace osu.Game.Configuration @@ -40,8 +41,9 @@ namespace osu.Game.Configuration foreach (var property in configProperties) { var attr = property.GetCustomAttribute(true); + var prop = property.GetValue(obj); - switch (property.GetValue(obj)) + switch (prop) { case BindableNumber bNumber: yield return new SettingsSlider @@ -78,6 +80,30 @@ namespace osu.Game.Configuration }; break; + + case Bindable bString: + yield return new SettingsTextBox + { + LabelText = attr.Label, + Bindable = bString + }; + + break; + + case IBindable bindable: + + var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]); + + var dropdown = (Drawable)Activator.CreateInstance(dropdownType); + + dropdown.GetType().GetProperty(nameof(IHasCurrentValue.Current))?.SetValue(dropdown, obj); + + yield return dropdown; + + break; + + default: + throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({prop})"); } } } From f84705ab9605776f579f745ac6164b856bf5903e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:49:52 +0900 Subject: [PATCH 119/178] Add tests --- .../Settings/TestSceneSettingsSource.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs new file mode 100644 index 0000000000..e3dae9c27e --- /dev/null +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Configuration; +using osuTK; + +namespace osu.Game.Tests.Visual.Settings +{ + [TestFixture] + public class TestSceneSettingsSource : OsuTestScene + { + public TestSceneSettingsSource() + { + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(20), + Width = 0.5f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(50), + ChildrenEnumerable = new TestTargetClass().CreateSettingsControls() + }, + }; + } + + private class TestTargetClass + { + [SettingSource("Sample bool", "Clicking this changes a setting")] + public BindableBool TickBindable { get; } = new BindableBool(); + + [SettingSource("Sample float", "Change something for a mod")] + public BindableFloat SliderBindable { get; } = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + Default = 5, + Value = 7 + }; + + [SettingSource("Sample enum", "Change something for a mod")] + public Bindable EnumBindable { get; } = new Bindable + { + Default = TestEnum.Value1, + Value = TestEnum.Value2 + }; + + [SettingSource("Sample string", "Change something for a mod")] + public Bindable StringBindable { get; } = new Bindable(); + } + + private enum TestEnum + { + Value1, + Value2 + } + } +} From 16f8341a0228ebb5fb92c8070918537b980352cb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 18:49:01 +0900 Subject: [PATCH 120/178] Handle control point positional updates within SliderPath --- .../Components/PathControlPointVisualiser.cs | 8 -------- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 ++ osu.Game/Rulesets/Objects/SliderPath.cs | 20 +++++++++++++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index cbc2a20328..974b611533 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -117,14 +117,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return true; } - // Make control points relative - Vector2 first = slider.Path.ControlPoints[0].Position.Value; - for (int i = 0; i < slider.Path.ControlPoints.Count; i++) - slider.Path.ControlPoints[i].Position.Value = slider.Path.ControlPoints[i].Position.Value - first; - - // The slider's position defines the position of the first control point, and all further control points are relative to that point - slider.Position += first; - // Since pieces are re-used, they will not point to the deleted control points while remaining selected foreach (var piece in Pieces) piece.IsSelected.Value = false; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index b68595c67e..09657b2d47 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -100,6 +100,8 @@ namespace osu.Game.Rulesets.Osu.Objects { SamplesBindable.ItemsAdded += _ => updateNestedSamples(); SamplesBindable.ItemsRemoved += _ => updateNestedSamples(); + + Path.OffsetChanged += offset => Position += offset; } protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index f6e7c40e12..969cdcc463 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -16,6 +16,12 @@ namespace osu.Game.Rulesets.Objects { public class SliderPath { + /// + /// Invoked when the offset of the path changes. + /// The provided value indicates the offset, and should be used to re-calculate the position of the containing drawable. + /// + public event Action OffsetChanged; + /// /// The current version of this . Updated when any change to the path occurs. /// @@ -62,6 +68,20 @@ namespace osu.Game.Rulesets.Objects foreach (var c in items) c.Changed -= invalidate; + // Make all control points relative to the first one + if (ControlPoints.Count > 0) + { + Vector2 first = ControlPoints[0].Position.Value; + + if (first != Vector2.Zero) + { + foreach (var c in ControlPoints) + c.Position.Value -= first; + + OffsetChanged?.Invoke(first); + } + } + invalidate(); }; } From 52dd7bf716fa277fc157397a2cdcebe0c134e180 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 18:49:14 +0900 Subject: [PATCH 121/178] Fix deleting the first control point not working --- .../Sliders/Components/PathControlPointVisualiser.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 974b611533..ee4a37a4f2 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -108,7 +108,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return false; foreach (var c in toRemove) + { + // The first control point in the slider must have a type, so take it from the previous "first" one + // Todo: Should be handled within SliderPath itself + if (c == slider.Path.ControlPoints[0] && slider.Path.ControlPoints.Count > 1 && slider.Path.ControlPoints[1].Type.Value == null) + slider.Path.ControlPoints[1].Type.Value = slider.Path.ControlPoints[0].Type.Value; + slider.Path.ControlPoints.Remove(c); + } // If there are 0 remaining control points, treat the slider as being deleted if (slider.Path.ControlPoints.Count == 0) From 680b2653aeedbaa02cf359f6079347931db267c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 18:49:03 +0900 Subject: [PATCH 122/178] Improve animation of popup dialog buttons --- .../UserInterface/TestScenePopupDialog.cs | 22 ++++++++++++++----- .../Graphics/UserInterface/DialogButton.cs | 13 ++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs index 3d39bb7003..7207506ccd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs @@ -1,9 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual.UserInterface @@ -11,13 +14,22 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestScenePopupDialog : OsuTestScene { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(PopupDialogOkButton), + typeof(PopupDialogCancelButton), + typeof(PopupDialogButton), + typeof(DialogButton), + }; + public TestScenePopupDialog() { - Add(new TestPopupDialog - { - RelativeSizeAxes = Axes.Both, - State = { Value = Framework.Graphics.Containers.Visibility.Visible }, - }); + AddStep("new popup", () => + Add(new TestPopupDialog + { + RelativeSizeAxes = Axes.Both, + State = { Value = Framework.Graphics.Containers.Visibility.Visible }, + })); } private class TestPopupDialog : PopupDialog diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 927ad13829..15c09b10b4 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -20,6 +20,7 @@ namespace osu.Game.Graphics.UserInterface { public class DialogButton : OsuClickableContainer { + private const float idle_width = 0.8f; private const float hover_width = 0.9f; private const float hover_duration = 500; private const float glow_fade_duration = 250; @@ -99,7 +100,7 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Width = 0.8f, + Width = idle_width, Masking = true, MaskingSmoothness = 2, EdgeEffect = new EdgeEffectParameters @@ -199,14 +200,18 @@ namespace osu.Game.Graphics.UserInterface public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos); + private bool clicked; + protected override bool OnClick(ClickEvent e) { + clicked = true; colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); flash(); this.Delay(click_duration).Schedule(delegate { - colourContainer.ResizeTo(new Vector2(0.8f, 1f)); + clicked = false; + colourContainer.ResizeTo(new Vector2(idle_width, 1f)); spriteText.Spacing = Vector2.Zero; glowContainer.FadeOut(); }); @@ -230,6 +235,8 @@ namespace osu.Game.Graphics.UserInterface private void selectionChanged(ValueChangedEvent args) { + if (clicked) return; + if (args.NewValue) { spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic); @@ -238,7 +245,7 @@ namespace osu.Game.Graphics.UserInterface } else { - colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic); + colourContainer.ResizeTo(new Vector2(idle_width, 1f), hover_duration, Easing.OutElastic); spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic); glowContainer.FadeOut(glow_fade_duration, Easing.Out); } From af2305bb77491397b87aebc7f9f3d1ed49de31bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 18:53:30 +0900 Subject: [PATCH 123/178] Add null check --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index ec9792d59d..fa701a3c41 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -50,7 +50,8 @@ namespace osu.Game.Rulesets.Osu.Skinning case OsuSkinComponents.SliderFollowCircle: var followCircle = this.GetAnimation("sliderfollowcircle", true, true); - followCircle.Scale *= 0.5f; + if (followCircle != null) + followCircle.Scale *= 0.5f; return followCircle; case OsuSkinComponents.SliderBall: From f958485be1c268d53967522ccb3481716e21d3a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 18:54:54 +0900 Subject: [PATCH 124/178] Add comment about size change --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index fa701a3c41..f5b7d9166f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -51,6 +51,7 @@ namespace osu.Game.Rulesets.Osu.Skinning case OsuSkinComponents.SliderFollowCircle: var followCircle = this.GetAnimation("sliderfollowcircle", true, true); if (followCircle != null) + // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x followCircle.Scale *= 0.5f; return followCircle; From 41437242a29c4fdd7dbae5055dd679fd4763e149 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 19:39:25 +0900 Subject: [PATCH 125/178] Add initial path type progression support --- .../Sliders/SliderPlacementBlueprint.cs | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index bbdc43e16f..cd93aa0074 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -30,6 +30,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private InputManager inputManager; private PlacementState state; + private PathControlPoint segmentStart; + private PathControlPoint cursor; + private int currentSegmentLength = 1; [Resolved(CanBeNull = true)] private HitObjectComposer composer { get; set; } @@ -38,7 +41,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders : base(new Objects.Slider()) { RelativeSizeAxes = Axes.Both; - HitObject.Path.ControlPoints.Add(new PathControlPoint { Position = { Value = Vector2.Zero } }); + + segmentStart = HitObject.Path.ControlPoints[0]; + } [BackgroundDependencyLoader] @@ -70,9 +75,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders break; case PlacementState.Body: + ensureCursor(); + // The given screen-space position may have been externally snapped, but the unsnapped position from the input manager // is used instead since snapping control points doesn't make much sense - HitObject.Path.ControlPoints.Last().Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; + cursor.Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; break; } } @@ -89,7 +96,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders switch (e.Button) { case MouseButton.Left: - HitObject.Path.ControlPoints.Add(new PathControlPoint { Position = { Value = HitObject.Path.ControlPoints.Last().Position.Value } }); + ensureCursor(); + + // Detatch the cursor + cursor = null; break; } @@ -108,8 +118,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override bool OnDoubleClick(DoubleClickEvent e) { - // At the point of a double click, there's guaranteed to be at least two points - one from the click, and one from the cursor - HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 2].Type.Value = PathType.Bezier; + // Todo: This should all not occur on double click, but rather if the previous control point is hovered. + segmentStart = HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1]; + segmentStart.Type.Value = PathType.Linear; + + currentSegmentLength = 1; return true; } @@ -131,6 +144,34 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders updateSlider(); } + private void updatePathType() + { + switch (currentSegmentLength) + { + case 1: + case 2: + segmentStart.Type.Value = PathType.Linear; + break; + case 3: + segmentStart.Type.Value = PathType.PerfectCurve; + break; + default: + segmentStart.Type.Value = PathType.Bezier; + break; + } + } + + private void ensureCursor() + { + if (cursor == null) + { + HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = { Value = Vector2.Zero } }); + currentSegmentLength++; + + updatePathType(); + } + } + private void updateSlider() { HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; From 5347e7c4a2aadd5694722786f57b45b8f2ab65d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 20:14:06 +0900 Subject: [PATCH 126/178] Apply code quality improvements --- osu.Game/Configuration/SettingSourceAttribute.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index dceef1cb7d..056fa8bcc0 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using JetBrains.Annotations; using osu.Framework.Bindables; @@ -36,11 +35,13 @@ namespace osu.Game.Configuration { public static IEnumerable CreateSettingsControls(this object obj) { - var configProperties = obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute(true) != null); - - foreach (var property in configProperties) + foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) { var attr = property.GetCustomAttribute(true); + + if (attr == null) + continue; + var prop = property.GetValue(obj); switch (prop) @@ -91,9 +92,7 @@ namespace osu.Game.Configuration break; case IBindable bindable: - var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]); - var dropdown = (Drawable)Activator.CreateInstance(dropdownType); dropdown.GetType().GetProperty(nameof(IHasCurrentValue.Current))?.SetValue(dropdown, obj); From b9d12e5fe4ba3446e168355220bdf9f35ccf36ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Dec 2019 20:53:40 +0900 Subject: [PATCH 127/178] Fix nested hitobjects not updating --- .../Objects/JuiceStream.cs | 18 ++++++++++++++- osu.Game.Rulesets.Osu/Objects/Slider.cs | 23 ++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 33780427b6..366f10d61d 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -116,7 +116,23 @@ namespace osu.Game.Rulesets.Catch.Objects public double Duration => EndTime - StartTime; - public SliderPath Path { get; set; } + private readonly SliderPath path = new SliderPath(new[] { new PathControlPoint { Type = { Value = PathType.Linear } } }); + + public SliderPath Path + { + get => path; + set + { + path.ControlPoints.Clear(); + path.ExpectedDistance.Value = null; + + if (value != null) + { + path.ControlPoints.AddRange(value.ControlPoints); + path.ExpectedDistance.Value = value.ExpectedDistance.Value; + } + } + } public double Distance => Path.Distance; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 09657b2d47..4ba5265d17 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -28,7 +28,23 @@ namespace osu.Game.Rulesets.Osu.Objects public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); - public SliderPath Path { get; set; } = new SliderPath(new[] { new PathControlPoint { Type = { Value = PathType.Bezier } } }); + private readonly SliderPath path = new SliderPath(new[] { new PathControlPoint { Type = { Value = PathType.Linear } } }); + + public SliderPath Path + { + get => path; + set + { + path.ControlPoints.Clear(); + path.ExpectedDistance.Value = null; + + if (value != null) + { + path.ControlPoints.AddRange(value.ControlPoints); + path.ExpectedDistance.Value = value.ExpectedDistance.Value; + } + } + } public double Distance => Path.Distance; @@ -38,8 +54,6 @@ namespace osu.Game.Rulesets.Osu.Objects set { base.Position = value; - endPositionCache.Invalidate(); - updateNestedPositions(); } } @@ -102,6 +116,7 @@ namespace osu.Game.Rulesets.Osu.Objects SamplesBindable.ItemsRemoved += _ => updateNestedSamples(); Path.OffsetChanged += offset => Position += offset; + Path.Version.ValueChanged += _ => updateNestedPositions(); } protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) @@ -179,6 +194,8 @@ namespace osu.Game.Rulesets.Osu.Objects private void updateNestedPositions() { + endPositionCache.Invalidate(); + if (HeadCircle != null) HeadCircle.Position = Position; From 54798b134e99fb136fc50a5749a8d38a2b201892 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 8 Dec 2019 03:16:41 +0900 Subject: [PATCH 128/178] Add accessibility modifiers --- osu.Game/Screens/Play/PlayerLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 774ca02be1..57021dfc68 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -55,9 +55,9 @@ namespace osu.Game.Screens.Play protected override bool PlayResumeSound => false; - protected Task LoadTask; + protected Task LoadTask { get; private set; } - protected Task DisposalTask; + protected Task DisposalTask { get; private set; } private InputManager inputManager; private IdleTracker idleTracker; From ff8544597c3bd5d0fa1dc26b19f469dcbbdc0632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 Dec 2019 19:51:54 +0100 Subject: [PATCH 129/178] Add explicit app manifest to desktop project After the .NET Core bump to version 3.0 in the 2019.1011.0 release, reports popped up of the game not starting any more on some computers using Intel graphics cards (HD 3000 in particular). After investigation the auto-generated application manifest changed in .NET Core 3.0. In particular this seems to be a root cause for the failed start-ups on Intel cards, due to a Windows version compatibility section appearing. The section in turn affects some WinAPI calls like GetVersionEx, which will return major version 10 instead of 6 if compatibility with Windows 10 is declared. This combined with a broken check in the Intel OpenGL driver caused the crashes. To resolve this without having to patch binaries, add an explicit application manifest to the desktop project with the compatibility section removed. --- osu.Desktop/app.manifest | 20 ++++++++++++++++++++ osu.Desktop/osu.Desktop.csproj | 1 + 2 files changed, 21 insertions(+) create mode 100644 osu.Desktop/app.manifest diff --git a/osu.Desktop/app.manifest b/osu.Desktop/app.manifest new file mode 100644 index 0000000000..2e9127bf44 --- /dev/null +++ b/osu.Desktop/app.manifest @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + true + + + \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 453cf6f94d..01e4ada2f1 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -8,6 +8,7 @@ osu!lazer osu!lazer lazer.ico + app.manifest 0.0.0 0.0.0 From 929be3e9e7e32954385f36f348f9cea8d9a7595c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 8 Dec 2019 12:34:07 +0300 Subject: [PATCH 130/178] Highlight own score in BeatmapSetOverlay --- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- .../Scores/ScoreTableRowBackground.cs | 24 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 58f5f02956..f6723839b2 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores return; for (int i = 0; i < value.Count; i++) - backgroundFlow.Add(new ScoreTableRowBackground(i)); + backgroundFlow.Add(new ScoreTableRowBackground(i, value[i])); Columns = createHeaders(value[0]); Content = value.Select((s, i) => createContent(i, s)).ToArray().ToRectangular(); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs index d820f4d89d..1e10c200ab 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Online.API; +using osu.Game.Scoring; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -17,8 +19,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly Box hoveredBackground; private readonly Box background; - public ScoreTableRowBackground(int index) + private readonly int index; + private readonly ScoreInfo score; + + public ScoreTableRowBackground(int index, ScoreInfo score) { + this.index = index; + this.score = score; + RelativeSizeAxes = Axes.X; Height = 25; @@ -37,16 +45,18 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Alpha = 0, }, }; - - if (index % 2 != 0) - background.Alpha = 0; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, IAPIProvider api) { - hoveredBackground.Colour = colours.Gray4; - background.Colour = colours.Gray3; + var isOwnScore = api.LocalUser.Value.Id == score.UserID; + + if (index % 2 != 0 && !isOwnScore) + background.Alpha = 0; + + hoveredBackground.Colour = isOwnScore ? colours.GreenDark : colours.Gray4; + background.Colour = isOwnScore ? colours.GreenDarker : colours.Gray3; } protected override bool OnHover(HoverEvent e) From 13b891f3f4ed6eae1c572737238a005e199efbd1 Mon Sep 17 00:00:00 2001 From: mcendu Date: Sun, 8 Dec 2019 20:05:02 +0800 Subject: [PATCH 131/178] Crude legacy cursor rotation support --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index 470ba3acae..6bef90fbaa 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Osu.Skinning Origin = Anchor.Centre; } + private NonPlayfieldSprite cursor; + [BackgroundDependencyLoader] private void load(ISkinSource skin) { @@ -30,13 +32,14 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - new NonPlayfieldSprite + cursor = new NonPlayfieldSprite { Texture = skin.GetTexture("cursor"), Anchor = Anchor.Centre, Origin = Anchor.Centre, } }; + cursor.Spin(10000, RotationDirection.Clockwise); } } } From 4cd0dd7856e8b553a9698d86dc114f0ca0768a92 Mon Sep 17 00:00:00 2001 From: mcendu Date: Sun, 8 Dec 2019 20:47:28 +0800 Subject: [PATCH 132/178] Move transformation to LoadComplete --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index 6bef90fbaa..11039227c6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -39,6 +39,10 @@ namespace osu.Game.Rulesets.Osu.Skinning Origin = Anchor.Centre, } }; + } + + protected override void LoadComplete() + { cursor.Spin(10000, RotationDirection.Clockwise); } } From 8956768fe0d49a9d433b7ff88b601f4d6dd90e1e Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 8 Dec 2019 08:55:45 -0800 Subject: [PATCH 133/178] Fix mod buttons being selected when drag scrolling overlay --- osu.Game/Overlays/Mods/ModButton.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index c6b4787ff1..ce623dc182 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -167,10 +167,6 @@ namespace osu.Game.Overlays.Mods { switch (e.Button) { - case MouseButton.Left: - SelectNext(1); - break; - case MouseButton.Right: SelectNext(-1); break; @@ -180,6 +176,15 @@ namespace osu.Game.Overlays.Mods return true; } + protected override bool OnClick(ClickEvent e) + { + scaleContainer.ScaleTo(1, 500, Easing.OutElastic); + + SelectNext(1); + + return base.OnClick(e); + } + /// /// Select the next available mod in a specified direction. /// From 463b6c00300b1686fe4eb66742ca0f63ea373e03 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 8 Dec 2019 09:04:34 -0800 Subject: [PATCH 134/178] Remove whitespace --- osu.Game/Overlays/Mods/ModButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index ce623dc182..5af3ebab3b 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -179,7 +179,7 @@ namespace osu.Game.Overlays.Mods protected override bool OnClick(ClickEvent e) { scaleContainer.ScaleTo(1, 500, Easing.OutElastic); - + SelectNext(1); return base.OnClick(e); From e394b28799f4df5e7e01e87dbed07a8bc50127a2 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 8 Dec 2019 09:12:32 -0800 Subject: [PATCH 135/178] Remove redundant transform --- osu.Game/Overlays/Mods/ModButton.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 5af3ebab3b..69a4a4181a 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -178,11 +178,9 @@ namespace osu.Game.Overlays.Mods protected override bool OnClick(ClickEvent e) { - scaleContainer.ScaleTo(1, 500, Easing.OutElastic); - SelectNext(1); - return base.OnClick(e); + return true; } /// From c2a40c574dabf83556a23272e869768759b966d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 02:13:47 +0900 Subject: [PATCH 136/178] Split out if statement for readability --- .../Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs index 1e10c200ab..724a7f8b55 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs @@ -52,11 +52,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { var isOwnScore = api.LocalUser.Value.Id == score.UserID; - if (index % 2 != 0 && !isOwnScore) + if (isOwnScore) + background.Colour = colours.GreenDarker; + else if (index % 2 == 0) + background.Colour = colours.Gray3; + else background.Alpha = 0; hoveredBackground.Colour = isOwnScore ? colours.GreenDark : colours.Gray4; - background.Colour = isOwnScore ? colours.GreenDarker : colours.Gray3; } protected override bool OnHover(HoverEvent e) From 5cf35869e956ffcf0fc2671705bf3cb7c33cab2c Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 8 Dec 2019 10:40:39 -0800 Subject: [PATCH 137/178] Fix key config search not clearing after pressing escape --- osu.Game/Overlays/SettingsSubPanel.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 5000156e97..2235f9f338 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -4,12 +4,10 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings; using osu.Game.Screens.Ranking; using osuTK; @@ -36,7 +34,7 @@ namespace osu.Game.Overlays protected override bool DimMainContent => false; // dimming is handled by main overlay - private class BackButton : OsuClickableContainer, IKeyBindingHandler + private class BackButton : OsuClickableContainer { private AspectContainer aspect; @@ -85,20 +83,6 @@ namespace osu.Game.Overlays aspect.ScaleTo(1, 1000, Easing.OutElastic); return base.OnMouseUp(e); } - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.Back: - Click(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => false; } } } From 9974fff5cc3d52d36e0ebaf2f57cc3af5eddc4ea Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 8 Dec 2019 10:51:25 -0800 Subject: [PATCH 138/178] Make sub panel back button inherit osu button --- osu.Game/Overlays/SettingsSubPanel.cs | 32 +++++++++------------------ 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 2235f9f338..1fa233d9d4 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -3,14 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; -using osu.Game.Screens.Ranking; using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays { @@ -34,21 +34,21 @@ namespace osu.Game.Overlays protected override bool DimMainContent => false; // dimming is handled by main overlay - private class BackButton : OsuClickableContainer + private class BackButton : OsuButton { - private AspectContainer aspect; - [BackgroundDependencyLoader] private void load() { Size = new Vector2(Sidebar.DEFAULT_WIDTH); - Children = new Drawable[] + + BackgroundColour = Color4.Black; + + AddRange(new Drawable[] { - aspect = new AspectContainer + new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, Children = new Drawable[] { new SpriteIcon @@ -69,19 +69,7 @@ namespace osu.Game.Overlays }, } } - }; - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(e); - } - - protected override bool OnMouseUp(MouseUpEvent e) - { - aspect.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(e); + }); } } } From b2b252a1cc446f96c306129fe6d6011b07dafe66 Mon Sep 17 00:00:00 2001 From: mcendu Date: Mon, 9 Dec 2019 08:36:07 +0800 Subject: [PATCH 139/178] Allow skin to disable spin --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 8 ++++++-- .../Skinning/OsuLegacySkinTransformer.cs | 4 +++- osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index 11039227c6..68cf99caf1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -11,15 +11,18 @@ namespace osu.Game.Rulesets.Osu.Skinning { public class LegacyCursor : CompositeDrawable { - public LegacyCursor() + public LegacyCursor(bool spin = true) { Size = new Vector2(50); Anchor = Anchor.Centre; Origin = Anchor.Centre; + + rotate = spin; } private NonPlayfieldSprite cursor; + private bool rotate; [BackgroundDependencyLoader] private void load(ISkinSource skin) @@ -43,7 +46,8 @@ namespace osu.Game.Rulesets.Osu.Skinning protected override void LoadComplete() { - cursor.Spin(10000, RotationDirection.Clockwise); + if (rotate) + cursor.Spin(10000, RotationDirection.Clockwise); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index f5b7d9166f..f58b96844e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -81,7 +81,9 @@ namespace osu.Game.Rulesets.Osu.Skinning case OsuSkinComponents.Cursor: if (source.GetTexture("cursor") != null) - return new LegacyCursor(); + { + return new LegacyCursor(GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true); + } return null; diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index 98219cafe8..5d99960f10 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Osu.Skinning SliderPathRadius, AllowSliderBallTint, CursorExpand, + CursorRotate } } From 1cf81c49066dbf3fdafc3d029c187ad012b26d0d Mon Sep 17 00:00:00 2001 From: mcendu Date: Mon, 9 Dec 2019 08:37:32 +0800 Subject: [PATCH 140/178] rm unnecessary curlies --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index f58b96844e..56c8e74509 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -81,9 +81,7 @@ namespace osu.Game.Rulesets.Osu.Skinning case OsuSkinComponents.Cursor: if (source.GetTexture("cursor") != null) - { return new LegacyCursor(GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true); - } return null; From eb065286ae5b17081910d708fc1deb2421c969bd Mon Sep 17 00:00:00 2001 From: mcendu Date: Mon, 9 Dec 2019 08:49:44 +0800 Subject: [PATCH 141/178] fix ci --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index 68cf99caf1..85ae9d0fc4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } private NonPlayfieldSprite cursor; - private bool rotate; + private readonly bool rotate; [BackgroundDependencyLoader] private void load(ISkinSource skin) From 76aabdd2971effca216074ee33e7399b8e8b92b0 Mon Sep 17 00:00:00 2001 From: mcendu Date: Mon, 9 Dec 2019 12:11:04 +0800 Subject: [PATCH 142/178] rename field rotate to spin --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index 85ae9d0fc4..d0d11b9157 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -18,11 +18,11 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.Centre; Origin = Anchor.Centre; - rotate = spin; + this.spin = spin; } private NonPlayfieldSprite cursor; - private readonly bool rotate; + private readonly bool spin; [BackgroundDependencyLoader] private void load(ISkinSource skin) @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Skinning protected override void LoadComplete() { - if (rotate) + if (spin) cursor.Spin(10000, RotationDirection.Clockwise); } } From 4905709ea497799416c71fbcdba485612c60b67e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 13:19:21 +0900 Subject: [PATCH 143/178] Remove unused usings --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 2 -- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 -- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 -- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs | 1 - osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs | 1 - osu.Game.Rulesets.Osu/Objects/Slider.cs | 1 - .../Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs | 1 - .../Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs | 1 - osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs | 1 - .../Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs | 1 - 10 files changed, 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index ee4a37a4f2..a9616026f5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using Humanizer; @@ -17,7 +16,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Compose; -using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index cd93aa0074..639e18681b 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 9a504445f4..7431972673 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -12,7 +11,6 @@ using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 166defbc41..c5609b01e0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osuTK; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 8e2f6ffa66..21a3a0d236 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -3,7 +3,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osuTK; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 4ba5265d17..4c299fd7c2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -6,7 +6,6 @@ using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; using osu.Game.Rulesets.Objects; using System.Linq; -using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Game.Audio; using osu.Game.Beatmaps; diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index afa1f2996e..43e8d01297 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -3,7 +3,6 @@ using osuTK; using osu.Game.Audio; -using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; namespace osu.Game.Rulesets.Objects.Legacy.Catch diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index d1a8adc2b7..f94c4aaa75 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -3,7 +3,6 @@ using osuTK; using osu.Game.Audio; -using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; namespace osu.Game.Rulesets.Objects.Legacy.Mania diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 6628f7d059..b95ec703b6 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osuTK; -using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; using osu.Game.Audio; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index 7b1d64b19f..db65a61c90 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osuTK; -using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; using osu.Game.Audio; From 69deb0ca96f2ef4d22233fd8333311c290b965b0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 9 Dec 2019 07:19:55 +0300 Subject: [PATCH 144/178] Fix unavailable replays can be accessed via leaderboard --- osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 79ce04ed66..b941cd8973 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -34,7 +34,7 @@ namespace osu.Game.Online.API.Requests.Responses PP = PP, Beatmap = Beatmap, RulesetID = OnlineRulesetID, - Hash = "online", // todo: temporary? + Hash = Replay ? "online" : string.Empty, // todo: temporary? Rank = Rank, Ruleset = ruleset, Mods = mods, From 03d18186c23ab92eddda00a04f958d592ca9abf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 14:11:44 +0900 Subject: [PATCH 145/178] Fix broken merge --- .../Graphics/UserInterface/DialogButton.cs | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 15c09b10b4..aed07e56ee 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -22,8 +22,8 @@ namespace osu.Game.Graphics.UserInterface { private const float idle_width = 0.8f; private const float hover_width = 0.9f; + private const float hover_duration = 500; - private const float glow_fade_duration = 250; private const float click_duration = 200; public readonly BindableBool Selected = new BindableBool(); @@ -200,30 +200,50 @@ namespace osu.Game.Graphics.UserInterface public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos); - private bool clicked; + private bool clickAnimating; protected override bool OnClick(ClickEvent e) { - clicked = true; - colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); - flash(); - - this.Delay(click_duration).Schedule(delegate + var flash = new Box { - clicked = false; - colourContainer.ResizeTo(new Vector2(idle_width, 1f)); - spriteText.Spacing = Vector2.Zero; - glowContainer.FadeOut(); - }); + RelativeSizeAxes = Axes.Both, + Colour = ButtonColour, + Blending = BlendingParameters.Additive, + Alpha = 0.05f + }; + + colourContainer.Add(flash); + flash.FadeOutFromOne(100).Expire(); + + clickAnimating = true; + colourContainer.ResizeWidthTo(colourContainer.Width * 1.05f, 100, Easing.OutQuint) + .OnComplete(_ => + { + clickAnimating = false; + Selected.TriggerChange(); + }); return base.OnClick(e); } + protected override bool OnMouseDown(MouseDownEvent e) + { + colourContainer.ResizeWidthTo(hover_width * 0.98f, click_duration * 4, Easing.OutQuad); + return base.OnMouseDown(e); + } + + protected override bool OnMouseUp(MouseUpEvent e) + { + if (Selected.Value) + colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In); + return base.OnMouseUp(e); + } + protected override bool OnHover(HoverEvent e) { base.OnHover(e); - Selected.Value = true; + return true; } @@ -235,38 +255,23 @@ namespace osu.Game.Graphics.UserInterface private void selectionChanged(ValueChangedEvent args) { - if (clicked) return; + if (clickAnimating) + return; if (args.NewValue) { spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic); - colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic); - glowContainer.FadeIn(glow_fade_duration, Easing.Out); + colourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic); + glowContainer.FadeIn(hover_duration, Easing.OutQuint); } else { - colourContainer.ResizeTo(new Vector2(idle_width, 1f), hover_duration, Easing.OutElastic); + colourContainer.ResizeWidthTo(idle_width, hover_duration, Easing.OutElastic); spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic); - glowContainer.FadeOut(glow_fade_duration, Easing.Out); + glowContainer.FadeOut(hover_duration, Easing.OutQuint); } } - private void flash() - { - var flash = new Box - { - RelativeSizeAxes = Axes.Both - }; - - colourContainer.Add(flash); - - flash.Colour = ButtonColour; - flash.Blending = BlendingParameters.Additive; - flash.Alpha = 0.3f; - flash.FadeOutFromOne(click_duration); - flash.Expire(); - } - private void updateGlow() { leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour); From aff1b93a0790672311926a9e0f6bec711c1ce011 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 14:43:23 +0900 Subject: [PATCH 146/178] Move config retrieval into LegacySliderBall --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 12 ++++++------ .../Skinning/OsuLegacySkinTransformer.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index d0d11b9157..05b38ae195 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -11,22 +11,22 @@ namespace osu.Game.Rulesets.Osu.Skinning { public class LegacyCursor : CompositeDrawable { - public LegacyCursor(bool spin = true) + private NonPlayfieldSprite cursor; + private bool spin; + + public LegacyCursor() { Size = new Vector2(50); Anchor = Anchor.Centre; Origin = Anchor.Centre; - - this.spin = spin; } - private NonPlayfieldSprite cursor; - private readonly bool spin; - [BackgroundDependencyLoader] private void load(ISkinSource skin) { + spin = skin.GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true; + InternalChildren = new Drawable[] { new NonPlayfieldSprite diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 56c8e74509..f5b7d9166f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Skinning case OsuSkinComponents.Cursor: if (source.GetTexture("cursor") != null) - return new LegacyCursor(GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true); + return new LegacyCursor(); return null; From 12cfb7dedb3a8e631abdeb73c18d2169d7dd8255 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2019 07:28:11 +0000 Subject: [PATCH 147/178] Bump System.ComponentModel.Annotations from 4.6.0 to 4.7.0 Bumps [System.ComponentModel.Annotations](https://github.com/dotnet/corefx) from 4.6.0 to 4.7.0. - [Release notes](https://github.com/dotnet/corefx/releases) - [Commits](https://github.com/dotnet/corefx/commits) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 086359ee41..ef16738908 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -27,6 +27,6 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 0eb8d98a63..5090190f28 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -86,7 +86,7 @@ - + From 53f7c753fb83cec669a56a12b9a4a19e096e9390 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 16:44:19 +0900 Subject: [PATCH 148/178] General cleanups --- .../Sliders/Components/PathControlPointVisualiser.cs | 2 +- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 3 ++- osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index a9616026f5..f0888b34fe 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool deleteSelected() { - List toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.Index).Select(i => slider.Path.ControlPoints[i]).ToList(); + List toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => slider.Path.ControlPoints[p.Index]).ToList(); // Ensure that there are any points to be deleted if (toRemove.Count == 0) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 639e18681b..7dd2017e48 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders RelativeSizeAxes = Axes.Both; segmentStart = HitObject.Path.ControlPoints[0]; - } [BackgroundDependencyLoader] @@ -150,9 +149,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders case 2: segmentStart.Type.Value = PathType.Linear; break; + case 3: segmentStart.Type.Value = PathType.PerfectCurve; break; + default: segmentStart.Type.Value = PathType.Bezier; break; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index 27d39d0f17..606395c289 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -6,7 +6,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Lines; -using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osuTK; @@ -141,6 +140,7 @@ namespace osu.Game.Tests.Visual.Gameplay case 2: path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100))); break; + case 4: path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); break; From b764a74919e5da3ac88b576e7b954b4a7bab6463 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2019 07:52:22 +0000 Subject: [PATCH 149/178] Bump Microsoft.Win32.Registry from 4.6.0 to 4.7.0 Bumps [Microsoft.Win32.Registry](https://github.com/dotnet/corefx) from 4.6.0 to 4.7.0. - [Release notes](https://github.com/dotnet/corefx/releases) - [Commits](https://github.com/dotnet/corefx/commits) Signed-off-by: dependabot-preview[bot] --- osu.Desktop/osu.Desktop.csproj | 2 +- osu.Game.Tournament/osu.Game.Tournament.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 01e4ada2f1..63aa999a97 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -28,7 +28,7 @@ - + diff --git a/osu.Game.Tournament/osu.Game.Tournament.csproj b/osu.Game.Tournament/osu.Game.Tournament.csproj index 8e881fdd9c..9cce40c9d3 100644 --- a/osu.Game.Tournament/osu.Game.Tournament.csproj +++ b/osu.Game.Tournament/osu.Game.Tournament.csproj @@ -9,6 +9,6 @@ - + \ No newline at end of file From 3861abce826e0f533f97cc678b5333046409b55f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2019 07:52:23 +0000 Subject: [PATCH 150/178] Bump System.IO.Packaging from 4.6.0 to 4.7.0 Bumps [System.IO.Packaging](https://github.com/dotnet/corefx) from 4.6.0 to 4.7.0. - [Release notes](https://github.com/dotnet/corefx/releases) - [Commits](https://github.com/dotnet/corefx/commits) Signed-off-by: dependabot-preview[bot] --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 01e4ada2f1..70b9d5c184 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,7 +24,7 @@ - + From eb074b7058cc6586f447d308131db79b8af5724e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 17:34:04 +0900 Subject: [PATCH 151/178] Allow mods to apply to track, not clock --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 4 +- osu.Game/Overlays/MusicController.cs | 4 +- .../Difficulty/DifficultyCalculator.cs | 8 ++-- .../Difficulty/PerformanceCalculator.cs | 8 ++-- ...icableToClock.cs => IApplicableToTrack.cs} | 6 +-- osu.Game/Rulesets/Mods/ModDaycore.cs | 10 ++--- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 2 +- osu.Game/Rulesets/Mods/ModHalfTime.cs | 2 +- osu.Game/Rulesets/Mods/ModNightcore.cs | 10 ++--- osu.Game/Rulesets/Mods/ModTimeAdjust.cs | 12 ++---- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 32 ++++------------ .../Screens/Play/GameplayClockContainer.cs | 37 +++++++++---------- 12 files changed, 52 insertions(+), 83 deletions(-) rename osu.Game/Rulesets/Mods/{IApplicableToClock.cs => IApplicableToTrack.cs} (69%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index dbea8d28a6..f02361e685 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -57,8 +57,8 @@ namespace osu.Game.Tests.Visual.Gameplay beforeLoadAction?.Invoke(); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToClock(Beatmap.Value.Track); + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToTrack(Beatmap.Value.Track); InputManager.Child = container = new TestPlayerLoaderContainer( loader = new TestPlayerLoader(() => diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 5e0a67c2f7..bafdad3508 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -261,8 +261,8 @@ namespace osu.Game.Overlays if (allowRateAdjustments) { - foreach (var mod in mods.Value.OfType()) - mod.ApplyToClock(track); + foreach (var mod in mods.Value.OfType()) + mod.ApplyToTrack(track); } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index e31c963403..1902de5bda 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; @@ -41,10 +41,10 @@ namespace osu.Game.Rulesets.Difficulty IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); - var clock = new StopwatchClock(); - mods.OfType().ForEach(m => m.ApplyToClock(clock)); + var track = new TrackVirtual(10000); + mods.OfType().ForEach(m => m.ApplyToTrack(track)); - return calculate(playableBeatmap, mods, clock.Rate); + return calculate(playableBeatmap, mods, track.Rate); } /// diff --git a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs index 9ab81b9580..ac3b817840 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -35,9 +35,9 @@ namespace osu.Game.Rulesets.Difficulty protected virtual void ApplyMods(Mod[] mods) { - var clock = new StopwatchClock(); - mods.OfType().ForEach(m => m.ApplyToClock(clock)); - TimeRate = clock.Rate; + var track = new TrackVirtual(10000); + mods.OfType().ForEach(m => m.ApplyToTrack(track)); + TimeRate = track.Rate; } public abstract double Calculate(Dictionary categoryDifficulty = null); diff --git a/osu.Game/Rulesets/Mods/IApplicableToClock.cs b/osu.Game/Rulesets/Mods/IApplicableToTrack.cs similarity index 69% rename from osu.Game/Rulesets/Mods/IApplicableToClock.cs rename to osu.Game/Rulesets/Mods/IApplicableToTrack.cs index e5767b5fbf..4d6d958e82 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToClock.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToTrack.cs @@ -1,15 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Timing; +using osu.Framework.Audio.Track; namespace osu.Game.Rulesets.Mods { /// /// An interface for mods that make adjustments to the track. /// - public interface IApplicableToClock : IApplicableMod + public interface IApplicableToTrack : IApplicableMod { - void ApplyToClock(IAdjustableClock clock); + void ApplyToTrack(Track track); } } diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 7e6d959119..dcb3cb5597 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Graphics.Sprites; -using osu.Framework.Timing; namespace osu.Game.Rulesets.Mods { @@ -14,12 +13,9 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => FontAwesome.Solid.Question; public override string Description => "Whoaaaaa..."; - public override void ApplyToClock(IAdjustableClock clock) + public override void ApplyToTrack(Track track) { - if (clock is IHasPitchAdjust pitchAdjust) - pitchAdjust.PitchAdjust *= RateAdjust; - else - base.ApplyToClock(clock); + track.Frequency.Value *= RateAdjust; } } } diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index a5e76e32b1..5e685b040e 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModDoubleTime : ModTimeAdjust, IApplicableToClock + public abstract class ModDoubleTime : ModTimeAdjust { public override string Name => "Double Time"; public override string Acronym => "DT"; diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 27369f4c30..d17ddd2253 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModHalfTime : ModTimeAdjust, IApplicableToClock + public abstract class ModHalfTime : ModTimeAdjust { public override string Name => "Half Time"; public override string Acronym => "HT"; diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index dc0fc33088..a4f1ef5a72 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Graphics.Sprites; -using osu.Framework.Timing; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods @@ -15,12 +14,9 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => OsuIcon.ModNightcore; public override string Description => "Uguuuuuuuu..."; - public override void ApplyToClock(IAdjustableClock clock) + public override void ApplyToTrack(Track track) { - if (clock is IHasPitchAdjust pitchAdjust) - pitchAdjust.PitchAdjust *= RateAdjust; - else - base.ApplyToClock(clock); + track.Frequency.Value *= RateAdjust; } } } diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs index 513883f552..f137a75ed8 100644 --- a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs @@ -2,23 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Audio; -using osu.Framework.Timing; +using osu.Framework.Audio.Track; namespace osu.Game.Rulesets.Mods { - public abstract class ModTimeAdjust : Mod + public abstract class ModTimeAdjust : Mod, IApplicableToTrack { public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) }; protected abstract double RateAdjust { get; } - public virtual void ApplyToClock(IAdjustableClock clock) + public virtual void ApplyToTrack(Track track) { - if (clock is IHasTempoAdjust tempo) - tempo.TempoAdjust *= RateAdjust; - else - clock.Rate *= RateAdjust; + track.TempoAdjust *= RateAdjust; } } } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index e231225e3c..d95d354487 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -3,15 +3,14 @@ using System; using System.Linq; -using osu.Framework.Audio; -using osu.Framework.Timing; +using osu.Framework.Audio.Track; using osu.Game.Beatmaps; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mods { - public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToClock, IApplicableToBeatmap + public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToTrack, IApplicableToBeatmap { /// /// The point in the beatmap at which the final ramping rate should be reached. @@ -24,11 +23,11 @@ namespace osu.Game.Rulesets.Mods private double finalRateTime; private double beginRampTime; - private IAdjustableClock clock; + private Track track; - public virtual void ApplyToClock(IAdjustableClock clock) + public virtual void ApplyToTrack(Track track) { - this.clock = clock; + this.track = track; lastAdjust = 1; @@ -46,7 +45,7 @@ namespace osu.Game.Rulesets.Mods public virtual void Update(Playfield playfield) { - applyAdjustment((clock.CurrentTime - beginRampTime) / finalRateTime); + applyAdjustment((track.CurrentTime - beginRampTime) / finalRateTime); } private double lastAdjust = 1; @@ -59,23 +58,8 @@ namespace osu.Game.Rulesets.Mods { double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment)); - switch (clock) - { - case IHasPitchAdjust pitch: - pitch.PitchAdjust /= lastAdjust; - pitch.PitchAdjust *= adjust; - break; - - case IHasTempoAdjust tempo: - tempo.TempoAdjust /= lastAdjust; - tempo.TempoAdjust *= adjust; - break; - - default: - clock.Rate /= lastAdjust; - clock.Rate *= adjust; - break; - } + track.Tempo.Value /= lastAdjust; + track.Tempo.Value *= lastAdjust; lastAdjust = adjust; } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 58c9a6a784..1508758c87 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -28,9 +28,9 @@ namespace osu.Game.Screens.Play private readonly IReadOnlyList mods; /// - /// The original source (usually a 's track). + /// The 's track. /// - private IAdjustableClock sourceClock; + private Track track; public readonly BindableBool IsPaused = new BindableBool(); @@ -72,8 +72,8 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both; - sourceClock = (IAdjustableClock)beatmap.Track ?? new StopwatchClock(); - (sourceClock as IAdjustableAudioComponent)?.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + track = beatmap.Track; + track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; @@ -127,11 +127,11 @@ namespace osu.Game.Screens.Play { Task.Run(() => { - sourceClock.Reset(); + track.Reset(); Schedule(() => { - adjustableClock.ChangeSource(sourceClock); + adjustableClock.ChangeSource(track); updateRate(); if (!IsPaused.Value) @@ -197,13 +197,13 @@ namespace osu.Game.Screens.Play /// public void StopUsingBeatmapClock() { - if (sourceClock != beatmap.Track) + if (track != beatmap.Track) return; removeSourceClockAdjustments(); - sourceClock = new TrackVirtual(beatmap.Track.Length); - adjustableClock.ChangeSource(sourceClock); + track = new TrackVirtual(beatmap.Track.Length); + adjustableClock.ChangeSource(track); } protected override void Update() @@ -218,18 +218,15 @@ namespace osu.Game.Screens.Play private void updateRate() { - if (sourceClock == null) return; + if (track == null) return; speedAdjustmentsApplied = true; - sourceClock.ResetSpeedAdjustments(); + track.ResetSpeedAdjustments(); - if (sourceClock is IHasTempoAdjust tempo) - tempo.TempoAdjust = UserPlaybackRate.Value; - else - sourceClock.Rate = UserPlaybackRate.Value; + track.Tempo.Value = UserPlaybackRate.Value; - foreach (var mod in mods.OfType()) - mod.ApplyToClock(sourceClock); + foreach (var mod in mods.OfType()) + mod.ApplyToTrack(track); } protected override void Dispose(bool isDisposing) @@ -237,18 +234,18 @@ namespace osu.Game.Screens.Play base.Dispose(isDisposing); removeSourceClockAdjustments(); - sourceClock = null; + track = null; } private void removeSourceClockAdjustments() { if (speedAdjustmentsApplied) { - sourceClock.ResetSpeedAdjustments(); + track.ResetSpeedAdjustments(); speedAdjustmentsApplied = false; } - (sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + (track as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); } } } From 5a093c039cedd3b214827871a77c3ef6c69852c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 17:45:08 +0900 Subject: [PATCH 152/178] Simplify path/point construction --- osu.Game/Rulesets/Objects/PathControlPoint.cs | 15 ++++++++++++++- osu.Game/Rulesets/Objects/SliderPath.cs | 15 ++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index 83436b7a36..de40c24060 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -26,12 +26,25 @@ namespace osu.Game.Rulesets.Objects /// internal event Action Changed; + /// + /// Creates a new . + /// public PathControlPoint() + : this(Vector2.Zero, null) + { + } + + /// + /// Creates a new with a provided position and type. + /// + /// The initial position. + /// The initial type. + public PathControlPoint(Vector2 position, PathType? type = null) { Position.ValueChanged += _ => Changed?.Invoke(); Type.ValueChanged += _ => Changed?.Invoke(); } - public bool Equals(PathControlPoint other) => Position.Value == other.Position.Value && Type.Value == other.Type.Value; + public bool Equals(PathControlPoint other) => Position.Value == other?.Position.Value && Type.Value == other.Type.Value; } } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 969cdcc463..dbd236107c 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -40,12 +40,10 @@ namespace osu.Game.Rulesets.Objects /// public readonly BindableList ControlPoints = new BindableList(); - public readonly List Test = new List(); - - private readonly Cached pathCache = new Cached(); - private readonly List calculatedPath = new List(); private readonly List cumulativeLength = new List(); + private readonly Cached pathCache = new Cached(); + private double calculatedLength; /// @@ -87,7 +85,7 @@ namespace osu.Game.Rulesets.Objects } /// - /// Creates a new . + /// Creates a new initialised with a list of control points. /// /// An optional set of s to initialise the path with. /// A user-set distance of the path that may be shorter or longer than the true distance between all control points. @@ -101,13 +99,8 @@ namespace osu.Game.Rulesets.Objects } public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) - : this() + : this(controlPoints.Select((c, i) => new PathControlPoint(c, i == 0 ? (PathType?)type : null)).ToArray(), expectedDistance) { - foreach (var c in controlPoints) - ControlPoints.Add(new PathControlPoint { Position = { Value = c } }); - ControlPoints[0].Type.Value = type; - - ExpectedDistance.Value = expectedDistance; } /// From 9cb649436c838047fdddbc91b87f08f07e94afd2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 17:47:05 +0900 Subject: [PATCH 153/178] Default to linear control point type --- osu.Game/Rulesets/Objects/SliderPath.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index dbd236107c..e323054234 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -196,9 +196,6 @@ namespace osu.Game.Rulesets.Objects if (ControlPoints.Count == 0) return; - if (ControlPoints[0].Type.Value == null) - throw new InvalidOperationException($"The first control point in a {nameof(SliderPath)} must have a non-null type."); - Vector2[] vertices = new Vector2[ControlPoints.Count]; for (int i = 0; i < ControlPoints.Count; i++) vertices[i] = ControlPoints[i].Position.Value; @@ -214,7 +211,7 @@ namespace osu.Game.Rulesets.Objects // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = ControlPoints[start].Type.Value.Value; + var segmentType = ControlPoints[start].Type.Value ?? PathType.Linear; foreach (Vector2 t in computeSubPath(segmentVertices, segmentType)) { From fa1468325e504a22f7a6e7f9dff5c34dd8878d41 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 17:48:27 +0900 Subject: [PATCH 154/178] Refactor hitobjects to remove default control point --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 366f10d61d..d5d99640af 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Catch.Objects public double Duration => EndTime - StartTime; - private readonly SliderPath path = new SliderPath(new[] { new PathControlPoint { Type = { Value = PathType.Linear } } }); + private readonly SliderPath path = new SliderPath(); public SliderPath Path { diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 4c299fd7c2..134576316a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); - private readonly SliderPath path = new SliderPath(new[] { new PathControlPoint { Type = { Value = PathType.Linear } } }); + private readonly SliderPath path = new SliderPath(); public SliderPath Path { From 883d5bc11d54cfd72e58f2e7d42c21b8fea1141b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 17:54:19 +0900 Subject: [PATCH 155/178] Remove automatic slider path offsetting --- .../Components/PathControlPointVisualiser.cs | 8 ++++++++ osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 -- osu.Game/Rulesets/Objects/SliderPath.cs | 20 ------------------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index f0888b34fe..434e74ddeb 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Compose; +using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components @@ -122,6 +123,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return true; } + // The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position + // So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0) + Vector2 first = slider.Path.ControlPoints[0].Position.Value; + foreach (var c in slider.Path.ControlPoints) + c.Position.Value -= first; + slider.Position += first; + // Since pieces are re-used, they will not point to the deleted control points while remaining selected foreach (var piece in Pieces) piece.IsSelected.Value = false; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 134576316a..34e5a7f3cd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -113,8 +113,6 @@ namespace osu.Game.Rulesets.Osu.Objects { SamplesBindable.ItemsAdded += _ => updateNestedSamples(); SamplesBindable.ItemsRemoved += _ => updateNestedSamples(); - - Path.OffsetChanged += offset => Position += offset; Path.Version.ValueChanged += _ => updateNestedPositions(); } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index e323054234..d868ee27f0 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -16,12 +16,6 @@ namespace osu.Game.Rulesets.Objects { public class SliderPath { - /// - /// Invoked when the offset of the path changes. - /// The provided value indicates the offset, and should be used to re-calculate the position of the containing drawable. - /// - public event Action OffsetChanged; - /// /// The current version of this . Updated when any change to the path occurs. /// @@ -66,20 +60,6 @@ namespace osu.Game.Rulesets.Objects foreach (var c in items) c.Changed -= invalidate; - // Make all control points relative to the first one - if (ControlPoints.Count > 0) - { - Vector2 first = ControlPoints[0].Position.Value; - - if (first != Vector2.Zero) - { - foreach (var c in ControlPoints) - c.Position.Value -= first; - - OffsetChanged?.Invoke(first); - } - } - invalidate(); }; } From bfbb9aa18e09640549f580748be35a5e7690d82b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 18:01:02 +0900 Subject: [PATCH 156/178] Remove outdated assert --- osu.Game/Rulesets/Objects/SliderPath.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index d868ee27f0..d5ae6f471d 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -187,8 +187,6 @@ namespace osu.Game.Rulesets.Objects if (ControlPoints[i].Type.Value == null && i < ControlPoints.Count - 1) continue; - Debug.Assert(ControlPoints[start].Type.Value.HasValue); - // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); var segmentType = ControlPoints[start].Type.Value ?? PathType.Linear; From a1798fd38d29e8cc25a0919025b78bd4e1f24e39 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 18:01:13 +0900 Subject: [PATCH 157/178] Fix bad ctor implementation --- osu.Game/Rulesets/Objects/PathControlPoint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index de40c24060..5737d3f618 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -41,6 +41,9 @@ namespace osu.Game.Rulesets.Objects /// The initial type. public PathControlPoint(Vector2 position, PathType? type = null) { + Position.Value = position; + Type.Value = type; + Position.ValueChanged += _ => Changed?.Invoke(); Type.ValueChanged += _ => Changed?.Invoke(); } From d650bfb6d6d3c56439c3a56e1c1afd940d9d989b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 18:05:14 +0900 Subject: [PATCH 158/178] Remove unnecessary cast --- osu.Game/Screens/Play/GameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 1508758c87..2cc03ae453 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -245,7 +245,7 @@ namespace osu.Game.Screens.Play speedAdjustmentsApplied = false; } - (track as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); } } } From 2dbf94f3abc054504d74a831fc8e885640710d1b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 18:10:33 +0900 Subject: [PATCH 159/178] Make placement blueprint add an initial segment --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 7dd2017e48..c004b6db28 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private PlacementState state; private PathControlPoint segmentStart; private PathControlPoint cursor; - private int currentSegmentLength = 1; + private int currentSegmentLength; [Resolved(CanBeNull = true)] private HitObjectComposer composer { get; set; } @@ -40,7 +40,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { RelativeSizeAxes = Axes.Both; - segmentStart = HitObject.Path.ControlPoints[0]; + HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.Linear)); + currentSegmentLength = 1; } [BackgroundDependencyLoader] From b6e2738236da446fd99de6f44fa6cb5930572a3f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 18:15:00 +0900 Subject: [PATCH 160/178] Remove unused using --- osu.Game/Rulesets/Objects/SliderPath.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index d5ae6f471d..d8c6320c6d 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using Newtonsoft.Json; using osu.Framework.Bindables; From 6d9cd0fafe6b405d48fe74061c529bcdf0aaeeac Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 18:25:13 +0900 Subject: [PATCH 161/178] Split out complex method --- osu.Game/Rulesets/Objects/SliderPath.cs | 38 ++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index d8c6320c6d..6a151d7d33 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Objects var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); var segmentType = ControlPoints[start].Type.Value ?? PathType.Linear; - foreach (Vector2 t in computeSubPath(segmentVertices, segmentType)) + foreach (Vector2 t in calculateSubPath(segmentVertices, segmentType)) { if (calculatedPath.Count == 0 || calculatedPath.Last() != t) calculatedPath.Add(t); @@ -199,32 +199,32 @@ namespace osu.Game.Rulesets.Objects // Start the new segment at the current vertex start = i; } + } - static List computeSubPath(ReadOnlySpan subControlPoints, PathType type) + private List calculateSubPath(ReadOnlySpan subControlPoints, PathType type) + { + switch (type) { - switch (type) - { - case PathType.Linear: - return PathApproximator.ApproximateLinear(subControlPoints); + case PathType.Linear: + return PathApproximator.ApproximateLinear(subControlPoints); - case PathType.PerfectCurve: - if (subControlPoints.Length != 3) - break; + case PathType.PerfectCurve: + if (subControlPoints.Length != 3) + break; - List subpath = PathApproximator.ApproximateCircularArc(subControlPoints); + List subpath = PathApproximator.ApproximateCircularArc(subControlPoints); - // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. - if (subpath.Count == 0) - break; + // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. + if (subpath.Count == 0) + break; - return subpath; + return subpath; - case PathType.Catmull: - return PathApproximator.ApproximateCatmull(subControlPoints); - } - - return PathApproximator.ApproximateBezier(subControlPoints); + case PathType.Catmull: + return PathApproximator.ApproximateCatmull(subControlPoints); } + + return PathApproximator.ApproximateBezier(subControlPoints); } private void calculateLength() From 04b3297a0515c9a204109b387bf4e5e3008cabe8 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 6 Dec 2019 21:32:31 +0800 Subject: [PATCH 162/178] Constrain configuration lookup as enum. --- osu.Game/Configuration/DatabasedConfigManager.cs | 7 ++++--- osu.Game/Configuration/InMemoryConfigManager.cs | 5 +++-- osu.Game/Rulesets/Configuration/RulesetConfigManager.cs | 5 +++-- osu.Game/Skinning/SkinConfigManager.cs | 3 ++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index 1ef4c2527a..b3783b45a8 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; @@ -9,8 +10,8 @@ using osu.Game.Rulesets; namespace osu.Game.Configuration { - public abstract class DatabasedConfigManager : ConfigManager - where T : struct + public abstract class DatabasedConfigManager : ConfigManager + where TLookup : struct, Enum { private readonly SettingsStore settings; @@ -53,7 +54,7 @@ namespace osu.Game.Configuration private readonly List dirtySettings = new List(); - protected override void AddBindable(T lookup, Bindable bindable) + protected override void AddBindable(TLookup lookup, Bindable bindable) { base.AddBindable(lookup, bindable); diff --git a/osu.Game/Configuration/InMemoryConfigManager.cs b/osu.Game/Configuration/InMemoryConfigManager.cs index b0dc6b0e9c..ccf697f680 100644 --- a/osu.Game/Configuration/InMemoryConfigManager.cs +++ b/osu.Game/Configuration/InMemoryConfigManager.cs @@ -1,12 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Configuration; namespace osu.Game.Configuration { - public class InMemoryConfigManager : ConfigManager - where T : struct + public class InMemoryConfigManager : ConfigManager + where TLookup : struct, Enum { public InMemoryConfigManager() { diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index ed5fdf9809..0ff3455f00 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -1,12 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Game.Configuration; namespace osu.Game.Rulesets.Configuration { - public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager - where T : struct + public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager + where TLookup : struct, Enum { protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) : base(settings, ruleset, variant) diff --git a/osu.Game/Skinning/SkinConfigManager.cs b/osu.Game/Skinning/SkinConfigManager.cs index 896444d1d2..682138a2e9 100644 --- a/osu.Game/Skinning/SkinConfigManager.cs +++ b/osu.Game/Skinning/SkinConfigManager.cs @@ -1,11 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Configuration; namespace osu.Game.Skinning { - public class SkinConfigManager : ConfigManager where T : struct + public class SkinConfigManager : ConfigManager where TLookup : struct, Enum { protected override void PerformLoad() { From 40a5c1fd96bfd197131f15bc6c9eececba82c437 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 7 Dec 2019 19:49:52 +0800 Subject: [PATCH 163/178] Constrain transformable with class. --- osu.Game/Graphics/IHasAccentColour.cs | 2 +- osu.Game/Storyboards/Drawables/IFlippable.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/IHasAccentColour.cs b/osu.Game/Graphics/IHasAccentColour.cs index 1a66819379..af497da70f 100644 --- a/osu.Game/Graphics/IHasAccentColour.cs +++ b/osu.Game/Graphics/IHasAccentColour.cs @@ -24,7 +24,7 @@ namespace osu.Game.Graphics /// /// A to which further transforms can be added. public static TransformSequence FadeAccent(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None) - where T : IHasAccentColour + where T : class, IHasAccentColour => accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing); /// diff --git a/osu.Game/Storyboards/Drawables/IFlippable.cs b/osu.Game/Storyboards/Drawables/IFlippable.cs index 9e12de5833..1c4cdde22d 100644 --- a/osu.Game/Storyboards/Drawables/IFlippable.cs +++ b/osu.Game/Storyboards/Drawables/IFlippable.cs @@ -41,7 +41,7 @@ namespace osu.Game.Storyboards.Drawables /// /// A to which further transforms can be added. public static TransformSequence TransformFlipH(this T flippable, bool newValue, double delay = 0) - where T : IFlippable + where T : class, IFlippable => flippable.TransformTo(flippable.PopulateTransform(new TransformFlipH(), newValue, delay)); /// @@ -49,7 +49,7 @@ namespace osu.Game.Storyboards.Drawables /// /// A to which further transforms can be added. public static TransformSequence TransformFlipV(this T flippable, bool newValue, double delay = 0) - where T : IFlippable + where T : class, IFlippable => flippable.TransformTo(flippable.PopulateTransform(new TransformFlipV(), newValue, delay)); } } From c3518a2b9491960e573e5de0e3860d66a178a0c7 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 7 Dec 2019 19:56:56 +0800 Subject: [PATCH 164/178] Enum constraint for enum dropdown. --- .../Graphics/UserInterface/OsuEnumDropdown.cs | 4 +--- .../SearchableList/DisplayStyleControl.cs | 2 ++ .../SearchableListFilterControl.cs | 19 +++++++++---------- .../SearchableList/SearchableListHeader.cs | 4 +--- .../SearchableList/SearchableListOverlay.cs | 14 +++++++++----- .../SearchableList/SlimEnumDropdown.cs | 2 ++ .../Overlays/Settings/SettingsEnumDropdown.cs | 2 ++ 7 files changed, 26 insertions(+), 21 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs index e132027787..528d7d60f8 100644 --- a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs @@ -6,12 +6,10 @@ using System; namespace osu.Game.Graphics.UserInterface { public class OsuEnumDropdown : OsuDropdown + where T : struct, Enum { public OsuEnumDropdown() { - if (!typeof(T).IsEnum) - throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument"); - Items = (T[])Enum.GetValues(typeof(T)); } } diff --git a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs index 0808cc8fcc..a33f4eb30d 100644 --- a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs +++ b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Bindables; using osuTK; using osu.Framework.Graphics; @@ -11,6 +12,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.SearchableList { public class DisplayStyleControl : Container + where T : struct, Enum { public readonly SlimEnumDropdown Dropdown; public readonly Bindable DisplayStyle = new Bindable(); diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs index 372da94b37..117f905de4 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs @@ -13,7 +13,9 @@ using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays.SearchableList { - public abstract class SearchableListFilterControl : Container + public abstract class SearchableListFilterControl : Container + where TTab : struct, Enum + where TCategory : struct, Enum { private const float padding = 10; @@ -21,12 +23,12 @@ namespace osu.Game.Overlays.SearchableList private readonly Box tabStrip; public readonly SearchTextBox Search; - public readonly PageTabControl Tabs; - public readonly DisplayStyleControl DisplayStyleControl; + public readonly PageTabControl Tabs; + public readonly DisplayStyleControl DisplayStyleControl; protected abstract Color4 BackgroundColour { get; } - protected abstract T DefaultTab { get; } - protected abstract U DefaultCategory { get; } + protected abstract TTab DefaultTab { get; } + protected abstract TCategory DefaultCategory { get; } protected virtual Drawable CreateSupplementaryControls() => null; /// @@ -36,9 +38,6 @@ namespace osu.Game.Overlays.SearchableList protected SearchableListFilterControl() { - if (!typeof(T).IsEnum) - throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument"); - RelativeSizeAxes = Axes.X; var controls = CreateSupplementaryControls(); @@ -90,7 +89,7 @@ namespace osu.Game.Overlays.SearchableList RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Right = 225 }, - Child = Tabs = new PageTabControl + Child = Tabs = new PageTabControl { RelativeSizeAxes = Axes.X, }, @@ -105,7 +104,7 @@ namespace osu.Game.Overlays.SearchableList }, }, }, - DisplayStyleControl = new DisplayStyleControl + DisplayStyleControl = new DisplayStyleControl { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, diff --git a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs index 73dca956d1..66fedf0a56 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Overlays.SearchableList { public abstract class SearchableListHeader : Container + where T : struct, Enum { public readonly HeaderTabControl Tabs; @@ -24,9 +25,6 @@ namespace osu.Game.Overlays.SearchableList protected SearchableListHeader() { - if (!typeof(T).IsEnum) - throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument"); - RelativeSizeAxes = Axes.X; Height = 90; diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs index fb0c1d9808..37478d902b 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -16,19 +17,22 @@ namespace osu.Game.Overlays.SearchableList public const float WIDTH_PADDING = 80; } - public abstract class SearchableListOverlay : SearchableListOverlay + public abstract class SearchableListOverlay : SearchableListOverlay + where THeader : struct, Enum + where TTab : struct, Enum + where TCategory : struct, Enum { private readonly Container scrollContainer; - protected readonly SearchableListHeader Header; - protected readonly SearchableListFilterControl Filter; + protected readonly SearchableListHeader Header; + protected readonly SearchableListFilterControl Filter; protected readonly FillFlowContainer ScrollFlow; protected abstract Color4 BackgroundColour { get; } protected abstract Color4 TrianglesColourLight { get; } protected abstract Color4 TrianglesColourDark { get; } - protected abstract SearchableListHeader CreateHeader(); - protected abstract SearchableListFilterControl CreateFilterControl(); + protected abstract SearchableListHeader CreateHeader(); + protected abstract SearchableListFilterControl CreateFilterControl(); protected SearchableListOverlay() { diff --git a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs index f320ef1344..9e7ff1205f 100644 --- a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs +++ b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -11,6 +12,7 @@ using osuTK; namespace osu.Game.Overlays.SearchableList { public class SlimEnumDropdown : OsuEnumDropdown + where T : struct, Enum { protected override DropdownHeader CreateHeader() => new SlimDropdownHeader(); diff --git a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs index 9f09f251c2..c77d14632b 100644 --- a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs @@ -1,12 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { public class SettingsEnumDropdown : SettingsDropdown + where T : struct, Enum { protected override OsuDropdown CreateDropdown() => new DropdownControl(); From ad1fb3bda2b75bb357d4bd2b8930af7d533e3384 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 9 Dec 2019 17:48:41 +0800 Subject: [PATCH 165/178] Remove IComparable in constraint. --- osu.Game/Screens/Select/FilterCriteria.cs | 2 +- osu.Game/Screens/Select/FilterQueryParser.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index abcb1f2171..e3ad76ac35 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Select } public struct OptionalRange : IEquatable> - where T : struct, IComparable + where T : struct { public bool HasFilter => Max != null || Min != null; diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index ffe1258168..89afc729fe 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Select } private static void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, T value) - where T : struct, IComparable + where T : struct { switch (op) { From 47f3c4a596a3b0b8f54e492dd723644e1665241f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 20:18:18 +0900 Subject: [PATCH 166/178] Don't serialise path version --- osu.Game/Rulesets/Objects/SliderPath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 6a151d7d33..86deba3b93 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Objects /// /// The current version of this . Updated when any change to the path occurs. /// + [JsonIgnore] public IBindable Version => version; private readonly Bindable version = new Bindable(); From bd2b0af2695048a1f5baae9cbde9f80a18e28957 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 22:35:19 +0900 Subject: [PATCH 167/178] Consider having only 1 control point as being deleted --- .../Sliders/Components/PathControlPointVisualiser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 434e74ddeb..f7692c64f4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -116,8 +116,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components slider.Path.ControlPoints.Remove(c); } - // If there are 0 remaining control points, treat the slider as being deleted - if (slider.Path.ControlPoints.Count == 0) + // If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted + if (slider.Path.ControlPoints.Count <= 1) { placementHandler?.Delete(slider); return true; From 2c4c190f15238574285407bd7fd5925b22c1c768 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 22:44:47 +0900 Subject: [PATCH 168/178] Fix control points not adding to last segment --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 7431972673..68873093a6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders int insertionIndex = 0; float minDistance = float.MaxValue; - for (int i = 0; i < HitObject.Path.ControlPoints.Count - 2; i++) + for (int i = 0; i < HitObject.Path.ControlPoints.Count - 1; i++) { float dist = new Line(HitObject.Path.ControlPoints[i].Position.Value, HitObject.Path.ControlPoints[i + 1].Position.Value).DistanceToPoint(position); From 1e71681916b3fe6473da6f3fda1e2fb35f9dc7fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 02:00:39 +0900 Subject: [PATCH 169/178] Fix osu!catch catcher not scaling down correctly --- osu.Game.Rulesets.Catch/UI/CatcherSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs index e3c6c93d01..025fa9c56e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle") + InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle", confineMode: ConfineMode.ScaleDownToFit) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, From dc9775742ca0dfc20f65e9de2d043c714a6f1a61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 02:23:17 +0900 Subject: [PATCH 170/178] Fix incorrect code migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bartłomiej Dach --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index d95d354487..839b2ae36e 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Mods double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment)); track.Tempo.Value /= lastAdjust; - track.Tempo.Value *= lastAdjust; + track.Tempo.Value *= adjust; lastAdjust = adjust; } From cdde5d1d690adf16a02cc8b48cdb85467f68fb61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 02:30:23 +0900 Subject: [PATCH 171/178] Fix song select filters not reapplied if in a child screen Closes https://github.com/ppy/osu/issues/6980. --- .../SongSelect/TestScenePlaySongSelect.cs | 36 +++++++++++++++++++ .../Visual/TestSceneOsuScreenStack.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 8 ++--- osu.Game/Tests/Visual/ScreenTestScene.cs | 10 +++--- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index a4b8d1a24a..5dd02c1ddd 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -95,6 +95,42 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("filter count is 1", () => songSelect.FilterCount == 1); } + [Test] + public void TestNoFilterOnSimpleResume() + { + addRulesetImportStep(0); + addRulesetImportStep(0); + + createSongSelect(); + + AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child"))); + AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen()); + + AddStep("return", () => songSelect.MakeCurrent()); + AddUntilStep("wait for current", () => songSelect.IsCurrentScreen()); + AddAssert("filter count is 1", () => songSelect.FilterCount == 1); + } + + [Test] + public void TestFilterOnResumeAfterChange() + { + addRulesetImportStep(0); + addRulesetImportStep(0); + + AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false)); + + createSongSelect(); + + AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child"))); + AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen()); + + AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true)); + + AddStep("return", () => songSelect.MakeCurrent()); + AddUntilStep("wait for current", () => songSelect.IsCurrentScreen()); + AddAssert("filter count is 2", () => songSelect.FilterCount == 2); + } + [Test] public void TestAudioResuming() { diff --git a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs index a68fd0ef40..c55988d1bb 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual AddAssert("Parallax is off", () => stack.ParallaxAmount == 0); } - private class TestScreen : ScreenWithBeatmapBackground + public class TestScreen : ScreenWithBeatmapBackground { private readonly string screenText; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a52edb70db..8f7ad2022d 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -262,8 +262,10 @@ namespace osu.Game.Screens.Select protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { - if (this.IsCurrentScreen()) - Carousel.Filter(criteria); + // if not the current screen, we want to get carousel in a good presentation state before displaying (resume or enter). + bool shouldDebounce = this.IsCurrentScreen(); + + Schedule(() => Carousel.Filter(criteria, shouldDebounce)); } private DependencyContainer dependencies; @@ -437,8 +439,6 @@ namespace osu.Game.Screens.Select { base.OnEntering(last); - Carousel.Filter(FilterControl.CreateCriteria(), false); - this.FadeInFromZero(250); FilterControl.Activate(); } diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index 23f45e0d0f..707aa61283 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual /// public abstract class ScreenTestScene : ManualInputManagerTestScene { - private readonly OsuScreenStack stack; + protected readonly OsuScreenStack Stack; private readonly Container content; @@ -22,16 +22,16 @@ namespace osu.Game.Tests.Visual { base.Content.AddRange(new Drawable[] { - stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, + Stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, content = new Container { RelativeSizeAxes = Axes.Both } }); } protected void LoadScreen(OsuScreen screen) { - if (stack.CurrentScreen != null) - stack.Exit(); - stack.Push(screen); + if (Stack.CurrentScreen != null) + Stack.Exit(); + Stack.Push(screen); } } } From 1db218f9082749578b8af301d7a329c677b8dfe1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 03:29:25 +0900 Subject: [PATCH 172/178] Don't show count when deleting only one control point Reads better. --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index cdca48490e..629604357d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return new MenuItem[] { - new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints)}", MenuItemType.Destructive, () => deleteSelected()) + new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints, selectedPoints > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => deleteSelected()) }; } } From ab0f2e7c6a1c1692a115ca54512344a162ec2230 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 13:12:54 +0900 Subject: [PATCH 173/178] Apply suggested refactorings --- .../Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 9 ++++----- osu.Game/Rulesets/Objects/PathControlPoint.cs | 7 +++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 1ac7284772..b5b1e26486 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -257,14 +257,13 @@ namespace osu.Game.Rulesets.Objects.Legacy { if (type == PathType.PerfectCurve) { - if (vertices.Length == 3) + if (vertices.Length != 3) + type = PathType.Bezier; + else if (isLinear(vertices)) { // osu-stable special-cased colinear perfect curves to a linear path - if (isLinear(vertices)) - type = PathType.Linear; + type = PathType.Linear; } - else - type = PathType.Bezier; } var points = new List(vertices.Length) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index 5737d3f618..0336f94313 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -30,8 +30,9 @@ namespace osu.Game.Rulesets.Objects /// Creates a new . /// public PathControlPoint() - : this(Vector2.Zero, null) { + Position.ValueChanged += _ => Changed?.Invoke(); + Type.ValueChanged += _ => Changed?.Invoke(); } /// @@ -40,12 +41,10 @@ namespace osu.Game.Rulesets.Objects /// The initial position. /// The initial type. public PathControlPoint(Vector2 position, PathType? type = null) + : this() { Position.Value = position; Type.Value = type; - - Position.ValueChanged += _ => Changed?.Invoke(); - Type.ValueChanged += _ => Changed?.Invoke(); } public bool Equals(PathControlPoint other) => Position.Value == other?.Position.Value && Type.Value == other.Type.Value; From 609c51130964ccb85e00160be1556ec4aa7bec61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 16:43:58 +0900 Subject: [PATCH 174/178] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 301c615ce4..914d352070 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ef16738908..473ce82443 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5090190f28..d03005012a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From f7f4a57c5f6b1b3d64bec8c22461e261c05bae97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 19:41:18 +0900 Subject: [PATCH 175/178] Update bindable types in line with framework --- osu.Game/Rulesets/Mods/ModTimeAdjust.cs | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 16 +++++++++++----- .../Tests/Visual/RateAdjustedBeatmapTestScene.cs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs index f137a75ed8..7d0cc2a7c3 100644 --- a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods public virtual void ApplyToTrack(Track track) { - track.TempoAdjust *= RateAdjust; + track.Tempo.Value *= RateAdjust; } } } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 96275c1274..a856974292 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -511,15 +511,19 @@ namespace osu.Game.Rulesets.UI public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException(); - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException(); - public BindableDouble Volume => throw new NotImplementedException(); + public BindableNumber Volume => throw new NotImplementedException(); - public BindableDouble Balance => throw new NotImplementedException(); + public BindableNumber Balance => throw new NotImplementedException(); - public BindableDouble Frequency => throw new NotImplementedException(); + public BindableNumber Frequency => throw new NotImplementedException(); + + public BindableNumber Tempo => throw new NotImplementedException(); + + public IBindable GetAggregate(AdjustableProperty type) => throw new NotImplementedException(); public IBindable AggregateVolume => throw new NotImplementedException(); @@ -527,6 +531,8 @@ namespace osu.Game.Rulesets.UI public IBindable AggregateFrequency => throw new NotImplementedException(); + public IBindable AggregateTempo => throw new NotImplementedException(); + public int PlaybackConcurrency { get => throw new NotImplementedException(); diff --git a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs index 921a1d9789..ad24ffc7b8 100644 --- a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs +++ b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual base.Update(); // note that this will override any mod rate application - Beatmap.Value.Track.TempoAdjust = Clock.Rate; + Beatmap.Value.Track.Tempo.Value = Clock.Rate; } } } From 65f2d1f8757f809eb5f33b639abc5e7b338b6060 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 17:49:42 +0900 Subject: [PATCH 176/178] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 914d352070..3cd4dc48bf 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 473ce82443..530d62f583 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d03005012a..fb753b8c6f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From 55c938e5daa07a0a471d313645c172d5372ba73c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 18:08:11 +0900 Subject: [PATCH 177/178] Fix bindable usage --- osu.Game/Online/DownloadTrackingComposite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index dcec17788a..9a0e112727 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -26,7 +26,7 @@ namespace osu.Game.Online /// protected readonly Bindable State = new Bindable(); - protected readonly Bindable Progress = new Bindable(); + protected readonly BindableNumber Progress = new BindableNumber { MinValue = 0, MaxValue = 1 }; protected DownloadTrackingComposite(TModel model = null) { From f593caf0eaa4d8750d314d16f17278c7fdc178d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 18:08:51 +0900 Subject: [PATCH 178/178] Remove unused class --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index c004b6db28..9b820261ab 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input; @@ -191,15 +190,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders Initial, Body, } - - private class Segment - { - public readonly List ControlPoints = new List(); - - public Segment(Vector2 offset) - { - ControlPoints.Add(offset); - } - } } }