From 65d664425bdfeca60343863d1ef441a7ade7a389 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 12 Apr 2019 23:13:13 +0200 Subject: [PATCH 01/95] Added DiscordRichPresence to osu.Desktop packages --- osu.Desktop/osu.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 66db439c82..7a19e73be6 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -22,6 +22,7 @@ + From 533afaa770be793be46f9948b7168e469f33431f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 13 Apr 2019 13:06:53 +0200 Subject: [PATCH 02/95] Added DiscordRichPresenceClient --- osu.Desktop/DiscordRichPresenceClient.cs | 106 +++++++++++++++++++++++ osu.Desktop/OsuGameDesktop.cs | 2 + 2 files changed, 108 insertions(+) create mode 100644 osu.Desktop/DiscordRichPresenceClient.cs diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs new file mode 100644 index 0000000000..03b2884ab6 --- /dev/null +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -0,0 +1,106 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using DiscordRPC; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Logging; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Users; +using User = osu.Game.Users.User; + +namespace osu.Desktop +{ + internal class DiscordRichPresenceClient : Component + { + private const string client_id = "559391129716391967"; + + private Bindable user; + + private readonly DiscordRpcClient client = new DiscordRpcClient(client_id); + + [BackgroundDependencyLoader] + private void load(IAPIProvider provider) + { + user = provider.LocalUser.GetBoundCopy(); + + user.ValueChanged += usr => + { + usr.OldValue.Status.ValueChanged -= updateStatus; + usr.NewValue.Status.ValueChanged += updateStatus; + }; + + user.Value.Status.ValueChanged += updateStatus; + + client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); + client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); + client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); + client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); + + client.Initialize(); + } + + private void updateStatus(ValueChangedEvent e) + { + var presence = defaultPresence(e.NewValue.Message); + + switch (e.NewValue) + { + case UserStatusSoloGame game: + presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; + setPresenceGamemode(game.Beatmap.Ruleset, presence); + break; + + case UserStatusEditing editing: + presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title}" + (editing.Beatmap.Version != null ? $"[{editing.Beatmap.Version}]" : ""); + break; + } + + client.SetPresence(presence); + } + + private void setPresenceGamemode(RulesetInfo ruleset, RichPresence presence) + { + switch (ruleset.ID) + { + case 0: + presence.Assets.SmallImageKey = "osu"; + break; + + case 1: + presence.Assets.SmallImageKey = "taiko"; + break; + + case 2: + presence.Assets.SmallImageKey = "fruits"; + break; + + case 3: + presence.Assets.SmallImageKey = "mania"; + break; + } + + presence.Assets.SmallImageText = ruleset.ShortName; + } + + private RichPresence defaultPresence(string status) => new RichPresence + { + Details = status, + Assets = new Assets + { + LargeImageKey = "lazer", + LargeImageText = "osu!lazer" + } + }; + + protected override void Update() + { + if (client.IsInitialized) + client?.Invoke(); + + base.Update(); + } + } +} diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index e7e0af7eea..41e2efd011 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -64,6 +64,8 @@ namespace osu.Desktop else Add(new SimpleUpdateManager()); } + + Add(new DiscordRichPresenceClient()); } protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen) From 4996ad4b042c73de5919818c2a047f19b701c75b Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 13 Apr 2019 13:25:52 +0200 Subject: [PATCH 03/95] Show the current gamemode on the rich presence --- osu.Desktop/DiscordRichPresenceClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 03b2884ab6..1e7c1f051a 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -50,7 +50,7 @@ namespace osu.Desktop { case UserStatusSoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; - setPresenceGamemode(game.Beatmap.Ruleset, presence); + setPresenceGamemode(game.Ruleset, presence); break; case UserStatusEditing editing: From 1b1ebb7fd928f0651e7c2777ce0807e47961ea54 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 14 Apr 2019 21:12:10 +0200 Subject: [PATCH 04/95] Show current logged-in user on rich presence --- osu.Desktop/DiscordRichPresenceClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 1e7c1f051a..1f4d91dbdb 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -91,7 +91,7 @@ namespace osu.Desktop Assets = new Assets { LargeImageKey = "lazer", - LargeImageText = "osu!lazer" + LargeImageText = user.Value.Username } }; From b38160177af31069b0c5377a7d1c8eaa92fe945e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 14 Apr 2019 22:18:57 +0200 Subject: [PATCH 05/95] Add some more icons to rich presence + prevent rich presence from displaying empty brackets when current beatmap doesn't have a version (trying to edit osu! main menu theme music ?) --- osu.Desktop/DiscordRichPresenceClient.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 1f4d91dbdb..93c2a8bdcf 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -15,7 +15,7 @@ namespace osu.Desktop { internal class DiscordRichPresenceClient : Component { - private const string client_id = "559391129716391967"; + private const string client_id = "563024054391537674"; private Bindable user; @@ -54,7 +54,9 @@ namespace osu.Desktop break; case UserStatusEditing editing: - presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title}" + (editing.Beatmap.Version != null ? $"[{editing.Beatmap.Version}]" : ""); + presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); + presence.Assets.SmallImageKey = "edit"; + presence.Assets.SmallImageText = "editing"; break; } @@ -80,6 +82,10 @@ namespace osu.Desktop case 3: presence.Assets.SmallImageKey = "mania"; break; + + default: + presence.Assets.SmallImageKey = "unknown"; + break; } presence.Assets.SmallImageText = ruleset.ShortName; From a4166ce1e3991ba23a7bac15bf10ca860dace0f1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 19 Apr 2019 19:50:13 +0200 Subject: [PATCH 06/95] Removed Update() override from DiscordRichPresenceClient because client.Invoke() is unecessary (events callbacks are automatically called) --- osu.Desktop/DiscordRichPresenceClient.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 93c2a8bdcf..9288141e5f 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -100,13 +100,5 @@ namespace osu.Desktop LargeImageText = user.Value.Username } }; - - protected override void Update() - { - if (client.IsInitialized) - client?.Invoke(); - - base.Update(); - } } } From ee2bbf950f682c82e6cb31ae20f871b1958724af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 14 May 2019 19:13:21 +0200 Subject: [PATCH 07/95] Update DiscordRichPresenceClient presence logic --- osu.Desktop/DiscordRichPresenceClient.cs | 36 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 9288141e5f..9537252216 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -9,6 +9,7 @@ using osu.Framework.Logging; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Users; +using static osu.Game.Users.UserActivity; using User = osu.Game.Users.User; namespace osu.Desktop @@ -28,32 +29,43 @@ namespace osu.Desktop user.ValueChanged += usr => { - usr.OldValue.Status.ValueChanged -= updateStatus; - usr.NewValue.Status.ValueChanged += updateStatus; + usr.NewValue.Activity.ValueChanged += activity => updateStatus(user.Value.Status.Value, activity.NewValue); + usr.NewValue.Status.ValueChanged += status => updateStatus(status.NewValue, user.Value.Activity.Value); }; - user.Value.Status.ValueChanged += updateStatus; + user.TriggerChange(); - client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); - client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); - client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); - client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); + enableLogging(); client.Initialize(); } - private void updateStatus(ValueChangedEvent e) + private void enableLogging() { - var presence = defaultPresence(e.NewValue.Message); + client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); + client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); + client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); + client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); + } - switch (e.NewValue) + private void updateStatus(UserStatus st, UserActivity a) + { + var presence = defaultPresence(st is UserStatusOnline ? a?.Status : st.Message); + + if (!(st is UserStatusOnline)) //don't update the presence any further if the current user status is DND / Offline & simply return with the default presence { - case UserStatusSoloGame game: + client.SetPresence(presence); + return; + } + + switch (a) + { + case UserActivitySoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; setPresenceGamemode(game.Ruleset, presence); break; - case UserStatusEditing editing: + case UserActivityEditing editing: presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); presence.Assets.SmallImageKey = "edit"; presence.Assets.SmallImageText = "editing"; From d4013ae0d894134c2397a84f2bf0427bfa026ec0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 14 May 2019 19:37:43 +0200 Subject: [PATCH 08/95] Make DiscordRichPresenceClient dispose client on disposal --- osu.Desktop/DiscordRichPresenceClient.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 9537252216..d49b08823c 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -112,5 +112,11 @@ namespace osu.Desktop LargeImageText = user.Value.Username } }; + + protected override void Dispose(bool isDisposing) + { + client.Dispose(); + base.Dispose(isDisposing); + } } } From 01b75db21a791732b1e2c57951eaae7a8c2523ac Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 16 May 2019 18:36:54 +0200 Subject: [PATCH 09/95] Use ruleset.ShortName instead of hardcoded names. --- osu.Desktop/DiscordRichPresenceClient.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index d49b08823c..38e929c1ae 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -80,19 +80,19 @@ namespace osu.Desktop switch (ruleset.ID) { case 0: - presence.Assets.SmallImageKey = "osu"; + presence.Assets.SmallImageKey = ruleset.ShortName; break; case 1: - presence.Assets.SmallImageKey = "taiko"; + presence.Assets.SmallImageKey = ruleset.ShortName; break; case 2: - presence.Assets.SmallImageKey = "fruits"; + presence.Assets.SmallImageKey = ruleset.ShortName; break; case 3: - presence.Assets.SmallImageKey = "mania"; + presence.Assets.SmallImageKey = ruleset.ShortName; break; default: From 446210b81223a7635496071a1204ae64d95d25aa Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 25 May 2019 11:33:31 +0200 Subject: [PATCH 10/95] Use an if statement instead of a switch --- osu.Desktop/DiscordRichPresenceClient.cs | 26 ++++-------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 38e929c1ae..4a2f882e2b 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -77,28 +77,10 @@ namespace osu.Desktop private void setPresenceGamemode(RulesetInfo ruleset, RichPresence presence) { - switch (ruleset.ID) - { - case 0: - presence.Assets.SmallImageKey = ruleset.ShortName; - break; - - case 1: - presence.Assets.SmallImageKey = ruleset.ShortName; - break; - - case 2: - presence.Assets.SmallImageKey = ruleset.ShortName; - break; - - case 3: - presence.Assets.SmallImageKey = ruleset.ShortName; - break; - - default: - presence.Assets.SmallImageKey = "unknown"; - break; - } + if (ruleset.ID != null && ruleset.ID <= 3) //legacy rulesets use an ID between 0 and 3 + presence.Assets.SmallImageKey = ruleset.ShortName; + else + presence.Assets.SmallImageKey = "unknown"; //not a legay ruleset so let's display the unknown ruleset icon. presence.Assets.SmallImageText = ruleset.ShortName; } From 9faba94978de1db4008a2124c90f909117e4e93a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 12 Jun 2019 12:22:52 +0200 Subject: [PATCH 11/95] Fix references to UserActivities --- osu.Desktop/DiscordRichPresenceClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 4a2f882e2b..15d5770041 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -60,12 +60,12 @@ namespace osu.Desktop switch (a) { - case UserActivitySoloGame game: + case UserActivity.SoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; setPresenceGamemode(game.Ruleset, presence); break; - case UserActivityEditing editing: + case UserActivity.Editing editing: presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); presence.Assets.SmallImageKey = "edit"; presence.Assets.SmallImageText = "editing"; From 4275f70cf453aeb3799d934fe9bfd67c81040b8f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 12 Jun 2019 13:14:01 +0200 Subject: [PATCH 12/95] Fix CI inspections. --- osu.Desktop/DiscordRichPresenceClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 15d5770041..81ab995b41 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -60,12 +60,12 @@ namespace osu.Desktop switch (a) { - case UserActivity.SoloGame game: + case SoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; setPresenceGamemode(game.Ruleset, presence); break; - case UserActivity.Editing editing: + case Editing editing: presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); presence.Assets.SmallImageKey = "edit"; presence.Assets.SmallImageText = "editing"; From 54e6e4701970d2372008869d55d3f5263df0b34d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 21 Jun 2019 12:48:13 +0200 Subject: [PATCH 13/95] Display current user activity on rich presence if current activity != null & user online status == online. --- osu.Desktop/DiscordRichPresenceClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 81ab995b41..af5b42b275 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -50,7 +50,7 @@ namespace osu.Desktop private void updateStatus(UserStatus st, UserActivity a) { - var presence = defaultPresence(st is UserStatusOnline ? a?.Status : st.Message); + var presence = defaultPresence(st is UserStatusOnline ? a?.Status ?? st.Message : st.Message); //display the current user activity if the user status is online & user activity != null, else display the current user online status if (!(st is UserStatusOnline)) //don't update the presence any further if the current user status is DND / Offline & simply return with the default presence { @@ -80,7 +80,7 @@ namespace osu.Desktop if (ruleset.ID != null && ruleset.ID <= 3) //legacy rulesets use an ID between 0 and 3 presence.Assets.SmallImageKey = ruleset.ShortName; else - presence.Assets.SmallImageKey = "unknown"; //not a legay ruleset so let's display the unknown ruleset icon. + presence.Assets.SmallImageKey = "unknown"; //not a legacy ruleset so let's display the unknown ruleset icon. presence.Assets.SmallImageText = ruleset.ShortName; } From 88ec0c14862a862750060e0717324cf2c03606f9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 8 Dec 2019 18:49:58 +0100 Subject: [PATCH 14/95] Add missing async content loading logic to NewsOverlay --- osu.Game/Overlays/NewsOverlay.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index aadca8883e..db4b118bf8 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -16,7 +17,6 @@ namespace osu.Game.Overlays { private NewsHeader header; - //ReSharper disable NotAccessedField.Local private Container content; public readonly Bindable Current = new Bindable(null); @@ -59,6 +59,21 @@ namespace osu.Game.Overlays Current.TriggerChange(); } + private CancellationTokenSource loadChildCancellation; + + protected void LoadAndShowChild(NewsContent newContent) + { + content.FadeTo(0.2f, 300, Easing.OutQuint); + + loadChildCancellation?.Cancel(); + + LoadComponentAsync(newContent, c => + { + content.Child = c; + content.FadeIn(300, Easing.OutQuint); + }, (loadChildCancellation = new CancellationTokenSource()).Token); + } + public void ShowFrontPage() { Current.Value = null; From 8012b21ffae925fc610fd23d96b334ea50d37e8f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 20:04:37 +0900 Subject: [PATCH 15/95] Extract legacy sound type enum --- .../Beatmaps/Legacy/LegacyHitSoundType.cs | 14 +++++++++++ .../Objects/Legacy/ConvertHitObjectParser.cs | 24 ++++++------------- 2 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs diff --git a/osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs b/osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs new file mode 100644 index 0000000000..69adbf8f67 --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs @@ -0,0 +1,14 @@ +using System; + +namespace osu.Game.Beatmaps.Legacy +{ + [Flags] + internal enum LegacyHitSoundType + { + None = 0, + Normal = 1, + Whistle = 2, + Finish = 4, + Clap = 8 + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index b5b1e26486..c22955cbf1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Objects.Legacy bool combo = type.HasFlag(ConvertHitObjectType.NewCombo); type &= ~ConvertHitObjectType.NewCombo; - var soundType = (LegacySoundType)Parsing.ParseInt(split[4]); + var soundType = (LegacyHitSoundType)Parsing.ParseInt(split[4]); var bankInfo = new SampleBankInfo(); HitObject result = null; @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } // Populate node sound types with the default hit object sound type - var nodeSoundTypes = new List(); + var nodeSoundTypes = new List(); for (int i = 0; i < nodes; i++) nodeSoundTypes.Add(soundType); @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Objects.Legacy break; int.TryParse(adds[i], out var sound); - nodeSoundTypes[i] = (LegacySoundType)sound; + nodeSoundTypes[i] = (LegacyHitSoundType)sound; } } @@ -333,7 +333,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The hold end time. protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime); - private List convertSoundType(LegacySoundType type, SampleBankInfo bankInfo) + private List convertSoundType(LegacyHitSoundType type, SampleBankInfo bankInfo) { // Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario if (!string.IsNullOrEmpty(bankInfo.Filename)) @@ -359,7 +359,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } }; - if (type.HasFlag(LegacySoundType.Finish)) + if (type.HasFlag(LegacyHitSoundType.Finish)) { soundTypes.Add(new LegacyHitSampleInfo { @@ -370,7 +370,7 @@ namespace osu.Game.Rulesets.Objects.Legacy }); } - if (type.HasFlag(LegacySoundType.Whistle)) + if (type.HasFlag(LegacyHitSoundType.Whistle)) { soundTypes.Add(new LegacyHitSampleInfo { @@ -381,7 +381,7 @@ namespace osu.Game.Rulesets.Objects.Legacy }); } - if (type.HasFlag(LegacySoundType.Clap)) + if (type.HasFlag(LegacyHitSoundType.Clap)) { soundTypes.Add(new LegacyHitSampleInfo { @@ -430,15 +430,5 @@ namespace osu.Game.Rulesets.Objects.Legacy Path.ChangeExtension(Filename, null) }; } - - [Flags] - private enum LegacySoundType - { - None = 0, - Normal = 1, - Whistle = 2, - Finish = 4, - Clap = 8 - } } } From e3f925f69afd6bb69081729c5f419c2af43208bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 20:19:16 +0900 Subject: [PATCH 16/95] Extract legacy hitobject type enum --- .../Beatmaps/Legacy/LegacyHitObjectType.cs | 15 +++++++++++++++ .../Objects/Legacy/ConvertHitObjectParser.cs | 19 ++++++++++--------- .../Objects/Legacy/ConvertHitObjectType.cs | 18 ------------------ 3 files changed, 25 insertions(+), 27 deletions(-) create mode 100644 osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs delete mode 100644 osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs diff --git a/osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs b/osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs new file mode 100644 index 0000000000..9223f7df31 --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs @@ -0,0 +1,15 @@ +using System; + +namespace osu.Game.Beatmaps.Legacy +{ + [Flags] + internal enum LegacyHitObjectType + { + Circle = 1, + Slider = 1 << 1, + NewCombo = 1 << 2, + Spinner = 1 << 3, + ComboOffset = (1 << 4) | (1 << 5) | (1 << 6), + Hold = 1 << 7 + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index c22955cbf1..b83c67acff 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -11,6 +11,7 @@ using osu.Game.Audio; using System.Linq; using JetBrains.Annotations; using osu.Framework.MathUtils; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Rulesets.Objects.Legacy { @@ -46,27 +47,27 @@ namespace osu.Game.Rulesets.Objects.Legacy double startTime = Parsing.ParseDouble(split[2]) + Offset; - ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]); + LegacyHitObjectType type = (LegacyHitObjectType)Parsing.ParseInt(split[3]); - int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4; - type &= ~ConvertHitObjectType.ComboOffset; + int comboOffset = (int)(type & LegacyHitObjectType.ComboOffset) >> 4; + type &= ~LegacyHitObjectType.ComboOffset; - bool combo = type.HasFlag(ConvertHitObjectType.NewCombo); - type &= ~ConvertHitObjectType.NewCombo; + bool combo = type.HasFlag(LegacyHitObjectType.NewCombo); + type &= ~LegacyHitObjectType.NewCombo; var soundType = (LegacyHitSoundType)Parsing.ParseInt(split[4]); var bankInfo = new SampleBankInfo(); HitObject result = null; - if (type.HasFlag(ConvertHitObjectType.Circle)) + if (type.HasFlag(LegacyHitObjectType.Circle)) { result = CreateHit(pos, combo, comboOffset); if (split.Length > 5) readCustomSampleBanks(split[5], bankInfo); } - else if (type.HasFlag(ConvertHitObjectType.Slider)) + else if (type.HasFlag(LegacyHitObjectType.Slider)) { PathType pathType = PathType.Catmull; double? length = null; @@ -186,7 +187,7 @@ namespace osu.Game.Rulesets.Objects.Legacy // The samples are played when the slider ends, which is the last node result.Samples = nodeSamples[nodeSamples.Count - 1]; } - else if (type.HasFlag(ConvertHitObjectType.Spinner)) + else if (type.HasFlag(LegacyHitObjectType.Spinner)) { double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset); @@ -195,7 +196,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (split.Length > 6) readCustomSampleBanks(split[6], bankInfo); } - else if (type.HasFlag(ConvertHitObjectType.Hold)) + else if (type.HasFlag(LegacyHitObjectType.Hold)) { // Note: Hold is generated by BMS converts diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs deleted file mode 100644 index eab37b682c..0000000000 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs +++ /dev/null @@ -1,18 +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; - -namespace osu.Game.Rulesets.Objects.Legacy -{ - [Flags] - internal enum ConvertHitObjectType - { - Circle = 1, - Slider = 1 << 1, - NewCombo = 1 << 2, - Spinner = 1 << 3, - ComboOffset = (1 << 4) | (1 << 5) | (1 << 6), - Hold = 1 << 7 - } -} From 3c18872a16605e4dc87261a520cdbc5ecb27e921 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 20:19:31 +0900 Subject: [PATCH 17/95] Extract legacy effect flags enum --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 15 ++++----------- osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs | 12 ++++++++++++ 2 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 838b1c2f07..ff4e6503ee 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -10,6 +10,7 @@ using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; using osu.Game.IO; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Beatmaps.Formats { @@ -358,9 +359,9 @@ namespace osu.Game.Beatmaps.Formats if (split.Length >= 8) { - EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]); - kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai); - omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine); + LegacyEffectFlags effectFlags = (LegacyEffectFlags)Parsing.ParseInt(split[7]); + kiaiMode = effectFlags.HasFlag(LegacyEffectFlags.Kiai); + omitFirstBarSignature = effectFlags.HasFlag(LegacyEffectFlags.OmitFirstBarLine); } string stringSampleSet = sampleSet.ToString().ToLowerInvariant(); @@ -448,13 +449,5 @@ namespace osu.Game.Beatmaps.Formats private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0); protected virtual TimingControlPoint CreateTimingControlPoint() => new TimingControlPoint(); - - [Flags] - internal enum EffectFlags - { - None = 0, - Kiai = 1, - OmitFirstBarLine = 8 - } } } diff --git a/osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs b/osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs new file mode 100644 index 0000000000..ce141031fd --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs @@ -0,0 +1,12 @@ +using System; + +namespace osu.Game.Beatmaps.Legacy +{ + [Flags] + internal enum LegacyEffectFlags + { + None = 0, + Kiai = 1, + OmitFirstBarLine = 8 + } +} From c378e525dae265a0d513090a002e2cc235a04415 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 20:23:15 +0900 Subject: [PATCH 18/95] Extract the rest of legacy enums --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 8 ++-- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 37 ------------------- .../Formats/LegacyStoryboardDecoder.cs | 11 +++--- osu.Game/Beatmaps/Legacy/LegacyEventType.cs | 13 +++++++ osu.Game/Beatmaps/Legacy/LegacyOrigins.cs | 16 ++++++++ osu.Game/Beatmaps/Legacy/LegacySampleBank.cs | 10 +++++ osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs | 10 +++++ .../Objects/Legacy/ConvertHitObjectParser.cs | 4 +- 8 files changed, 61 insertions(+), 48 deletions(-) create mode 100644 osu.Game/Beatmaps/Legacy/LegacyEventType.cs create mode 100644 osu.Game/Beatmaps/Legacy/LegacyOrigins.cs create mode 100644 osu.Game/Beatmaps/Legacy/LegacySampleBank.cs create mode 100644 osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index ff4e6503ee..1b24e2953b 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -294,22 +294,22 @@ namespace osu.Game.Beatmaps.Formats { string[] split = line.Split(','); - if (!Enum.TryParse(split[0], out EventType type)) + if (!Enum.TryParse(split[0], out LegacyEventType type)) throw new InvalidDataException($@"Unknown event type: {split[0]}"); switch (type) { - case EventType.Background: + case LegacyEventType.Background: string bgFilename = split[2].Trim('"'); beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(bgFilename); break; - case EventType.Video: + case LegacyEventType.Video: string videoFilename = split[2].Trim('"'); beatmap.BeatmapInfo.Metadata.VideoFile = FileSafety.PathStandardise(videoFilename); break; - case EventType.Break: + case LegacyEventType.Break: double start = getOffsetTime(Parsing.ParseDouble(split[1])); var breakEvent = new BreakPeriod diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 2b914669cb..1c2ca4b6f8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -148,46 +148,9 @@ namespace osu.Game.Beatmaps.Formats Fonts } - internal enum LegacySampleBank - { - None = 0, - Normal = 1, - Soft = 2, - Drum = 3 - } - internal enum EventType - { - Background = 0, - Video = 1, - Break = 2, - Colour = 3, - Sprite = 4, - Sample = 5, - Animation = 6 - } - internal enum LegacyOrigins - { - TopLeft, - Centre, - CentreLeft, - TopRight, - BottomCentre, - TopCentre, - Custom, - CentreRight, - BottomLeft, - BottomRight - } - internal enum StoryLayer - { - Background = 0, - Fail = 1, - Pass = 2, - Foreground = 3 - } internal class LegacyDifficultyControlPoint : DifficultyControlPoint { diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index f94ab3f27b..b8323edc23 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.IO.File; using osu.Game.IO; using osu.Game.Storyboards; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Beatmaps.Formats { @@ -83,12 +84,12 @@ namespace osu.Game.Beatmaps.Formats { storyboardSprite = null; - if (!Enum.TryParse(split[0], out EventType type)) + if (!Enum.TryParse(split[0], out LegacyEventType type)) throw new InvalidDataException($@"Unknown event type: {split[0]}"); switch (type) { - case EventType.Sprite: + case LegacyEventType.Sprite: { var layer = parseLayer(split[1]); var origin = parseOrigin(split[2]); @@ -100,7 +101,7 @@ namespace osu.Game.Beatmaps.Formats break; } - case EventType.Animation: + case LegacyEventType.Animation: { var layer = parseLayer(split[1]); var origin = parseOrigin(split[2]); @@ -115,7 +116,7 @@ namespace osu.Game.Beatmaps.Formats break; } - case EventType.Sample: + case LegacyEventType.Sample: { var time = double.Parse(split[1], CultureInfo.InvariantCulture); var layer = parseLayer(split[2]); @@ -271,7 +272,7 @@ namespace osu.Game.Beatmaps.Formats } } - private string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); + private string parseLayer(string value) => Enum.Parse(typeof(LegacyStoryLayer), value).ToString(); private Anchor parseOrigin(string value) { diff --git a/osu.Game/Beatmaps/Legacy/LegacyEventType.cs b/osu.Game/Beatmaps/Legacy/LegacyEventType.cs new file mode 100644 index 0000000000..57b1e6c29f --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacyEventType.cs @@ -0,0 +1,13 @@ +namespace osu.Game.Beatmaps.Legacy +{ + internal enum LegacyEventType + { + Background = 0, + Video = 1, + Break = 2, + Colour = 3, + Sprite = 4, + Sample = 5, + Animation = 6 + } +} diff --git a/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs b/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs new file mode 100644 index 0000000000..93c8920761 --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs @@ -0,0 +1,16 @@ +namespace osu.Game.Beatmaps.Legacy +{ + internal enum LegacyOrigins + { + TopLeft, + Centre, + CentreLeft, + TopRight, + BottomCentre, + TopCentre, + Custom, + CentreRight, + BottomLeft, + BottomRight + } +} diff --git a/osu.Game/Beatmaps/Legacy/LegacySampleBank.cs b/osu.Game/Beatmaps/Legacy/LegacySampleBank.cs new file mode 100644 index 0000000000..0d54998d15 --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacySampleBank.cs @@ -0,0 +1,10 @@ +namespace osu.Game.Beatmaps.Legacy +{ + internal enum LegacySampleBank + { + None = 0, + Normal = 1, + Soft = 2, + Drum = 3 + } +} diff --git a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs new file mode 100644 index 0000000000..509f39f830 --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs @@ -0,0 +1,10 @@ +namespace osu.Game.Beatmaps.Legacy +{ + internal enum LegacyStoryLayer + { + Background = 0, + Fail = 1, + Pass = 2, + Foreground = 3 + } +} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index b83c67acff..d3682de73f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -232,8 +232,8 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] split = str.Split(':'); - var bank = (LegacyBeatmapDecoder.LegacySampleBank)Parsing.ParseInt(split[0]); - var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Parsing.ParseInt(split[1]); + var bank = (LegacySampleBank)Parsing.ParseInt(split[0]); + var addbank = (LegacySampleBank)Parsing.ParseInt(split[1]); string stringBank = bank.ToString().ToLowerInvariant(); if (stringBank == @"none") From e7a06aeadcefa773cb13efb2f39916b0a66bfd89 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 11 Dec 2019 14:32:43 +0100 Subject: [PATCH 19/95] Update NewsOverlay visual tests to expose LoadAndShowChild() for testing purposes --- osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index 546f6ac182..98f90f2daa 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -2,22 +2,28 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Overlays; +using osu.Game.Overlays.News; namespace osu.Game.Tests.Visual.Online { public class TestSceneNewsOverlay : OsuTestScene { - private NewsOverlay news; + private TestNewsOverlay news; protected override void LoadComplete() { base.LoadComplete(); - Add(news = new NewsOverlay()); + Add(news = new TestNewsOverlay()); AddStep(@"Show", news.Show); AddStep(@"Hide", news.Hide); AddStep(@"Show front page", () => news.ShowFrontPage()); AddStep(@"Custom article", () => news.Current.Value = "Test Article 101"); } + + private class TestNewsOverlay : NewsOverlay + { + public new void LoadAndShowChild(NewsContent content) => base.LoadAndShowChild(content); + } } } From be000e13e445ff6c63c0b43b3551e5eb34607992 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 20:44:45 +0900 Subject: [PATCH 20/95] Implement initial legacy beatmap encoding support --- .../Formats/LegacyBeatmapEncoderTest.cs | 142 +++++++ .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 375 ++++++++++++++++++ 2 files changed, 517 insertions(+) create mode 100644 osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs create mode 100644 osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs new file mode 100644 index 0000000000..c4a3877c1c --- /dev/null +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -0,0 +1,142 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using System.Linq; +using NUnit.Framework; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Tests.Resources; +using osuTK; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class LegacyBeatmapEncoderTest + { + private const string normal = "Soleily - Renatus (Gamu) [Insane].osu"; + + [Test] + public void TestDecodeMetadata() + { + var beatmap = decode(normal); + var meta = beatmap.BeatmapInfo.Metadata; + Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID); + Assert.AreEqual("Soleily", meta.Artist); + Assert.AreEqual("Soleily", meta.ArtistUnicode); + Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); + Assert.AreEqual("Gamu", meta.AuthorString); + Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); + Assert.AreEqual(164471, meta.PreviewTime); + Assert.AreEqual(string.Empty, meta.Source); + Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); + Assert.AreEqual("Renatus", meta.Title); + Assert.AreEqual("Renatus", meta.TitleUnicode); + } + + [Test] + public void TestDecodeGeneral() + { + var beatmap = decode(normal); + var beatmapInfo = beatmap.BeatmapInfo; + Assert.AreEqual(0, beatmapInfo.AudioLeadIn); + Assert.AreEqual(false, beatmapInfo.Countdown); + Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); + Assert.AreEqual(false, beatmapInfo.SpecialStyle); + Assert.IsTrue(beatmapInfo.RulesetID == 0); + Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); + Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); + } + + [Test] + public void TestDecodeEditor() + { + var beatmap = decode(normal); + var beatmapInfo = beatmap.BeatmapInfo; + + int[] expectedBookmarks = + { + 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, + 95901, 106450, 116999, 119637, 130186, 140735, 151285, + 161834, 164471, 175020, 185570, 196119, 206669, 209306 + }; + Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length); + for (int i = 0; i < expectedBookmarks.Length; i++) + Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]); + Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing); + Assert.AreEqual(4, beatmapInfo.BeatDivisor); + Assert.AreEqual(4, beatmapInfo.GridSize); + Assert.AreEqual(2, beatmapInfo.TimelineZoom); + } + + [Test] + public void TestDecodeDifficulty() + { + var beatmap = decode(normal); + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + Assert.AreEqual(6.5f, difficulty.DrainRate); + Assert.AreEqual(4, difficulty.CircleSize); + Assert.AreEqual(8, difficulty.OverallDifficulty); + Assert.AreEqual(9, difficulty.ApproachRate); + Assert.AreEqual(1.8, difficulty.SliderMultiplier); + Assert.AreEqual(2, difficulty.SliderTickRate); + } + + [Test] + public void TestDecodeHitObjects() + { + var beatmap = decode(normal); + + var curveData = beatmap.HitObjects[0] as IHasCurve; + var positionData = beatmap.HitObjects[0] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.IsNotNull(curveData); + Assert.AreEqual(new Vector2(192, 168), positionData.Position); + Assert.AreEqual(956, beatmap.HitObjects[0].StartTime); + Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL)); + + positionData = beatmap.HitObjects[1] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.AreEqual(new Vector2(304, 56), positionData.Position); + Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime); + Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)); + } + + private Beatmap decode(string filename) + { + decode(filename, out Beatmap jsonDecoded); + return jsonDecoded; + } + + private Beatmap decode(string filename, out Beatmap decoded) + { + using (var stream = TestResources.OpenResource(filename)) + using (var sr = new LineBufferedReader(stream)) + { + var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); + + using (var ms = new MemoryStream()) + using (var sw = new StreamWriter(ms)) + using (var sr2 = new LineBufferedReader(ms)) + { + new LegacyBeatmapEncoder(legacyDecoded).Encode(sw); + sw.Flush(); + + ms.Position = 0; + + string result = sr2.ReadToEnd(); + + ms.Position = 0; + + decoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2); + return legacyDecoded; + } + } + } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs new file mode 100644 index 0000000000..1dc29ba5db --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -0,0 +1,375 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Beatmaps.Formats +{ + public class LegacyBeatmapEncoder + { + public const int LATEST_VERSION = 14234; + + private readonly IBeatmap beatmap; + + public LegacyBeatmapEncoder(IBeatmap beatmap) + { + this.beatmap = beatmap; + + if (beatmap.BeatmapInfo.RulesetID < 0 || beatmap.BeatmapInfo.RulesetID > 3) + throw new ArgumentException("Only beatmaps in the osu, taiko, catch, or mania rulesets can be encoded to the legacy beatmap format.", nameof(beatmap)); + } + + public void Encode(TextWriter writer) + { + writer.WriteLine($"osu file format v{LATEST_VERSION}"); + + writer.WriteLine(); + handleGeneral(writer); + + writer.WriteLine(); + handleEditor(writer); + + writer.WriteLine(); + handleMetadata(writer); + + writer.WriteLine(); + handleDifficulty(writer); + + writer.WriteLine(); + handleEvents(writer); + + writer.WriteLine(); + handleTimingPoints(writer); + + writer.WriteLine(); + handleHitObjects(writer); + } + + private void handleGeneral(TextWriter writer) + { + writer.WriteLine("[General]"); + + 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 + writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? "1" : "0")}")); + writer.WriteLine(FormattableString.Invariant($"SampleSet: {(int)toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}")); + writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); + writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); + writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? "1" : "0")}")); + // if (beatmap.BeatmapInfo.UseSkinSprites) + // writer.WriteLine(@"UseSkinSprites: 1"); + // if (b.AlwaysShowPlayfield) + // writer.WriteLine(@"AlwaysShowPlayfield: 1"); + // if (b.OverlayPosition != OverlayPosition.NoChange) + // writer.WriteLine(@"OverlayPosition: " + b.OverlayPosition); + // if (!string.IsNullOrEmpty(b.SkinPreference)) + // writer.WriteLine(@"SkinPreference:" + b.SkinPreference); + // if (b.EpilepsyWarning) + // writer.WriteLine(@"EpilepsyWarning: 1"); + // if (b.CountdownOffset > 0) + // writer.WriteLine(@"CountdownOffset: " + b.CountdownOffset.ToString()); + if (beatmap.BeatmapInfo.RulesetID == 3) + writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? "1" : "0")}")); + writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? "1" : "0")}")); + // if (b.SamplesMatchPlaybackRate) + // writer.WriteLine(@"SamplesMatchPlaybackRate: 1"); + } + + private void handleEditor(TextWriter writer) + { + writer.WriteLine("[Editor]"); + + if (beatmap.BeatmapInfo.Bookmarks.Length > 0) + writer.WriteLine(FormattableString.Invariant($"Bookmarks: {string.Join(',', beatmap.BeatmapInfo.Bookmarks)}")); + writer.WriteLine(FormattableString.Invariant($"DistanceSpacing: {beatmap.BeatmapInfo.DistanceSpacing}")); + writer.WriteLine(FormattableString.Invariant($"BeatDivisor: {beatmap.BeatmapInfo.BeatDivisor}")); + writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.BeatmapInfo.GridSize}")); + writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.BeatmapInfo.TimelineZoom}")); + } + + private void handleMetadata(TextWriter writer) + { + writer.WriteLine("[Metadata]"); + + writer.WriteLine(FormattableString.Invariant($"Title: {beatmap.Metadata.Title}")); + writer.WriteLine(FormattableString.Invariant($"TitleUnicode: {beatmap.Metadata.TitleUnicode}")); + writer.WriteLine(FormattableString.Invariant($"Artist: {beatmap.Metadata.Artist}")); + writer.WriteLine(FormattableString.Invariant($"ArtistUnicode: {beatmap.Metadata.ArtistUnicode}")); + writer.WriteLine(FormattableString.Invariant($"Creator: {beatmap.Metadata.AuthorString}")); + writer.WriteLine(FormattableString.Invariant($"Version: {beatmap.Metadata.Artist}")); + 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 ?? 0}")); + } + + private void handleDifficulty(TextWriter writer) + { + writer.WriteLine("[Difficulty]"); + + writer.WriteLine(FormattableString.Invariant($"HPDrainRate: {beatmap.BeatmapInfo.BaseDifficulty.DrainRate}")); + writer.WriteLine(FormattableString.Invariant($"CircleSize: {beatmap.BeatmapInfo.BaseDifficulty.CircleSize}")); + writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}")); + writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}")); + writer.WriteLine(FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}")); + writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}")); + } + + private void handleEvents(TextWriter writer) + { + // Todo: Storyboard events + } + + private void handleTimingPoints(TextWriter writer) + { + if (beatmap.ControlPointInfo.Groups.Count == 0) + return; + + writer.WriteLine("[TimingPoints]"); + + 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); + + // Convert beat length the legacy format + double beatLength; + if (timingPoint != null) + beatLength = timingPoint.BeatLength; + else + 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()); + + // Convert effect flags to the legacy format + LegacyEffectFlags effectFlags = LegacyEffectFlags.None; + if (effectPoint.KiaiMode) + effectFlags |= LegacyEffectFlags.Kiai; + 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)toLegacySampleBank(tempHitSample.Bank)},")); + writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample.Suffix)},")); + writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},")); + writer.Write(FormattableString.Invariant($"{(timingPoint != null ? "1" : "0")},")); + writer.Write(FormattableString.Invariant($"{(int)effectFlags}")); + writer.Write("\n"); + } + } + + private void handleHitObjects(TextWriter writer) + { + if (beatmap.HitObjects.Count == 0) + return; + + writer.WriteLine("[HitObjects]"); + + foreach (var h in beatmap.HitObjects) + { + switch (beatmap.BeatmapInfo.RulesetID) + { + case 0: + handleOsuHitObject(writer, h); + break; + + case 1: + handleTaikoHitObject(writer, h); + break; + + case 2: + handleCatchHitObject(writer, h); + break; + + case 3: + handleManiaHitObject(writer, h); + break; + } + } + } + + private void handleOsuHitObject(TextWriter writer, HitObject hitObject) + { + var positionData = hitObject as IHasPosition; + var comboData = hitObject as IHasCombo; + + Debug.Assert(positionData != null); + Debug.Assert(comboData != null); + + LegacyHitObjectType hitObjectType = (LegacyHitObjectType)(comboData.ComboOffset << 4); + if (comboData.NewCombo) + hitObjectType |= LegacyHitObjectType.NewCombo; + + if (hitObject is IHasCurve _) + hitObjectType |= LegacyHitObjectType.Slider; + else if (hitObject is IHasEndTime _) + hitObjectType |= LegacyHitObjectType.Spinner; + else + hitObjectType |= LegacyHitObjectType.Circle; + + LegacyHitSoundType soundType = LegacyHitSoundType.Normal; + HitSampleInfo firstAdditionSound = hitObject.Samples.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL); + if (firstAdditionSound != null) + soundType |= toLegacyHitSound(firstAdditionSound.Name); + + writer.Write(FormattableString.Invariant($"{positionData.X},")); + writer.Write(FormattableString.Invariant($"{positionData.Y},")); + writer.Write(FormattableString.Invariant($"{hitObject.StartTime},")); + writer.Write(FormattableString.Invariant($"{(int)hitObjectType},")); + writer.Write(FormattableString.Invariant($"{(int)soundType},")); + + if (hitObject is IHasCurve curveData) + { + for (int i = 0; i < curveData.Path.ControlPoints.Count; i++) + { + PathControlPoint point = curveData.Path.ControlPoints[i]; + + switch (point.Type.Value) + { + case PathType.Bezier: + writer.Write("B|"); + break; + + case PathType.Catmull: + writer.Write("C|"); + break; + + case PathType.PerfectCurve: + writer.Write("P|"); + break; + + case PathType.Linear: + writer.Write("L|"); + break; + } + + writer.Write(FormattableString.Invariant($"{point.Position.Value.X}:{point.Position.Value.Y}")); + writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); + } + + writer.Write(FormattableString.Invariant($"{curveData.RepeatCount - 1},")); + writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},")); + + for (int i = 0; i < curveData.NodeSamples.Count; i++) + { + LegacyHitSoundType type = LegacyHitSoundType.None; + + foreach (var sample in curveData.NodeSamples[i]) + type |= toLegacyHitSound(sample.Name); + + writer.Write(FormattableString.Invariant($"{(int)type}")); + writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + } + + for (int i = 0; i < curveData.NodeSamples.Count; i++) + { + writer.Write(getSampleBank(curveData.NodeSamples[i], true)); + writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + } + } + else if (hitObject is IHasEndTime endTimeData) + writer.Write(FormattableString.Invariant($"{endTimeData.EndTime},")); + + writer.Write(getSampleBank(hitObject.Samples)); + writer.Write(Environment.NewLine); + } + + private void handleTaikoHitObject(TextWriter writer, HitObject hitObject) + { + } + + private void handleCatchHitObject(TextWriter writer, HitObject hitObject) + { + } + + private void handleManiaHitObject(TextWriter writer, HitObject hitObject) + { + } + + private string getSampleBank(IList samples, bool banksOnly = false) + { + LegacySampleBank normalBank = toLegacySampleBank(samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank); + LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL)?.Bank); + + if (addBank == LegacySampleBank.None) + addBank = normalBank; + + string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault()?.Suffix); + string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty; + + int volume = samples.First().Volume; + + StringBuilder sb = new StringBuilder(); + + sb.Append(FormattableString.Invariant($"{(int)normalBank}:")); + sb.Append(FormattableString.Invariant($"{(int)addBank}:")); + + if (!banksOnly) + { + sb.Append(FormattableString.Invariant($"{customSampleBank}:")); + sb.Append(FormattableString.Invariant($"{volume}:")); + sb.Append(FormattableString.Invariant($"{sampleFilename}")); + } + + return sb.ToString(); + } + + private LegacyHitSoundType toLegacyHitSound(string hitSoundName) + { + switch (hitSoundName) + { + case HitSampleInfo.HIT_NORMAL: + return LegacyHitSoundType.Normal; + + case HitSampleInfo.HIT_WHISTLE: + return LegacyHitSoundType.Whistle; + + case HitSampleInfo.HIT_FINISH: + return LegacyHitSoundType.Finish; + + case HitSampleInfo.HIT_CLAP: + return LegacyHitSoundType.Clap; + + default: + return LegacyHitSoundType.None; + } + } + + private LegacySampleBank toLegacySampleBank(string sampleBank) + { + switch (sampleBank?.ToLower()) + { + case "normal": + return LegacySampleBank.Normal; + + case "soft": + return LegacySampleBank.Soft; + + case "drum": + return LegacySampleBank.Drum; + + default: + return LegacySampleBank.None; + } + } + + private string toLegacyCustomSampleBank(string sampleSuffix) => string.IsNullOrEmpty(sampleSuffix) ? "0" : sampleSuffix; + } +} From 8f03599a62971f50443cbe772f8f89a96dcc6c6d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 18:40:33 +0900 Subject: [PATCH 21/95] Write default sampleset by name --- 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 1dc29ba5db..4011bf2d02 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); // Todo: Not all countdown types are supported by lazer yet writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? "1" : "0")}")); - writer.WriteLine(FormattableString.Invariant($"SampleSet: {(int)toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}")); + writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}")); writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? "1" : "0")}")); From c3475a2ddeca1333053f204ebbdeef11bef510eb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 18:41:13 +0900 Subject: [PATCH 22/95] Write control points in absolute coordinates --- 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 4011bf2d02..bfbc3babed 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -260,7 +260,7 @@ namespace osu.Game.Beatmaps.Formats break; } - writer.Write(FormattableString.Invariant($"{point.Position.Value.X}:{point.Position.Value.Y}")); + writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}")); writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); } From d1dc3456d101fde973a84ac800c89601ece949ad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 18:42:48 +0900 Subject: [PATCH 23/95] Fix incorrect repeat point count --- 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 bfbc3babed..23054838f8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -264,7 +264,7 @@ namespace osu.Game.Beatmaps.Formats writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); } - writer.Write(FormattableString.Invariant($"{curveData.RepeatCount - 1},")); + writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},")); writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},")); for (int i = 0; i < curveData.NodeSamples.Count; i++) From e09bbf0315643c4d8bd4c6b7ccfa19176780caab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 18:47:28 +0900 Subject: [PATCH 24/95] Implement background/video/break encoding --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 23054838f8..20706c73c1 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -129,7 +129,11 @@ namespace osu.Game.Beatmaps.Formats private void handleEvents(TextWriter writer) { - // Todo: Storyboard events + writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,{beatmap.BeatmapInfo.Metadata.BackgroundFile}")); + writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,{beatmap.BeatmapInfo.Metadata.VideoFile}")); + + foreach (var b in beatmap.Breaks) + writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}")); } private void handleTimingPoints(TextWriter writer) From f89042cd0aa3c9e7c120901e2e2d6dd5e0a4706d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 18:48:22 +0900 Subject: [PATCH 25/95] Add missing section header --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 20706c73c1..8acd8eb17b 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -129,6 +129,8 @@ namespace osu.Game.Beatmaps.Formats private void handleEvents(TextWriter writer) { + writer.WriteLine("[Events]"); + writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,{beatmap.BeatmapInfo.Metadata.BackgroundFile}")); writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,{beatmap.BeatmapInfo.Metadata.VideoFile}")); From 51bdb73b913255b0f4f2fd8079c0079fc684680e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 18:49:47 +0900 Subject: [PATCH 26/95] Don't write empty file names --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 8acd8eb17b..136b152eff 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -131,8 +131,11 @@ namespace osu.Game.Beatmaps.Formats { writer.WriteLine("[Events]"); - writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,{beatmap.BeatmapInfo.Metadata.BackgroundFile}")); - writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,{beatmap.BeatmapInfo.Metadata.VideoFile}")); + if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile)) + writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,{beatmap.BeatmapInfo.Metadata.BackgroundFile}")); + + if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.VideoFile)) + writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,{beatmap.BeatmapInfo.Metadata.VideoFile}")); foreach (var b in beatmap.Breaks) writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}")); From 60063eefd237572af3c6905096db837dcc0d7401 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 18:51:05 +0900 Subject: [PATCH 27/95] Fix up background/video events --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 136b152eff..f7a70b0d32 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -132,10 +132,10 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[Events]"); if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile)) - writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,{beatmap.BeatmapInfo.Metadata.BackgroundFile}")); + writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,\"{beatmap.BeatmapInfo.Metadata.BackgroundFile}\",0,0")); if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.VideoFile)) - writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,{beatmap.BeatmapInfo.Metadata.VideoFile}")); + writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,\"{beatmap.BeatmapInfo.Metadata.VideoFile}\",0,0")); foreach (var b in beatmap.Breaks) writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}")); From 4760307bbb6d30b59f14c78ef8a19e4d0af8b582 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 19:01:15 +0900 Subject: [PATCH 28/95] Don't output the first slider control point --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index f7a70b0d32..479f2765bb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -269,8 +269,11 @@ namespace osu.Game.Beatmaps.Formats break; } - writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}")); - writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); + if (i != 0) + { + writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}")); + writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); + } } writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},")); From d35d34c01b148fb96a4f129764a3b53eb7f4e655 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 19:01:43 +0900 Subject: [PATCH 29/95] Fix hanging semicolon for per-node bank output --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 479f2765bb..41cb49615c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -331,10 +331,11 @@ namespace osu.Game.Beatmaps.Formats StringBuilder sb = new StringBuilder(); sb.Append(FormattableString.Invariant($"{(int)normalBank}:")); - sb.Append(FormattableString.Invariant($"{(int)addBank}:")); + sb.Append(FormattableString.Invariant($"{(int)addBank}")); if (!banksOnly) { + sb.Append(":"); sb.Append(FormattableString.Invariant($"{customSampleBank}:")); sb.Append(FormattableString.Invariant($"{volume}:")); sb.Append(FormattableString.Invariant($"{sampleFilename}")); From dedae69db8b4144586eb94f9661c42354749ee32 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 19:52:15 +0900 Subject: [PATCH 30/95] Prefer legacy curve format if possible --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 41cb49615c..f64d505df9 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -246,27 +246,42 @@ namespace osu.Game.Beatmaps.Formats if (hitObject is IHasCurve curveData) { + PathType? lastType = null; + for (int i = 0; i < curveData.Path.ControlPoints.Count; i++) { PathControlPoint point = curveData.Path.ControlPoints[i]; - switch (point.Type.Value) + if (point.Type.Value != null) { - case PathType.Bezier: - writer.Write("B|"); - break; + if (point.Type.Value != lastType) + { + switch (point.Type.Value) + { + case PathType.Bezier: + writer.Write("B|"); + break; - case PathType.Catmull: - writer.Write("C|"); - break; + case PathType.Catmull: + writer.Write("C|"); + break; - case PathType.PerfectCurve: - writer.Write("P|"); - break; + case PathType.PerfectCurve: + writer.Write("P|"); + break; - case PathType.Linear: - writer.Write("L|"); - break; + case PathType.Linear: + writer.Write("L|"); + break; + } + + lastType = point.Type.Value; + } + else + { + // New segment with the same type - duplicate the control point + writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}|")); + } } if (i != 0) From ac984423bb9c149fab9d09922d79e03bd0765418 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 19:53:30 +0900 Subject: [PATCH 31/95] Fix only single sound type being written --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index f64d505df9..0eeacf733d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -225,7 +225,6 @@ namespace osu.Game.Beatmaps.Formats LegacyHitObjectType hitObjectType = (LegacyHitObjectType)(comboData.ComboOffset << 4); if (comboData.NewCombo) hitObjectType |= LegacyHitObjectType.NewCombo; - if (hitObject is IHasCurve _) hitObjectType |= LegacyHitObjectType.Slider; else if (hitObject is IHasEndTime _) @@ -233,16 +232,11 @@ namespace osu.Game.Beatmaps.Formats else hitObjectType |= LegacyHitObjectType.Circle; - LegacyHitSoundType soundType = LegacyHitSoundType.Normal; - HitSampleInfo firstAdditionSound = hitObject.Samples.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL); - if (firstAdditionSound != null) - soundType |= toLegacyHitSound(firstAdditionSound.Name); - writer.Write(FormattableString.Invariant($"{positionData.X},")); writer.Write(FormattableString.Invariant($"{positionData.Y},")); writer.Write(FormattableString.Invariant($"{hitObject.StartTime},")); writer.Write(FormattableString.Invariant($"{(int)hitObjectType},")); - writer.Write(FormattableString.Invariant($"{(int)soundType},")); + writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); if (hitObject is IHasCurve curveData) { @@ -296,12 +290,7 @@ namespace osu.Game.Beatmaps.Formats for (int i = 0; i < curveData.NodeSamples.Count; i++) { - LegacyHitSoundType type = LegacyHitSoundType.None; - - foreach (var sample in curveData.NodeSamples[i]) - type |= toLegacyHitSound(sample.Name); - - writer.Write(FormattableString.Invariant($"{(int)type}")); + writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}")); writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); } @@ -359,25 +348,29 @@ namespace osu.Game.Beatmaps.Formats return sb.ToString(); } - private LegacyHitSoundType toLegacyHitSound(string hitSoundName) + private LegacyHitSoundType toLegacyHitSoundType(IList samples) { - switch (hitSoundName) + LegacyHitSoundType type = LegacyHitSoundType.None; + + foreach (var sample in samples) { - case HitSampleInfo.HIT_NORMAL: - return LegacyHitSoundType.Normal; + switch (sample.Name) + { + case HitSampleInfo.HIT_WHISTLE: + type |= LegacyHitSoundType.Whistle; + break; - case HitSampleInfo.HIT_WHISTLE: - return LegacyHitSoundType.Whistle; + case HitSampleInfo.HIT_FINISH: + type |= LegacyHitSoundType.Finish; + break; - case HitSampleInfo.HIT_FINISH: - return LegacyHitSoundType.Finish; - - case HitSampleInfo.HIT_CLAP: - return LegacyHitSoundType.Clap; - - default: - return LegacyHitSoundType.None; + case HitSampleInfo.HIT_CLAP: + type |= LegacyHitSoundType.Clap; + break; + } } + + return type; } private LegacySampleBank toLegacySampleBank(string sampleBank) From ec5b50696058f293dda54c2f0e8b8f2cf9620a57 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 Dec 2019 23:41:46 +0800 Subject: [PATCH 32/95] apply mod difficulty settings during song select --- .../SongSelect/TestSceneBeatmapDetailArea.cs | 69 ++++++++++++++++- .../Screens/Select/Details/AdvancedStats.cs | 77 +++++++++++++++---- 2 files changed, 128 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index ed9e01a67e..ad7ef558c4 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -8,6 +8,9 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Select; using osu.Game.Tests.Beatmaps; using osuTK; @@ -20,8 +23,10 @@ namespace osu.Game.Tests.Visual.SongSelect { public override IReadOnlyList RequiredTypes => new[] { typeof(BeatmapDetails) }; + private ModDisplay modDisplay; + [BackgroundDependencyLoader] - private void load(OsuGameBase game) + private void load(OsuGameBase game, RulesetStore rulesets) { BeatmapDetailArea detailsArea; Add(detailsArea = new BeatmapDetailArea @@ -31,6 +36,16 @@ namespace osu.Game.Tests.Visual.SongSelect Size = new Vector2(550f, 450f), }); + Add(modDisplay = new ModDisplay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Position = new Vector2(0, 25), + }); + + modDisplay.Current.BindTo(Mods); + AddStep("all metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap { BeatmapInfo = @@ -163,6 +178,58 @@ namespace osu.Game.Tests.Visual.SongSelect })); AddStep("null beatmap", () => detailsArea.Beatmap = null); + + AddStep("with EZ mod", () => + { + detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Version = "Has Easy Mod", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has the easy mod enabled", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 3, + DrainRate = 3, + OverallDifficulty = 3, + ApproachRate = 3, + }, + StarDifficulty = 1f, + } + }); + + Mods.Value = new[] { rulesets.AvailableRulesets.First().CreateInstance().GetAllMods().First(m => m is ModEasy) }; + }); + + AddStep("with HR mod", () => + { + detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Version = "Has Hard Rock Mod", + Metadata = new BeatmapMetadata + { + Source = "osu!lazer", + Tags = "this beatmap has the hard rock mod enabled", + }, + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 3, + DrainRate = 3, + OverallDifficulty = 3, + ApproachRate = 3, + }, + StarDifficulty = 1f, + } + }); + + Mods.Value = new[] { rulesets.AvailableRulesets.First().CreateInstance().GetAllMods().First(m => m is ModHardRock) }; + }); } } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index c5bdc230d0..be03b7ddd5 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -12,11 +12,21 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using System; using osu.Game.Beatmaps; +using osu.Framework.Bindables; +using System.Collections.Generic; +using osu.Game.Rulesets.Mods; +using System.Linq; namespace osu.Game.Screens.Select.Details { public class AdvancedStats : Container { + [Resolved] + private IBindable> mods { get; set; } + + [Resolved] + private OsuColour colours { get; set; } + private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty; private BeatmapInfo beatmap; @@ -30,22 +40,7 @@ namespace osu.Game.Screens.Select.Details beatmap = value; - //mania specific - if ((Beatmap?.Ruleset?.ID ?? 0) == 3) - { - firstValue.Title = "Key Amount"; - firstValue.Value = (int)MathF.Round(Beatmap?.BaseDifficulty?.CircleSize ?? 0); - } - else - { - firstValue.Title = "Circle Size"; - firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0; - } - - hpDrain.Value = Beatmap?.BaseDifficulty?.DrainRate ?? 0; - accuracy.Value = Beatmap?.BaseDifficulty?.OverallDifficulty ?? 0; - approachRate.Value = Beatmap?.BaseDifficulty?.ApproachRate ?? 0; - starDifficulty.Value = (float)(Beatmap?.StarDifficulty ?? 0); + updateStatistics(); } } @@ -65,12 +60,60 @@ namespace osu.Game.Screens.Select.Details starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" }, }, }; + } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { starDifficulty.AccentColour = colours.Yellow; + mods.ValueChanged += _ => updateStatistics(); + } + + private void updateStatistics() + { + BeatmapInfo processed = Beatmap?.Clone(); + + if (processed != null && mods.Value.Any(m => m is IApplicableToDifficulty)) + { + processed.BaseDifficulty = processed.BaseDifficulty.Clone(); + + foreach (var mod in mods.Value.OfType()) + mod.ApplyToDifficulty(processed.BaseDifficulty); + } + + BeatmapDifficulty baseDifficulty = Beatmap?.BaseDifficulty; + BeatmapDifficulty moddedDifficulty = processed?.BaseDifficulty; + + //mania specific + if ((processed?.Ruleset?.ID ?? 0) == 3) + { + firstValue.Title = "Key Amount"; + firstValue.Value = (int)MathF.Round(moddedDifficulty?.CircleSize ?? 0); + } + else + { + firstValue.Title = "Circle Size"; + firstValue.Value = moddedDifficulty?.CircleSize ?? 0; + } + + hpDrain.Value = moddedDifficulty?.DrainRate ?? 0; + accuracy.Value = moddedDifficulty?.OverallDifficulty ?? 0; + approachRate.Value = moddedDifficulty?.ApproachRate ?? 0; + starDifficulty.Value = (float)(processed?.StarDifficulty ?? 0); + + hpDrain.AccentColour = (moddedDifficulty?.DrainRate ?? 0) == (baseDifficulty?.DrainRate ?? 0) ? + Color4.White : (moddedDifficulty?.DrainRate ?? 0) < (baseDifficulty?.DrainRate ?? 0) ? + colours.BlueLight : colours.RedLight; + accuracy.AccentColour = (moddedDifficulty?.OverallDifficulty ?? 0) == (baseDifficulty?.OverallDifficulty ?? 0) ? + Color4.White : (moddedDifficulty?.OverallDifficulty ?? 0) < (baseDifficulty?.OverallDifficulty ?? 0) ? + colours.BlueLight : colours.RedLight; + approachRate.AccentColour = (moddedDifficulty?.ApproachRate ?? 0) == (baseDifficulty?.ApproachRate ?? 0) ? + Color4.White : (moddedDifficulty?.ApproachRate ?? 0) < (baseDifficulty?.ApproachRate ?? 0) ? + colours.BlueLight : colours.RedLight; + firstValue.AccentColour = (moddedDifficulty?.CircleSize ?? 0) == (baseDifficulty?.CircleSize ?? 0) ? + Color4.White : (moddedDifficulty?.CircleSize ?? 0) < (baseDifficulty?.CircleSize ?? 0) ? + colours.BlueLight : colours.RedLight; } private class StatisticRow : Container, IHasAccentColour From 3945e7403aec4c08a249a9fd6a041f71327e96ee Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 13 Dec 2019 09:39:54 +0800 Subject: [PATCH 33/95] improve tests and refactor AdvancedStats --- .../SongSelect/TestSceneBeatmapDetailArea.cs | 6 +- .../Screens/Select/Details/AdvancedStats.cs | 77 ++++++++++++------- 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index ad7ef558c4..d4805a73e4 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -179,6 +179,8 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("null beatmap", () => detailsArea.Beatmap = null); + Ruleset ruleset = rulesets.AvailableRulesets.First().CreateInstance(); + AddStep("with EZ mod", () => { detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap @@ -202,7 +204,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - Mods.Value = new[] { rulesets.AvailableRulesets.First().CreateInstance().GetAllMods().First(m => m is ModEasy) }; + Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) }; }); AddStep("with HR mod", () => @@ -228,7 +230,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - Mods.Value = new[] { rulesets.AvailableRulesets.First().CreateInstance().GetAllMods().First(m => m is ModHardRock) }; + Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) }; }); } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index be03b7ddd5..3e7d4e244b 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -24,9 +24,6 @@ namespace osu.Game.Screens.Select.Details [Resolved] private IBindable> mods { get; set; } - [Resolved] - private OsuColour colours { get; set; } - private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty; private BeatmapInfo beatmap; @@ -60,11 +57,10 @@ namespace osu.Game.Screens.Select.Details starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" }, }, }; - } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { starDifficulty.AccentColour = colours.Yellow; mods.ValueChanged += _ => updateStatistics(); @@ -89,31 +85,24 @@ namespace osu.Game.Screens.Select.Details if ((processed?.Ruleset?.ID ?? 0) == 3) { firstValue.Title = "Key Amount"; - firstValue.Value = (int)MathF.Round(moddedDifficulty?.CircleSize ?? 0); + firstValue.BaseValue = (int)MathF.Round(baseDifficulty?.CircleSize ?? 0); + firstValue.ModdedValue = (int)MathF.Round(moddedDifficulty?.CircleSize ?? 0); } else { firstValue.Title = "Circle Size"; - firstValue.Value = moddedDifficulty?.CircleSize ?? 0; + firstValue.BaseValue = baseDifficulty?.CircleSize ?? 0; + firstValue.ModdedValue = moddedDifficulty?.CircleSize ?? 0; } - hpDrain.Value = moddedDifficulty?.DrainRate ?? 0; - accuracy.Value = moddedDifficulty?.OverallDifficulty ?? 0; - approachRate.Value = moddedDifficulty?.ApproachRate ?? 0; - starDifficulty.Value = (float)(processed?.StarDifficulty ?? 0); + hpDrain.BaseValue = baseDifficulty?.DrainRate ?? 0; + accuracy.BaseValue = baseDifficulty?.OverallDifficulty ?? 0; + approachRate.BaseValue = baseDifficulty?.ApproachRate ?? 0; + starDifficulty.BaseValue = (float)(processed?.StarDifficulty ?? 0); - hpDrain.AccentColour = (moddedDifficulty?.DrainRate ?? 0) == (baseDifficulty?.DrainRate ?? 0) ? - Color4.White : (moddedDifficulty?.DrainRate ?? 0) < (baseDifficulty?.DrainRate ?? 0) ? - colours.BlueLight : colours.RedLight; - accuracy.AccentColour = (moddedDifficulty?.OverallDifficulty ?? 0) == (baseDifficulty?.OverallDifficulty ?? 0) ? - Color4.White : (moddedDifficulty?.OverallDifficulty ?? 0) < (baseDifficulty?.OverallDifficulty ?? 0) ? - colours.BlueLight : colours.RedLight; - approachRate.AccentColour = (moddedDifficulty?.ApproachRate ?? 0) == (baseDifficulty?.ApproachRate ?? 0) ? - Color4.White : (moddedDifficulty?.ApproachRate ?? 0) < (baseDifficulty?.ApproachRate ?? 0) ? - colours.BlueLight : colours.RedLight; - firstValue.AccentColour = (moddedDifficulty?.CircleSize ?? 0) == (baseDifficulty?.CircleSize ?? 0) ? - Color4.White : (moddedDifficulty?.CircleSize ?? 0) < (baseDifficulty?.CircleSize ?? 0) ? - colours.BlueLight : colours.RedLight; + hpDrain.ModdedValue = moddedDifficulty?.DrainRate ?? 0; + accuracy.ModdedValue = moddedDifficulty?.OverallDifficulty ?? 0; + approachRate.ModdedValue = moddedDifficulty?.ApproachRate ?? 0; } private class StatisticRow : Container, IHasAccentColour @@ -124,7 +113,10 @@ namespace osu.Game.Screens.Select.Details private readonly float maxValue; private readonly bool forceDecimalPlaces; private readonly OsuSpriteText name, value; - private readonly Bar bar; + private readonly Bar bar, modBar; + + [Resolved] + private OsuColour colours { get; set; } public string Title { @@ -132,19 +124,39 @@ namespace osu.Game.Screens.Select.Details set => name.Text = value; } - private float difficultyValue; + private float baseValue; - public float Value + private float moddedValue; + + public float BaseValue { - get => difficultyValue; + get => baseValue; set { - difficultyValue = value; + baseValue = value; bar.Length = value / maxValue; this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##"); } } + public float ModdedValue + { + get => moddedValue; + set + { + moddedValue = value; + modBar.Length = value / maxValue; + this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##"); + + if (moddedValue > baseValue) + modBar.AccentColour = this.value.Colour = colours.Red; + else if (moddedValue < baseValue) + modBar.AccentColour = this.value.Colour = colours.BlueDark; + else + modBar.AccentColour = this.value.Colour = Color4.White; + } + } + public Color4 AccentColour { get => bar.AccentColour; @@ -178,6 +190,15 @@ namespace osu.Game.Screens.Select.Details BackgroundColour = Color4.White.Opacity(0.5f), Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 }, }, + modBar = new Bar + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Alpha = 0.5f, + Height = 5, + Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 }, + }, new Container { Anchor = Anchor.TopRight, From 53a665a034cfa95f926c3334c4a8e77bf370a803 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 20:04:46 +0900 Subject: [PATCH 34/95] Fix sound types and banks not being written correctly --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 0eeacf733d..4c8b83820d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -236,6 +236,10 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{positionData.Y},")); writer.Write(FormattableString.Invariant($"{hitObject.StartTime},")); writer.Write(FormattableString.Invariant($"{(int)hitObjectType},")); + + if (hitObject is IHasCurve _) + writer.Write(FormattableString.Invariant($"0,")); // A sound type of "none" is written since it's stored per-node + else writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); if (hitObject is IHasCurve curveData) @@ -303,7 +307,11 @@ namespace osu.Game.Beatmaps.Formats else if (hitObject is IHasEndTime endTimeData) writer.Write(FormattableString.Invariant($"{endTimeData.EndTime},")); - writer.Write(getSampleBank(hitObject.Samples)); + if (hitObject is IHasCurve _) + writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true)); // A bank of "none" is written since it's stored per-node + else + writer.Write(getSampleBank(hitObject.Samples)); + writer.Write(Environment.NewLine); } @@ -319,14 +327,11 @@ namespace osu.Game.Beatmaps.Formats { } - private string getSampleBank(IList samples, bool banksOnly = false) + private string getSampleBank(IList samples, bool banksOnly = false, bool zeroBanks = false) { LegacySampleBank normalBank = toLegacySampleBank(samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank); LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL)?.Bank); - if (addBank == LegacySampleBank.None) - addBank = normalBank; - string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault()?.Suffix); string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty; @@ -334,8 +339,8 @@ namespace osu.Game.Beatmaps.Formats StringBuilder sb = new StringBuilder(); - sb.Append(FormattableString.Invariant($"{(int)normalBank}:")); - sb.Append(FormattableString.Invariant($"{(int)addBank}")); + sb.Append(FormattableString.Invariant($"{(zeroBanks ? 0 : (int)normalBank)}:")); + sb.Append(FormattableString.Invariant($"{(zeroBanks ? 0 : (int)addBank)}")); if (!banksOnly) { From 0b7c4f252c19578ff4d1da03cb4d6f3962e497fe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Dec 2019 17:01:59 +0900 Subject: [PATCH 35/95] Fix artist being written in place of version --- 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 4c8b83820d..efb8527d65 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -108,7 +108,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"Artist: {beatmap.Metadata.Artist}")); writer.WriteLine(FormattableString.Invariant($"ArtistUnicode: {beatmap.Metadata.ArtistUnicode}")); writer.WriteLine(FormattableString.Invariant($"Creator: {beatmap.Metadata.AuthorString}")); - writer.WriteLine(FormattableString.Invariant($"Version: {beatmap.Metadata.Artist}")); + 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}")); From 97158fce7de679c07393307303d8438f3ea473f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Dec 2019 17:02:10 +0900 Subject: [PATCH 36/95] Always attach a new combo to 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 efb8527d65..ff5d6ceebe 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -228,7 +228,7 @@ namespace osu.Game.Beatmaps.Formats if (hitObject is IHasCurve _) hitObjectType |= LegacyHitObjectType.Slider; else if (hitObject is IHasEndTime _) - hitObjectType |= LegacyHitObjectType.Spinner; + hitObjectType |= LegacyHitObjectType.Spinner | LegacyHitObjectType.NewCombo; else hitObjectType |= LegacyHitObjectType.Circle; From 654499d8b017c19a37b76ef1a3dbbe45a410ace3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Dec 2019 17:33:18 +0900 Subject: [PATCH 37/95] Remove whitespace --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 1c2ca4b6f8..5bc7897499 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -148,10 +148,6 @@ namespace osu.Game.Beatmaps.Formats Fonts } - - - - internal class LegacyDifficultyControlPoint : DifficultyControlPoint { public LegacyDifficultyControlPoint() From c976427206680d9cfbad6b7ac6a463d50c85081f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Dec 2019 19:00:28 +0900 Subject: [PATCH 38/95] Refactor test to be more complete --- .../Formats/LegacyBeatmapEncoderTest.cs | 112 ++---------------- osu.Game.Tests/Resources/TestResources.cs | 6 +- 2 files changed, 16 insertions(+), 102 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index c4a3877c1c..f2b3a16f68 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -1,16 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.IO; using System.Linq; using NUnit.Framework; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; -using osu.Game.Rulesets.Objects.Types; +using osu.Game.IO.Serialization; using osu.Game.Tests.Resources; -using osuTK; namespace osu.Game.Tests.Beatmaps.Formats { @@ -19,101 +18,18 @@ namespace osu.Game.Tests.Beatmaps.Formats { private const string normal = "Soleily - Renatus (Gamu) [Insane].osu"; - [Test] - public void TestDecodeMetadata() + private static IEnumerable allBeatmaps => TestResources.GetStore().GetAvailableResources().Where(res => res.EndsWith(".osu")); + + [TestCaseSource(nameof(allBeatmaps))] + public void TestDecodeEncodedBeatmap(string name) { - var beatmap = decode(normal); - var meta = beatmap.BeatmapInfo.Metadata; - Assert.AreEqual(241526, beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID); - Assert.AreEqual("Soleily", meta.Artist); - Assert.AreEqual("Soleily", meta.ArtistUnicode); - Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); - Assert.AreEqual("Gamu", meta.AuthorString); - Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); - Assert.AreEqual(164471, meta.PreviewTime); - Assert.AreEqual(string.Empty, meta.Source); - Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); - Assert.AreEqual("Renatus", meta.Title); - Assert.AreEqual("Renatus", meta.TitleUnicode); + var decoded = decode(normal, out var encoded); + + Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count)); + Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); } - [Test] - public void TestDecodeGeneral() - { - var beatmap = decode(normal); - var beatmapInfo = beatmap.BeatmapInfo; - Assert.AreEqual(0, beatmapInfo.AudioLeadIn); - Assert.AreEqual(false, beatmapInfo.Countdown); - Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); - Assert.AreEqual(false, beatmapInfo.SpecialStyle); - Assert.IsTrue(beatmapInfo.RulesetID == 0); - Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); - Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); - } - - [Test] - public void TestDecodeEditor() - { - var beatmap = decode(normal); - var beatmapInfo = beatmap.BeatmapInfo; - - int[] expectedBookmarks = - { - 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, - 95901, 106450, 116999, 119637, 130186, 140735, 151285, - 161834, 164471, 175020, 185570, 196119, 206669, 209306 - }; - Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length); - for (int i = 0; i < expectedBookmarks.Length; i++) - Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]); - Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing); - Assert.AreEqual(4, beatmapInfo.BeatDivisor); - Assert.AreEqual(4, beatmapInfo.GridSize); - Assert.AreEqual(2, beatmapInfo.TimelineZoom); - } - - [Test] - public void TestDecodeDifficulty() - { - var beatmap = decode(normal); - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; - Assert.AreEqual(6.5f, difficulty.DrainRate); - Assert.AreEqual(4, difficulty.CircleSize); - Assert.AreEqual(8, difficulty.OverallDifficulty); - Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8, difficulty.SliderMultiplier); - Assert.AreEqual(2, difficulty.SliderTickRate); - } - - [Test] - public void TestDecodeHitObjects() - { - var beatmap = decode(normal); - - var curveData = beatmap.HitObjects[0] as IHasCurve; - var positionData = beatmap.HitObjects[0] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.IsNotNull(curveData); - Assert.AreEqual(new Vector2(192, 168), positionData.Position); - Assert.AreEqual(956, beatmap.HitObjects[0].StartTime); - Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL)); - - positionData = beatmap.HitObjects[1] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.AreEqual(new Vector2(304, 56), positionData.Position); - Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime); - Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP)); - } - - private Beatmap decode(string filename) - { - decode(filename, out Beatmap jsonDecoded); - return jsonDecoded; - } - - private Beatmap decode(string filename, out Beatmap decoded) + private Beatmap decode(string filename, out Beatmap encoded) { using (var stream = TestResources.OpenResource(filename)) using (var sr = new LineBufferedReader(stream)) @@ -129,11 +45,7 @@ namespace osu.Game.Tests.Beatmaps.Formats ms.Position = 0; - string result = sr2.ReadToEnd(); - - ms.Position = 0; - - decoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2); + encoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2); return legacyDecoded; } } diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index 66084a3204..932021afe5 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -9,9 +9,11 @@ namespace osu.Game.Tests.Resources { public static class TestResources { - public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}"); + public static DllResourceStore GetStore() => new DllResourceStore("osu.Game.Tests.dll"); - public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz"); + public static Stream OpenResource(string name) => GetStore().GetStream($"Resources/{name}"); + + public static Stream GetTestBeatmapStream(bool virtualTrack = false) => GetStore().GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz"); public static string GetTestBeatmapForImport(bool virtualTrack = false) { From 0ad28a9400b821e2d11a8e0c234f2030387e0119 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Dec 2019 19:11:45 +0900 Subject: [PATCH 39/95] Start at version 128 --- 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 ff5d6ceebe..f367ab3817 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyBeatmapEncoder { - public const int LATEST_VERSION = 14234; + public const int LATEST_VERSION = 128; private readonly IBeatmap beatmap; From 9bc02f489ee46f098cbea53d78e3cb6c2d001e5b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Dec 2019 19:14:49 +0900 Subject: [PATCH 40/95] Add missing headers --- osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs | 3 +++ osu.Game/Beatmaps/Legacy/LegacyEventType.cs | 3 +++ osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs | 3 +++ osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs | 3 +++ osu.Game/Beatmaps/Legacy/LegacyOrigins.cs | 3 +++ osu.Game/Beatmaps/Legacy/LegacySampleBank.cs | 3 +++ osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs | 3 +++ 7 files changed, 21 insertions(+) diff --git a/osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs b/osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs index ce141031fd..5bf80c34d7 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; namespace osu.Game.Beatmaps.Legacy diff --git a/osu.Game/Beatmaps/Legacy/LegacyEventType.cs b/osu.Game/Beatmaps/Legacy/LegacyEventType.cs index 57b1e6c29f..32a7122978 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyEventType.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyEventType.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + namespace osu.Game.Beatmaps.Legacy { internal enum LegacyEventType diff --git a/osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs b/osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs index 9223f7df31..ec9839b893 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyHitObjectType.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; namespace osu.Game.Beatmaps.Legacy diff --git a/osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs b/osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs index 69adbf8f67..d7743565f8 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; namespace osu.Game.Beatmaps.Legacy diff --git a/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs b/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs index 93c8920761..31f67d6dfd 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + namespace osu.Game.Beatmaps.Legacy { internal enum LegacyOrigins diff --git a/osu.Game/Beatmaps/Legacy/LegacySampleBank.cs b/osu.Game/Beatmaps/Legacy/LegacySampleBank.cs index 0d54998d15..8cac29cb87 100644 --- a/osu.Game/Beatmaps/Legacy/LegacySampleBank.cs +++ b/osu.Game/Beatmaps/Legacy/LegacySampleBank.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + namespace osu.Game.Beatmaps.Legacy { internal enum LegacySampleBank diff --git a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs index 509f39f830..5237445640 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + namespace osu.Game.Beatmaps.Legacy { internal enum LegacyStoryLayer From ea4eb6b2043e6cc9cb63c20a2c3d8461f276d983 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Dec 2019 21:29:10 +0900 Subject: [PATCH 41/95] CI cleanups --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index f367ab3817..e16411f343 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -225,9 +225,9 @@ namespace osu.Game.Beatmaps.Formats LegacyHitObjectType hitObjectType = (LegacyHitObjectType)(comboData.ComboOffset << 4); if (comboData.NewCombo) hitObjectType |= LegacyHitObjectType.NewCombo; - if (hitObject is IHasCurve _) + if (hitObject is IHasCurve) hitObjectType |= LegacyHitObjectType.Slider; - else if (hitObject is IHasEndTime _) + else if (hitObject is IHasEndTime) hitObjectType |= LegacyHitObjectType.Spinner | LegacyHitObjectType.NewCombo; else hitObjectType |= LegacyHitObjectType.Circle; @@ -237,10 +237,9 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{hitObject.StartTime},")); writer.Write(FormattableString.Invariant($"{(int)hitObjectType},")); - if (hitObject is IHasCurve _) - writer.Write(FormattableString.Invariant($"0,")); // A sound type of "none" is written since it's stored per-node - else - writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); + writer.Write(hitObject is IHasCurve + ? FormattableString.Invariant($"0,") + : FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},")); if (hitObject is IHasCurve curveData) { @@ -307,10 +306,9 @@ namespace osu.Game.Beatmaps.Formats else if (hitObject is IHasEndTime endTimeData) writer.Write(FormattableString.Invariant($"{endTimeData.EndTime},")); - if (hitObject is IHasCurve _) - writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true)); // A bank of "none" is written since it's stored per-node - else - writer.Write(getSampleBank(hitObject.Samples)); + writer.Write(hitObject is IHasCurve + ? getSampleBank(hitObject.Samples, zeroBanks: true) + : getSampleBank(hitObject.Samples)); writer.Write(Environment.NewLine); } From 0d49bc244e6972990e83a3b21474e8a1a4af8ad9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Dec 2019 21:30:28 +0900 Subject: [PATCH 42/95] Fix test beatmap not being retrievable anymore --- osu.Game.Tests/Resources/TestResources.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index 932021afe5..a57405628a 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Resources public static Stream OpenResource(string name) => GetStore().GetStream($"Resources/{name}"); - public static Stream GetTestBeatmapStream(bool virtualTrack = false) => GetStore().GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz"); + public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz"); public static string GetTestBeatmapForImport(bool virtualTrack = false) { From e05c9426ed56921fc982c10e9cf5d3be169ebc78 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 13 Dec 2019 18:50:49 +0100 Subject: [PATCH 43/95] Initial implementation of NewsArticleCover class --- .../Visual/Online/TestSceneNewsOverlay.cs | 38 +++++ osu.Game/Overlays/News/NewsArticleCover.cs | 156 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 osu.Game/Overlays/News/NewsArticleCover.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index 98f90f2daa..7903709bd6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -1,6 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Overlays.News; @@ -19,11 +22,46 @@ namespace osu.Game.Tests.Visual.Online AddStep(@"Show front page", () => news.ShowFrontPage()); AddStep(@"Custom article", () => news.Current.Value = "Test Article 101"); + + AddStep(@"Article covers", () => news.LoadAndShowChild(new NewsCoverTest())); } private class TestNewsOverlay : NewsOverlay { public new void LoadAndShowChild(NewsContent content) => base.LoadAndShowChild(content); } + + private class NewsCoverTest : NewsContent + { + public NewsCoverTest() + { + Spacing = new osuTK.Vector2(0, 10); + + var article = new NewsArticleCover.ArticleInfo() + { + Author = "Ephemeral", + CoverURL = "https://assets.ppy.sh/artists/58/header.jpg", + Time = new DateTime(2019, 12, 4), + Title = "New Featured Artist: Kurokotei" + }; + + Children = new Drawable[] + { + new NewsArticleCover(article) + { + Height = 200 + }, + new NewsArticleCover(article) + { + Height = 120 + }, + new NewsArticleCover(article) + { + RelativeSizeAxes = Axes.None, + Size = new osuTK.Vector2(400, 200), + } + }; + } + } } } diff --git a/osu.Game/Overlays/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs new file mode 100644 index 0000000000..3274cedbac --- /dev/null +++ b/osu.Game/Overlays/News/NewsArticleCover.cs @@ -0,0 +1,156 @@ +using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK.Graphics; + +namespace osu.Game.Overlays.News +{ + public class NewsArticleCover : Container + { + public NewsArticleCover(ArticleInfo info) + { + RelativeSizeAxes = Axes.X; + Masking = true; + CornerRadius = 4; + + NewsBackground bg; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.1f)) + }, + new DelayedLoadWrapper(bg = new NewsBackground(info.CoverURL) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, + Alpha = 0 + }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.6f)), + Alpha = 1f, + }, + new DateContainer(info.Time) + { + Margin = new MarginPadding() + { + Right = 20, + Top = 20, + } + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding() + { + Left = 25, + Bottom = 50, + }, + Font = OsuFont.GetFont(Typeface.Exo, 24, FontWeight.Bold), + Text = info.Title, + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding() + { + Left = 25, + Bottom = 30, + }, + Font = OsuFont.GetFont(Typeface.Exo, 16, FontWeight.Bold), + Text = "by " + info.Author + } + }; + + bg.OnLoadComplete += d => d.FadeIn(250, Easing.In); + } + + //news article cover background + [LongRunningLoad] + private class NewsBackground : Sprite + { + private readonly string url; + + public NewsBackground(string coverUrl) + { + url = coverUrl ?? "Headers/news"; + } + + [BackgroundDependencyLoader] + private void load(LargeTextureStore store) + { + Texture = store.Get(url); + } + } + + //date container + private class DateContainer : Container, IHasTooltip + { + private readonly DateTime date; + + public DateContainer(DateTime date) + { + this.date = date; + + Anchor = Anchor.TopRight; + Origin = Anchor.TopRight; + Masking = true; + CornerRadius = 4; + AutoSizeAxes = Axes.Both; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(Typeface.Exo, 12, FontWeight.Black, false, false), + Text = date.ToString("dd MMM yyy"), + Margin = new MarginPadding() + { + Vertical = 4, + Horizontal = 8, + } + } + }; + } + + public string TooltipText => date.ToString("dddd dd MMMM yyyy hh:mm:ss UTCz"); + } + + //fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now + public class ArticleInfo + { + public string Title { get; set; } + public string CoverURL { get; set; } + public DateTime Time { get; set; } + public string Author { get; set; } + } + } +} From 43720fbf45869a4e22d9ebb1ffebcb7f79e20557 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 13 Dec 2019 18:59:40 +0100 Subject: [PATCH 44/95] Fix CI issues --- .../Visual/Online/TestSceneNewsOverlay.cs | 5 ++--- osu.Game/Overlays/News/NewsArticleCover.cs | 17 ++++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index 7903709bd6..f870a12fc3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Overlays.News; @@ -37,10 +36,10 @@ namespace osu.Game.Tests.Visual.Online { Spacing = new osuTK.Vector2(0, 10); - var article = new NewsArticleCover.ArticleInfo() + var article = new NewsArticleCover.ArticleInfo { Author = "Ephemeral", - CoverURL = "https://assets.ppy.sh/artists/58/header.jpg", + CoverUrl = "https://assets.ppy.sh/artists/58/header.jpg", Time = new DateTime(2019, 12, 4), Title = "New Featured Artist: Kurokotei" }; diff --git a/osu.Game/Overlays/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs index 3274cedbac..e2485bd170 100644 --- a/osu.Game/Overlays/News/NewsArticleCover.cs +++ b/osu.Game/Overlays/News/NewsArticleCover.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -31,7 +34,7 @@ namespace osu.Game.Overlays.News RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.1f)) }, - new DelayedLoadWrapper(bg = new NewsBackground(info.CoverURL) + new DelayedLoadWrapper(bg = new NewsBackground(info.CoverUrl) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -52,7 +55,7 @@ namespace osu.Game.Overlays.News }, new DateContainer(info.Time) { - Margin = new MarginPadding() + Margin = new MarginPadding { Right = 20, Top = 20, @@ -62,7 +65,7 @@ namespace osu.Game.Overlays.News { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Margin = new MarginPadding() + Margin = new MarginPadding { Left = 25, Bottom = 50, @@ -74,7 +77,7 @@ namespace osu.Game.Overlays.News { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Margin = new MarginPadding() + Margin = new MarginPadding { Left = 25, Bottom = 30, @@ -132,7 +135,7 @@ namespace osu.Game.Overlays.News Origin = Anchor.Centre, Font = OsuFont.GetFont(Typeface.Exo, 12, FontWeight.Black, false, false), Text = date.ToString("dd MMM yyy"), - Margin = new MarginPadding() + Margin = new MarginPadding { Vertical = 4, Horizontal = 8, @@ -148,7 +151,7 @@ namespace osu.Game.Overlays.News public class ArticleInfo { public string Title { get; set; } - public string CoverURL { get; set; } + public string CoverUrl { get; set; } public DateTime Time { get; set; } public string Author { get; set; } } From 01c036b0b0c88421a639d9e3d92c7f6b006b421c Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 15 Dec 2019 16:56:02 +0800 Subject: [PATCH 45/95] implement backing beats for nightcore mods --- .../Mods/CatchModNightcore.cs | 3 +- .../Mods/ManiaModNightcore.cs | 3 +- osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs | 3 +- .../Mods/TaikoModNightcore.cs | 3 +- osu.Game/Rulesets/Mods/ModNightcore.cs | 57 ++++++++++++++++++- 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs b/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs index da2edcee44..c07087efaf 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModNightcore.cs @@ -1,11 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModNightcore : ModNightcore + public class CatchModNightcore : ModNightcore { public override double ScoreMultiplier => 1.06; } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs index 2d94fb6af5..4cc712060c 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs @@ -1,11 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModNightcore : ModNightcore + public class ManiaModNightcore : ModNightcore { public override double ScoreMultiplier => 1; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs index 5668c17792..7780e23a26 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNightcore.cs @@ -2,10 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModNightcore : ModNightcore + public class OsuModNightcore : ModNightcore { public override double ScoreMultiplier => 1.12; } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs index e45081b6d6..5377eb1072 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModNightcore.cs @@ -2,10 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModNightcore : ModNightcore + public class TaikoModNightcore : ModNightcore { public override double ScoreMultiplier => 1.12; } diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index c14e02e64d..22d8d4ba66 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -1,15 +1,28 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { - public abstract class ModNightcore : ModDoubleTime + public abstract class ModNightcore : ModDoubleTime, IApplicableToDrawableRuleset + where TObject : HitObject { public override string Name => "Nightcore"; public override string Acronym => "NC"; @@ -34,5 +47,47 @@ namespace osu.Game.Rulesets.Mods track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); } + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableRuleset.Overlays.Add(new NightcoreBeatContainer()); + } + + public class NightcoreBeatContainer : BeatSyncedContainer + { + private SkinnableSound hatSample; + private SkinnableSound clapSample; + private SkinnableSound kickSample; + private SkinnableSound finishSample; + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + InternalChildren = new Drawable[] + { + hatSample = new SkinnableSound(new SampleInfo("nightcore-hat")), + clapSample = new SkinnableSound(new SampleInfo("nightcore-clap")), + kickSample = new SkinnableSound(new SampleInfo("nightcore-kick")), + finishSample = new SkinnableSound(new SampleInfo("nightcore-finish")), + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (beatIndex > -1) + { + if (beatIndex % 16 == 0) + finishSample?.Play(); + else if (beatIndex % 2 == 0) + kickSample?.Play(); + else if (beatIndex % 2 == 1) + clapSample?.Play(); + else + hatSample?.Play(); + } + } + } } } From 6da168118e84965914bfec15bfae2794676c4470 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 15 Dec 2019 17:02:29 +0800 Subject: [PATCH 46/95] remove unused usings --- osu.Game/Rulesets/Mods/ModNightcore.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 22d8d4ba66..97d96bbca2 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -1,17 +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.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Audio; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; From 154bc57c6e62136b49f4560af8614fe5889a7d9e Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 15 Dec 2019 17:46:44 +0800 Subject: [PATCH 47/95] remove unused dependency --- osu.Game/Rulesets/Mods/ModNightcore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 97d96bbca2..3a67fedd4d 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Mods private SkinnableSound finishSample; [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load() { InternalChildren = new Drawable[] { From 88d3a1707d0b2d3893e497d4eb319d31a845d2d9 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 16 Dec 2019 06:35:18 +0800 Subject: [PATCH 48/95] ensure beatIndex is 0 before starting beats --- osu.Game/Rulesets/Mods/ModNightcore.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 3a67fedd4d..1374c385cc 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -55,6 +55,7 @@ namespace osu.Game.Rulesets.Mods private SkinnableSound clapSample; private SkinnableSound kickSample; private SkinnableSound finishSample; + private bool started; [BackgroundDependencyLoader] private void load() @@ -72,7 +73,10 @@ namespace osu.Game.Rulesets.Mods { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - if (beatIndex > -1) + if (!started && beatIndex == 0) + started = true; + + if (started && beatIndex > -1) { if (beatIndex % 16 == 0) finishSample?.Play(); From 58e3fb0d0fbd506aa4e2bc8342d503f60a7a6a54 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 16:43:20 +0900 Subject: [PATCH 49/95] Coalesce to -1 --- 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 e16411f343..4280112967 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -112,7 +112,7 @@ namespace osu.Game.Beatmaps.Formats 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 ?? 0}")); + writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID ?? -1}")); } private void handleDifficulty(TextWriter writer) From 596fda3c1fd0f1844842a721453e0980d64fcfa6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 16:57:40 +0900 Subject: [PATCH 50/95] Refactor switch --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 4280112967..efe20e8d34 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -191,26 +191,27 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[HitObjects]"); - foreach (var h in beatmap.HitObjects) + switch (beatmap.BeatmapInfo.RulesetID) { - switch (beatmap.BeatmapInfo.RulesetID) - { - case 0: + case 0: + foreach (var h in beatmap.HitObjects) handleOsuHitObject(writer, h); - break; + break; - case 1: + case 1: + foreach (var h in beatmap.HitObjects) handleTaikoHitObject(writer, h); - break; + break; - case 2: + case 2: + foreach (var h in beatmap.HitObjects) handleCatchHitObject(writer, h); - break; + break; - case 3: + case 3: + foreach (var h in beatmap.HitObjects) handleManiaHitObject(writer, h); - break; - } + break; } } From d56e99865cdc859be192c547e895e2792b03c9e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 16:57:49 +0900 Subject: [PATCH 51/95] Throw not implemented exceptions --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index efe20e8d34..e600dc06b7 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -314,17 +314,11 @@ namespace osu.Game.Beatmaps.Formats writer.Write(Environment.NewLine); } - private void handleTaikoHitObject(TextWriter writer, HitObject hitObject) - { - } + private void handleTaikoHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); - private void handleCatchHitObject(TextWriter writer, HitObject hitObject) - { - } + private void handleCatchHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); - private void handleManiaHitObject(TextWriter writer, HitObject hitObject) - { - } + private void handleManiaHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); private string getSampleBank(IList samples, bool banksOnly = false, bool zeroBanks = false) { From 98dd1c2590796010060759b17cd1a444ab3f60b2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 17:03:58 +0900 Subject: [PATCH 52/95] Use SingleOrDefault() where possible --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index e600dc06b7..5c8d128d5a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -322,13 +322,13 @@ namespace osu.Game.Beatmaps.Formats private string getSampleBank(IList samples, bool banksOnly = false, bool zeroBanks = false) { - LegacySampleBank normalBank = toLegacySampleBank(samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank); + LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank); LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL)?.Bank); string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault()?.Suffix); string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty; - int volume = samples.First().Volume; + int volume = samples.FirstOrDefault()?.Volume ?? 100; StringBuilder sb = new StringBuilder(); From 9fa6954ac2f139171ec33b5b887b25eb4cb20d14 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 17:05:24 +0900 Subject: [PATCH 53/95] Refactor getSampleBank a bit --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 5c8d128d5a..9e643857f1 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -325,11 +325,6 @@ namespace osu.Game.Beatmaps.Formats LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank); LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL)?.Bank); - string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault()?.Suffix); - string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty; - - int volume = samples.FirstOrDefault()?.Volume ?? 100; - StringBuilder sb = new StringBuilder(); sb.Append(FormattableString.Invariant($"{(zeroBanks ? 0 : (int)normalBank)}:")); @@ -337,6 +332,10 @@ namespace osu.Game.Beatmaps.Formats if (!banksOnly) { + string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault()?.Suffix); + string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty; + int volume = samples.FirstOrDefault()?.Volume ?? 100; + sb.Append(":"); sb.Append(FormattableString.Invariant($"{customSampleBank}:")); sb.Append(FormattableString.Invariant($"{volume}:")); From 27150d6bbc484c14fd1466e3751c64149c35c53a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 17:06:52 +0900 Subject: [PATCH 54/95] Use char concatenation for performance --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 9e643857f1..65edd356d7 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -63,11 +63,11 @@ namespace osu.Game.Beatmaps.Formats 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 - writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? "1" : "0")}")); + writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? '1' : '0')}")); writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}")); writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); - writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? "1" : "0")}")); + writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}")); // if (beatmap.BeatmapInfo.UseSkinSprites) // writer.WriteLine(@"UseSkinSprites: 1"); // if (b.AlwaysShowPlayfield) @@ -81,8 +81,8 @@ namespace osu.Game.Beatmaps.Formats // if (b.CountdownOffset > 0) // writer.WriteLine(@"CountdownOffset: " + b.CountdownOffset.ToString()); if (beatmap.BeatmapInfo.RulesetID == 3) - writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? "1" : "0")}")); - writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? "1" : "0")}")); + writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? '1' : '0')}")); + writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}")); // if (b.SamplesMatchPlaybackRate) // writer.WriteLine(@"SamplesMatchPlaybackRate: 1"); } @@ -178,7 +178,7 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},")); writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample.Suffix)},")); writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},")); - writer.Write(FormattableString.Invariant($"{(timingPoint != null ? "1" : "0")},")); + writer.Write(FormattableString.Invariant($"{(timingPoint != null ? '1' : '0')},")); writer.Write(FormattableString.Invariant($"{(int)effectFlags}")); writer.Write("\n"); } From 5278236458c82cd6b0a0be6a207dbc3ce689af88 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 17:07:30 +0900 Subject: [PATCH 55/95] Use invariant ToLower() --- 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 65edd356d7..d55adfa756 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -372,7 +372,7 @@ namespace osu.Game.Beatmaps.Formats private LegacySampleBank toLegacySampleBank(string sampleBank) { - switch (sampleBank?.ToLower()) + switch (sampleBank?.ToLowerInvariant()) { case "normal": return LegacySampleBank.Normal; From 3c9884456f0111afb9e71c3ebeeb0860aaa31fae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 17:08:46 +0900 Subject: [PATCH 56/95] Use writeline --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index d55adfa756..139be3ce2f 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -180,7 +180,7 @@ namespace osu.Game.Beatmaps.Formats writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},")); writer.Write(FormattableString.Invariant($"{(timingPoint != null ? '1' : '0')},")); writer.Write(FormattableString.Invariant($"{(int)effectFlags}")); - writer.Write("\n"); + writer.WriteLine(); } } @@ -311,7 +311,7 @@ namespace osu.Game.Beatmaps.Formats ? getSampleBank(hitObject.Samples, zeroBanks: true) : getSampleBank(hitObject.Samples)); - writer.Write(Environment.NewLine); + writer.WriteLine(); } private void handleTaikoHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); From be7b00cc34e64d66b69550f0496f4c948dc62d5d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 17:12:31 +0900 Subject: [PATCH 57/95] Fix potentially incorrect custom sample bank --- 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 139be3ce2f..d38ff482ad 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -332,7 +332,7 @@ namespace osu.Game.Beatmaps.Formats if (!banksOnly) { - string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault()?.Suffix); + string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name))?.Suffix); string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty; int volume = samples.FirstOrDefault()?.Volume ?? 100; From 9de6b62fb136e7b7705858970b3b15c818e353de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 18:24:29 +0900 Subject: [PATCH 58/95] Fix nightcore beat not playing if song doesn't hit beat index 0 --- osu.Game/Rulesets/Mods/ModNightcore.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 1374c385cc..4d11b9de1d 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -55,7 +56,8 @@ namespace osu.Game.Rulesets.Mods private SkinnableSound clapSample; private SkinnableSound kickSample; private SkinnableSound finishSample; - private bool started; + + private int? firstBeat; [BackgroundDependencyLoader] private void load() @@ -73,10 +75,10 @@ namespace osu.Game.Rulesets.Mods { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - if (!started && beatIndex == 0) - started = true; + if (beatIndex < firstBeat || !firstBeat.HasValue) + firstBeat = Math.Max(0, beatIndex / 16 * 16); - if (started && beatIndex > -1) + if (beatIndex > firstBeat) { if (beatIndex % 16 == 0) finishSample?.Play(); From 3e0fda58ea59e10d6fac5ce75d70afc769478bd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 18:47:06 +0900 Subject: [PATCH 59/95] Play finish in addition to kick, not isolated --- osu.Game/Rulesets/Mods/ModNightcore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 4d11b9de1d..f4599ed88a 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -82,7 +82,8 @@ namespace osu.Game.Rulesets.Mods { if (beatIndex % 16 == 0) finishSample?.Play(); - else if (beatIndex % 2 == 0) + + if (beatIndex % 2 == 0) kickSample?.Play(); else if (beatIndex % 2 == 1) clapSample?.Play(); From c883c97bab8e81a3bd67db0ef5d4c7110b5d3161 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 18:50:58 +0900 Subject: [PATCH 60/95] Fix off-by-one starting bar --- osu.Game/Rulesets/Mods/ModNightcore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index f4599ed88a..1c46b82f79 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Mods base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); if (beatIndex < firstBeat || !firstBeat.HasValue) - firstBeat = Math.Max(0, beatIndex / 16 * 16); + firstBeat = Math.Max(0, (beatIndex / 16 + 1) * 16); if (beatIndex > firstBeat) { From 0f9ff32cdc8ee2528024e2c8a3dbf545af5916d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 18:51:22 +0900 Subject: [PATCH 61/95] Fix beat playing while paused --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 8 ++++++++ osu.Game/Rulesets/Mods/ModNightcore.cs | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 2e76ab964f..1374b34aca 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -42,6 +42,8 @@ namespace osu.Game.Graphics.Containers private EffectControlPoint defaultEffect; private TrackAmplitudes defaultAmplitudes; + protected bool IsBeatSyncedWithTrack { get; private set; } + protected override void Update() { Track track = null; @@ -65,10 +67,16 @@ namespace osu.Game.Graphics.Containers effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); if (timingPoint.BeatLength == 0) + { + IsBeatSyncedWithTrack = false; return; + } + + IsBeatSyncedWithTrack = true; } else { + IsBeatSyncedWithTrack = false; currentTrackTime = Clock.CurrentTime; timingPoint = defaultTiming; effectPoint = defaultEffect; diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 1c46b82f79..f1f25f70e6 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -75,7 +75,13 @@ namespace osu.Game.Rulesets.Mods { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - if (beatIndex < firstBeat || !firstBeat.HasValue) + if (!IsBeatSyncedWithTrack) + { + firstBeat = null; + return; + } + + if (!firstBeat.HasValue || beatIndex < firstBeat) firstBeat = Math.Max(0, (beatIndex / 16 + 1) * 16); if (beatIndex > firstBeat) From 210fecc95188b72cfc1d988fc48936872161ccf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 18:57:11 +0900 Subject: [PATCH 62/95] Fix incorrect hat logic; add support for first barline omission --- osu.Game/Rulesets/Mods/ModNightcore.cs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index f1f25f70e6..830cb87822 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -84,17 +84,25 @@ namespace osu.Game.Rulesets.Mods if (!firstBeat.HasValue || beatIndex < firstBeat) firstBeat = Math.Max(0, (beatIndex / 16 + 1) * 16); - if (beatIndex > firstBeat) + if (beatIndex >= firstBeat) { - if (beatIndex % 16 == 0) + if (beatIndex % 16 == 0 && (beatIndex > firstBeat || !effectPoint.OmitFirstBarLine)) finishSample?.Play(); - if (beatIndex % 2 == 0) - kickSample?.Play(); - else if (beatIndex % 2 == 1) - clapSample?.Play(); - else - hatSample?.Play(); + switch (beatIndex % (int)timingPoint.TimeSignature) + { + case 0: + kickSample?.Play(); + break; + + case 2: + clapSample?.Play(); + break; + + default: + hatSample?.Play(); + break; + } } } } From 72404bff9a7d24466bdc5ef761b44de981f119b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 19:16:54 +0900 Subject: [PATCH 63/95] Add support for simple triple time --- .../Containers/BeatSyncedContainer.cs | 15 +++-- osu.Game/Rulesets/Mods/ModNightcore.cs | 55 +++++++++++++++---- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 1374b34aca..a68e536a18 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -33,6 +33,11 @@ namespace osu.Game.Graphics.Containers /// public double TimeSinceLastBeat { get; private set; } + /// + /// How many baets per beatlength to trigger. Defaults to 1. + /// + public int Divisor { get; set; } = 1; + /// /// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing. /// @@ -82,17 +87,19 @@ namespace osu.Game.Graphics.Containers effectPoint = defaultEffect; } - int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); + double beatLength = timingPoint.BeatLength / Divisor; + + int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength); // The beats before the start of the first control point are off by 1, this should do the trick if (currentTrackTime < timingPoint.Time) beatIndex--; - TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength; + TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % beatLength; if (TimeUntilNextBeat < 0) - TimeUntilNextBeat += timingPoint.BeatLength; + TimeUntilNextBeat += beatLength; - TimeSinceLastBeat = timingPoint.BeatLength - TimeUntilNextBeat; + TimeSinceLastBeat = beatLength - TimeUntilNextBeat; if (timingPoint.Equals(lastTimingPoint) && beatIndex == lastBeat) return; diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 830cb87822..16ed3a5e31 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Objects; @@ -59,6 +60,11 @@ namespace osu.Game.Rulesets.Mods private int? firstBeat; + public NightcoreBeatContainer() + { + Divisor = 2; + } + [BackgroundDependencyLoader] private void load() { @@ -71,10 +77,15 @@ namespace osu.Game.Rulesets.Mods }; } + private const int segment_bar_length = 4; + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + int beatsPerBar = (int)timingPoint.TimeSignature; + int segmentLength = beatsPerBar * Divisor * segment_bar_length; + if (!IsBeatSyncedWithTrack) { firstBeat = null; @@ -82,25 +93,49 @@ namespace osu.Game.Rulesets.Mods } if (!firstBeat.HasValue || beatIndex < firstBeat) - firstBeat = Math.Max(0, (beatIndex / 16 + 1) * 16); + firstBeat = Math.Max(0, (beatIndex / segmentLength + 1) * segmentLength); if (beatIndex >= firstBeat) { - if (beatIndex % 16 == 0 && (beatIndex > firstBeat || !effectPoint.OmitFirstBarLine)) + if (beatIndex % segmentLength == 0 && (beatIndex > firstBeat || !effectPoint.OmitFirstBarLine)) finishSample?.Play(); - switch (beatIndex % (int)timingPoint.TimeSignature) + switch (timingPoint.TimeSignature) { - case 0: - kickSample?.Play(); + case TimeSignatures.SimpleTriple: + switch (beatIndex % 6) + { + case 0: + kickSample?.Play(); + break; + + case 3: + clapSample?.Play(); + break; + + default: + hatSample?.Play(); + break; + } + break; - case 2: - clapSample?.Play(); - break; + case TimeSignatures.SimpleQuadruple: + switch (beatIndex % 4) + { + case 0: + kickSample?.Play(); + break; + + case 2: + clapSample?.Play(); + break; + + default: + hatSample?.Play(); + break; + } - default: - hatSample?.Play(); break; } } From cfd811112061663c00edf15096273da32a1f5102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 19:40:43 +0900 Subject: [PATCH 64/95] Better initial beat handling --- osu.Game/Rulesets/Mods/ModNightcore.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 16ed3a5e31..abbeca3150 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -93,7 +92,7 @@ namespace osu.Game.Rulesets.Mods } if (!firstBeat.HasValue || beatIndex < firstBeat) - firstBeat = Math.Max(0, (beatIndex / segmentLength + 1) * segmentLength); + firstBeat = beatIndex < 0 ? 0 : (beatIndex / segmentLength + 1) * segmentLength; if (beatIndex >= firstBeat) { From 7d979e12649ef7ea4346a7c535ebd2ace3202f14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 19:41:04 +0900 Subject: [PATCH 65/95] Add finalised test scene --- .../TestSceneNightcoreBeatContainer.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneNightcoreBeatContainer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneNightcoreBeatContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneNightcoreBeatContainer.cs new file mode 100644 index 0000000000..3473b03eaf --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneNightcoreBeatContainer.cs @@ -0,0 +1,38 @@ +// 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.Extensions.IEnumerableExtensions; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Tests.Visual.UserInterface; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneNightcoreBeatContainer : TestSceneBeatSyncedContainer + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ModNightcore<>) + }; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + + Beatmap.Value.Track.Start(); + Beatmap.Value.Track.Seek(Beatmap.Value.Beatmap.HitObjects.First().StartTime - 1000); + + Add(new ModNightcore.NightcoreBeatContainer()); + + AddStep("change signature to quadruple", () => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ForEach(p => p.TimeSignature = TimeSignatures.SimpleQuadruple)); + AddStep("change signature to triple", () => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ForEach(p => p.TimeSignature = TimeSignatures.SimpleTriple)); + } + } +} From 438d97f4f56ac84058745dafd809b5fa9198ab18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 19:42:28 +0900 Subject: [PATCH 66/95] Rename variable for clarity --- osu.Game/Rulesets/Mods/ModNightcore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index abbeca3150..16902cc792 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -76,14 +76,14 @@ namespace osu.Game.Rulesets.Mods }; } - private const int segment_bar_length = 4; + private const int bars_per_segment = 4; protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); int beatsPerBar = (int)timingPoint.TimeSignature; - int segmentLength = beatsPerBar * Divisor * segment_bar_length; + int segmentLength = beatsPerBar * Divisor * bars_per_segment; if (!IsBeatSyncedWithTrack) { From 795416c066437f9b5f1cc44898c68c429b2059ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Dec 2019 12:06:09 +0900 Subject: [PATCH 67/95] Move first beat offset to BeatSyncedContainer --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index a68e536a18..b04d01004a 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -89,7 +89,7 @@ namespace osu.Game.Graphics.Containers double beatLength = timingPoint.BeatLength / Divisor; - int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength); + int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (effectPoint.OmitFirstBarLine ? 1 : 0); // The beats before the start of the first control point are off by 1, this should do the trick if (currentTrackTime < timingPoint.Time) From 87035f8251965b81d5a5ecb572a6fd803fd97837 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Dec 2019 12:12:15 +0900 Subject: [PATCH 68/95] Simplify complex method --- osu.Game/Rulesets/Mods/ModNightcore.cs | 72 ++++++++++++++------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 16902cc792..a8c79bb896 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -92,51 +92,55 @@ namespace osu.Game.Rulesets.Mods } if (!firstBeat.HasValue || beatIndex < firstBeat) + // decide on a good starting beat index if once has not yet been decided. firstBeat = beatIndex < 0 ? 0 : (beatIndex / segmentLength + 1) * segmentLength; if (beatIndex >= firstBeat) + playBeatFor(beatIndex % segmentLength, timingPoint.TimeSignature); + } + + private void playBeatFor(int beatIndex, TimeSignatures signature) + { + if (beatIndex == 0) + finishSample?.Play(); + + switch (signature) { - if (beatIndex % segmentLength == 0 && (beatIndex > firstBeat || !effectPoint.OmitFirstBarLine)) - finishSample?.Play(); + case TimeSignatures.SimpleTriple: + switch (beatIndex % 6) + { + case 0: + kickSample?.Play(); + break; - switch (timingPoint.TimeSignature) - { - case TimeSignatures.SimpleTriple: - switch (beatIndex % 6) - { - case 0: - kickSample?.Play(); - break; + case 3: + clapSample?.Play(); + break; - case 3: - clapSample?.Play(); - break; + default: + hatSample?.Play(); + break; + } - default: - hatSample?.Play(); - break; - } + break; - break; + case TimeSignatures.SimpleQuadruple: + switch (beatIndex % 4) + { + case 0: + kickSample?.Play(); + break; - case TimeSignatures.SimpleQuadruple: - switch (beatIndex % 4) - { - case 0: - kickSample?.Play(); - break; + case 2: + clapSample?.Play(); + break; - case 2: - clapSample?.Play(); - break; + default: + hatSample?.Play(); + break; + } - default: - hatSample?.Play(); - break; - } - - break; - } + break; } } } From 0a278ef9432d06ab6ae6f94be332296c5eba9e6f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 14 Dec 2019 11:40:59 +0100 Subject: [PATCH 69/95] Apply review suggestions --- osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs | 2 +- osu.Game/Overlays/News/NewsArticleCover.cs | 6 ++---- osu.Game/Overlays/NewsOverlay.cs | 8 ++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index f870a12fc3..a8e1f40ecf 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Online private class TestNewsOverlay : NewsOverlay { - public new void LoadAndShowChild(NewsContent content) => base.LoadAndShowChild(content); + public new void LoadAndShowChild(NewsContent content) => base.LoadAndShowContent(content); } private class NewsCoverTest : NewsContent diff --git a/osu.Game/Overlays/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs index e2485bd170..e484309a18 100644 --- a/osu.Game/Overlays/News/NewsArticleCover.cs +++ b/osu.Game/Overlays/News/NewsArticleCover.cs @@ -90,7 +90,6 @@ namespace osu.Game.Overlays.News bg.OnLoadComplete += d => d.FadeIn(250, Easing.In); } - //news article cover background [LongRunningLoad] private class NewsBackground : Sprite { @@ -108,7 +107,6 @@ namespace osu.Game.Overlays.News } } - //date container private class DateContainer : Container, IHasTooltip { private readonly DateTime date; @@ -134,7 +132,7 @@ namespace osu.Game.Overlays.News Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(Typeface.Exo, 12, FontWeight.Black, false, false), - Text = date.ToString("dd MMM yyy"), + Text = date.ToString("d MMM yyy").ToUpper(), Margin = new MarginPadding { Vertical = 4, @@ -144,7 +142,7 @@ namespace osu.Game.Overlays.News }; } - public string TooltipText => date.ToString("dddd dd MMMM yyyy hh:mm:ss UTCz"); + public string TooltipText => date.ToString("dddd dd MMMM yyyy hh:mm:ss UTCz").ToUpper(); } //fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index db4b118bf8..e7471cb21d 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -59,19 +59,19 @@ namespace osu.Game.Overlays Current.TriggerChange(); } - private CancellationTokenSource loadChildCancellation; + private CancellationTokenSource loadContentCancellation; - protected void LoadAndShowChild(NewsContent newContent) + protected void LoadAndShowContent(NewsContent newContent) { content.FadeTo(0.2f, 300, Easing.OutQuint); - loadChildCancellation?.Cancel(); + loadContentCancellation?.Cancel(); LoadComponentAsync(newContent, c => { content.Child = c; content.FadeIn(300, Easing.OutQuint); - }, (loadChildCancellation = new CancellationTokenSource()).Token); + }, (loadContentCancellation = new CancellationTokenSource()).Token); } public void ShowFrontPage() From ad7923f9b9edf87dfd6bd1acedaa44fad9f234be Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 17 Dec 2019 19:25:17 +0100 Subject: [PATCH 70/95] Fix test methods not being renamed. --- osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index a8e1f40ecf..d47c972564 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -22,12 +22,12 @@ namespace osu.Game.Tests.Visual.Online AddStep(@"Show front page", () => news.ShowFrontPage()); AddStep(@"Custom article", () => news.Current.Value = "Test Article 101"); - AddStep(@"Article covers", () => news.LoadAndShowChild(new NewsCoverTest())); + AddStep(@"Article covers", () => news.LoadAndShowContent(new NewsCoverTest())); } private class TestNewsOverlay : NewsOverlay { - public new void LoadAndShowChild(NewsContent content) => base.LoadAndShowContent(content); + public new void LoadAndShowContent(NewsContent content) => base.LoadAndShowContent(content); } private class NewsCoverTest : NewsContent From 7e58b4a948c1d4fb3b1b50e4df01b315917afe77 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Dec 2019 03:03:12 +0300 Subject: [PATCH 71/95] Early-return on potential division by zero in SPM calculation --- .../Objects/Drawables/Pieces/SpinnerSpmCounter.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs index b1d90c49f6..97a7b98c5b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -62,6 +63,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void SetRotation(float currentRotation) { + // Never calculate SPM by same time of record to avoid 0 / 0 = NaN or X / 0 = Infinity result. + if (Precision.AlmostEquals(0, Time.Elapsed)) + return; + // If we've gone back in time, it's fine to work with a fresh set of records for now if (records.Count > 0 && Time.Current < records.Last().Time) records.Clear(); @@ -71,6 +76,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces var record = records.Peek(); while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration) record = records.Dequeue(); + SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360; } From cbfbbf9999be5dfb60a36ebad95c9d29f292b8f8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Dec 2019 03:04:37 +0300 Subject: [PATCH 72/95] Make SpmCounter public --- .../Objects/Drawables/DrawableSpinner.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 1261d3d19a..de11ab6419 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public readonly SpinnerDisc Disc; public readonly SpinnerTicks Ticks; - private readonly SpinnerSpmCounter spmCounter; + public readonly SpinnerSpmCounter SpmCounter; private readonly Container mainContainer; @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, } }, - spmCounter = new SpinnerSpmCounter + SpmCounter = new SpinnerSpmCounter { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -177,8 +177,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void Update() { Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false; - if (!spmCounter.IsPresent && Disc.Tracking) - spmCounter.FadeIn(HitObject.TimeFadeIn); + if (!SpmCounter.IsPresent && Disc.Tracking) + SpmCounter.FadeIn(HitObject.TimeFadeIn); base.Update(); } @@ -189,7 +189,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables circle.Rotation = Disc.Rotation; Ticks.Rotation = Disc.Rotation; - spmCounter.SetRotation(Disc.RotationAbsolute); + SpmCounter.SetRotation(Disc.RotationAbsolute); float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint); From 12a02cf6d927e583c6cba57142beda24f7bef041 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Dec 2019 03:07:06 +0300 Subject: [PATCH 73/95] Extend spinner duration a bit To allow the times sought to be in the spinner time range --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index d0ce0c33c2..ea42251a69 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Tests new Spinner { Position = new Vector2(256, 192), - EndTime = 5000, + EndTime = 6000, }, // placeholder object to avoid hitting the results screen new HitObject From f359a79b7ec5d12b9ec8aa2b71e31c86039ab2ad Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Dec 2019 03:08:05 +0300 Subject: [PATCH 74/95] Add test ensuring correct SPM calculation on rewinding --- .../TestSceneSpinnerRotation.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index ea42251a69..2eda555018 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -70,6 +70,19 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("is rotation absolute almost same", () => Precision.AlmostEquals(drawableSpinner.Disc.RotationAbsolute, estimatedRotation, 100)); } + [Test] + public void TestSpinPerMinuteOnRewind() + { + double estimatedSpm = 0; + + addSeekStep(2500); + AddStep("retrieve spm", () => estimatedSpm = drawableSpinner.SpmCounter.SpinsPerMinute); + + addSeekStep(5000); + addSeekStep(2500); + AddAssert("is spm almost same", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0)); + } + private void addSeekStep(double time) { AddStep($"seek to {time}", () => track.Seek(time)); From 0f5ef78b69c552545e5cc7dfc9b6a91bd2afafb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 12:39:50 +0900 Subject: [PATCH 75/95] Update client id --- osu.Desktop/DiscordRichPresenceClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index af5b42b275..7a661fe6a2 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -16,7 +16,7 @@ namespace osu.Desktop { internal class DiscordRichPresenceClient : Component { - private const string client_id = "563024054391537674"; + private const string client_id = "367827983903490050"; private Bindable user; From 1fe0e45a9cf4e3c1de1deab229aadce365008d26 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Dec 2019 13:37:37 +0900 Subject: [PATCH 76/95] Implement legacy slider border shadow --- .../Objects/Drawables/Pieces/PlaySliderBody.cs | 7 +------ osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs index aa9caf193e..cedf2f6e09 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs @@ -24,16 +24,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private OsuRulesetConfigManager config { get; set; } private Slider slider; - private float defaultPathRadius; [BackgroundDependencyLoader] private void load(ISkinSource skin) { slider = (Slider)drawableObject.HitObject; - defaultPathRadius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; scaleBindable = slider.ScaleBindable.GetBoundCopy(); - scaleBindable.BindValueChanged(_ => updatePathRadius(), true); + scaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true); pathVersion = slider.Path.Version.GetBoundCopy(); pathVersion.BindValueChanged(_ => Refresh()); @@ -48,9 +46,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces BorderColour = skin.GetConfig(OsuSkinColour.SliderBorder)?.Value ?? Color4.White; } - private void updatePathRadius() - => PathRadius = defaultPathRadius * scaleBindable.Value; - private void updateAccentColour(ISkinSource skin, Color4 defaultAccentColour) => AccentColour = skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? defaultAccentColour; } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs index dea08f843e..8fc07d2c9d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs @@ -15,19 +15,27 @@ namespace osu.Game.Rulesets.Osu.Skinning private class LegacyDrawableSliderPath : DrawableSliderPath { + private const float shadow_portion = 0.06f; + public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f); protected override Color4 ColourAt(float position) { - if (CalculatedBorderPortion != 0f && position <= CalculatedBorderPortion) + float realBorderPortion = shadow_portion + CalculatedBorderPortion; + float realGradientPortion = 1 - realBorderPortion; + + if (position <= shadow_portion) + return new Color4(0f, 0f, 0f, 0.25f * position / shadow_portion); + + if (position <= realBorderPortion) return BorderColour; - position -= BORDER_PORTION; + position -= realBorderPortion; Color4 outerColour = AccentColour.Darken(0.1f); Color4 innerColour = lighten(AccentColour, 0.5f); - return Interpolation.ValueAt(position / GRADIENT_PORTION, outerColour, innerColour, 0, 1); + return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1); } /// From 756d847ad8c848ac24800fd4436ef31fbf261d59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:03 +0900 Subject: [PATCH 77/95] Fix user not getting an initial status --- osu.Game/Online/API/APIAccess.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 8bfc28e774..23c931d161 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -153,6 +153,10 @@ namespace osu.Game.Online.API userReq.Success += u => { LocalUser.Value = u; + + // todo: save/pull from settings + LocalUser.Value.Status.Value = new UserStatusOnline(); + failureCount = 0; //we're connected! From 2f5b27e97c11d122be488e2dbb4ff5dee349e4e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:12 +0900 Subject: [PATCH 78/95] Make user bindables readonly --- osu.Game/Users/User.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index ebd9dbecd1..5d0ffd5a67 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -26,9 +26,9 @@ namespace osu.Game.Users [JsonProperty(@"country")] public Country Country; - public Bindable Status = new Bindable(); + public readonly Bindable Status = new Bindable(); - public IBindable Activity = new Bindable(); + public readonly Bindable Activity = new Bindable(); //public Team Team; From f53fd6e4bcfce9b22eb692c223bdf0cb30332e09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:21 +0900 Subject: [PATCH 79/95] Fix status capitalisation --- osu.Game/Users/UserActivity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index 918c547978..8030fc55a2 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -62,7 +62,7 @@ namespace osu.Game.Users public class InLobby : UserActivity { - public override string Status => @"In a Multiplayer Lobby"; + public override string Status => @"In a multiplayer lobby"; } } } From 0a3d339dd91a6d18f23e1bf7097aec50fe8a65a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:32 +0900 Subject: [PATCH 80/95] Load discord RPC asynchronously --- osu.Desktop/OsuGameDesktop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 9f2a4b12a1..f70cc24159 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -61,7 +61,7 @@ namespace osu.Desktop Add(new SimpleUpdateManager()); } - Add(new DiscordRichPresenceClient()); + LoadComponentAsync(new DiscordRichPresence(), Add); } protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen) From b65847b0d76bd3fffb629176e0a33cdb34764c40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:53 +0900 Subject: [PATCH 81/95] Refactor / rewrite discord code --- osu.Desktop/DiscordRichPresence.cs | 120 +++++++++++++++++++++++ osu.Desktop/DiscordRichPresenceClient.cs | 104 -------------------- 2 files changed, 120 insertions(+), 104 deletions(-) create mode 100644 osu.Desktop/DiscordRichPresence.cs delete mode 100644 osu.Desktop/DiscordRichPresenceClient.cs diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs new file mode 100644 index 0000000000..d1bd8fd292 --- /dev/null +++ b/osu.Desktop/DiscordRichPresence.cs @@ -0,0 +1,120 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using DiscordRPC; +using DiscordRPC.Message; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Logging; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Users; +using LogLevel = osu.Framework.Logging.LogLevel; +using User = osu.Game.Users.User; + +namespace osu.Desktop +{ + internal class DiscordRichPresence : Component + { + private const string client_id = "367827983903490050"; + + private DiscordRpcClient client; + + [Resolved] + private IBindable ruleset { get; set; } + + private Bindable user; + + private readonly IBindable status = new Bindable(); + private readonly IBindable activity = new Bindable(); + + private readonly RichPresence presence = new RichPresence + { + Assets = new Assets { LargeImageKey = "osu_logo_lazer", } + }; + + [BackgroundDependencyLoader] + private void load(IAPIProvider provider) + { + client = new DiscordRpcClient(client_id) + { + SkipIdenticalPresence = false // handles better on discord IPC loss, see updateStatus call in onReady. + }; + + client.OnReady += onReady; + client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network, LogLevel.Error); + client.OnConnectionFailed += (_, e) => Logger.Log($"An connection occurred with Discord RPC Client: {e.Type}", LoggingTarget.Network, LogLevel.Error); + + (user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u => + { + status.UnbindBindings(); + status.BindTo(u.NewValue.Status); + + activity.UnbindBindings(); + activity.BindTo(u.NewValue.Activity); + }, true); + + ruleset.BindValueChanged(_ => updateStatus()); + status.BindValueChanged(_ => updateStatus()); + activity.BindValueChanged(_ => updateStatus()); + + client.Initialize(); + } + + private void onReady(object _, ReadyMessage __) + { + Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); + updateStatus(); + } + + private void updateStatus() + { + if (status.Value is UserStatusOffline) + { + client.ClearPresence(); + return; + } + + if (status.Value is UserStatusOnline && activity.Value != null) + { + presence.State = activity.Value.Status; + presence.Details = getDetails(activity.Value); + } + else + { + presence.State = "Idle"; + presence.Details = string.Empty; + } + + // update user information + presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); + + // update ruleset + presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_unknown"; + presence.Assets.SmallImageText = ruleset.Value.Name; + + client.SetPresence(presence); + } + + private string getDetails(UserActivity activity) + { + switch (activity) + { + case UserActivity.SoloGame solo: + return solo.Beatmap.ToString(); + + case UserActivity.Editing edit: + return edit.Beatmap.ToString(); + } + + return string.Empty; + } + + protected override void Dispose(bool isDisposing) + { + client.Dispose(); + base.Dispose(isDisposing); + } + } +} diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs deleted file mode 100644 index 7a661fe6a2..0000000000 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ /dev/null @@ -1,104 +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 DiscordRPC; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Logging; -using osu.Game.Online.API; -using osu.Game.Rulesets; -using osu.Game.Users; -using static osu.Game.Users.UserActivity; -using User = osu.Game.Users.User; - -namespace osu.Desktop -{ - internal class DiscordRichPresenceClient : Component - { - private const string client_id = "367827983903490050"; - - private Bindable user; - - private readonly DiscordRpcClient client = new DiscordRpcClient(client_id); - - [BackgroundDependencyLoader] - private void load(IAPIProvider provider) - { - user = provider.LocalUser.GetBoundCopy(); - - user.ValueChanged += usr => - { - usr.NewValue.Activity.ValueChanged += activity => updateStatus(user.Value.Status.Value, activity.NewValue); - usr.NewValue.Status.ValueChanged += status => updateStatus(status.NewValue, user.Value.Activity.Value); - }; - - user.TriggerChange(); - - enableLogging(); - - client.Initialize(); - } - - private void enableLogging() - { - client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); - client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); - client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); - client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); - } - - private void updateStatus(UserStatus st, UserActivity a) - { - var presence = defaultPresence(st is UserStatusOnline ? a?.Status ?? st.Message : st.Message); //display the current user activity if the user status is online & user activity != null, else display the current user online status - - if (!(st is UserStatusOnline)) //don't update the presence any further if the current user status is DND / Offline & simply return with the default presence - { - client.SetPresence(presence); - return; - } - - switch (a) - { - case SoloGame game: - presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; - setPresenceGamemode(game.Ruleset, presence); - break; - - case Editing editing: - presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); - presence.Assets.SmallImageKey = "edit"; - presence.Assets.SmallImageText = "editing"; - break; - } - - client.SetPresence(presence); - } - - private void setPresenceGamemode(RulesetInfo ruleset, RichPresence presence) - { - if (ruleset.ID != null && ruleset.ID <= 3) //legacy rulesets use an ID between 0 and 3 - presence.Assets.SmallImageKey = ruleset.ShortName; - else - presence.Assets.SmallImageKey = "unknown"; //not a legacy ruleset so let's display the unknown ruleset icon. - - presence.Assets.SmallImageText = ruleset.ShortName; - } - - private RichPresence defaultPresence(string status) => new RichPresence - { - Details = status, - Assets = new Assets - { - LargeImageKey = "lazer", - LargeImageText = user.Value.Username - } - }; - - protected override void Dispose(bool isDisposing) - { - client.Dispose(); - base.Dispose(isDisposing); - } - } -} From 0710e5ba13749212b158f008ecc9c2b5987b3ca8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:35:18 +0900 Subject: [PATCH 82/95] Rename unknown mode assets (discord dev page broken) --- osu.Desktop/DiscordRichPresence.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index d1bd8fd292..b53ca6161b 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -91,7 +91,7 @@ namespace osu.Desktop presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); // update ruleset - presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_unknown"; + presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom"; presence.Assets.SmallImageText = ruleset.Value.Name; client.SetPresence(presence); From 6e0802e50cfdfe0cb1010a64ca1f1a1fbaff7dba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:49:09 +0900 Subject: [PATCH 83/95] Remove RulesetInfo parameter from Ruleset constructor --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 5 ----- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 5 ----- osu.Game.Rulesets.Osu/OsuRuleset.cs | 5 ----- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 5 ----- .../Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs | 5 ----- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 7 +------ osu.Game/Rulesets/Ruleset.cs | 4 ++-- osu.Game/Rulesets/RulesetInfo.cs | 2 +- osu.Game/Rulesets/RulesetStore.cs | 4 ++-- 9 files changed, 6 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 065771bc4a..b8a844cb86 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -137,10 +137,5 @@ namespace osu.Game.Rulesets.Catch public override int? LegacyID => 2; public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); - - public CatchRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 8b53ce01f6..bf630cf892 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -186,11 +186,6 @@ namespace osu.Game.Rulesets.Mania public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this); - public ManiaRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } - public override IEnumerable AvailableVariants { get diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 835ae2564c..27af615935 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -183,10 +183,5 @@ namespace osu.Game.Rulesets.Osu public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo); - - public OsuRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } } } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 4d6c5fa1c0..ca7ab30867 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -136,10 +136,5 @@ namespace osu.Game.Rulesets.Taiko public override int? LegacyID => 1; public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); - - public TaikoRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index c958932730..ae20bbc86d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -194,11 +194,6 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestScrollingRuleset : Ruleset { - public TestScrollingRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } - public override IEnumerable GetModsFor(ModType type) => throw new NotImplementedException(); public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new TestDrawableScrollingRuleset(this, beatmap, mods); diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 9ea254b23f..46efe38d37 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -51,7 +51,7 @@ namespace osu.Game.Beatmaps private class DummyRulesetInfo : RulesetInfo { - public override Ruleset CreateInstance() => new DummyRuleset(this); + public override Ruleset CreateInstance() => new DummyRuleset(); private class DummyRuleset : Ruleset { @@ -70,11 +70,6 @@ namespace osu.Game.Beatmaps public override string ShortName => "dummy"; - public DummyRuleset(RulesetInfo rulesetInfo = null) - : base(rulesetInfo) - { - } - private class DummyBeatmapConverter : IBeatmapConverter { public event Action> ObjectConverted; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 6f5fe066aa..7ad93379f0 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -49,9 +49,9 @@ namespace osu.Game.Rulesets public virtual ISkin CreateLegacySkinProvider(ISkinSource source) => null; - protected Ruleset(RulesetInfo rulesetInfo = null) + protected Ruleset() { - RulesetInfo = rulesetInfo ?? createRulesetInfo(); + RulesetInfo = createRulesetInfo(); } /// diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 6a69fd8dd0..d695e0b56d 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets { if (!Available) return null; - return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); + return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo)); } public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 7d13afe9e5..5d0c5c7ccf 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets { var context = usage.Context; - var instances = loadedAssemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, (RulesetInfo)null)).ToList(); + var instances = loadedAssemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r)).ToList(); //add all legacy modes in correct order foreach (var r in instances.Where(r => r.LegacyID != null).OrderBy(r => r.LegacyID)) @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets // this allows for debug builds to successfully load rulesets (even though debug rulesets have a 0.0.0 version). asm.Version = null; return Assembly.Load(asm); - }, null), (RulesetInfo)null)).RulesetInfo; + }, null))).RulesetInfo; r.Name = instanceInfo.Name; r.ShortName = instanceInfo.ShortName; From 826b271371188a64d9e9e6660fbe2d23af254ca6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 15:39:36 +0900 Subject: [PATCH 84/95] Use exact ratio of legacy to default object size --- osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs | 3 ++- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs index 8fc07d2c9d..d41135ca69 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.MathUtils; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osuTK.Graphics; @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Skinning private class LegacyDrawableSliderPath : DrawableSliderPath { - private const float shadow_portion = 0.06f; + private const float shadow_portion = 1 - (OsuLegacySkinTransformer.LEGACY_CIRCLE_RADIUS / OsuHitObject.OBJECT_RADIUS); public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f); diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 71770dedce..266b619334 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Skinning /// Their hittable area is 128px, but the actual circle portion is 118px. /// We must account for some gameplay elements such as slider bodies, where this padding is not present. /// - private const float legacy_circle_radius = 64 - 5; + public const float LEGACY_CIRCLE_RADIUS = 64 - 5; public OsuLegacySkinTransformer(ISkinSource source) { @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { case OsuSkinConfiguration.SliderPathRadius: if (hasHitCircle.Value) - return SkinUtils.As(new BindableFloat(legacy_circle_radius)); + return SkinUtils.As(new BindableFloat(LEGACY_CIRCLE_RADIUS)); break; } From d4a4efb734f8799337525c767d01e3825083d897 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 16:07:14 +0900 Subject: [PATCH 85/95] Tidy up test --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 2eda555018..02ce77e707 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -79,8 +79,10 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("retrieve spm", () => estimatedSpm = drawableSpinner.SpmCounter.SpinsPerMinute); addSeekStep(5000); + AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0)); + addSeekStep(2500); - AddAssert("is spm almost same", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0)); + AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0)); } private void addSeekStep(double time) From 54572b6de9e685e96168ff36263103437684ede9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 16:42:18 +0900 Subject: [PATCH 86/95] Update selected mdos references --- .../Visual/SongSelect/TestSceneBeatmapDetailArea.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs index d4805a73e4..66144cbfe4 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetailArea.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.SongSelect Position = new Vector2(0, 25), }); - modDisplay.Current.BindTo(Mods); + modDisplay.Current.BindTo(SelectedMods); AddStep("all metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap { @@ -204,7 +204,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) }; + SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) }; }); AddStep("with HR mod", () => @@ -230,7 +230,7 @@ namespace osu.Game.Tests.Visual.SongSelect } }); - Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) }; + SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) }; }); } } From e87aa281bfde25de44342ceed4ac21c7309c1c14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 17:00:35 +0900 Subject: [PATCH 87/95] Don't clone beatmap unnecessarily --- .../Screens/Select/Details/AdvancedStats.cs | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 3e7d4e244b..529d63b475 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -68,41 +68,39 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { - BeatmapInfo processed = Beatmap?.Clone(); + var baseDifficulty = Beatmap?.BaseDifficulty; + var adjustedDifficulty = baseDifficulty; - if (processed != null && mods.Value.Any(m => m is IApplicableToDifficulty)) + if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty)) { - processed.BaseDifficulty = processed.BaseDifficulty.Clone(); + adjustedDifficulty = adjustedDifficulty?.Clone(); foreach (var mod in mods.Value.OfType()) - mod.ApplyToDifficulty(processed.BaseDifficulty); + mod.ApplyToDifficulty(adjustedDifficulty); } - BeatmapDifficulty baseDifficulty = Beatmap?.BaseDifficulty; - BeatmapDifficulty moddedDifficulty = processed?.BaseDifficulty; - //mania specific - if ((processed?.Ruleset?.ID ?? 0) == 3) + if ((Beatmap?.Ruleset?.ID ?? 0) == 3) { firstValue.Title = "Key Amount"; firstValue.BaseValue = (int)MathF.Round(baseDifficulty?.CircleSize ?? 0); - firstValue.ModdedValue = (int)MathF.Round(moddedDifficulty?.CircleSize ?? 0); + firstValue.ModdedValue = (int)MathF.Round(adjustedDifficulty?.CircleSize ?? 0); } else { firstValue.Title = "Circle Size"; firstValue.BaseValue = baseDifficulty?.CircleSize ?? 0; - firstValue.ModdedValue = moddedDifficulty?.CircleSize ?? 0; + firstValue.ModdedValue = adjustedDifficulty?.CircleSize ?? 0; } hpDrain.BaseValue = baseDifficulty?.DrainRate ?? 0; accuracy.BaseValue = baseDifficulty?.OverallDifficulty ?? 0; approachRate.BaseValue = baseDifficulty?.ApproachRate ?? 0; - starDifficulty.BaseValue = (float)(processed?.StarDifficulty ?? 0); + starDifficulty.BaseValue = (float)(Beatmap?.StarDifficulty ?? 0); - hpDrain.ModdedValue = moddedDifficulty?.DrainRate ?? 0; - accuracy.ModdedValue = moddedDifficulty?.OverallDifficulty ?? 0; - approachRate.ModdedValue = moddedDifficulty?.ApproachRate ?? 0; + hpDrain.ModdedValue = adjustedDifficulty?.DrainRate ?? 0; + accuracy.ModdedValue = adjustedDifficulty?.OverallDifficulty ?? 0; + approachRate.ModdedValue = adjustedDifficulty?.ApproachRate ?? 0; } private class StatisticRow : Container, IHasAccentColour From ada2ae2b2c9e279b4ab4922be136c42e87d7205b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 17:12:41 +0900 Subject: [PATCH 88/95] Use tuple to avoid potential for incorrect display --- .../Screens/Select/Details/AdvancedStats.cs | 66 ++++++++----------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 529d63b475..f684238a38 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -68,12 +68,12 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { - var baseDifficulty = Beatmap?.BaseDifficulty; - var adjustedDifficulty = baseDifficulty; + BeatmapDifficulty baseDifficulty = Beatmap?.BaseDifficulty; + BeatmapDifficulty adjustedDifficulty = null; if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty)) { - adjustedDifficulty = adjustedDifficulty?.Clone(); + adjustedDifficulty = baseDifficulty.Clone(); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(adjustedDifficulty); @@ -83,24 +83,19 @@ namespace osu.Game.Screens.Select.Details if ((Beatmap?.Ruleset?.ID ?? 0) == 3) { firstValue.Title = "Key Amount"; - firstValue.BaseValue = (int)MathF.Round(baseDifficulty?.CircleSize ?? 0); - firstValue.ModdedValue = (int)MathF.Round(adjustedDifficulty?.CircleSize ?? 0); + firstValue.Value = ((int)MathF.Round(baseDifficulty?.CircleSize ?? 0), (int)MathF.Round(adjustedDifficulty?.CircleSize ?? 0)); } else { firstValue.Title = "Circle Size"; - firstValue.BaseValue = baseDifficulty?.CircleSize ?? 0; - firstValue.ModdedValue = adjustedDifficulty?.CircleSize ?? 0; + firstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); } - hpDrain.BaseValue = baseDifficulty?.DrainRate ?? 0; - accuracy.BaseValue = baseDifficulty?.OverallDifficulty ?? 0; - approachRate.BaseValue = baseDifficulty?.ApproachRate ?? 0; - starDifficulty.BaseValue = (float)(Beatmap?.StarDifficulty ?? 0); + starDifficulty.Value = ((float)(Beatmap?.StarDifficulty ?? 0), null); - hpDrain.ModdedValue = adjustedDifficulty?.DrainRate ?? 0; - accuracy.ModdedValue = adjustedDifficulty?.OverallDifficulty ?? 0; - approachRate.ModdedValue = adjustedDifficulty?.ApproachRate ?? 0; + hpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate); + accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty); + approachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate); } private class StatisticRow : Container, IHasAccentColour @@ -110,7 +105,7 @@ namespace osu.Game.Screens.Select.Details private readonly float maxValue; private readonly bool forceDecimalPlaces; - private readonly OsuSpriteText name, value; + private readonly OsuSpriteText name, valueText; private readonly Bar bar, modBar; [Resolved] @@ -122,36 +117,29 @@ namespace osu.Game.Screens.Select.Details set => name.Text = value; } - private float baseValue; + private (float baseValue, float? adjustedValue) value; - private float moddedValue; - - public float BaseValue + public (float baseValue, float? adjustedValue) Value { - get => baseValue; + get => value; set { - baseValue = value; - bar.Length = value / maxValue; - this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##"); - } - } + if (value == this.value) + return; - public float ModdedValue - { - get => moddedValue; - set - { - moddedValue = value; - modBar.Length = value / maxValue; - this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##"); + this.value = value; - if (moddedValue > baseValue) - modBar.AccentColour = this.value.Colour = colours.Red; - else if (moddedValue < baseValue) - modBar.AccentColour = this.value.Colour = colours.BlueDark; + bar.Length = value.baseValue / maxValue; + + valueText.Text = (value.adjustedValue ?? value.baseValue).ToString(forceDecimalPlaces ? "0.00" : "0.##"); + modBar.Length = (value.adjustedValue ?? 0) / maxValue; + + if (value.adjustedValue > value.baseValue) + modBar.AccentColour = valueText.Colour = colours.Red; + else if (value.adjustedValue < value.baseValue) + modBar.AccentColour = valueText.Colour = colours.BlueDark; else - modBar.AccentColour = this.value.Colour = Color4.White; + modBar.AccentColour = valueText.Colour = Color4.White; } } @@ -203,7 +191,7 @@ namespace osu.Game.Screens.Select.Details Origin = Anchor.TopRight, Width = value_width, RelativeSizeAxes = Axes.Y, - Child = value = new OsuSpriteText + Child = valueText = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 602ce698d5633a3b655682a1b94d952a4adc99ee Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Dec 2019 17:21:38 +0900 Subject: [PATCH 89/95] Fix storyboard vectorscale and scale cross-pollution --- .../Formats/LegacyStoryboardDecoder.cs | 4 +-- osu.Game/Storyboards/CommandTimelineGroup.cs | 3 ++- .../Drawables/DrawableStoryboardAnimation.cs | 27 +++++++++++++++++-- .../Drawables/DrawableStoryboardSprite.cs | 27 +++++++++++++++++-- osu.Game/Storyboards/Drawables/IFlippable.cs | 8 +++--- .../Storyboards/Drawables/IVectorScalable.cs | 21 +++++++++++++++ osu.Game/Storyboards/StoryboardSprite.cs | 20 ++++++++++---- 7 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Storyboards/Drawables/IVectorScalable.cs diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index ccd46ab559..756aa45eed 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -176,7 +176,7 @@ namespace osu.Game.Beatmaps.Formats { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); + timelineGroup?.Scale.Add(easing, startTime, endTime, startValue, endValue); break; } @@ -186,7 +186,7 @@ namespace osu.Game.Beatmaps.Formats var startY = float.Parse(split[5], CultureInfo.InvariantCulture); var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); + timelineGroup?.VectorScale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); break; } diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs index 364c971874..7b6e667d4f 100644 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ b/osu.Game/Storyboards/CommandTimelineGroup.cs @@ -16,7 +16,8 @@ namespace osu.Game.Storyboards { public CommandTimeline X = new CommandTimeline(); public CommandTimeline Y = new CommandTimeline(); - public CommandTimeline Scale = new CommandTimeline(); + public CommandTimeline Scale = new CommandTimeline(); + public CommandTimeline VectorScale = new CommandTimeline(); public CommandTimeline Rotation = new CommandTimeline(); public CommandTimeline Colour = new CommandTimeline(); public CommandTimeline Alpha = new CommandTimeline(); diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 4f8e39fa1b..a452c7540d 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -8,21 +8,44 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Textures; +using osu.Framework.MathUtils; using osu.Game.Beatmaps; namespace osu.Game.Storyboards.Drawables { - public class DrawableStoryboardAnimation : TextureAnimation, IFlippable + public class DrawableStoryboardAnimation : TextureAnimation, IFlippable, IVectorScalable { public StoryboardAnimation Animation { get; private set; } public bool FlipH { get; set; } public bool FlipV { get; set; } + private Vector2 vectorScale = Vector2.One; + + public Vector2 VectorScale + { + get => vectorScale; + set + { + if (Math.Abs(value.X) < Precision.FLOAT_EPSILON) + value.X = Precision.FLOAT_EPSILON; + if (Math.Abs(value.Y) < Precision.FLOAT_EPSILON) + value.Y = Precision.FLOAT_EPSILON; + + if (vectorScale == value) + return; + + if (!Validation.IsFinite(value)) throw new ArgumentException($@"{nameof(VectorScale)} must be finite, but is {value}."); + + vectorScale = value; + Invalidate(Invalidation.MiscGeometry); + } + } + public override bool RemoveWhenNotAlive => false; protected override Vector2 DrawScale - => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y); + => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y) * VectorScale; public override Anchor Origin { diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index ff48dab7e5..42c7cfacb2 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -8,21 +8,44 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.MathUtils; using osu.Game.Beatmaps; namespace osu.Game.Storyboards.Drawables { - public class DrawableStoryboardSprite : Sprite, IFlippable + public class DrawableStoryboardSprite : Sprite, IFlippable, IVectorScalable { public StoryboardSprite Sprite { get; private set; } public bool FlipH { get; set; } public bool FlipV { get; set; } + private Vector2 vectorScale = Vector2.One; + + public Vector2 VectorScale + { + get => vectorScale; + set + { + if (Math.Abs(value.X) < Precision.FLOAT_EPSILON) + value.X = Precision.FLOAT_EPSILON; + if (Math.Abs(value.Y) < Precision.FLOAT_EPSILON) + value.Y = Precision.FLOAT_EPSILON; + + if (vectorScale == value) + return; + + if (!Validation.IsFinite(value)) throw new ArgumentException($@"{nameof(VectorScale)} must be finite, but is {value}."); + + vectorScale = value; + Invalidate(Invalidation.MiscGeometry); + } + } + public override bool RemoveWhenNotAlive => false; protected override Vector2 DrawScale - => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y); + => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y) * VectorScale; public override Anchor Origin { diff --git a/osu.Game/Storyboards/Drawables/IFlippable.cs b/osu.Game/Storyboards/Drawables/IFlippable.cs index 1c4cdde22d..165b3d97cc 100644 --- a/osu.Game/Storyboards/Drawables/IFlippable.cs +++ b/osu.Game/Storyboards/Drawables/IFlippable.cs @@ -6,13 +6,13 @@ using osu.Framework.Graphics.Transforms; namespace osu.Game.Storyboards.Drawables { - public interface IFlippable : ITransformable + internal interface IFlippable : ITransformable { bool FlipH { get; set; } bool FlipV { get; set; } } - public class TransformFlipH : Transform + internal class TransformFlipH : Transform { private bool valueAt(double time) => time < EndTime ? StartValue : EndValue; @@ -23,7 +23,7 @@ namespace osu.Game.Storyboards.Drawables protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipH; } - public class TransformFlipV : Transform + internal class TransformFlipV : Transform { private bool valueAt(double time) => time < EndTime ? StartValue : EndValue; @@ -34,7 +34,7 @@ namespace osu.Game.Storyboards.Drawables protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipV; } - public static class FlippableExtensions + internal static class FlippableExtensions { /// /// Adjusts after a delay. diff --git a/osu.Game/Storyboards/Drawables/IVectorScalable.cs b/osu.Game/Storyboards/Drawables/IVectorScalable.cs new file mode 100644 index 0000000000..fcc407d460 --- /dev/null +++ b/osu.Game/Storyboards/Drawables/IVectorScalable.cs @@ -0,0 +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 osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osuTK; + +namespace osu.Game.Storyboards.Drawables +{ + internal interface IVectorScalable : ITransformable + { + Vector2 VectorScale { get; set; } + } + + internal static class VectorScalableExtensions + { + public static TransformSequence VectorScaleTo(this T target, Vector2 newVectorScale, double duration = 0, Easing easing = Easing.None) + where T : class, IVectorScalable + => target.TransformTo(nameof(IVectorScalable.VectorScale), newVectorScale, duration, easing); + } +} diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index d5e69fd103..abf9f58804 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -65,20 +65,30 @@ namespace osu.Game.Storyboards { applyCommands(drawable, getCommands(g => g.X, triggeredGroups), (d, value) => d.X = value, (d, value, duration, easing) => d.MoveToX(value, duration, easing)); applyCommands(drawable, getCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value, (d, value, duration, easing) => d.MoveToY(value, duration, easing)); - applyCommands(drawable, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = value, (d, value, duration, easing) => d.ScaleTo(value, duration, easing)); + applyCommands(drawable, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = new Vector2(value), (d, value, duration, easing) => d.ScaleTo(value, duration, easing)); applyCommands(drawable, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value, (d, value, duration, easing) => d.RotateTo(value, duration, easing)); applyCommands(drawable, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value, (d, value, duration, easing) => d.FadeColour(value, duration, easing)); applyCommands(drawable, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value, (d, value, duration, easing) => d.FadeTo(value, duration, easing)); - applyCommands(drawable, getCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), false); + applyCommands(drawable, getCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), + false); + + if (drawable is IVectorScalable vectorScalable) + { + applyCommands(drawable, getCommands(g => g.VectorScale, triggeredGroups), (d, value) => vectorScalable.VectorScale = value, + (d, value, duration, easing) => vectorScalable.VectorScaleTo(value, duration, easing)); + } if (drawable is IFlippable flippable) { - applyCommands(drawable, getCommands(g => g.FlipH, triggeredGroups), (d, value) => flippable.FlipH = value, (d, value, duration, easing) => flippable.TransformFlipH(value, duration), false); - applyCommands(drawable, getCommands(g => g.FlipV, triggeredGroups), (d, value) => flippable.FlipV = value, (d, value, duration, easing) => flippable.TransformFlipV(value, duration), false); + applyCommands(drawable, getCommands(g => g.FlipH, triggeredGroups), (d, value) => flippable.FlipH = value, (d, value, duration, easing) => flippable.TransformFlipH(value, duration), + false); + applyCommands(drawable, getCommands(g => g.FlipV, triggeredGroups), (d, value) => flippable.FlipV = value, (d, value, duration, easing) => flippable.TransformFlipV(value, duration), + false); } } - private void applyCommands(Drawable drawable, IEnumerable.TypedCommand> commands, DrawablePropertyInitializer initializeProperty, DrawableTransformer transform, bool alwaysInitialize = true) + private void applyCommands(Drawable drawable, IEnumerable.TypedCommand> commands, DrawablePropertyInitializer initializeProperty, DrawableTransformer transform, + bool alwaysInitialize = true) where T : struct { var initialized = false; From 5aca523d353f46881df089c698275fdb9344d90e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Dec 2019 17:27:13 +0900 Subject: [PATCH 90/95] Fix storyboard flipping potentially not having an effect --- .../Drawables/DrawableStoryboardAnimation.cs | 31 +++++++++++++++++-- .../Drawables/DrawableStoryboardSprite.cs | 31 +++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 4f8e39fa1b..66c6d179b8 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -16,8 +16,35 @@ namespace osu.Game.Storyboards.Drawables { public StoryboardAnimation Animation { get; private set; } - public bool FlipH { get; set; } - public bool FlipV { get; set; } + private bool flipH; + + public bool FlipH + { + get => flipH; + set + { + if (flipH == value) + return; + + flipH = value; + Invalidate(Invalidation.MiscGeometry); + } + } + + private bool flipV; + + public bool FlipV + { + get => flipV; + set + { + if (flipV == value) + return; + + flipV = value; + Invalidate(Invalidation.MiscGeometry); + } + } public override bool RemoveWhenNotAlive => false; diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index ff48dab7e5..aa60096661 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -16,8 +16,35 @@ namespace osu.Game.Storyboards.Drawables { public StoryboardSprite Sprite { get; private set; } - public bool FlipH { get; set; } - public bool FlipV { get; set; } + private bool flipH; + + public bool FlipH + { + get => flipH; + set + { + if (flipH == value) + return; + + flipH = value; + Invalidate(Invalidation.MiscGeometry); + } + } + + private bool flipV; + + public bool FlipV + { + get => flipV; + set + { + if (flipV == value) + return; + + flipV = value; + Invalidate(Invalidation.MiscGeometry); + } + } public override bool RemoveWhenNotAlive => false; From 4befabc2573072c06130ff21903e93d94dbf6f69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 17:35:51 +0900 Subject: [PATCH 91/95] Split out complex method --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 180 ++++++++++-------- 1 file changed, 99 insertions(+), 81 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index d38ff482ad..8d16278f60 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -217,26 +217,12 @@ namespace osu.Game.Beatmaps.Formats private void handleOsuHitObject(TextWriter writer, HitObject hitObject) { - var positionData = hitObject as IHasPosition; - var comboData = hitObject as IHasCombo; - - Debug.Assert(positionData != null); - Debug.Assert(comboData != null); - - LegacyHitObjectType hitObjectType = (LegacyHitObjectType)(comboData.ComboOffset << 4); - if (comboData.NewCombo) - hitObjectType |= LegacyHitObjectType.NewCombo; - if (hitObject is IHasCurve) - hitObjectType |= LegacyHitObjectType.Slider; - else if (hitObject is IHasEndTime) - hitObjectType |= LegacyHitObjectType.Spinner | LegacyHitObjectType.NewCombo; - else - hitObjectType |= LegacyHitObjectType.Circle; + var positionData = (IHasPosition)hitObject; writer.Write(FormattableString.Invariant($"{positionData.X},")); writer.Write(FormattableString.Invariant($"{positionData.Y},")); writer.Write(FormattableString.Invariant($"{hitObject.StartTime},")); - writer.Write(FormattableString.Invariant($"{(int)hitObjectType},")); + writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},")); writer.Write(hitObject is IHasCurve ? FormattableString.Invariant($"0,") @@ -244,76 +230,108 @@ namespace osu.Game.Beatmaps.Formats if (hitObject is IHasCurve curveData) { - PathType? lastType = null; - - for (int i = 0; i < curveData.Path.ControlPoints.Count; i++) - { - PathControlPoint point = curveData.Path.ControlPoints[i]; - - if (point.Type.Value != null) - { - if (point.Type.Value != lastType) - { - switch (point.Type.Value) - { - case PathType.Bezier: - writer.Write("B|"); - break; - - case PathType.Catmull: - writer.Write("C|"); - break; - - case PathType.PerfectCurve: - writer.Write("P|"); - break; - - case PathType.Linear: - writer.Write("L|"); - break; - } - - lastType = point.Type.Value; - } - else - { - // New segment with the same type - duplicate the control point - writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}|")); - } - } - - if (i != 0) - { - writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}")); - writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); - } - } - - writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},")); - writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},")); - - for (int i = 0; i < curveData.NodeSamples.Count; i++) - { - writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}")); - writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); - } - - for (int i = 0; i < curveData.NodeSamples.Count; i++) - { - writer.Write(getSampleBank(curveData.NodeSamples[i], true)); - writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); - } + addCurveData(writer, curveData, positionData); + writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true)); + } + else + { + if (hitObject is IHasEndTime endTimeData) + writer.Write(FormattableString.Invariant($"{endTimeData.EndTime},")); + writer.Write(getSampleBank(hitObject.Samples)); } - else if (hitObject is IHasEndTime endTimeData) - writer.Write(FormattableString.Invariant($"{endTimeData.EndTime},")); - - writer.Write(hitObject is IHasCurve - ? getSampleBank(hitObject.Samples, zeroBanks: true) - : getSampleBank(hitObject.Samples)); writer.WriteLine(); } + private static LegacyHitObjectType getObjectType(HitObject hitObject) + { + var comboData = (IHasCombo)hitObject; + + var type = (LegacyHitObjectType)(comboData.ComboOffset << 4); + + if (comboData.NewCombo) type |= LegacyHitObjectType.NewCombo; + + switch (hitObject) + { + case IHasCurve _: + type |= LegacyHitObjectType.Slider; + break; + + case IHasEndTime _: + type |= LegacyHitObjectType.Spinner | LegacyHitObjectType.NewCombo; + break; + + default: + type |= LegacyHitObjectType.Circle; + break; + } + + return type; + } + + private void addCurveData(TextWriter writer, IHasCurve curveData, IHasPosition positionData) + { + PathType? lastType = null; + + for (int i = 0; i < curveData.Path.ControlPoints.Count; i++) + { + PathControlPoint point = curveData.Path.ControlPoints[i]; + + if (point.Type.Value != null) + { + if (point.Type.Value != lastType) + { + switch (point.Type.Value) + { + case PathType.Bezier: + writer.Write("B|"); + break; + + case PathType.Catmull: + writer.Write("C|"); + break; + + case PathType.PerfectCurve: + writer.Write("P|"); + break; + + case PathType.Linear: + writer.Write("L|"); + break; + } + + lastType = point.Type.Value; + } + else + { + // New segment with the same type - duplicate the control point + writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}|")); + } + } + + if (i != 0) + { + writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}")); + writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); + } + } + + writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},")); + writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},")); + + for (int i = 0; i < curveData.NodeSamples.Count; i++) + { + writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}")); + writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + } + + for (int i = 0; i < curveData.NodeSamples.Count; i++) + { + writer.Write(getSampleBank(curveData.NodeSamples[i], true)); + writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ","); + } + } + private void handleTaikoHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); private void handleCatchHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException(); From 8e651962c7d7c5dbe98bc8e801db9a3514745e7a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Dec 2019 17:41:30 +0900 Subject: [PATCH 92/95] Fix incorrectly binding inside BDL load() --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f684238a38..9c9c33274f 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -63,7 +63,13 @@ namespace osu.Game.Screens.Select.Details private void load(OsuColour colours) { starDifficulty.AccentColour = colours.Yellow; - mods.ValueChanged += _ => updateStatistics(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + mods.BindValueChanged(_ => updateStatistics(), true); } private void updateStatistics() From bf85f4affb1eca46513b17dd2442b40df78cce32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 17:53:26 +0900 Subject: [PATCH 93/95] Fix editor crashing when loading a beatmap for an unsupported ruleset --- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 6984716a2c..5d9757778d 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -32,6 +32,6 @@ namespace osu.Game.Screens.Edit.Compose return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer)); } - protected override Drawable CreateTimelineContent() => new TimelineHitObjectDisplay(composer.EditorBeatmap); + protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineHitObjectDisplay(composer.EditorBeatmap); } } From d65e37d7959fe4e89a6091c53b805059dbe73935 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Dec 2019 17:58:29 +0900 Subject: [PATCH 94/95] Fix typo --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index b04d01004a..b9ef279f5c 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Graphics.Containers public double TimeSinceLastBeat { get; private set; } /// - /// How many baets per beatlength to trigger. Defaults to 1. + /// How many beats per beatlength to trigger. Defaults to 1. /// public int Divisor { get; set; } = 1; From 1f3e1b308590409c6c09c33a91d62c88f3664962 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 17:59:07 +0900 Subject: [PATCH 95/95] Remove unused using --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 8d16278f60..433becd8cc 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Text;