From 8cdae790c3b0fc90996ae473ffe998206f9af51a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 3 Apr 2020 17:32:37 +0200 Subject: [PATCH 01/42] Load user rulesets from the game data directory --- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Rulesets/RulesetStore.cs | 40 +++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5487bd9320..609b6ce98e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -168,7 +168,7 @@ namespace osu.Game var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); - dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); + dependencies.Cache(RulesetStore = new RulesetStore(contextFactory, Storage)); dependencies.Cache(FileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index a389d4ff75..c3c7b653da 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection; using osu.Framework.Logging; +using osu.Framework.Platform; using osu.Game.Database; namespace osu.Game.Rulesets @@ -17,16 +18,20 @@ namespace osu.Game.Rulesets private readonly Dictionary loadedAssemblies = new Dictionary(); - public RulesetStore(IDatabaseContextFactory factory) + private readonly Storage rulesetStorage; + + public RulesetStore(IDatabaseContextFactory factory, Storage storage = null) : base(factory) { + rulesetStorage = storage?.GetStorageForDirectory("rulesets"); + AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; + // On android in release configuration assemblies are loaded from the apk directly into memory. // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); loadFromDisk(); + loadUserRulesets(); addMissingRulesets(); - - AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetAssembly; } /// @@ -48,7 +53,17 @@ namespace osu.Game.Rulesets /// public IEnumerable AvailableRulesets { get; private set; } - private Assembly resolveRulesetAssembly(object sender, ResolveEventArgs args) => loadedAssemblies.Keys.FirstOrDefault(a => a.FullName == args.Name); + private Assembly resolveRulesetDependencyAssembly(object sender, ResolveEventArgs args) + { + var asm = new AssemblyName(args.Name); + + // this assumes the only explicit dependency of the ruleset is the game core assembly. + // the ruleset dependency on the game core assembly requires manual resolving, transient dependencies should be resolved automatically + if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) + return Assembly.GetExecutingAssembly(); + + return null; + } private void addMissingRulesets() { @@ -120,6 +135,21 @@ namespace osu.Game.Rulesets } } + private void loadUserRulesets() + { + try + { + var rulesets = rulesetStorage?.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); + + foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) + loadRulesetFromFile(rulesetStorage?.GetFullPath(ruleset)); + } + catch (Exception e) + { + Logger.Error(e, "Couldn't load user rulesets"); + } + } + private void loadFromDisk() { try @@ -175,7 +205,7 @@ namespace osu.Game.Rulesets protected virtual void Dispose(bool disposing) { - AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetAssembly; + AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetDependencyAssembly; } } } From 0e45a4d54e1dd372f120dbfb926234065f4158af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 4 Apr 2020 20:13:46 +0200 Subject: [PATCH 02/42] Add back cached ruleset assembly lookup --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index c3c7b653da..7b4c0302aa 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) return Assembly.GetExecutingAssembly(); - return null; + return loadedAssemblies.Keys.FirstOrDefault(a => a.FullName == asm.FullName); } private void addMissingRulesets() From 08308e07e7a1ea594214594400f3cb784936a9a9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 7 Apr 2020 12:20:54 +0200 Subject: [PATCH 03/42] Apply review suggestions --- osu.Game/Rulesets/RulesetStore.cs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 7b4c0302aa..ef72f187c3 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -24,12 +24,12 @@ namespace osu.Game.Rulesets : base(factory) { rulesetStorage = storage?.GetStorageForDirectory("rulesets"); - AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; // On android in release configuration assemblies are loaded from the apk directly into memory. // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); loadFromDisk(); + AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; loadUserRulesets(); addMissingRulesets(); } @@ -57,6 +57,7 @@ namespace osu.Game.Rulesets { var asm = new AssemblyName(args.Name); + // the requesting assembly may be located out of the executable's base directory, thus requiring manual resolving of its dependencies. // this assumes the only explicit dependency of the ruleset is the game core assembly. // the ruleset dependency on the game core assembly requires manual resolving, transient dependencies should be resolved automatically if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) @@ -137,17 +138,10 @@ namespace osu.Game.Rulesets private void loadUserRulesets() { - try - { - var rulesets = rulesetStorage?.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); + var rulesets = rulesetStorage?.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); - foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) - loadRulesetFromFile(rulesetStorage?.GetFullPath(ruleset)); - } - catch (Exception e) - { - Logger.Error(e, "Couldn't load user rulesets"); - } + foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) + loadRulesetFromFile(rulesetStorage?.GetFullPath(ruleset)); } private void loadFromDisk() From 2087d8d09e92c4662a9cc228e25476801624539a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 7 Apr 2020 16:01:47 +0200 Subject: [PATCH 04/42] Don't search for user rulesets if rulesetsStorage isn't set (Testing environnment) --- osu.Game/Rulesets/RulesetStore.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index ef72f187c3..34da2dc2db 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -138,10 +138,12 @@ namespace osu.Game.Rulesets private void loadUserRulesets() { - var rulesets = rulesetStorage?.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); + if (rulesetStorage == null) return; + + var rulesets = rulesetStorage.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) - loadRulesetFromFile(rulesetStorage?.GetFullPath(ruleset)); + loadRulesetFromFile(rulesetStorage.GetFullPath(ruleset)); } private void loadFromDisk() From a7179d1c8751b179ae8d676ee76cd73b8930336f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 19 Apr 2020 15:15:04 +0200 Subject: [PATCH 05/42] Fix comment wording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bartłomiej Dach --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 34da2dc2db..d4b41dcb38 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets // the requesting assembly may be located out of the executable's base directory, thus requiring manual resolving of its dependencies. // this assumes the only explicit dependency of the ruleset is the game core assembly. - // the ruleset dependency on the game core assembly requires manual resolving, transient dependencies should be resolved automatically + // the ruleset dependency on the game core assembly requires manual resolving, transitive dependencies should be resolved automatically if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) return Assembly.GetExecutingAssembly(); From ba1c465edf296fe55bf4d623e5543918ab85f7af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 19 Apr 2020 15:25:21 +0200 Subject: [PATCH 06/42] Add comment in regards to the attaching of event handler to the assembly lookup event before call to loadUserRulesets(). --- osu.Game/Rulesets/RulesetStore.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index d4b41dcb38..ab169c741d 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -29,6 +29,10 @@ namespace osu.Game.Rulesets // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); loadFromDisk(); + + // the event handler contains code for resolving dependency on the game assembly for rulesets located outside the base game directory. + // It needs to be attached to the assembly lookup event before the actual call to loadUserRulesets() else rulesets located out of the base game directory will fail + // to load as unable to locate the game core assembly. AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; loadUserRulesets(); addMissingRulesets(); From 1dcb0f53a233452392efadd949c12342842eb7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Apr 2020 16:29:32 +0200 Subject: [PATCH 07/42] Fix whitespace formatting --- osu.Game/Rulesets/RulesetStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index ab169c741d..7e165311a3 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); loadFromDisk(); - + // the event handler contains code for resolving dependency on the game assembly for rulesets located outside the base game directory. // It needs to be attached to the assembly lookup event before the actual call to loadUserRulesets() else rulesets located out of the base game directory will fail // to load as unable to locate the game core assembly. From a541f9268205f76c1eb4321faefeb1721d4f0f8f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 20 Apr 2020 13:56:15 +0200 Subject: [PATCH 08/42] Resolve ruleset dependencies on game core / framework assemblies by checking already loaded assemblies in AppDomain. --- osu.Game/Rulesets/RulesetStore.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 7e165311a3..543134cfb4 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -62,10 +62,13 @@ namespace osu.Game.Rulesets var asm = new AssemblyName(args.Name); // the requesting assembly may be located out of the executable's base directory, thus requiring manual resolving of its dependencies. - // this assumes the only explicit dependency of the ruleset is the game core assembly. - // the ruleset dependency on the game core assembly requires manual resolving, transitive dependencies should be resolved automatically - if (asm.Name.Equals(typeof(OsuGame).Assembly.GetName().Name, StringComparison.Ordinal)) - return Assembly.GetExecutingAssembly(); + // this attempts resolving the ruleset dependencies on game core and framework assemblies by returning assemblies with the same assembly name + // already loaded in the AppDomain. + foreach (var curAsm in AppDomain.CurrentDomain.GetAssemblies()) + { + if (asm.Name.Equals(curAsm.GetName().Name, StringComparison.Ordinal)) + return curAsm; + } return loadedAssemblies.Keys.FirstOrDefault(a => a.FullName == asm.FullName); } From 5e3fad86cffb380f93eb78d1e541bb050b94094c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 15:28:25 +0900 Subject: [PATCH 09/42] Fix relax replays playing back incorrectly --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 13 +++++++++++-- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 1ef235f764..16414261a5 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -9,17 +9,26 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; using osuTK; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset + public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset, IApplicableToPlayer { public override string Description => @"Use the mouse to control the catcher."; + private DrawableRuleset drawableRuleset; + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield)); + this.drawableRuleset = drawableRuleset; + } + + public void ApplyToPlayer(Player player) + { + if (!drawableRuleset.HasReplayLoaded.Value) + drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield)); } private class MouseInputHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 9b0759d9d2..9a7b967117 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -11,11 +11,12 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; using static osu.Game.Input.Handlers.ReplayInputHandler; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset + public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToPlayer { public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); @@ -33,15 +34,28 @@ namespace osu.Game.Rulesets.Osu.Mods private ReplayState state; private double lastStateChangeTime; + private bool hasReplay; + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { // grab the input manager for future use. osuInputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager; - osuInputManager.AllowUserPresses = false; + } + + public void ApplyToPlayer(Player player) + { + if (osuInputManager.ReplayInputHandler != null) + { + hasReplay = true; + return; + } } public void Update(Playfield playfield) { + if (hasReplay) + return; + bool requiresHold = false; bool requiresHit = false; From 1f0b7465e2410c8c69d16f31becd52e076602901 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 16:06:40 +0900 Subject: [PATCH 10/42] Add back missing line --- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 9a7b967117..7b1941b7f9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -49,6 +49,8 @@ namespace osu.Game.Rulesets.Osu.Mods hasReplay = true; return; } + + osuInputManager.AllowUserPresses = false; } public void Update(Playfield playfield) From 594cef14738f99d3f0fa3466756bcf151c3df4ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 15:47:43 +0900 Subject: [PATCH 11/42] Fix BeatmapListingOverlay not taking focus --- .../BeatmapListing/BeatmapListingFilterControl.cs | 2 ++ .../BeatmapListing/BeatmapListingSearchControl.cs | 2 ++ osu.Game/Overlays/BeatmapListingOverlay.cs | 12 +++++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 8817031bce..8c50409783 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -159,5 +159,7 @@ namespace osu.Game.Overlays.BeatmapListing base.Dispose(isDisposing); } + + public void TakeFocus() => searchControl.TakeFocus(); } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 2ecdb18667..29c4fe0d2e 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -121,6 +121,8 @@ namespace osu.Game.Overlays.BeatmapListing background.Colour = colourProvider.Dark6; } + public void TakeFocus() => textBox.TakeFocus(); + private class BeatmapSearchTextBox : SearchTextBox { protected override Color4 SelectionColour => Color4.Gray; diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index e16924464d..000ca6b91c 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; @@ -35,6 +36,8 @@ namespace osu.Game.Overlays { } + private BeatmapListingFilterControl filterControl; + [BackgroundDependencyLoader] private void load() { @@ -57,7 +60,7 @@ namespace osu.Game.Overlays Children = new Drawable[] { new BeatmapListingHeader(), - new BeatmapListingFilterControl + filterControl = new BeatmapListingFilterControl { SearchStarted = onSearchStarted, SearchFinished = onSearchFinished, @@ -88,6 +91,13 @@ namespace osu.Game.Overlays }; } + protected override void OnFocus(FocusEvent e) + { + base.OnFocus(e); + + filterControl.TakeFocus(); + } + private CancellationTokenSource cancellationToken; private void onSearchStarted() From 1cec0575b78203dbc257b4d72851012d37eeac91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 16:00:00 +0900 Subject: [PATCH 12/42] Remove unused classes and replace overlay in game --- .../Visual/Online/TestSceneDirectOverlay.cs | 215 ------------- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 7 + .../API/Requests/SearchBeatmapSetsRequest.cs | 5 +- osu.Game/OsuGame.cs | 8 +- .../BeatmapListingFilterControl.cs | 3 +- .../BeatmapListingSortTabControl.cs | 13 +- .../Overlays/BeatmapListing/SortCriteria.cs | 17 + .../Overlays/Direct/DirectRulesetSelector.cs | 93 ------ osu.Game/Overlays/Direct/FilterControl.cs | 47 --- osu.Game/Overlays/Direct/Header.cs | 43 --- osu.Game/Overlays/DirectOverlay.cs | 299 ------------------ osu.Game/Overlays/SocialOverlay.cs | 6 - osu.Game/Overlays/SortDirection.cs | 11 + osu.Game/Overlays/Toolbar/Toolbar.cs | 2 +- ...tton.cs => ToolbarBeatmapListingButton.cs} | 8 +- osu.Game/Screens/Menu/ButtonSystem.cs | 4 +- osu.Game/Screens/Menu/MainMenu.cs | 4 +- 17 files changed, 57 insertions(+), 728 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs create mode 100644 osu.Game/Overlays/BeatmapListing/SortCriteria.cs delete mode 100644 osu.Game/Overlays/Direct/DirectRulesetSelector.cs delete mode 100644 osu.Game/Overlays/Direct/FilterControl.cs delete mode 100644 osu.Game/Overlays/Direct/Header.cs delete mode 100644 osu.Game/Overlays/DirectOverlay.cs create mode 100644 osu.Game/Overlays/SortDirection.cs rename osu.Game/Overlays/Toolbar/{ToolbarDirectButton.cs => ToolbarBeatmapListingButton.cs} (63%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs deleted file mode 100644 index d9873ea243..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs +++ /dev/null @@ -1,215 +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.Collections.Generic; -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual.Online -{ - [TestFixture] - public class TestSceneDirectOverlay : OsuTestScene - { - private DirectOverlay direct; - - protected override bool UseOnlineAPI => true; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(direct = new DirectOverlay()); - newBeatmaps(); - - AddStep(@"toggle", direct.ToggleVisibility); - AddStep(@"result counts", () => direct.ResultAmounts = new DirectOverlay.ResultCounts(1, 4, 13)); - AddStep(@"trigger disabled", () => Ruleset.Disabled = !Ruleset.Disabled); - } - - private void newBeatmaps() - { - direct.BeatmapSets = new[] - { - new BeatmapSetInfo - { - OnlineBeatmapSetID = 578332, - Metadata = new BeatmapMetadata - { - Title = @"OrVid", - Artist = @"An", - AuthorString = @"RLC", - Source = @"", - Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/578332/covers/card.jpg?1494591390", - Cover = @"https://assets.ppy.sh/beatmaps/578332/covers/cover.jpg?1494591390", - }, - Preview = @"https://b.ppy.sh/preview/578332.mp3", - PlayCount = 97, - FavouriteCount = 72, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 5.35f, - Metadata = new BeatmapMetadata(), - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 599627, - Metadata = new BeatmapMetadata - { - Title = @"tiny lamp", - Artist = @"fhana", - AuthorString = @"Sotarks", - Source = @"ぎんぎつね", - Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/599627/covers/card.jpg?1494539318", - Cover = @"https://assets.ppy.sh/beatmaps/599627/covers/cover.jpg?1494539318", - }, - Preview = @"https//b.ppy.sh/preview/599627.mp3", - PlayCount = 3082, - FavouriteCount = 14, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 5.81f, - Metadata = new BeatmapMetadata(), - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 513268, - Metadata = new BeatmapMetadata - { - Title = @"At Gwanghwamun", - Artist = @"KYUHYUN", - AuthorString = @"Cerulean Veyron", - Source = @"", - Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/513268/covers/card.jpg?1494502863", - Cover = @"https://assets.ppy.sh/beatmaps/513268/covers/cover.jpg?1494502863", - }, - Preview = @"https//b.ppy.sh/preview/513268.mp3", - PlayCount = 2762, - FavouriteCount = 15, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 0.9f, - Metadata = new BeatmapMetadata(), - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 1.1f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 2.02f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 3.49f, - }, - }, - }, - new BeatmapSetInfo - { - OnlineBeatmapSetID = 586841, - Metadata = new BeatmapMetadata - { - Title = @"RHAPSODY OF BLUE SKY", - Artist = @"fhana", - AuthorString = @"[Kamiya]", - Source = @"小林さんちのメイドラゴン", - Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc", - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Card = @"https://assets.ppy.sh/beatmaps/586841/covers/card.jpg?1494052741", - Cover = @"https://assets.ppy.sh/beatmaps/586841/covers/cover.jpg?1494052741", - }, - Preview = @"https//b.ppy.sh/preview/586841.mp3", - PlayCount = 62317, - FavouriteCount = 161, - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 1.26f, - Metadata = new BeatmapMetadata(), - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 2.01f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 2.87f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 3.76f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 3.93f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 4.37f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 5.13f, - }, - new BeatmapInfo - { - Ruleset = Ruleset.Value, - StarDifficulty = 5.42f, - }, - }, - }, - }; - } - } -} diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index 8793d880e3..d68217dcfd 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -47,8 +47,15 @@ namespace osu.Game.Tests.Visual typeof(IdleTracker), typeof(OnScreenDisplay), typeof(NotificationOverlay), +<<<<<<< HEAD typeof(DirectOverlay), typeof(DashboardOverlay), +||||||| parent of 96a3a08a9... Remove unused classes and replace overlay in game + typeof(DirectOverlay), + typeof(SocialOverlay), +======= + typeof(SocialOverlay), +>>>>>>> 96a3a08a9... Remove unused classes and replace overlay in game typeof(ChannelManager), typeof(ChatOverlay), typeof(SettingsOverlay), diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 8345be5f82..047496b473 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -4,7 +4,6 @@ using osu.Framework.IO.Network; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; -using osu.Game.Overlays.Direct; using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests @@ -13,7 +12,7 @@ namespace osu.Game.Online.API.Requests { public SearchCategory SearchCategory { get; set; } - public DirectSortCriteria SortCriteria { get; set; } + public SortCriteria SortCriteria { get; set; } public SortDirection SortDirection { get; set; } @@ -32,7 +31,7 @@ namespace osu.Game.Online.API.Requests this.ruleset = ruleset; SearchCategory = SearchCategory.Any; - SortCriteria = DirectSortCriteria.Ranked; + SortCriteria = SortCriteria.Ranked; SortDirection = SortDirection.Descending; Genre = SearchGenre.Any; Language = SearchLanguage.Any; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c861b84835..f5f7d0cef4 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -65,7 +65,7 @@ namespace osu.Game private NowPlayingOverlay nowPlaying; - private DirectOverlay direct; + private BeatmapListingOverlay beatmapListing; private DashboardOverlay dashboard; @@ -610,7 +610,7 @@ namespace osu.Game loadComponentSingleFile(screenshotManager, Add); //overlay elements - loadComponentSingleFile(direct = new DirectOverlay(), overlayContent.Add, true); + loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); @@ -670,7 +670,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, dashboard, direct, changelogOverlay, rankingsOverlay }; + var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, dashboard, beatmapListing, changelogOverlay, rankingsOverlay }; foreach (var overlay in singleDisplayOverlays) { @@ -865,7 +865,7 @@ namespace osu.Game return true; case GlobalAction.ToggleDirect: - direct.ToggleVisibility(); + beatmapListing.ToggleVisibility(); return true; case GlobalAction.ToggleGameplayMouseButtons: diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 8c50409783..4dd60c7113 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -14,7 +14,6 @@ using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Overlays.Direct; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -101,7 +100,7 @@ namespace osu.Game.Overlays.BeatmapListing searchControl.Query.BindValueChanged(query => { - sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance; + sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? SortCriteria.Ranked : SortCriteria.Relevance; sortDirection.Value = SortDirection.Descending; queueUpdateSearch(true); }); diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs index 27c43b092a..4c77a736ac 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs @@ -8,17 +8,16 @@ using osu.Framework.Graphics; using osuTK.Graphics; using osuTK; using osu.Framework.Input.Events; -using osu.Game.Overlays.Direct; namespace osu.Game.Overlays.BeatmapListing { - public class BeatmapListingSortTabControl : OverlaySortTabControl + public class BeatmapListingSortTabControl : OverlaySortTabControl { public readonly Bindable SortDirection = new Bindable(Overlays.SortDirection.Descending); public BeatmapListingSortTabControl() { - Current.Value = DirectSortCriteria.Ranked; + Current.Value = SortCriteria.Ranked; } protected override SortTabControl CreateControl() => new BeatmapSortTabControl @@ -30,7 +29,7 @@ namespace osu.Game.Overlays.BeatmapListing { public readonly Bindable SortDirection = new Bindable(); - protected override TabItem CreateTabItem(DirectSortCriteria value) => new BeatmapSortTabItem(value) + protected override TabItem CreateTabItem(SortCriteria value) => new BeatmapSortTabItem(value) { SortDirection = { BindTarget = SortDirection } }; @@ -40,12 +39,12 @@ namespace osu.Game.Overlays.BeatmapListing { public readonly Bindable SortDirection = new Bindable(); - public BeatmapSortTabItem(DirectSortCriteria value) + public BeatmapSortTabItem(SortCriteria value) : base(value) { } - protected override TabButton CreateTabButton(DirectSortCriteria value) => new BeatmapTabButton(value) + protected override TabButton CreateTabButton(SortCriteria value) => new BeatmapTabButton(value) { Active = { BindTarget = Active }, SortDirection = { BindTarget = SortDirection } @@ -67,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapListing private readonly SpriteIcon icon; - public BeatmapTabButton(DirectSortCriteria value) + public BeatmapTabButton(SortCriteria value) : base(value) { Add(icon = new SpriteIcon diff --git a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs new file mode 100644 index 0000000000..e409cbdda7 --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Overlays.BeatmapListing +{ + public enum SortCriteria + { + Title, + Artist, + Difficulty, + Ranked, + Rating, + Plays, + Favourites, + Relevance + } +} diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs deleted file mode 100644 index 106aaa616b..0000000000 --- a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs +++ /dev/null @@ -1,93 +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.UserInterface; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Direct -{ - public class DirectRulesetSelector : RulesetSelector - { - public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput; - - public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput; - - public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree; - - public DirectRulesetSelector() - { - TabContainer.Masking = false; - TabContainer.Spacing = new Vector2(10, 0); - AutoSizeAxes = Axes.Both; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Current.BindDisabledChanged(value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint), true); - } - - protected override TabItem CreateTabItem(RulesetInfo value) => new DirectRulesetTabItem(value); - - protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - }; - - private class DirectRulesetTabItem : TabItem - { - private readonly ConstrainedIconContainer iconContainer; - - public DirectRulesetTabItem(RulesetInfo value) - : base(value) - { - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - iconContainer = new ConstrainedIconContainer - { - Icon = value.CreateInstance().CreateIcon(), - Size = new Vector2(32), - }, - new HoverClickSounds() - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateState(); - } - - protected override bool OnHover(HoverEvent e) - { - base.OnHover(e); - updateState(); - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - updateState(); - } - - protected override void OnActivated() => updateState(); - - protected override void OnDeactivated() => updateState(); - - private void updateState() => iconContainer.FadeColour(IsHovered || Active.Value ? Color4.White : Color4.Gray, 120, Easing.InQuad); - } - } -} diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs deleted file mode 100644 index 4ab5544550..0000000000 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ /dev/null @@ -1,47 +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.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Overlays.BeatmapListing; -using osu.Game.Overlays.SearchableList; -using osu.Game.Rulesets; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Direct -{ - public class FilterControl : SearchableListFilterControl - { - private DirectRulesetSelector rulesetSelector; - - protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"384552"); - protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; - protected override SearchCategory DefaultCategory => SearchCategory.Leaderboard; - - protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector(); - - public Bindable Ruleset => rulesetSelector.Current; - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, Bindable ruleset) - { - DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; - rulesetSelector.Current.BindTo(ruleset); - } - } - - public enum DirectSortCriteria - { - Title, - Artist, - Difficulty, - Ranked, - Rating, - Plays, - Favourites, - Relevance, - } -} diff --git a/osu.Game/Overlays/Direct/Header.cs b/osu.Game/Overlays/Direct/Header.cs deleted file mode 100644 index 5b3e394a18..0000000000 --- a/osu.Game/Overlays/Direct/Header.cs +++ /dev/null @@ -1,43 +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.ComponentModel; -using osu.Framework.Extensions.Color4Extensions; -using osuTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.SearchableList; - -namespace osu.Game.Overlays.Direct -{ - public class Header : SearchableListHeader - { - protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"252f3a"); - - protected override DirectTab DefaultTab => DirectTab.Search; - protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", Font = OsuFont.GetFont(size: 25) }; - protected override IconUsage Icon => OsuIcon.ChevronDownCircle; - - public Header() - { - Tabs.Current.Value = DirectTab.NewestMaps; - Tabs.Current.TriggerChange(); - } - } - - public enum DirectTab - { - Search, - - [Description("Newest Maps")] - NewestMaps = DirectSortCriteria.Ranked, - - [Description("Top Rated")] - TopRated = DirectSortCriteria.Rating, - - [Description("Most Played")] - MostPlayed = DirectSortCriteria.Plays, - } -} diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs deleted file mode 100644 index 5ed39af0dc..0000000000 --- a/osu.Game/Overlays/DirectOverlay.cs +++ /dev/null @@ -1,299 +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.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Humanizer; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Threading; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.BeatmapListing; -using osu.Game.Overlays.Direct; -using osu.Game.Overlays.SearchableList; -using osu.Game.Rulesets; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays -{ - public class DirectOverlay : SearchableListOverlay - { - private const float panel_padding = 10f; - - [Resolved] - private RulesetStore rulesets { get; set; } - - private readonly FillFlowContainer resultCountsContainer; - private readonly OsuSpriteText resultCountsText; - private FillFlowContainer panels; - - protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"485e74"); - protected override Color4 TrianglesColourLight => Color4Extensions.FromHex(@"465b71"); - protected override Color4 TrianglesColourDark => Color4Extensions.FromHex(@"3f5265"); - - protected override SearchableListHeader CreateHeader() => new Header(); - protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); - - private IEnumerable beatmapSets; - - public IEnumerable BeatmapSets - { - get => beatmapSets; - set - { - if (ReferenceEquals(beatmapSets, value)) return; - - beatmapSets = value?.ToList(); - - if (beatmapSets == null) return; - - var artists = new List(); - var songs = new List(); - var tags = new List(); - - foreach (var s in beatmapSets) - { - artists.Add(s.Metadata.Artist); - songs.Add(s.Metadata.Title); - tags.AddRange(s.Metadata.Tags.Split(' ')); - } - - ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags)); - } - } - - private ResultCounts resultAmounts; - - public ResultCounts ResultAmounts - { - get => resultAmounts; - set - { - if (value == ResultAmounts) return; - - resultAmounts = value; - - updateResultCounts(); - } - } - - public DirectOverlay() - : base(OverlayColourScheme.Blue) - { - ScrollFlow.Children = new Drawable[] - { - resultCountsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Top = 5 }, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Found ", - Font = OsuFont.GetFont(size: 15) - }, - resultCountsText = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold) - }, - } - }, - }; - - Filter.Search.Current.ValueChanged += text => - { - if (!string.IsNullOrEmpty(text.NewValue)) - { - Header.Tabs.Current.Value = DirectTab.Search; - - if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked) - Filter.Tabs.Current.Value = DirectSortCriteria.Relevance; - } - else - { - Header.Tabs.Current.Value = DirectTab.NewestMaps; - - if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance) - Filter.Tabs.Current.Value = DirectSortCriteria.Ranked; - } - }; - ((FilterControl)Filter).Ruleset.ValueChanged += _ => queueUpdateSearch(); - Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue); - Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => queueUpdateSearch(); - - Header.Tabs.Current.ValueChanged += tab => - { - if (tab.NewValue != DirectTab.Search) - { - currentQuery.Value = string.Empty; - Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value; - queueUpdateSearch(); - } - }; - - currentQuery.ValueChanged += text => queueUpdateSearch(!string.IsNullOrEmpty(text.NewValue)); - - currentQuery.BindTo(Filter.Search.Current); - - Filter.Tabs.Current.ValueChanged += tab => - { - if (Header.Tabs.Current.Value != DirectTab.Search && tab.NewValue != (DirectSortCriteria)Header.Tabs.Current.Value) - Header.Tabs.Current.Value = DirectTab.Search; - - queueUpdateSearch(); - }; - - updateResultCounts(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - resultCountsContainer.Colour = colours.Yellow; - } - - private void updateResultCounts() - { - resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint); - if (ResultAmounts == null) return; - - resultCountsText.Text = "Artist".ToQuantity(ResultAmounts.Artists) + ", " + - "Song".ToQuantity(ResultAmounts.Songs) + ", " + - "Tag".ToQuantity(ResultAmounts.Tags); - } - - private void recreatePanels(PanelDisplayStyle displayStyle) - { - if (panels != null) - { - panels.FadeOut(200); - panels.Expire(); - panels = null; - } - - if (BeatmapSets == null) return; - - var newPanels = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(panel_padding), - Margin = new MarginPadding { Top = 10 }, - ChildrenEnumerable = BeatmapSets.Select(b => - { - switch (displayStyle) - { - case PanelDisplayStyle.Grid: - return new DirectGridPanel(b) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }; - - default: - return new DirectListPanel(b); - } - }) - }; - - LoadComponentAsync(newPanels, p => - { - if (panels != null) ScrollFlow.Remove(panels); - ScrollFlow.Add(panels = newPanels); - }); - } - - protected override void PopIn() - { - base.PopIn(); - - // Queries are allowed to be run only on the first pop-in - if (getSetsRequest == null) - queueUpdateSearch(); - } - - private SearchBeatmapSetsRequest getSetsRequest; - - private readonly Bindable currentQuery = new Bindable(string.Empty); - - private ScheduledDelegate queryChangedDebounce; - - [Resolved] - private PreviewTrackManager previewTrackManager { get; set; } - - private void queueUpdateSearch(bool queryTextChanged = false) - { - BeatmapSets = null; - ResultAmounts = null; - - getSetsRequest?.Cancel(); - - queryChangedDebounce?.Cancel(); - queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100); - } - - private void updateSearch() - { - if (!IsLoaded) - return; - - if (State.Value == Visibility.Hidden) - return; - - if (API == null) - return; - - previewTrackManager.StopAnyPlaying(this); - - getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value, ((FilterControl)Filter).Ruleset.Value) - { - SearchCategory = Filter.DisplayStyleControl.Dropdown.Current.Value, - SortCriteria = Filter.Tabs.Current.Value - }; - - getSetsRequest.Success += response => - { - Task.Run(() => - { - var sets = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList(); - - // may not need scheduling; loads async internally. - Schedule(() => - { - BeatmapSets = sets; - recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); - }); - }); - }; - - API.Queue(getSetsRequest); - } - - private int distinctCount(List list) => list.Distinct().ToArray().Length; - - public class ResultCounts - { - public readonly int Artists; - public readonly int Songs; - public readonly int Tags; - - public ResultCounts(int artists, int songs, int tags) - { - Artists = artists; - Songs = songs; - Tags = tags; - } - } - } -} diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs index 02f7c9b0d3..9548573b4f 100644 --- a/osu.Game/Overlays/SocialOverlay.cs +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -239,10 +239,4 @@ namespace osu.Game.Overlays } } } - - public enum SortDirection - { - Ascending, - Descending - } } diff --git a/osu.Game/Overlays/SortDirection.cs b/osu.Game/Overlays/SortDirection.cs new file mode 100644 index 0000000000..3af9614972 --- /dev/null +++ b/osu.Game/Overlays/SortDirection.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Overlays +{ + public enum SortDirection + { + Ascending, + Descending + } +} diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 897587d198..227347112c 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Toolbar { new ToolbarChangelogButton(), new ToolbarRankingsButton(), - new ToolbarDirectButton(), + new ToolbarBeatmapListingButton(), new ToolbarChatButton(), new ToolbarSocialButton(), new ToolbarMusicButton(), diff --git a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs similarity index 63% rename from osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs rename to osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index 1d07a3ae70..eecb368ee9 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -6,17 +6,17 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Toolbar { - public class ToolbarDirectButton : ToolbarOverlayToggleButton + public class ToolbarBeatmapListingButton : ToolbarOverlayToggleButton { - public ToolbarDirectButton() + public ToolbarBeatmapListingButton() { SetIcon(OsuIcon.ChevronDownCircle); } [BackgroundDependencyLoader(true)] - private void load(DirectOverlay direct) + private void load(BeatmapListingOverlay beatmapListing) { - StateContainer = direct; + StateContainer = beatmapListing; } } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index fe538728e3..30e5e9702e 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Menu public Action OnEdit; public Action OnExit; - public Action OnDirect; + public Action OnBeatmapListing; public Action OnSolo; public Action OnSettings; public Action OnMulti; @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Menu buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); - buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D)); + buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 174eadfe26..0589e4d12b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Menu private SongTicker songTicker; [BackgroundDependencyLoader(true)] - private void load(DirectOverlay direct, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) + private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) { holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); @@ -133,7 +133,7 @@ namespace osu.Game.Screens.Menu }; buttons.OnSettings = () => settings?.ToggleVisibility(); - buttons.OnDirect = () => direct?.ToggleVisibility(); + buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); buttons.OnChart = () => rankings?.ShowSpotlights(); LoadComponentAsync(background = new BackgroundScreenDefault()); From 9b9b710ded76bfc99d326f94c9fd6a40c69f9412 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 16:03:18 +0900 Subject: [PATCH 13/42] Move and rename remaining direct classes --- .../Online/TestSceneDirectDownloadButton.cs | 6 ++--- .../Visual/Online/TestSceneDirectPanel.cs | 18 +++++++-------- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 9 +------- .../BeatmapDownloadTrackingComposite.cs | 2 +- .../Panels/BeatmapPanel.cs} | 8 +++---- .../Panels/BeatmapPanelDownloadButton.cs} | 6 ++--- .../Panels/BeatmapPanelGrid.cs} | 16 +++++++------- .../Panels/BeatmapPanelList.cs} | 22 +++++++++---------- .../Panels}/DownloadProgressBar.cs | 2 +- .../Panels}/IconPill.cs | 2 +- .../Panels}/PlayButton.cs | 2 +- osu.Game/Overlays/BeatmapListingOverlay.cs | 6 ++--- .../Buttons/HeaderDownloadButton.cs | 2 +- .../BeatmapSet/Buttons/PreviewButton.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 4 ++-- .../Beatmaps/PaginatedBeatmapContainer.cs | 4 ++-- .../Overlays/Rankings/SpotlightsLayout.cs | 4 ++-- .../Screens/Multi/DrawableRoomPlaylistItem.cs | 4 ++-- 18 files changed, 56 insertions(+), 63 deletions(-) rename osu.Game/Overlays/{Direct => }/BeatmapDownloadTrackingComposite.cs (94%) rename osu.Game/Overlays/{Direct/DirectPanel.cs => BeatmapListing/Panels/BeatmapPanel.cs} (96%) rename osu.Game/Overlays/{Direct/PanelDownloadButton.cs => BeatmapListing/Panels/BeatmapPanelDownloadButton.cs} (93%) rename osu.Game/Overlays/{Direct/DirectGridPanel.cs => BeatmapListing/Panels/BeatmapPanelGrid.cs} (97%) rename osu.Game/Overlays/{Direct/DirectListPanel.cs => BeatmapListing/Panels/BeatmapPanelList.cs} (97%) rename osu.Game/Overlays/{Direct => BeatmapListing/Panels}/DownloadProgressBar.cs (97%) rename osu.Game/Overlays/{Direct => BeatmapListing/Panels}/IconPill.cs (96%) rename osu.Game/Overlays/{Direct => BeatmapListing/Panels}/PlayButton.cs (98%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index f612992bf6..9fe873cb6a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -9,7 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets.Osu; using osu.Game.Tests.Resources; using osuTK; @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(PanelDownloadButton) + typeof(BeatmapPanelDownloadButton) }; private TestDownloadButton downloadButton; @@ -143,7 +143,7 @@ namespace osu.Game.Tests.Visual.Online return beatmap; } - private class TestDownloadButton : PanelDownloadButton + private class TestDownloadButton : BeatmapPanelDownloadButton { public new bool DownloadEnabled => base.DownloadEnabled; diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index cb08cded37..5809f93d90 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Users; using osuTK; @@ -20,8 +20,8 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(DirectGridPanel), - typeof(DirectListPanel), + typeof(BeatmapPanelGrid), + typeof(BeatmapPanelList), typeof(IconPill) }; @@ -126,12 +126,12 @@ namespace osu.Game.Tests.Visual.Online Spacing = new Vector2(5, 20), Children = new Drawable[] { - new DirectGridPanel(normal), - new DirectGridPanel(undownloadable), - new DirectGridPanel(manyDifficulties), - new DirectListPanel(normal), - new DirectListPanel(undownloadable), - new DirectListPanel(manyDifficulties), + new BeatmapPanelGrid(normal), + new BeatmapPanelGrid(undownloadable), + new BeatmapPanelGrid(manyDifficulties), + new BeatmapPanelList(normal), + new BeatmapPanelList(undownloadable), + new BeatmapPanelList(manyDifficulties), }, }, }; diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index d68217dcfd..2eaac2a45f 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -47,15 +47,8 @@ namespace osu.Game.Tests.Visual typeof(IdleTracker), typeof(OnScreenDisplay), typeof(NotificationOverlay), -<<<<<<< HEAD - typeof(DirectOverlay), + typeof(BeatmapListingOverlay), typeof(DashboardOverlay), -||||||| parent of 96a3a08a9... Remove unused classes and replace overlay in game - typeof(DirectOverlay), - typeof(SocialOverlay), -======= - typeof(SocialOverlay), ->>>>>>> 96a3a08a9... Remove unused classes and replace overlay in game typeof(ChannelManager), typeof(ChatOverlay), typeof(SettingsOverlay), diff --git a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs b/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs similarity index 94% rename from osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs rename to osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs index fd04a1541e..f6b5b181c3 100644 --- a/osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs +++ b/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs @@ -5,7 +5,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays { public abstract class BeatmapDownloadTrackingComposite : DownloadTrackingComposite { diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs similarity index 96% rename from osu.Game/Overlays/Direct/DirectPanel.cs rename to osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index 4ad8e95512..f260bf1573 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -26,9 +26,9 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { - public abstract class DirectPanel : OsuClickableContainer, IHasContextMenu + public abstract class BeatmapPanel : OsuClickableContainer, IHasContextMenu { public readonly BeatmapSetInfo SetInfo; @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Direct protected Action ViewBeatmap; - protected DirectPanel(BeatmapSetInfo setInfo) + protected BeatmapPanel(BeatmapSetInfo setInfo) { Debug.Assert(setInfo.OnlineBeatmapSetID != null); @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Direct if (SetInfo.Beatmaps.Count > maximum_difficulty_icons) { foreach (var ruleset in SetInfo.Beatmaps.Select(b => b.Ruleset).Distinct()) - icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is DirectListPanel ? Color4.White : colours.Gray5)); + icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is BeatmapPanelList ? Color4.White : colours.Gray5)); } else { diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs similarity index 93% rename from osu.Game/Overlays/Direct/PanelDownloadButton.cs rename to osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 387ced6acb..589f2d5072 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -11,9 +11,9 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { - public class PanelDownloadButton : BeatmapDownloadTrackingComposite + public class BeatmapPanelDownloadButton : BeatmapDownloadTrackingComposite { protected bool DownloadEnabled => button.Enabled.Value; @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Direct private readonly DownloadButton button; private Bindable noVideoSetting; - public PanelDownloadButton(BeatmapSetInfo beatmapSet) + public BeatmapPanelDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) { InternalChild = shakeContainer = new ShakeContainer diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs similarity index 97% rename from osu.Game/Overlays/Direct/DirectGridPanel.cs rename to osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs index 2528ccec41..caa7eb6441 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs @@ -1,25 +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 osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { - public class DirectGridPanel : DirectPanel + public class BeatmapPanelGrid : BeatmapPanel { private const float horizontal_padding = 10; private const float vertical_padding = 5; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Direct protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public DirectGridPanel(BeatmapSetInfo beatmap) + public BeatmapPanelGrid(BeatmapSetInfo beatmap) : base(beatmap) { Width = 380; @@ -156,7 +156,7 @@ namespace osu.Game.Overlays.Direct }, }, }, - new PanelDownloadButton(SetInfo) + new BeatmapPanelDownloadButton(SetInfo) { Size = new Vector2(50, 30), Margin = new MarginPadding(horizontal_padding), diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs similarity index 97% rename from osu.Game/Overlays/Direct/DirectListPanel.cs rename to osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs index b64142dfe7..3245ddea99 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs @@ -1,25 +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 osuTK; -using osuTK.Graphics; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Colour; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { - public class DirectListPanel : DirectPanel + public class BeatmapPanelList : BeatmapPanel { private const float transition_duration = 120; private const float horizontal_padding = 10; @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Direct private const float height = 70; private FillFlowContainer statusContainer; - protected PanelDownloadButton DownloadButton; + protected BeatmapPanelDownloadButton DownloadButton; private PlayButton playButton; private Box progressBar; @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Direct protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public DirectListPanel(BeatmapSetInfo beatmap) + public BeatmapPanelList(BeatmapSetInfo beatmap) : base(beatmap) { RelativeSizeAxes = Axes.X; @@ -151,7 +151,7 @@ namespace osu.Game.Overlays.Direct Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, - Child = DownloadButton = new PanelDownloadButton(SetInfo) + Child = DownloadButton = new BeatmapPanelDownloadButton(SetInfo) { Size = new Vector2(height - vertical_padding * 3), Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding }, diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs similarity index 97% rename from osu.Game/Overlays/Direct/DownloadProgressBar.cs rename to osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs index 9a8644efd2..93cf8799b5 100644 --- a/osu.Game/Overlays/Direct/DownloadProgressBar.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { public class DownloadProgressBar : BeatmapDownloadTrackingComposite { diff --git a/osu.Game/Overlays/Direct/IconPill.cs b/osu.Game/Overlays/BeatmapListing/Panels/IconPill.cs similarity index 96% rename from osu.Game/Overlays/Direct/IconPill.cs rename to osu.Game/Overlays/BeatmapListing/Panels/IconPill.cs index d63bb2a292..1cb6c84f13 100644 --- a/osu.Game/Overlays/Direct/IconPill.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/IconPill.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Sprites; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { public class IconPill : CircularContainer { diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs similarity index 98% rename from osu.Game/Overlays/Direct/PlayButton.cs rename to osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index d9f335b6a7..e95fdeecf4 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.BeatmapListing.Panels { public class PlayButton : Container { diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 000ca6b91c..a024e2c74e 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -17,7 +17,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.BeatmapListing; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osuTK; namespace osu.Game.Overlays @@ -118,14 +118,14 @@ namespace osu.Game.Overlays return; } - var newPanels = new FillFlowContainer + var newPanels = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(10), Alpha = 0, Margin = new MarginPadding { Vertical = 15 }, - ChildrenEnumerable = beatmaps.Select(b => new DirectGridPanel(b) + ChildrenEnumerable = beatmaps.Select(b => new BeatmapPanelGrid(b) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index e64256b850..56c0052bfe 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online; using osu.Game.Online.API; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Users; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 7eae05e4a9..6accce7d77 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -11,7 +11,7 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osuTK; namespace osu.Game.Overlays.BeatmapSet.Buttons diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 11dc424183..17fa689cd2 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -15,8 +15,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Overlays.BeatmapSet.Buttons; -using osu.Game.Overlays.Direct; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -274,7 +274,7 @@ namespace osu.Game.Overlays.BeatmapSet { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new PanelDownloadButton(BeatmapSet.Value) + downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value) { Width = 50, RelativeSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index fcd12e2b54..5f70dc4d75 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Users; using osuTK; @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override Drawable CreateDrawableItem(APIBeatmapSet model) => !model.OnlineBeatmapSetID.HasValue ? null - : new DirectGridPanel(model.ToBeatmapSet(Rulesets)) + : new BeatmapPanelGrid(model.ToBeatmapSet(Rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index 6f06eecd6e..895fa94af5 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -12,10 +12,10 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Rankings.Tables; using System.Linq; -using osu.Game.Overlays.Direct; using System.Threading; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.BeatmapListing.Panels; namespace osu.Game.Overlays.Rankings { @@ -140,7 +140,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Spacing = new Vector2(10), - Children = response.BeatmapSets.Select(b => new DirectGridPanel(b.ToBeatmapSet(rulesets)) + Children = response.BeatmapSets.Select(b => new BeatmapPanelGrid(b.ToBeatmapSet(rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs index d7dcca9809..c024304856 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs @@ -21,7 +21,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osu.Game.Online.Chat; using osu.Game.Online.Multiplayer; -using osu.Game.Overlays.Direct; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Multi return true; } - private class PlaylistDownloadButton : PanelDownloadButton + private class PlaylistDownloadButton : BeatmapPanelDownloadButton { public PlaylistDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) From b8a1831d98feb8fc6752a2755b66df55de66c832 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:14:04 +0900 Subject: [PATCH 14/42] Read line widths from skin --- osu.Game/Skinning/LegacySkin.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 91f970d19f..003fa24d5b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -249,6 +249,14 @@ namespace osu.Game.Skinning case LegacyManiaSkinConfigurationLookups.RightStageImage: return SkinUtils.As(getManiaImage(existing, "StageRight")); + + case LegacyManiaSkinConfigurationLookups.LeftLineWidth: + Debug.Assert(maniaLookup.TargetColumn != null); + return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value])); + + case LegacyManiaSkinConfigurationLookups.RightLineWidth: + Debug.Assert(maniaLookup.TargetColumn != null); + return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value + 1])); } return null; From 0a2b585c65ca963a1fac0eb917328f329b722d54 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:14:49 +0900 Subject: [PATCH 15/42] Apply missing scale --- osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs index 6504321bb2..1a097405ac 100644 --- a/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs @@ -67,6 +67,7 @@ namespace osu.Game.Rulesets.Mania.Skinning { RelativeSizeAxes = Axes.Y, Width = leftLineWidth, + Scale = new Vector2(0.740f, 1), Colour = lineColour, Alpha = hasLeftLine ? 1 : 0 }, @@ -76,6 +77,7 @@ namespace osu.Game.Rulesets.Mania.Skinning Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Y, Width = rightLineWidth, + Scale = new Vector2(0.740f, 1), Colour = lineColour, Alpha = hasRightLine ? 1 : 0 }, From a41ac50e2f052854032131e62d12407e6fd3242d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:15:06 +0900 Subject: [PATCH 16/42] Line widths should not receive scale factor --- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index 2db902c182..a988bd589f 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -74,7 +74,7 @@ namespace osu.Game.Skinning switch (pair.Key) { case "ColumnLineWidth": - parseArrayValue(pair.Value, currentConfig.ColumnLineWidth); + parseArrayValue(pair.Value, currentConfig.ColumnLineWidth, false); break; case "ColumnSpacing": @@ -124,7 +124,7 @@ namespace osu.Game.Skinning pendingLines.Clear(); } - private void parseArrayValue(string value, float[] output) + private void parseArrayValue(string value, float[] output, bool applyScaleFactor = true) { string[] values = value.Split(','); @@ -133,7 +133,7 @@ namespace osu.Game.Skinning if (i >= output.Length) break; - output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR; + output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * (applyScaleFactor ? LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR : 1); } } } From 4642a6093c2b6e5fa559059567e77863125e2c2a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 17:15:13 +0900 Subject: [PATCH 17/42] Add test --- .../Resources/special-skin/mania-key1@2x.png | Bin 0 -> 12914 bytes .../Resources/special-skin/skin.ini | 6 ++++++ .../Skinning/TestSceneColumnBackground.cs | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-key1@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-key1@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/mania-key1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..aa681f6f223ea44319b3a383935237d1a2e4b345 GIT binary patch literal 12914 zcmbVz2{_d2`?qzPDKbL~r3}U(WgBaDjY8HGl`L7y5>l4zOUEcmD8`5a?bTU&%J-{&wYQLSTmD-+-tY2Wnp3AHqa*@ zfIpq!S2r33zl*;U-@(FyXE7k_91LKctiR}M^Xc61{LD;X&k(k89m^1F0*k?w+%so_ z1uLiXGV{JGj4QMoxs3Srf8d#Vopkaou}1CGM6X7?o86;)ZhHszV@ zUbF$12RkuWcFV@J{Obao`SP^^adwMkc4IuDGyV(lE8}+ZSo?Fe^x74#s*0bQ6<)(1 zT(t&n4DNqY{;K?H{~>ukdiEOOP<}aQRAMTxJxVO{)rPeEmvL-TR6F&i62=$Pg=bm| zHp|T`3o`s@+3Mk?p4!HsqMk<~OPj)eJ-Fsq@@nc(fB6A-v98`RcZcj$`Qgi{@-loD zV{Hw|i^n-qvvhc6NO-J$4H5M;HG*L4pdBn_z(o*_JrJ3XNi(lMxXEPur;Ac#oc zC`|L0vtXlXqZ^Xz!&wZt;&3t#baKV9_9QU^Tp*$n6LOMsPOf#{pnX5!Cx_PJMbMA{9pNe<3u~dIPswV~Oi$6L+(MhDfyB1Lgg&1yG`8Ar z^O__6VKc*DO~dHSxP;AyF{GsJ9<@vjC@-dG`=9ZIpxiZfD|R9(QP^GV^P@RZd-!@8 zk*vZPSta`irn*&s)#bY)|9OTe=PX;oYyU$43sxsXY%JJ7DfTfv6oG;^v^bHLBe2n} zJY#otooVQzXy`moXv*-yr(K zt;On^orXgEFY$y0XKJln)EX6HjD|hc4j=#3C-h6R)m8dy#C?uD38QqVZzT#O-~!i`9d zneCaQKVy0pS}%vS46n>A%OUY~4P(UY%&w{XB{Th%{^=?dY1!4gkW|!fw5`;TCUnGt zAIcG>TlFGUl65b-ft7v4>pa#=Bvu(WVjOpc9GinUNPY*NKHcfR@Gh!|@ljmsV=d!* zSOcQ{Vm3prwZJmKdqr9+%yzs#Z?Nc$=S@q;tu*&kD}HP(KA)DHD$kGAU`3I{&W(Jh zwQ{i0WKty~Sm@RIRb<{_J>Dg*rr@mx$-K!#2{DNqkp`DKgg$;LlL}@u+s)cBlxy1& zIkjP5XAbW!qdkkUn`H#vbWgf#`~;9D@uY0YaoG~_a}L>Unz52mtniDo`JsUEMT_BuU`{Qg zYvtkt`u;mwH67r!yNaIHw`>kn5=f8C+x?Jf zc1d+(Qi<4M-6~x)cEnnrD>{G=lZQ7nyKrJJrkSJM^Zlt8GO1o-5%H?{Izg=cdcdt*@rb~nk&U`l*R&}msqAb} z0+sq~u~;%Dm*XY|t@3r6(rP6=w>a4G=tog4Qa~ZgU8aZC&!vf{Uk+TZdR1mHwFA0h zT5r`APuqRGYvS6Z$fj&@ZJGv*=lBNA*bpVVqyWCTbpR~!1OkPZjA<#vayI#%Z?!7% z{?yKWcezD|1ZromG(OKz54|Ry^jw{p*naRZHI|Qy!VZ4&wB4_3M9pe*H$dNO)5OdP zWnn2URi?$g?o65IFmO65mC=R*;N2N#QKM?Q4=dYEi4 z?MVZKPXVM#>uo|o?k-mDWAo8QLu+)TB_}jU5o9$Gpr;vODB%>yqP-EwgYGjnW7EH^ zk1fvaL*iL&s_Fdx#mOuWO_vhIGE!L)yyuqD6wblsAsbeqk8en36^`w>-zP2%Ue*8) zpa^ruBjLJa3M5G+cJ%SPS68Lp1}&(%3x(7n3a)%pTDNag;LkIe)zt(wi^{x-9Y^B3 z_(@m?`}~IFK&py;&Beu2BZpiJji_F{Y_Smw#E5|NG#O~ORNghTgb$PAF$p!S!Up8R+*RYW1JDZ&M`d#s~=uEPW2MSV9QzIHyM%z07X-<9xpq<2J48Y z!N^&TRCb(${Z_8Ea`(7V26*8J5eYpqUm>OQn0Ki2`R1lQ?Y4H#U=C4v#@FH0mEx(h z{Tdc(FVdV8sZCUSZCx~7#J8^Z8!a6jkt&b3b?73o8|c^9nRw8rq6vh#qbOf!)m*e> ztPB<{CyR<=i{|g$%WGHKrp>F5#9e@MLuXv$;z#E!qwU^5eyhgi2#)gkL1-VC+ zE0#q~8iD6j_}F#z9a@0RcsOcTQ3FYH+`Oq-fqahI=m_oLqyTE*)qpF|{Dkvqb!cG> zeIs37E+JYsnU^dokt?v~lhw6A`NtjO?(RsQe(2U18&+-W{(xkY{e(1nx)~0Y)M-*E$ zwnTJ&YLDD7fglXHKY#|1LbK%}L5n4|N=>SVG&~JzMI8F$(^f%GlY5nF*C%rP47&6D zh+VzGG62a71J0d_-GCV%^vPMmFZSHaa#7Yai!{ zO5_K6#b!Xp$*ny|7KrWol+rtxB^&l}Cs3V=kf5hfkan8sFU!oVlisKZBl$a0DS%+e zNDQw%SFZ~mGv}W|KvE@5TQ3=xu{$hluoy$PYDY#R>k4tfZ0mQeuIUeh3_Oi3lTK#X zZ}s>QD@otMigMJZWN|2!VC`+i^0~?ZOaVxj04;=4V9qhd$4clVYwHzKL&%0&~VYik&6;RXkZ;k-7FwXkdtl7ch%O{71U2p5x^#5wGslTi^rj8h$)Ner`{pDk8ljw+a5=`7=}a8MF3h_ zxM<2i14y#ERhO8P7DY%`I2xe9gdQRh4Tz_Yrw}Q;$ymAdCeqo?^W!LbN;|>^2K>bZ z-NzPB`zMu?`gANINX9PmSnxo@gaAnlH4-j|w5z!(gzcb!u;FbxV8+ARgKQ%K>~9&4 zaRD0ax*&($(KZjeLq{Q(Y}Vuep0PXSc{+HC3&{mv|0KsYOxBdm)mIL)8_Qs7(F@wphvZhXT=&Ko>!Doqdf^C!Ymy zAYQDl9vOF&!!*c@V$5^tOeDB|( z?j=QR&}WO|_LGN)t*DZ7Y^{YY&T0%Z8Jyw}i0zcIT+cwdptagFq}>csy@MatrBw3ucfWSKA6ev+M*AS8%TEu%KxsO& z(dLDt8aE-cq{-`nYd>2hHQJWVh z0#Kp|&;;lE2K()oU#TLXJD!PozKOAcwI7jBkl%%FVxuTrPIizpmRY{qobTPjMGVe9j}#q z&Vvaij@p8tS{}(I|XV} zGtJseJDP%xeksA<##xYZt#>Hk#Yovkq)$(PfVN%tmOc^(vu=4NZp;8@QB`+F03*`H z>c;a6NoMS>ZTQi&XH!YBEYzH-m&Vw_>ibjF{0?g6 zY|mF^`Q^kSpDp4}Y1C&)mJ=h*Ymm` z9Oo=3jc=nJM-yO}MI(rGIOE7smru>2o9;>@Y}~p+$k*dxUHf!&KeZoVBhx3+(`Eg$ z@C0o*uP6eQU{d(I@+eHbCERy>VJYMWutm(Yj<@C-hZ$Ue{~a`UY)XmsIIM9FWxpa9G;wE?FsYE8h zfj9&RUk^XQwh?#JtZC$J%UWyG?Wez1Gi!}`esf@XW%E!L5V$la*Ng-uQjDlJIEc3p z*SVnu-Clg>-uDkU@nP%ZP|QkR8t)d~id3qlKPcGcGh{g9y|%StF^2P2D_0J za^vjBpRP3(=TSZDc@=36Rg0X@hgF+@{D{7L{x0c=v0rSLy`wgbfklx`SMMOh&nuV` zkm09)3mC2ZI52zE_u{07*UR#JzXZkRc9nQf8Xyu(Gi1zbibO7T-8C3AeNShTdb*KJ zjklgxm9uqn4azJ(-%mY?2thIr!fc3*r|>E`mY$gtNoo#hx3xw%RkzomYiQMr_8+^E z;ioKZlDD9g8n}*^%&|jc3r_4rlf(Fko|5>ys;_4}*kE0IHhXbSL~URzl18`QJlg+v z-r>Z6VN+aoUbB?B|9CE(v>N2^*7H;$nfni_bq3Nvop`k*1YlIy5WtB3M}V@B_t+O^ zltU*p>7#mny zv4B{%KLd#6DyACZn2ATYCUeZ5-n^&%m<CK|;$5t~#GpKIvaiE_c@eE=#vTy4OucrzA#S)t!AqKf&ZnoDU zFz|ev_+=IPGSdp5QP$^U0DGA)q3^&hq1Yh59>JsQXhbEswJrKPWlHWnUZjwEtdLr0 zj$b2~neOWEuiFEyu%B2){Egep`I`*A+2Q2L^UWWGU9}D)VIyX1y!#xG1ytC^)y>^4 z{dF4Th0A(V-U~S!R*RwW)mKhpI*)2l2s8H{&4oMqt`2<`))oVKFhtyAh1Lm(YL28g zQNppbDmbEf?9YUcEG*{yH}&;VU!Ki(PwFQgF-A<-1!MXFHXv4<&V*}L(z1O4b$vXm ziMQ2Xb-7)#++X~m=Yjdeelg@iQ+Vp4Fc*@o$$%MhI}}Fkonepglf6CE8}wt5-J?lVUQeLy=4AZy&XGKGrMu zR~kRc+@w^(=*-H_Mx*fvC&1u<1QFAsf9x;>aon-Jsuo#`7;9Fxc6N#|(FRsqX-?@* zTf`ygtjvX&ZgNf>qFI5dkniuc!`D8IOP4nF4pCqFCD_19*E8{C1{4w}83CtAXr+-? zVNG$W#P@flE>3RWw{M-tjK?cKBC_5ArF`V+n>GNM5zWQq0wP;pvkHKIKJH$GaE%WS zf&{nix>x-GX3u`HG9oMpZ+nncTts zKxM<5RerVB99iiB*{DCiuGK>`kVc*FL7?D!$MosoQ&*;nx%=B+x4%vUvIr)}h$U#_ zlJ`QI^FlN}KIK#*@~HU#l1Dh=!UMI8RM<&$1O0Y`#t5x+OCq%bhZTmUG%{!i9y?t9 z;%8GxyB%}Msqu4Cn(lki!^`vKH1%lGQRrPpd)QpYWH~q>O(G>k3}41Yt#dEB;}u-L zIAMh-wJ&?nIrP+(%wp~zKN9cV*f=9Qy&k3%xbd+;4XiJI08$XPrAYlt0b#IM2s#cDOP91>`mU{$zdJbBVY6&CaPhpG5OqNV?x_?zq-18$o2qGK;Vq= zMF7!o!5l9_F~2Sld$DM9U#j{$#&;%(9W|Xf&B)X%uTbtCD0;2*FSQJ@tbn4CE`rCZ z06#%_v7>Q821kl3%ij)t`m*7&->n$0q+)~_x>w}+gI;hV{DV(wTC zdjL|9UB+JI4)9qRLL`toMMawftLDOQ^4vw36pvPQTdZh7Z(kbnfdxTQxN{760>u`( zAM7@g;Ztzbo3caOHEW)?#4yoxDSOBPHqUfpbzqJ*n2*MZt;$KU z44j=^zd7`qCgi^VN|*IcLDjbQ?&wb)Qb<#{Poy?skZ4Na4YAl(g=c$c z`+1@24geL6g>OpqeI}(3%IfK#ka#8`TM4JJUC0$N3XIEXkDq?IskmM9$+MOiW>K!m z9&%aEw3zhpoDG(_EYiCg1x0oBP$z1;>2kVScON(X4J`sDTgq z-!0Pf5Fhv%R~vc;K}k*I&52D*hAKuu%+N5$>M!;XtlOlQx%>L1DmDIbgZ!05}l92GCFN znl|D+Xcoq1O`s##Rnz+0uBGhIrjbh5-gEcshvSj`u+veEH<(wzdw%)dc@qF1@^mFO5wavyHRS2eZfU!b`lx|p3!nEOP_Qhn z3?EhgDlk>*4a#zIbu)e<-K;w-9a5mu%1G`A8@;jd2*0BZh{nK z@9dt^6Yy<8T1v82K!CUp&DpB$F(J3qQXF{;b=R2>=**mo55|nk=&jc zO^4iW1WB-@fllC*;zTro`C={XF@E2;bGP{GiC^0{Ydt|eYz{6h5aH`s>Gw04eloYV z{rd(V(gML}wnsm9js_iL{0|vQ|Gpsx9T~Z?(^95*?(>|6bm*DPf7=2>)p{tn{qlST zR8>L*0iX?x2R6(s?Be0kk$pV~O$rhkc6$FpvOMcf7qBXTDfsdUIktyaAD9Bv8)7=B zHTvJx>OO-EK1|c;&jheJ0By-or}5Bme&=%I<-5ofJ+HYOcV4uhFZ4+}UBkjUNfG>v zd3938E{p>}6^fgQ=wq36m+R8C)*Dwt3Y%K~q^ z-7j)VGXihp;HMIRB*RB>McO=nPsKwgIm@G6=0d8^u@N%6o>_U^@%-BMCfSerTFKiq7X@7;YO5KiqTZoSk;CRK z`hD9jtE#EJ@A?(S;enBTR-1RG=y>wY6WjpUWFN5}GP5|iaaZKVn{mm5 zbEyv=@M#W)>6?b<`JP+25MQ<{^o*kOV=lL?kHpQRDEP?wvzWs0wA}o{wU){EZ!3(- z&9s{=oLLdpU`=cve=nufUB1xyhO^cAz(9irWlQuLY692YJuK#ChaO6}MN$t~UPWZk z`80ox{C!2U|3i9_Jv%U!b+wdKQl5Q6^)f<&=JnsM2dv1Bx{143=;@YoNHG-R)`Zo>J z@^=p8bQ9EPl_+R=yuh&-mfW1%ht2aOTnq&;mB!B+mnz;4EX8H97<~FRi`elv&G<-p zL=TwJ9s3^=8jU1Hk5`<06cE-@UH#j!cBQ4|ZErjb2T*0iX`P2AWZwFBsDnDI1{exR zR<=oRotB|LUV)lIx_LA5jY%$-J-*I@oVb=xffxRuK9<(+tSxir9;(wh7pI)Gv!TByQI5{YYXB?Ao#)QZ2{#+**TmS~28&RZui53_59R+YG z-1se_$LsO@qU&mE$n-6oxac)zam~}rePlbC`)L%HKB`BUO(ZA(g!$_Yq?>WkgDxrK zJ+8q^bh{kTD$sXfW7u)e^rF6G=dA(8Z`r*vzY!Aa%Lw?w$lJMkS8`L=I_FJZ@~4LW zx)hWhwiijL;MvIH?`kv|L@^{1Tk;@1kYH=;VJ+@}W@!iZ8j_Zi>EqUZwF~8=B7#_LbN_IM!4ISw+`#D2LCV5Bf0(W=T9_o zyKWX&SPDYC`FxE`@3GovjMFXmxvL;kU*pwZK$u0@DF1}n<*m1$kM4uoeZM2t_T7`| z{gFT3C$Ju$T@hhrC37Cqxp-Yq*1{t%PMq8I)4zlayM(GjeU=f;x0UDwBvPH^AE9tq z+-nZ0zf$e<@7&qamiK;=@h>k89Ba$pc}>TYeI9>QTs_vwoBejq?Gxqz0WB@JUl}!J zWM=>Il2%y#*z^B(el^C<&WBDC9oySYPyCqw3>9%HyVl+}Bz@5X%myG^(+OVvjg%L2 z^ADR_?%hi|siLNJy>FO_7@7g~WBp2WB(nind2-K+B%)EuIe%hiX?fK}e(XQAUxXB@ zpmrbtQy%d@>iIsvWRl!J{wV=%CPr7CYBo@(L4lzC17M9tM+79>oM?T2XXu}do{an` zU#K{{1u4#d)G5tPz9N^UP^G4IylORO-xvu~muKX&ZSEaIm7#3eOH2Y1S)1SOCTnxh zm^rN{DSCsdkdY}w8d`}&lJs*7vu27GIWOwH0|^0VpC;)e2H(E0QO_789D?&zYHBWD zKd(bN&kY%#z9(l>p!6G`&V32sej7E5=SAQmN2Czr0#(x}Q3M_hn4eZmdcMRg<63nm z`luRU*n`6dtGM5?m*|kOOpH7v2bg3Tci&B6)MhAi8!52L6cUgAvyU~sV*0dVScil8 z9K@jNPje$dbIUy}*K)fvZtGCBn#0!*IHc@5Q`UG4_aE0xzyI&|>y6Bz^C0g-juv=b zezfxY`~&v#Q#0O*|EKegCc`x&%SrnX1NL>S?|oAJ?2Sju|4ttWLPm|o-{)scaZewCOw7?F)@#QW>q%?2?%@)1tn+o18>-avXNaqFI6 z5V=bsBdh8|eb@6pkwC6YX26weu|Z}VqH$bF%XO|kR%vd_>n~&u z0j|6IwK5S6+XrYbbMuJq z6_x^*w+?0eV|cB9-H(nbZ&kJbA0b*QLqyyEZi%?#kYHG2RKaD0JzUD#cJF)h`Uicg ztvr`x_~vsg#YG_QHh%}CxxW}U+8nL?9HO{td=JzHM5MLV_u9oAY4-WLBEBooL*03? zxPzQvefU$jlGGfXG+Gic`%Mlh*X9(NUd9J$yReWZhxf0Yk8OQAFw;b|M;AG$5 z`2O_Q(!6TmuZ=vRi_AL{kny3XQ~Ew~UOaD+5)lN<=6AEZ9Z5Z|_fiC=E7`|d?-#z$ z&@=5qb(t{IDs=zsl5|SS%*hlJ8-T+%09B&GtjE2%b0gCZ7?tLpz_r}=Sy>J-8LIi9 zc3DUieq{Vp+k4in6V^28+I*YcX9XU5zS~abz+nJFISz6v-b{7SE8W<*R(;|8nySG0 zwB}_|4lI7WDkSg)(98#|`}_57b~`;jQw2?itozj>u|9j>f+JggB#?>T3vichbg8l` zJDWZ=6KsO$I5wYM`pZ|VgK@fPWKW~i!`2Huc=cE=`Nbr89e&2cJW)pKtfL4(tE6SL z{EkA*jVrVC`I^S1KF-SQDI}E`(_u>;>VjHMnoEB*t{lZmzlNWG6IX9Gl3$E{F!ceZ|g)ATRu1v3)Sw>7p;8&@zc%*8T!zPp$_N~0LTKqYo+NyOJdn+LQbvMRe z9-k!C>NM+WxJ+R|=AFn5H#a=HfKR(~x1^L_jQqU(iqgtLPgo}Mh!c)gRpP`Ps3(NCib+rr1E7w+L wg;q4E3=A6~DqG<9Z(%4z15@U|t!yQ{`F`%RWy3uDza%UMdM4z%y7rO(3s2?PJpcdz literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini new file mode 100644 index 0000000000..56564776b3 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Resources/special-skin/skin.ini @@ -0,0 +1,6 @@ +[General] +Version: 2.4 + +[Mania] +Keys: 4 +ColumnLineWidth: 3,1,3,1,1 \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs index d6bacbe59e..bde323f187 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { RelativeSizeAxes = Axes.Both, Width = 0.5f, - Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground()) + Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 1), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both } From d1c701a9972a42af10021bccb5d528ddb51f71c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 18:34:56 +0900 Subject: [PATCH 18/42] Rename existing test to something more relevant --- .../{TestSceneTaikoPlayfield.cs => TestSceneHits.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Rulesets.Taiko.Tests/{TestSceneTaikoPlayfield.cs => TestSceneHits.cs} (99%) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs similarity index 99% rename from osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs rename to osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 0d9e813c60..c2ca578dfa 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -24,7 +24,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Tests { [TestFixture] - public class TestSceneTaikoPlayfield : OsuTestScene + public class TestSceneHits : OsuTestScene { private const double default_duration = 1000; private const float scroll_time = 1000; From e74f9024836b56b4485d724d867fa1f71ea0a5b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 18:40:18 +0900 Subject: [PATCH 19/42] Add playfield test scene --- .../Skinning/TestSceneTaikoPlayfield.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs new file mode 100644 index 0000000000..e255baf459 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -0,0 +1,32 @@ +// 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.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + public class TestSceneTaikoPlayfield : TaikoSkinnableTestScene + { + [Cached(typeof(IScrollingInfo))] + private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo + { + Direction = { Value = ScrollingDirection.Left }, + TimeRange = { Value = 5000 }, + }; + + public TestSceneTaikoPlayfield() + { + AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo()) + { + Height = 0.4f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + })); + } + } +} From bfc17bf4c09ac2493748c3751f3921b374746086 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 19:00:34 +0900 Subject: [PATCH 20/42] Add taiko hit target skinning --- .../metrics-skin/approachcircle@2x.png | Bin 0 -> 13816 bytes .../metrics-skin/taikobigcircle@2x.png | Bin 0 -> 12145 bytes .../Resources/old-skin/approachcircle.png | Bin 0 -> 10333 bytes .../Resources/special-skin/approachcircle.png | Bin 0 -> 4504 bytes .../special-skin/taikobigcircle@2x.png | Bin 0 -> 12374 bytes .../Skinning/TestSceneTaikoPlayfield.cs | 10 +++++ .../Skinning/LegacyHitTarget.cs | 41 ++++++++++++++++++ .../Skinning/TaikoLegacySkinTransformer.cs | 6 +++ .../TaikoSkinComponents.cs | 3 +- osu.Game.Rulesets.Taiko/UI/HitTarget.cs | 2 + osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 +-- 11 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/approachcircle@2x.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikobigcircle@2x.png create mode 100755 osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/approachcircle.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/approachcircle.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/taikobigcircle@2x.png create mode 100644 osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/approachcircle@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/approachcircle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..72ef665478b758c364197aa79f238230e229b3c4 GIT binary patch literal 13816 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 X;|*6ltxe$d|7PiG>1$T1+eQB$HM;D+ literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikobigcircle@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taikobigcircle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..440e5b55e52c8629f69272993a75b1a56002fb60 GIT binary patch literal 12145 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/approachcircle.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/approachcircle.png new file mode 100755 index 0000000000000000000000000000000000000000..5aba6887564782a216c030b6a0143830612193ee GIT binary patch literal 10333 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 H<7ECnYE>!V literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/approachcircle.png b/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/approachcircle.png new file mode 100644 index 0000000000000000000000000000000000000000..56d6d34c1abf6ab153c9cef62eecff3016d21ed9 GIT binary patch literal 4504 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 Qd;A||`ImC#G8P~IA7%MrKL7v# literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index e255baf459..730eed0e0f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -1,9 +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 System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Skinning; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; @@ -12,6 +16,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { public class TestSceneTaikoPlayfield : TaikoSkinnableTestScene { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] + { + typeof(HitTarget), + typeof(LegacyHitTarget), + }).ToList(); + [Cached(typeof(IScrollingInfo))] private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo { diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs new file mode 100644 index 0000000000..51aea9b9ab --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitTarget.cs @@ -0,0 +1,41 @@ +// 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.Sprites; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.Skinning +{ + public class LegacyHitTarget : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Sprite + { + Texture = skin.GetTexture("approachcircle"), + Scale = new Vector2(0.73f), + Alpha = 0.7f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new Sprite + { + Texture = skin.GetTexture("taikobigcircle"), + Scale = new Vector2(0.7f), + Alpha = 0.5f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 3af7df07c4..6b59718173 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -49,6 +49,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.DrumRollTick: return this.GetAnimation("sliderscorepoint", false, false); + + case TaikoSkinComponents.HitTarget: + if (GetTexture("taikobigcircle") != null) + return new LegacyHitTarget(); + + return null; } return source.GetDrawableComponent(component); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index 156ea71c16..775eeb4e38 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Taiko RimHit, DrumRollBody, DrumRollTick, - Swell + Swell, + HitTarget } } diff --git a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs index 2bb208bd1d..88886508af 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitTarget.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Taiko.UI public HitTarget() { + RelativeSizeAxes = Axes.Both; + Children = new Drawable[] { new Box diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index bde9085c23..375d9995c0 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; private readonly JudgementContainer judgementContainer; - internal readonly HitTarget HitTarget; + internal readonly Drawable HitTarget; private readonly ProxyContainer topLevelHitContainer; private readonly ProxyContainer barlineContainer; @@ -90,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.UI RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, Masking = true, - Children = new Drawable[] + Children = new[] { hitExplosionContainer = new Container { @@ -98,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.UI FillMode = FillMode.Fit, Blending = BlendingParameters.Additive, }, - HitTarget = new HitTarget + HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new HitTarget()) { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, From ed9663985b439dba6b5c51281ba40cd6cb2e1c07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 20:55:33 +0900 Subject: [PATCH 21/42] Rename panels --- .../Visual/Online/TestSceneDirectPanel.cs | 16 ++++++++-------- .../BeatmapListing/Panels/BeatmapPanel.cs | 2 +- .../{BeatmapPanelGrid.cs => GridBeatmapPanel.cs} | 4 ++-- .../{BeatmapPanelList.cs => ListBeatmapPanel.cs} | 4 ++-- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 +- .../Beatmaps/PaginatedBeatmapContainer.cs | 2 +- osu.Game/Overlays/Rankings/SpotlightsLayout.cs | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game/Overlays/BeatmapListing/Panels/{BeatmapPanelGrid.cs => GridBeatmapPanel.cs} (98%) rename osu.Game/Overlays/BeatmapListing/Panels/{BeatmapPanelList.cs => ListBeatmapPanel.cs} (99%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 5809f93d90..d6ed654bac 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -20,8 +20,8 @@ namespace osu.Game.Tests.Visual.Online { public override IReadOnlyList RequiredTypes => new[] { - typeof(BeatmapPanelGrid), - typeof(BeatmapPanelList), + typeof(GridBeatmapPanel), + typeof(ListBeatmapPanel), typeof(IconPill) }; @@ -126,12 +126,12 @@ namespace osu.Game.Tests.Visual.Online Spacing = new Vector2(5, 20), Children = new Drawable[] { - new BeatmapPanelGrid(normal), - new BeatmapPanelGrid(undownloadable), - new BeatmapPanelGrid(manyDifficulties), - new BeatmapPanelList(normal), - new BeatmapPanelList(undownloadable), - new BeatmapPanelList(manyDifficulties), + new GridBeatmapPanel(normal), + new GridBeatmapPanel(undownloadable), + new GridBeatmapPanel(manyDifficulties), + new ListBeatmapPanel(normal), + new ListBeatmapPanel(undownloadable), + new ListBeatmapPanel(manyDifficulties), }, }, }; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index f260bf1573..88c15776cd 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels if (SetInfo.Beatmaps.Count > maximum_difficulty_icons) { foreach (var ruleset in SetInfo.Beatmaps.Select(b => b.Ruleset).Distinct()) - icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is BeatmapPanelList ? Color4.White : colours.Gray5)); + icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is ListBeatmapPanel ? Color4.White : colours.Gray5)); } else { diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs similarity index 98% rename from osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs rename to osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index caa7eb6441..84d35da096 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelGrid.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -19,7 +19,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapListing.Panels { - public class BeatmapPanelGrid : BeatmapPanel + public class GridBeatmapPanel : BeatmapPanel { private const float horizontal_padding = 10; private const float vertical_padding = 5; @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public BeatmapPanelGrid(BeatmapSetInfo beatmap) + public GridBeatmapPanel(BeatmapSetInfo beatmap) : base(beatmap) { Width = 380; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs similarity index 99% rename from osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs rename to osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs index 3245ddea99..433ea37f06 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelList.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs @@ -19,7 +19,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapListing.Panels { - public class BeatmapPanelList : BeatmapPanel + public class ListBeatmapPanel : BeatmapPanel { private const float transition_duration = 120; private const float horizontal_padding = 10; @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public BeatmapPanelList(BeatmapSetInfo beatmap) + public ListBeatmapPanel(BeatmapSetInfo beatmap) : base(beatmap) { RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index a024e2c74e..f680f7c67b 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -125,7 +125,7 @@ namespace osu.Game.Overlays Spacing = new Vector2(10), Alpha = 0, Margin = new MarginPadding { Vertical = 15 }, - ChildrenEnumerable = beatmaps.Select(b => new BeatmapPanelGrid(b) + ChildrenEnumerable = beatmaps.Select(b => new GridBeatmapPanel(b) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 5f70dc4d75..191f3c908a 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override Drawable CreateDrawableItem(APIBeatmapSet model) => !model.OnlineBeatmapSetID.HasValue ? null - : new BeatmapPanelGrid(model.ToBeatmapSet(Rulesets)) + : new GridBeatmapPanel(model.ToBeatmapSet(Rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index 895fa94af5..917509e842 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -140,7 +140,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Spacing = new Vector2(10), - Children = response.BeatmapSets.Select(b => new BeatmapPanelGrid(b.ToBeatmapSet(rulesets)) + Children = response.BeatmapSets.Select(b => new GridBeatmapPanel(b.ToBeatmapSet(rulesets)) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, From 6193b80589790cc4f90a6141cd34fa6900b2723f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Apr 2020 21:20:57 +0900 Subject: [PATCH 22/42] 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 d2bdbc8b61..25942863c5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 35ee0864e1..9c17c453a6 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 0200fca9a3..07ea4b9c2a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From e9a2e92adf3573c27096b796d99db094f2fd6f5d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:43:22 +0900 Subject: [PATCH 23/42] Fix incorrect beatmap comments --- osu.Game.Tests/Resources/hitobject-combo-offset.osu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Resources/hitobject-combo-offset.osu b/osu.Game.Tests/Resources/hitobject-combo-offset.osu index c1f0dab8e9..d39a3e8548 100644 --- a/osu.Game.Tests/Resources/hitobject-combo-offset.osu +++ b/osu.Game.Tests/Resources/hitobject-combo-offset.osu @@ -5,27 +5,27 @@ osu file format v14 255,193,1000,49,0,0:0:0:0: // Combo index = 4 -// Slider with new combo followed by circle with no new combo +// Spinner with new combo followed by circle with no new combo 256,192,2000,12,0,2000,0:0:0:0: 255,193,3000,1,0,0:0:0:0: // Combo index = 5 -// Slider without new combo followed by circle with no new combo +// Spinner without new combo followed by circle with no new combo 256,192,4000,8,0,5000,0:0:0:0: 255,193,6000,1,0,0:0:0:0: // Combo index = 5 -// Slider without new combo followed by circle with new combo +// Spinner without new combo followed by circle with new combo 256,192,7000,8,0,8000,0:0:0:0: 255,193,9000,5,0,0:0:0:0: // Combo index = 6 -// Slider with new combo and offset (1) followed by circle with new combo and offset (3) +// Spinner with new combo and offset (1) followed by circle with new combo and offset (3) 256,192,10000,28,0,11000,0:0:0:0: 255,193,12000,53,0,0:0:0:0: // Combo index = 11 -// Slider with new combo and offset (2) followed by slider with no new combo followed by circle with no new combo +// Spinner with new combo and offset (2) followed by slider with no new combo followed by circle with no new combo 256,192,13000,44,0,14000,0:0:0:0: 256,192,15000,8,0,16000,0:0:0:0: 255,193,17000,1,0,0:0:0:0: From 9713d903884469c247397ae3f7bd223fd0df21e9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:47:12 +0900 Subject: [PATCH 24/42] Always apply beatmap converter/processor --- .../Formats/LegacyBeatmapEncoderTest.cs | 61 +++++++++++++++++-- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index f2b3a16f68..62cf2cec43 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -1,14 +1,21 @@ // 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.IO; using System.Linq; using NUnit.Framework; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.IO.Serialization; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Beatmaps.Formats @@ -29,26 +36,68 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); } - private Beatmap decode(string filename, out Beatmap encoded) + private IBeatmap decode(string filename, out IBeatmap encoded) { - using (var stream = TestResources.OpenResource(filename)) + using (var stream = TestResources.GetStore().GetStream(filename)) using (var sr = new LineBufferedReader(stream)) { - var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); + var legacyDecoded = convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr)); using (var ms = new MemoryStream()) using (var sw = new StreamWriter(ms)) - using (var sr2 = new LineBufferedReader(ms)) + using (var sr2 = new LineBufferedReader(ms, true)) { new LegacyBeatmapEncoder(legacyDecoded).Encode(sw); - sw.Flush(); + sw.Flush(); ms.Position = 0; - encoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2); + encoded = convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2)); + return legacyDecoded; } } } + + private IBeatmap convert(IBeatmap beatmap) + { + switch (beatmap.BeatmapInfo.RulesetID) + { + case 0: + beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo; + break; + + case 1: + beatmap.BeatmapInfo.Ruleset = new TaikoRuleset().RulesetInfo; + break; + + case 2: + beatmap.BeatmapInfo.Ruleset = new CatchRuleset().RulesetInfo; + break; + + case 3: + beatmap.BeatmapInfo.Ruleset = new ManiaRuleset().RulesetInfo; + break; + } + + return new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset); + } + + private class TestWorkingBeatmap : WorkingBeatmap + { + private readonly IBeatmap beatmap; + + public TestWorkingBeatmap(IBeatmap beatmap) + : base(beatmap.BeatmapInfo, null) + { + this.beatmap = beatmap; + } + + protected override IBeatmap GetBeatmap() => beatmap; + + protected override Texture GetBackground() => throw new NotImplementedException(); + + protected override Track GetTrack() => throw new NotImplementedException(); + } } } From 8ea76244a26699bc98a27926315292ee44a83897 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:48:32 +0900 Subject: [PATCH 25/42] Fix only single beatmap being tested --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 62cf2cec43..01edafcf31 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -23,14 +23,12 @@ namespace osu.Game.Tests.Beatmaps.Formats [TestFixture] public class LegacyBeatmapEncoderTest { - private const string normal = "Soleily - Renatus (Gamu) [Insane].osu"; - private static IEnumerable allBeatmaps => TestResources.GetStore().GetAvailableResources().Where(res => res.EndsWith(".osu")); [TestCaseSource(nameof(allBeatmaps))] - public void TestDecodeEncodedBeatmap(string name) + public void TestBeatmap(string name) { - var decoded = decode(normal, out var encoded); + var decoded = decode(name, out var encoded); Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count)); Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); From 1e7e7417ed77dcec18944565b826c28919490a94 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:49:31 +0900 Subject: [PATCH 26/42] Fix testing relying on control point order --- .../Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 01edafcf31..bcc873b0b7 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,6 +10,7 @@ using NUnit.Framework; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.IO.Serialization; @@ -30,10 +32,22 @@ namespace osu.Game.Tests.Beatmaps.Formats { var decoded = decode(name, out var encoded); - Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count)); + sort(decoded); + sort(encoded); + Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); } + private void sort(IBeatmap beatmap) + { + // Sort control points to ensure a sane ordering, as they may be parsed in different orders. This works because each group contains only uniquely-typed control points. + foreach (var g in beatmap.ControlPointInfo.Groups) + { + ArrayList.Adapter((IList)g.ControlPoints).Sort( + Comparer.Create((c1, c2) => string.Compare(c1.GetType().ToString(), c2.GetType().ToString(), StringComparison.Ordinal))); + } + } + private IBeatmap decode(string filename, out IBeatmap encoded) { using (var stream = TestResources.GetStore().GetStream(filename)) From 21949ac499ab3ece38ddfb799a4f704a0a589c4b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:49:43 +0900 Subject: [PATCH 27/42] Add osu! test beatmap --- .../Resources/sample-beatmap-osu.osu | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game.Tests/Resources/sample-beatmap-osu.osu diff --git a/osu.Game.Tests/Resources/sample-beatmap-osu.osu b/osu.Game.Tests/Resources/sample-beatmap-osu.osu new file mode 100644 index 0000000000..27c96077e6 --- /dev/null +++ b/osu.Game.Tests/Resources/sample-beatmap-osu.osu @@ -0,0 +1,32 @@ +osu file format v14 + +[General] +SampleSet: Normal +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:3 +CircleSize:5 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:3.59999990463257 +SliderTickRate:2 + +[TimingPoints] +24,352.941176470588,4,1,1,100,1,0 +6376,-50,4,1,1,100,0,0 + +[HitObjects] +98,69,24,1,0,0:0:0:0: +419,72,200,1,2,0:0:0:0: +81,314,376,1,6,0:0:0:0: +423,321,553,1,12,0:0:0:0: +86,192,729,2,0,P|459:193|460:193,1,359.999990463257 +86,192,1259,2,0,P|246:82|453:203,1,449.999988079071 +86,192,1876,2,0,B|256:30|257:313|464:177,1,359.999990463257 +86,55,2406,2,12,B|447:51|447:51|452:348|452:348|78:344,1,989.999973773957,14|2,0:0|0:0,0:0:0:0: +256,192,3553,12,0,4259,0:0:0:0: +67,57,4435,5,0,0:0:0:0: +440,52,4612,5,0,0:0:0:0: +86,181,4788,6,0,L|492:183,1,359.999990463257 \ No newline at end of file From a702a521f8c4af9fe4d645fcbd472466b6ba6ff5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:52:58 +0900 Subject: [PATCH 28/42] Fix not being able to serialise converted beatmaps --- .../Converters/TypedListConverter.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 6d244bff60..837650eb0a 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -43,13 +44,13 @@ namespace osu.Game.IO.Serialization.Converters var list = new List(); var obj = JObject.Load(reader); - var lookupTable = serializer.Deserialize>(obj["lookup_table"].CreateReader()); + var lookupTable = serializer.Deserialize>(obj["$lookup_table"].CreateReader()); - foreach (var tok in obj["items"]) + foreach (var tok in obj["$items"]) { var itemReader = tok.CreateReader(); - var typeName = lookupTable[(int)tok["type"]]; + var typeName = lookupTable[(int)tok["$type"]]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); serializer.Populate(itemReader, instance); @@ -61,7 +62,7 @@ namespace osu.Game.IO.Serialization.Converters public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - var list = (List)value; + var list = (IList)value; var lookupTable = new List(); var objects = new List(); @@ -84,16 +85,16 @@ namespace osu.Game.IO.Serialization.Converters } var itemObject = JObject.FromObject(item, serializer); - itemObject.AddFirst(new JProperty("type", typeId)); + itemObject.AddFirst(new JProperty("$type", typeId)); objects.Add(itemObject); } writer.WriteStartObject(); - writer.WritePropertyName("lookup_table"); + writer.WritePropertyName("$lookup_table"); serializer.Serialize(writer, lookupTable); - writer.WritePropertyName("items"); + writer.WritePropertyName("$items"); serializer.Serialize(writer, objects); writer.WriteEndObject(); From 3093c3e1857c8d5bc7fce5f5b8fa21b3bfeead05 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 14:55:17 +0900 Subject: [PATCH 29/42] Fix custom sample set not being written correctly --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index fe63eec3f9..7721d50227 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -10,6 +11,7 @@ using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps.Formats @@ -159,7 +161,7 @@ namespace osu.Game.Beatmaps.Formats beatLength = -100 / difficultyPoint.SpeedMultiplier; // Apply the control point to a hit sample to uncover legacy properties (e.g. suffix) - HitSampleInfo tempHitSample = samplePoint.ApplyTo(new HitSampleInfo()); + HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo()); // Convert effect flags to the legacy format LegacyEffectFlags effectFlags = LegacyEffectFlags.None; @@ -172,7 +174,7 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{beatLength},")); writer.Write(FormattableString.Invariant($"{(int)beatmap.ControlPointInfo.TimingPointAt(group.Time).TimeSignature},")); writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},")); - writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample.Suffix)},")); + writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},")); writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},")); writer.Write(FormattableString.Invariant($"{(timingPoint != null ? '1' : '0')},")); writer.Write(FormattableString.Invariant($"{(int)effectFlags}")); @@ -326,7 +328,7 @@ namespace osu.Game.Beatmaps.Formats if (!banksOnly) { - string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name))?.Suffix); + string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name))); string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty; int volume = samples.FirstOrDefault()?.Volume ?? 100; @@ -382,6 +384,15 @@ namespace osu.Game.Beatmaps.Formats } } - private string toLegacyCustomSampleBank(string sampleSuffix) => string.IsNullOrEmpty(sampleSuffix) ? "0" : sampleSuffix; + private string toLegacyCustomSampleBank(HitSampleInfo hitSampleInfo) + { + if (hitSampleInfo == null) + return "0"; + + if (hitSampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy) + return legacy.CustomSampleBank.ToString(CultureInfo.InvariantCulture); + + return "0"; + } } } From d8d85e5b08980c80a9f5f0a917dafe16a1ce71a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 15:04:04 +0900 Subject: [PATCH 30/42] Don't output certain properties if they don't exist --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7721d50227..3ba68c6086 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps.Formats { writer.WriteLine("[General]"); - writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}")); + if (beatmap.Metadata.AudioFile != null) writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}")); writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}")); writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); // Todo: Not all countdown types are supported by lazer yet @@ -105,15 +105,15 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[Metadata]"); writer.WriteLine(FormattableString.Invariant($"Title: {beatmap.Metadata.Title}")); - writer.WriteLine(FormattableString.Invariant($"TitleUnicode: {beatmap.Metadata.TitleUnicode}")); + if (beatmap.Metadata.TitleUnicode != null) writer.WriteLine(FormattableString.Invariant($"TitleUnicode: {beatmap.Metadata.TitleUnicode}")); writer.WriteLine(FormattableString.Invariant($"Artist: {beatmap.Metadata.Artist}")); - writer.WriteLine(FormattableString.Invariant($"ArtistUnicode: {beatmap.Metadata.ArtistUnicode}")); + if (beatmap.Metadata.ArtistUnicode != null) writer.WriteLine(FormattableString.Invariant($"ArtistUnicode: {beatmap.Metadata.ArtistUnicode}")); writer.WriteLine(FormattableString.Invariant($"Creator: {beatmap.Metadata.AuthorString}")); writer.WriteLine(FormattableString.Invariant($"Version: {beatmap.BeatmapInfo.Version}")); - writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}")); - writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}")); - writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID ?? 0}")); - writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID ?? -1}")); + if (beatmap.Metadata.Source != null) writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}")); + if (beatmap.Metadata.Tags != null) writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}")); + if (beatmap.BeatmapInfo.OnlineBeatmapID != null) writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID}")); + if (beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID != null) writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID}")); } private void handleDifficulty(TextWriter writer) From 1421e876b11479cdb4d7d313dc84124c9f3c254d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 15:04:43 +0900 Subject: [PATCH 31/42] Remove implicit new combo from spinners --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 3ba68c6086..f1f0a0a5de 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -242,7 +242,7 @@ namespace osu.Game.Beatmaps.Formats break; case IHasEndTime _: - type |= LegacyHitObjectType.Spinner | LegacyHitObjectType.NewCombo; + type |= LegacyHitObjectType.Spinner; break; default: From 516e6a4bb10586bfa3505bf776f7ab263d6a8ddd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Apr 2020 15:05:24 +0900 Subject: [PATCH 32/42] Fix overlapping control points not written correctly --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index f1f0a0a5de..44ccbb350d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -50,7 +50,7 @@ namespace osu.Game.Beatmaps.Formats handleEvents(writer); writer.WriteLine(); - handleTimingPoints(writer); + handleControlPoints(writer); writer.WriteLine(); handleHitObjects(writer); @@ -139,7 +139,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}")); } - private void handleTimingPoints(TextWriter writer) + private void handleControlPoints(TextWriter writer) { if (beatmap.ControlPointInfo.Groups.Count == 0) return; @@ -148,17 +148,27 @@ namespace osu.Game.Beatmaps.Formats foreach (var group in beatmap.ControlPointInfo.Groups) { - var timingPoint = group.ControlPoints.OfType().FirstOrDefault(); - var difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(group.Time); - var samplePoint = beatmap.ControlPointInfo.SamplePointAt(group.Time); - var effectPoint = beatmap.ControlPointInfo.EffectPointAt(group.Time); + var groupTimingPoint = group.ControlPoints.OfType().FirstOrDefault(); - // Convert beat length the legacy format - double beatLength; - if (timingPoint != null) - beatLength = timingPoint.BeatLength; - else - beatLength = -100 / difficultyPoint.SpeedMultiplier; + // If the group contains a timing control point, it needs to be output separately. + if (groupTimingPoint != null) + { + writer.Write(FormattableString.Invariant($"{groupTimingPoint.Time},")); + writer.Write(FormattableString.Invariant($"{groupTimingPoint.BeatLength},")); + outputControlPointEffectsAt(groupTimingPoint.Time, true); + } + + // Output any remaining effects as secondary non-timing control point. + var difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(group.Time); + writer.Write(FormattableString.Invariant($"{group.Time},")); + writer.Write(FormattableString.Invariant($"{-100 / difficultyPoint.SpeedMultiplier},")); + outputControlPointEffectsAt(group.Time, false); + } + + void outputControlPointEffectsAt(double time, bool isTimingPoint) + { + var samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); + var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); // Apply the control point to a hit sample to uncover legacy properties (e.g. suffix) HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo()); @@ -170,13 +180,11 @@ namespace osu.Game.Beatmaps.Formats if (effectPoint.OmitFirstBarLine) effectFlags |= LegacyEffectFlags.OmitFirstBarLine; - writer.Write(FormattableString.Invariant($"{group.Time},")); - writer.Write(FormattableString.Invariant($"{beatLength},")); - writer.Write(FormattableString.Invariant($"{(int)beatmap.ControlPointInfo.TimingPointAt(group.Time).TimeSignature},")); + writer.Write(FormattableString.Invariant($"{(int)beatmap.ControlPointInfo.TimingPointAt(time).TimeSignature},")); writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},")); writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},")); writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},")); - writer.Write(FormattableString.Invariant($"{(timingPoint != null ? '1' : '0')},")); + writer.Write(FormattableString.Invariant($"{(isTimingPoint ? '1' : '0')},")); writer.Write(FormattableString.Invariant($"{(int)effectFlags}")); writer.WriteLine(); } From d27ca725f946d7a85da2c37bbb9ee53bd5efb95e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 08:51:21 +0900 Subject: [PATCH 33/42] Use IEnumerable instead --- osu.Game/IO/Serialization/Converters/TypedListConverter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 837650eb0a..64f1ebeb1a 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -62,7 +61,7 @@ namespace osu.Game.IO.Serialization.Converters public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - var list = (IList)value; + var list = (IEnumerable)value; var lookupTable = new List(); var objects = new List(); From 0c74f1aaa91c70e7984bbe9c93b68509d6b09d38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Apr 2020 09:08:33 +0900 Subject: [PATCH 34/42] Fix now playing output showing empty brackets when no difficulty specified --- osu.Game/Beatmaps/BeatmapInfo.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 68d113ce40..90c100db05 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -149,7 +149,12 @@ namespace osu.Game.Beatmaps } } - public override string ToString() => $"{Metadata} [{Version}]".Trim(); + public override string ToString() + { + string version = string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; + + return $"{Metadata} {version}".Trim(); + } public bool Equals(BeatmapInfo other) { From 360c9f8e387bffbb2d4d19d07465d412d0d94e75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Apr 2020 09:19:34 +0900 Subject: [PATCH 35/42] Add test coverage and handle null creator --- .../Beatmaps/ToStringFormattingTest.cs | 61 +++++++++++++++++++ osu.Game/Beatmaps/BeatmapMetadata.cs | 6 +- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Beatmaps/ToStringFormattingTest.cs diff --git a/osu.Game.Tests/Beatmaps/ToStringFormattingTest.cs b/osu.Game.Tests/Beatmaps/ToStringFormattingTest.cs new file mode 100644 index 0000000000..c477bbd9cf --- /dev/null +++ b/osu.Game.Tests/Beatmaps/ToStringFormattingTest.cs @@ -0,0 +1,61 @@ +// 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.Beatmaps; +using osu.Game.Users; + +namespace osu.Game.Tests.Beatmaps +{ + [TestFixture] + public class ToStringFormattingTest + { + [Test] + public void TestArtistTitle() + { + var beatmap = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "artist", + Title = "title" + } + }; + + Assert.That(beatmap.ToString(), Is.EqualTo("artist - title")); + } + + [Test] + public void TestArtistTitleCreator() + { + var beatmap = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "artist", + Title = "title", + Author = new User { Username = "creator" } + } + }; + + Assert.That(beatmap.ToString(), Is.EqualTo("artist - title (creator)")); + } + + [Test] + public void TestArtistTitleCreatorDifficulty() + { + var beatmap = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "artist", + Title = "title", + Author = new User { Username = "creator" } + }, + Version = "difficulty" + }; + + Assert.That(beatmap.ToString(), Is.EqualTo("artist - title (creator) [difficulty]")); + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 001f319307..775d78f1fb 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -53,7 +53,11 @@ namespace osu.Game.Beatmaps public string AudioFile { get; set; } public string BackgroundFile { get; set; } - public override string ToString() => $"{Artist} - {Title} ({Author})"; + public override string ToString() + { + string author = Author == null ? string.Empty : $"({Author})"; + return $"{Artist} - {Title} {author}".Trim(); + } [JsonIgnore] public string[] SearchableTerms => new[] From f841eb7e0607b19feb84b0cc896b027390128663 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 07:27:15 +0300 Subject: [PATCH 36/42] Replace constructing a whole Catcher with static calculation methods --- .../Difficulty/CatchDifficultyCalculator.cs | 3 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 36 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 4d9dbbbc5f..d99325ff87 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -71,8 +71,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap) { - using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty)) - halfCatcherWidth = catcher.CatchWidth * 0.5f; + halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f; return new Skill[] { diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 920d804e72..ee806b7b99 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -44,11 +44,6 @@ namespace osu.Game.Rulesets.Catch.UI /// private const float allowed_catch_range = 0.8f; - /// - /// Width of the area that can be used to attempt catches during gameplay. - /// - internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X) * allowed_catch_range; - protected bool Dashing { get => dashing; @@ -79,6 +74,11 @@ namespace osu.Game.Rulesets.Catch.UI } } + /// + /// Width of the area that can be used to attempt catches during gameplay. + /// + private readonly float catchWidth; + private Container caughtFruit; private CatcherSprite catcherIdle; @@ -106,7 +106,9 @@ namespace osu.Game.Rulesets.Catch.UI Size = new Vector2(CatcherArea.CATCHER_SIZE); if (difficulty != null) - Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + Scale = CalculateScale(difficulty); + + catchWidth = CalculateCatchWidth(Scale); } [BackgroundDependencyLoader] @@ -139,6 +141,26 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } + /// + /// Calculates the scale of the catcher based off the provided beatmap difficulty. + /// + internal static Vector2 CalculateScale(BeatmapDifficulty difficulty) + => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + + /// + /// Calculates the width of the area used for attempting catches in gameplay. + /// + internal static float CalculateCatchWidth(Vector2 scale) + => CatcherArea.CATCHER_SIZE * Math.Abs(scale.X) * allowed_catch_range; + + /// + /// Calculates the width of the area used for attempting catches in gameplay. + /// + /// + /// + internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) + => CalculateCatchWidth(CalculateScale(difficulty)); + /// /// Add a caught fruit to the catcher's stack. /// @@ -177,7 +199,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Whether the catch is possible. public bool AttemptCatch(CatchHitObject fruit) { - var halfCatchWidth = CatchWidth * 0.5f; + var halfCatchWidth = catchWidth * 0.5f; // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; From fccb30e031a40e08533e5ce2f862cbe81d6fc504 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 07:36:59 +0300 Subject: [PATCH 37/42] Adjust documents *whoops* --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index ee806b7b99..4dace76008 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -150,14 +150,14 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Calculates the width of the area used for attempting catches in gameplay. /// + /// The scale of the catcher. internal static float CalculateCatchWidth(Vector2 scale) => CatcherArea.CATCHER_SIZE * Math.Abs(scale.X) * allowed_catch_range; /// /// Calculates the width of the area used for attempting catches in gameplay. /// - /// - /// + /// The beatmap difficulty. internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(CalculateScale(difficulty)); From 883788dd5aec1b228328812fb4ea14043c299118 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 22 Apr 2020 07:37:49 +0300 Subject: [PATCH 38/42] Privatize externally-unused methods --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 4dace76008..daf9456919 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Catch.UI Size = new Vector2(CatcherArea.CATCHER_SIZE); if (difficulty != null) - Scale = CalculateScale(difficulty); + Scale = calculateScale(difficulty); catchWidth = CalculateCatchWidth(Scale); } @@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Calculates the scale of the catcher based off the provided beatmap difficulty. /// - internal static Vector2 CalculateScale(BeatmapDifficulty difficulty) + private static Vector2 calculateScale(BeatmapDifficulty difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); /// @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// The beatmap difficulty. internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) - => CalculateCatchWidth(CalculateScale(difficulty)); + => CalculateCatchWidth(calculateScale(difficulty)); /// /// Add a caught fruit to the catcher's stack. From 9c22d2f1dd167cd6b28ec2bdd7a3949219773209 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 17:41:24 +0900 Subject: [PATCH 39/42] Use platform bindings for editor actions --- osu.Game/Screens/Edit/Editor.cs | 49 +++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 9a1f450dc6..5665f4b25d 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -22,6 +22,7 @@ using osu.Game.Screens.Edit.Design; using osuTK.Input; using System.Collections.Generic; using osu.Framework; +using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Logging; using osu.Game.Beatmaps; @@ -37,7 +38,7 @@ using osu.Game.Users; namespace osu.Game.Screens.Edit { [Cached(typeof(IBeatSnapProvider))] - public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IBeatSnapProvider + public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IKeyBindingHandler, IBeatSnapProvider { public override float BackgroundParallaxAmount => 0.1f; @@ -230,6 +231,30 @@ namespace osu.Game.Screens.Edit clock.ProcessFrame(); } + public bool OnPressed(PlatformAction action) + { + switch (action.ActionType) + { + case PlatformActionType.Undo: + undo(); + return true; + + case PlatformActionType.Redo: + redo(); + return true; + + case PlatformActionType.Save: + saveBeatmap(); + return true; + } + + return false; + } + + public void OnReleased(PlatformAction action) + { + } + protected override bool OnKeyDown(KeyDownEvent e) { switch (e.Key) @@ -241,28 +266,6 @@ namespace osu.Game.Screens.Edit case Key.Right: seek(e, 1); return true; - - case Key.S: - if (e.ControlPressed) - { - saveBeatmap(); - return true; - } - - break; - - case Key.Z: - if (e.ControlPressed) - { - if (e.ShiftPressed) - redo(); - else - undo(); - - return true; - } - - break; } return base.OnKeyDown(e); From 8b0274fedd51ff2e897930786b4c04187f541daa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 17:55:50 +0900 Subject: [PATCH 40/42] Remove obsolete methods --- .../Objects/Drawables/DrawableHitObject.cs | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index e847dcec40..1316ac1156 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -180,11 +179,6 @@ namespace osu.Game.Rulesets.Objects.Drawables private void apply(HitObject hitObject) { -#pragma warning disable 618 // can be removed 20200417 - if (GetType().GetMethod(nameof(AddNested), BindingFlags.NonPublic | BindingFlags.Instance)?.DeclaringType != typeof(DrawableHitObject)) - return; -#pragma warning restore 618 - if (nestedHitObjects.IsValueCreated) { nestedHitObjects.Value.Clear(); @@ -194,8 +188,6 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var h in hitObject.NestedHitObjects) { var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); - - addNested(drawableNested); AddNestedHitObject(drawableNested); } } @@ -208,13 +200,6 @@ namespace osu.Game.Rulesets.Objects.Drawables { } - /// - /// Adds a nested . This should not be used except for legacy nested usages. - /// - /// - [Obsolete("Use AddNestedHitObject() / ClearNestedHitObjects() / CreateNestedHitObject() instead.")] // can be removed 20200417 - protected virtual void AddNested(DrawableHitObject h) => addNested(h); - /// /// Invoked by the base to remove all previously-added nested s. /// @@ -229,17 +214,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// The drawable representation for . protected virtual DrawableHitObject CreateNestedHitObject(HitObject hitObject) => null; - private void addNested(DrawableHitObject hitObject) - { - // Todo: Exists for legacy purposes, can be removed 20200417 - - hitObject.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r); - hitObject.OnRevertResult += (d, r) => OnRevertResult?.Invoke(d, r); - hitObject.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); - - nestedHitObjects.Value.Add(hitObject); - } - #region State / Transform Management /// From e1142b424d2f140dbb61521920c33336d5f0992e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 18:14:21 +0900 Subject: [PATCH 41/42] Fix test failures --- .../Editor/TestSceneEditorChangeStates.cs | 41 ++++--------------- osu.Game/Screens/Edit/Editor.cs | 12 +++--- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs index dd1b6cf6aa..efc2a6f552 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorChangeStates.cs @@ -10,24 +10,22 @@ 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.Input; namespace osu.Game.Tests.Visual.Editor { public class TestSceneEditorChangeStates : ScreenTestScene { private EditorBeatmap editorBeatmap; + private TestEditor editor; public override void SetUpSteps() { base.SetUpSteps(); - Screens.Edit.Editor editor = null; - AddStep("load editor", () => { Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - LoadScreen(editor = new Screens.Edit.Editor()); + LoadScreen(editor = new TestEditor()); }); AddUntilStep("wait for editor to load", () => editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true @@ -160,36 +158,15 @@ namespace osu.Game.Tests.Visual.Editor AddAssert("no hitobject added", () => addedObject == null); } - private void addUndoSteps() + private void addUndoSteps() => AddStep("undo", () => editor.Undo()); + + private void addRedoSteps() => AddStep("redo", () => editor.Redo()); + + private class TestEditor : Screens.Edit.Editor { - AddStep("press undo", () => - { - InputManager.PressKey(Key.LControl); - InputManager.PressKey(Key.Z); - }); + public new void Undo() => base.Undo(); - AddStep("release keys", () => - { - InputManager.ReleaseKey(Key.LControl); - InputManager.ReleaseKey(Key.Z); - }); - } - - private void addRedoSteps() - { - AddStep("press redo", () => - { - InputManager.PressKey(Key.LControl); - InputManager.PressKey(Key.LShift); - InputManager.PressKey(Key.Z); - }); - - AddStep("release keys", () => - { - InputManager.ReleaseKey(Key.LControl); - InputManager.ReleaseKey(Key.LShift); - InputManager.ReleaseKey(Key.Z); - }); + public new void Redo() => base.Redo(); } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 5665f4b25d..54e4af94a4 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -158,8 +158,8 @@ namespace osu.Game.Screens.Edit { Items = new[] { - undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, undo), - redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, redo) + undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, Undo), + redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, Redo) } } } @@ -236,11 +236,11 @@ namespace osu.Game.Screens.Edit switch (action.ActionType) { case PlatformActionType.Undo: - undo(); + Undo(); return true; case PlatformActionType.Redo: - redo(); + Redo(); return true; case PlatformActionType.Save: @@ -329,9 +329,9 @@ namespace osu.Game.Screens.Edit return base.OnExiting(next); } - private void undo() => changeHandler.RestoreState(-1); + protected void Undo() => changeHandler.RestoreState(-1); - private void redo() => changeHandler.RestoreState(1); + protected void Redo() => changeHandler.RestoreState(1); private void resetTrack(bool seekToStart = false) { From 93151f761215c135ae625241532fa6b899e73747 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Apr 2020 18:32:59 +0900 Subject: [PATCH 42/42] Add back necessary events + addition to list --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1316ac1156..0047142cbd 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -188,6 +188,12 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var h in hitObject.NestedHitObjects) { var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); + + drawableNested.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r); + drawableNested.OnRevertResult += (d, r) => OnRevertResult?.Invoke(d, r); + drawableNested.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); + + nestedHitObjects.Value.Add(drawableNested); AddNestedHitObject(drawableNested); } }