From f98e96e45b22b3b34e56543f2249d43e62585d5f Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Sun, 16 Aug 2020 10:52:23 +0930 Subject: [PATCH 01/68] Add osu!-specific enum for confine mouse mode --- osu.Game/Input/OsuConfineMouseMode.cs | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 osu.Game/Input/OsuConfineMouseMode.cs diff --git a/osu.Game/Input/OsuConfineMouseMode.cs b/osu.Game/Input/OsuConfineMouseMode.cs new file mode 100644 index 0000000000..32b456395c --- /dev/null +++ b/osu.Game/Input/OsuConfineMouseMode.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.ComponentModel; +using osu.Framework.Input; + +namespace osu.Game.Input +{ + /// + /// Determines the situations in which the mouse cursor should be confined to the window. + /// Expands upon by providing the option to confine during gameplay. + /// + public enum OsuConfineMouseMode + { + /// + /// The mouse cursor will be free to move outside the game window. + /// + Never, + + /// + /// The mouse cursor will be locked to the window bounds while in fullscreen mode. + /// + Fullscreen, + + /// + /// The mouse cursor will be locked to the window bounds during gameplay, + /// but may otherwise move freely. + /// + [Description("During Gameplay")] + DuringGameplay, + + /// + /// The mouse cursor will always be locked to the window bounds while the game has focus. + /// + Always + } +} From 322d179076a383cf7fd2e7506e27189fba025278 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Sun, 16 Aug 2020 11:04:28 +0930 Subject: [PATCH 02/68] Replace settings item with osu! confine cursor mode --- osu.Game/Configuration/OsuConfigManager.cs | 3 +++ osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index a8a8794320..9ef846c974 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -6,6 +6,7 @@ using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Extensions; using osu.Framework.Platform; +using osu.Game.Input; using osu.Game.Overlays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select; @@ -66,6 +67,7 @@ namespace osu.Game.Configuration Set(OsuSetting.MouseDisableButtons, false); Set(OsuSetting.MouseDisableWheel, false); + Set(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); // Graphics Set(OsuSetting.ShowFpsDisplay, false); @@ -191,6 +193,7 @@ namespace osu.Game.Configuration FadePlayfieldWhenHealthLow, MouseDisableButtons, MouseDisableWheel, + ConfineMouseMode, AudioOffset, VolumeInactive, MenuMusic, diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index d27ab63fb7..0d98508e3b 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -6,9 +6,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Input; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; +using osu.Game.Input; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -47,10 +47,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Map absolute input to window", Bindable = config.GetBindable(FrameworkSetting.MapAbsoluteInputToWindow) }, - new SettingsEnumDropdown + new SettingsEnumDropdown { LabelText = "Confine mouse cursor to window", - Bindable = config.GetBindable(FrameworkSetting.ConfineMouseMode), + Bindable = osuConfig.GetBindable(OsuSetting.ConfineMouseMode) }, new SettingsCheckbox { From ef3c8fa21f8105ec181be6392bd65c929a597f40 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Sun, 16 Aug 2020 11:38:35 +0930 Subject: [PATCH 03/68] Add tracking component to handle OsuConfineMouseMode --- osu.Game/Input/ConfineMouseTracker.cs | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 osu.Game/Input/ConfineMouseTracker.cs diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs new file mode 100644 index 0000000000..b111488a5b --- /dev/null +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Game.Configuration; +using osu.Game.Screens.Play; + +namespace osu.Game.Input +{ + /// + /// Connects with + /// while providing a property for to indicate whether gameplay is currently active. + /// + public class ConfineMouseTracker : Component + { + private Bindable frameworkConfineMode; + private Bindable osuConfineMode; + + private bool gameplayActive; + + /// + /// Indicates whether osu! is currently considered "in gameplay" for the + /// purposes of . + /// + public bool GameplayActive + { + get => gameplayActive; + set + { + if (gameplayActive == value) + return; + + gameplayActive = value; + updateConfineMode(); + } + } + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) + { + frameworkConfineMode = frameworkConfigManager.GetBindable(FrameworkSetting.ConfineMouseMode); + osuConfineMode = osuConfigManager.GetBindable(OsuSetting.ConfineMouseMode); + osuConfineMode.BindValueChanged(_ => updateConfineMode(), true); + } + + private void updateConfineMode() + { + switch (osuConfineMode.Value) + { + case OsuConfineMouseMode.Never: + frameworkConfineMode.Value = ConfineMouseMode.Never; + break; + + case OsuConfineMouseMode.Fullscreen: + frameworkConfineMode.Value = ConfineMouseMode.Fullscreen; + break; + + case OsuConfineMouseMode.DuringGameplay: + frameworkConfineMode.Value = GameplayActive ? ConfineMouseMode.Always : ConfineMouseMode.Never; + break; + + case OsuConfineMouseMode.Always: + frameworkConfineMode.Value = ConfineMouseMode.Always; + break; + } + } + } +} From 00f15231bc78a6e7830694bf41cbc1db331e505f Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Sun, 16 Aug 2020 21:52:39 +0930 Subject: [PATCH 04/68] Cache ConfineMouseTracker --- osu.Game/OsuGame.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 053eb01dcd..7358918758 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -88,6 +88,8 @@ namespace osu.Game private IdleTracker idleTracker; + private ConfineMouseTracker confineMouseTracker; + public readonly Bindable OverlayActivationMode = new Bindable(); protected OsuScreenStack ScreenStack; @@ -553,6 +555,7 @@ namespace osu.Game BackButton.Receptor receptor; dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); + dependencies.Cache(confineMouseTracker = new ConfineMouseTracker()); AddRange(new Drawable[] { @@ -588,7 +591,8 @@ namespace osu.Game rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, topMostOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, - idleTracker + idleTracker, + confineMouseTracker }); ScreenStack.ScreenPushed += screenPushed; From 85b3fff9c8a695d89453b0dbd1b55eb0d27fe5e4 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Sun, 16 Aug 2020 23:11:09 +0930 Subject: [PATCH 05/68] Update mouse confine when gameplay state changes --- osu.Game/Screens/Play/Player.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 541275cf55..3b8c4aea01 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -18,6 +18,7 @@ using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; +using osu.Game.Input; using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Overlays; @@ -63,6 +64,9 @@ namespace osu.Game.Screens.Play private Bindable mouseWheelDisabled; + [Resolved(CanBeNull = true)] + private ConfineMouseTracker confineMouseTracker { get; set; } + private readonly Bindable storyboardReplacesBackground = new Bindable(); public int RestartCount; @@ -197,10 +201,15 @@ namespace osu.Game.Screens.Play skipOverlay.Hide(); } - DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); + DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => + { + updatePauseOnFocusLostState(); + updateConfineMouse(); + }, true); // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); + DrawableRuleset.IsPaused.ValueChanged += _ => updateConfineMouse(); DrawableRuleset.OnNewResult += r => { @@ -346,6 +355,12 @@ namespace osu.Game.Screens.Play && !DrawableRuleset.HasReplayLoaded.Value && !breakTracker.IsBreakTime.Value; + private void updateConfineMouse() + { + if (confineMouseTracker != null) + confineMouseTracker.GameplayActive = !GameplayClockContainer.IsPaused.Value && !DrawableRuleset.HasReplayLoaded.Value && !HasFailed; + } + private IBeatmap loadPlayableBeatmap() { IBeatmap playable; @@ -379,7 +394,7 @@ namespace osu.Game.Screens.Play } catch (Exception e) { - Logger.Error(e, "Could not load beatmap sucessfully!"); + Logger.Error(e, "Could not load beatmap successfully!"); //couldn't load, hard abort! return null; } From 5859755886ac3e141e00e72a421bf61d19d6524e Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Mon, 5 Oct 2020 11:11:46 +1030 Subject: [PATCH 06/68] Use current OverlayActivationMode to determine confine logic --- osu.Game/Input/ConfineMouseTracker.cs | 33 +++++++++++---------------- osu.Game/Screens/Play/Player.cs | 5 ++-- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs index b111488a5b..6565967d1d 100644 --- a/osu.Game/Input/ConfineMouseTracker.cs +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -7,44 +7,37 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Configuration; +using osu.Game.Overlays; using osu.Game.Screens.Play; namespace osu.Game.Input { /// - /// Connects with - /// while providing a property for to indicate whether gameplay is currently active. + /// Connects with , + /// while optionally binding an mode, usually that of the current . + /// It is assumed that while overlay activation is , we should also confine the + /// mouse cursor if it has been requested with . /// public class ConfineMouseTracker : Component { private Bindable frameworkConfineMode; private Bindable osuConfineMode; - private bool gameplayActive; - /// - /// Indicates whether osu! is currently considered "in gameplay" for the - /// purposes of . + /// The bindable used to indicate whether gameplay is active. + /// Should be bound to the corresponding bindable of the current . + /// Defaults to to assume that all other screens are considered "not gameplay". /// - public bool GameplayActive - { - get => gameplayActive; - set - { - if (gameplayActive == value) - return; - - gameplayActive = value; - updateConfineMode(); - } - } + public IBindable OverlayActivationMode { get; } = new Bindable(OverlayActivation.All); [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) { frameworkConfineMode = frameworkConfigManager.GetBindable(FrameworkSetting.ConfineMouseMode); osuConfineMode = osuConfigManager.GetBindable(OsuSetting.ConfineMouseMode); - osuConfineMode.BindValueChanged(_ => updateConfineMode(), true); + osuConfineMode.ValueChanged += _ => updateConfineMode(); + + OverlayActivationMode.BindValueChanged(_ => updateConfineMode(), true); } private void updateConfineMode() @@ -60,7 +53,7 @@ namespace osu.Game.Input break; case OsuConfineMouseMode.DuringGameplay: - frameworkConfineMode.Value = GameplayActive ? ConfineMouseMode.Always : ConfineMouseMode.Never; + frameworkConfineMode.Value = OverlayActivationMode.Value == OverlayActivation.Disabled ? ConfineMouseMode.Always : ConfineMouseMode.Never; break; case OsuConfineMouseMode.Always: diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 77f873083a..6d2f61bdf1 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -229,6 +229,8 @@ namespace osu.Game.Screens.Play DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); + confineMouseTracker.OverlayActivationMode.BindTo(OverlayActivationMode); + // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); @@ -365,9 +367,6 @@ namespace osu.Game.Screens.Play OverlayActivationMode.Value = OverlayActivation.UserTriggered; else OverlayActivationMode.Value = OverlayActivation.Disabled; - - if (confineMouseTracker != null) - confineMouseTracker.GameplayActive = !GameplayClockContainer.IsPaused.Value && !DrawableRuleset.HasReplayLoaded.Value && !HasFailed; } private void updatePauseOnFocusLostState() => From a483dfd2d7157131d886d8b7c92a4b08defdbf63 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Mon, 5 Oct 2020 11:54:39 +1030 Subject: [PATCH 07/68] Allow confineMouseTracker to be null --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 6d2f61bdf1..de67b2d46d 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Play DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); - confineMouseTracker.OverlayActivationMode.BindTo(OverlayActivationMode); + confineMouseTracker?.OverlayActivationMode.BindTo(OverlayActivationMode); // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); From 22b0105d629a70344609d33314a76978053e633d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 13:00:02 +0900 Subject: [PATCH 08/68] Show a notification if checking for updates via button and there are none available --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 8 ++++--- .../Sections/General/UpdateSettings.cs | 21 +++++++++++++++++-- osu.Game/Updater/SimpleUpdateManager.cs | 7 ++++++- osu.Game/Updater/UpdateManager.cs | 19 ++++++++++------- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 05c8e835ac..b9b148b383 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -37,9 +37,9 @@ namespace osu.Desktop.Updater Splat.Locator.CurrentMutable.Register(() => new SquirrelLogger(), typeof(Splat.ILogger)); } - protected override async Task PerformUpdateCheck() => await checkForUpdateAsync(); + protected override async Task PerformUpdateCheck() => await checkForUpdateAsync(); - private async Task checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) + private async Task checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) { // should we schedule a retry on completion of this check? bool scheduleRecheck = true; @@ -51,7 +51,7 @@ namespace osu.Desktop.Updater var info = await updateManager.CheckForUpdate(!useDeltaPatching); if (info.ReleasesToApply.Count == 0) // no updates available. bail and retry later. - return; + return false; if (notification == null) { @@ -103,6 +103,8 @@ namespace osu.Desktop.Updater Scheduler.AddDelayed(async () => await checkForUpdateAsync(), 60000 * 30); } } + + return true; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 9c7d0b0be4..9b7b7392d8 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -4,9 +4,11 @@ using System.Threading.Tasks; using osu.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Configuration; +using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Settings.Sections.Maintenance; using osu.Game.Updater; @@ -21,6 +23,9 @@ namespace osu.Game.Overlays.Settings.Sections.General private SettingsButton checkForUpdatesButton; + [Resolved] + private NotificationOverlay notifications { get; set; } + [BackgroundDependencyLoader(true)] private void load(Storage storage, OsuConfigManager config, OsuGame game) { @@ -30,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.General Bindable = config.GetBindable(OsuSetting.ReleaseStream), }); - if (updateManager?.CanCheckForUpdate == true) + //if (updateManager?.CanCheckForUpdate == true) { Add(checkForUpdatesButton = new SettingsButton { @@ -38,7 +43,19 @@ namespace osu.Game.Overlays.Settings.Sections.General Action = () => { checkForUpdatesButton.Enabled.Value = false; - Task.Run(updateManager.CheckForUpdateAsync).ContinueWith(t => Schedule(() => checkForUpdatesButton.Enabled.Value = true)); + Task.Run(updateManager.CheckForUpdateAsync).ContinueWith(t => Schedule(() => + { + if (!t.Result) + { + notifications.Post(new SimpleNotification + { + Text = $"You are running the latest release ({game.Version})", + Icon = FontAwesome.Solid.CheckCircle, + }); + } + + checkForUpdatesButton.Enabled.Value = true; + })); } }); } diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs index ebb9995c66..79b2d46b93 100644 --- a/osu.Game/Updater/SimpleUpdateManager.cs +++ b/osu.Game/Updater/SimpleUpdateManager.cs @@ -30,7 +30,7 @@ namespace osu.Game.Updater version = game.Version; } - protected override async Task PerformUpdateCheck() + protected override async Task PerformUpdateCheck() { try { @@ -53,12 +53,17 @@ namespace osu.Game.Updater return true; } }); + + return true; } } catch { // we shouldn't crash on a web failure. or any failure for the matter. + return true; } + + return false; } private string getBestUrl(GitHubRelease release) diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 61775a26b7..30e28f0e95 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -57,25 +57,28 @@ namespace osu.Game.Updater private readonly object updateTaskLock = new object(); - private Task updateCheckTask; + private Task updateCheckTask; - public async Task CheckForUpdateAsync() + public async Task CheckForUpdateAsync() { - if (!CanCheckForUpdate) - return; - - Task waitTask; + Task waitTask; lock (updateTaskLock) waitTask = (updateCheckTask ??= PerformUpdateCheck()); - await waitTask; + bool hasUpdates = await waitTask; lock (updateTaskLock) updateCheckTask = null; + + return hasUpdates; } - protected virtual Task PerformUpdateCheck() => Task.CompletedTask; + /// + /// Performs an asynchronous check for application updates. + /// + /// Whether any update is waiting. May return true if an error occured (there is potentially an update available). + protected virtual Task PerformUpdateCheck() => Task.FromResult(false); private class UpdateCompleteNotification : SimpleNotification { From de47392e3d11ddf7e7831c029ac4c5bf353007d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 13:18:42 +0900 Subject: [PATCH 09/68] Display the "restart to update" notification on checking for update after dismissal --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 50 ++++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index b9b148b383..71f9fafe57 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -29,6 +29,11 @@ namespace osu.Desktop.Updater private static readonly Logger logger = Logger.GetLogger("updater"); + /// + /// Whether an update has been downloaded but not yet applied. + /// + private bool updatePending; + [BackgroundDependencyLoader] private void load(NotificationOverlay notification) { @@ -49,9 +54,19 @@ namespace osu.Desktop.Updater updateManager ??= await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); var info = await updateManager.CheckForUpdate(!useDeltaPatching); + if (info.ReleasesToApply.Count == 0) + { + if (updatePending) + { + // the user may have dismissed the completion notice, so show it again. + notificationOverlay.Post(new UpdateCompleteNotification(this)); + return true; + } + // no updates available. bail and retry later. return false; + } if (notification == null) { @@ -72,6 +87,7 @@ namespace osu.Desktop.Updater await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f); notification.State = ProgressNotificationState.Completed; + updatePending = true; } catch (Exception e) { @@ -113,10 +129,27 @@ namespace osu.Desktop.Updater updateManager?.Dispose(); } + private class UpdateCompleteNotification : ProgressCompletionNotification + { + [Resolved] + private OsuGame game { get; set; } + + public UpdateCompleteNotification(SquirrelUpdateManager updateManager) + { + Text = @"Update ready to install. Click to restart!"; + + Activated = () => + { + updateManager.PrepareUpdateAsync() + .ContinueWith(_ => updateManager.Schedule(() => game.GracefullyExit())); + return true; + }; + } + } + private class UpdateProgressNotification : ProgressNotification { private readonly SquirrelUpdateManager updateManager; - private OsuGame game; public UpdateProgressNotification(SquirrelUpdateManager updateManager) { @@ -125,23 +158,12 @@ namespace osu.Desktop.Updater protected override Notification CreateCompletionNotification() { - return new ProgressCompletionNotification - { - Text = @"Update ready to install. Click to restart!", - Activated = () => - { - updateManager.PrepareUpdateAsync() - .ContinueWith(_ => updateManager.Schedule(() => game.GracefullyExit())); - return true; - } - }; + return new UpdateCompleteNotification(updateManager); } [BackgroundDependencyLoader] - private void load(OsuColour colours, OsuGame game) + private void load(OsuColour colours) { - this.game = game; - IconContent.AddRange(new Drawable[] { new Box From 1877312a917ce017b5fa8e58951a4c373cb7cecb Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Tue, 6 Oct 2020 19:52:18 +1030 Subject: [PATCH 10/68] Rename DuringGameplay --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Input/ConfineMouseTracker.cs | 4 ++-- osu.Game/Input/OsuConfineMouseMode.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 78179a781a..71cbdb345c 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -70,7 +70,7 @@ namespace osu.Game.Configuration Set(OsuSetting.MouseDisableButtons, false); Set(OsuSetting.MouseDisableWheel, false); - Set(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); + Set(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.WhenOverlaysDisabled); // Graphics Set(OsuSetting.ShowFpsDisplay, false); diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs index 6565967d1d..653622e881 100644 --- a/osu.Game/Input/ConfineMouseTracker.cs +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -16,7 +16,7 @@ namespace osu.Game.Input /// Connects with , /// while optionally binding an mode, usually that of the current . /// It is assumed that while overlay activation is , we should also confine the - /// mouse cursor if it has been requested with . + /// mouse cursor if it has been requested with . /// public class ConfineMouseTracker : Component { @@ -52,7 +52,7 @@ namespace osu.Game.Input frameworkConfineMode.Value = ConfineMouseMode.Fullscreen; break; - case OsuConfineMouseMode.DuringGameplay: + case OsuConfineMouseMode.WhenOverlaysDisabled: frameworkConfineMode.Value = OverlayActivationMode.Value == OverlayActivation.Disabled ? ConfineMouseMode.Always : ConfineMouseMode.Never; break; diff --git a/osu.Game/Input/OsuConfineMouseMode.cs b/osu.Game/Input/OsuConfineMouseMode.cs index 32b456395c..53e352c8bd 100644 --- a/osu.Game/Input/OsuConfineMouseMode.cs +++ b/osu.Game/Input/OsuConfineMouseMode.cs @@ -27,7 +27,7 @@ namespace osu.Game.Input /// but may otherwise move freely. /// [Description("During Gameplay")] - DuringGameplay, + WhenOverlaysDisabled, /// /// The mouse cursor will always be locked to the window bounds while the game has focus. From 782fc1d60fe1c10768ef393ba3102a77e3237662 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Tue, 6 Oct 2020 20:11:48 +1030 Subject: [PATCH 11/68] Use OsuGame.OverlayActivationMode rather than per-Player --- osu.Game/Input/ConfineMouseTracker.cs | 19 ++++++------------- osu.Game/Input/OsuConfineMouseMode.cs | 2 +- osu.Game/Screens/Play/Player.cs | 6 ------ 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs index 653622e881..3b54c03bb0 100644 --- a/osu.Game/Input/ConfineMouseTracker.cs +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -8,13 +8,12 @@ using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Configuration; using osu.Game.Overlays; -using osu.Game.Screens.Play; namespace osu.Game.Input { /// /// Connects with , - /// while optionally binding an mode, usually that of the current . + /// while binding . /// It is assumed that while overlay activation is , we should also confine the /// mouse cursor if it has been requested with . /// @@ -22,22 +21,16 @@ namespace osu.Game.Input { private Bindable frameworkConfineMode; private Bindable osuConfineMode; - - /// - /// The bindable used to indicate whether gameplay is active. - /// Should be bound to the corresponding bindable of the current . - /// Defaults to to assume that all other screens are considered "not gameplay". - /// - public IBindable OverlayActivationMode { get; } = new Bindable(OverlayActivation.All); + private IBindable overlayActivationMode; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) + private void load(OsuGame game, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) { frameworkConfineMode = frameworkConfigManager.GetBindable(FrameworkSetting.ConfineMouseMode); osuConfineMode = osuConfigManager.GetBindable(OsuSetting.ConfineMouseMode); osuConfineMode.ValueChanged += _ => updateConfineMode(); - - OverlayActivationMode.BindValueChanged(_ => updateConfineMode(), true); + overlayActivationMode = game.OverlayActivationMode.GetBoundCopy(); + overlayActivationMode.BindValueChanged(_ => updateConfineMode(), true); } private void updateConfineMode() @@ -53,7 +46,7 @@ namespace osu.Game.Input break; case OsuConfineMouseMode.WhenOverlaysDisabled: - frameworkConfineMode.Value = OverlayActivationMode.Value == OverlayActivation.Disabled ? ConfineMouseMode.Always : ConfineMouseMode.Never; + frameworkConfineMode.Value = overlayActivationMode?.Value == OverlayActivation.Disabled ? ConfineMouseMode.Always : ConfineMouseMode.Never; break; case OsuConfineMouseMode.Always: diff --git a/osu.Game/Input/OsuConfineMouseMode.cs b/osu.Game/Input/OsuConfineMouseMode.cs index 53e352c8bd..e8411a3d9f 100644 --- a/osu.Game/Input/OsuConfineMouseMode.cs +++ b/osu.Game/Input/OsuConfineMouseMode.cs @@ -23,7 +23,7 @@ namespace osu.Game.Input Fullscreen, /// - /// The mouse cursor will be locked to the window bounds during gameplay, + /// The mouse cursor will be locked to the window bounds while overlays are disabled, /// but may otherwise move freely. /// [Description("During Gameplay")] diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index de67b2d46d..175722c44e 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -18,7 +18,6 @@ using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; -using osu.Game.Input; using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Overlays; @@ -67,9 +66,6 @@ namespace osu.Game.Screens.Play private Bindable mouseWheelDisabled; - [Resolved(CanBeNull = true)] - private ConfineMouseTracker confineMouseTracker { get; set; } - private readonly Bindable storyboardReplacesBackground = new Bindable(); public int RestartCount; @@ -229,8 +225,6 @@ namespace osu.Game.Screens.Play DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); - confineMouseTracker?.OverlayActivationMode.BindTo(OverlayActivationMode); - // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); From e8b34ba4acd0d5fb61e06bb3f1ce4c14bffdded4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 20:57:39 +0900 Subject: [PATCH 12/68] Fix incorrectly committed testing change --- osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 9b7b7392d8..c216d1efe9 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.General Bindable = config.GetBindable(OsuSetting.ReleaseStream), }); - //if (updateManager?.CanCheckForUpdate == true) + if (updateManager?.CanCheckForUpdate == true) { Add(checkForUpdatesButton = new SettingsButton { From 478f2dec9624f13c9fdf8503774094a19da2e9da Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Tue, 6 Oct 2020 22:39:35 +1030 Subject: [PATCH 13/68] Maintain the current gameplay state in OsuGame --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Input/ConfineMouseTracker.cs | 19 +++++++-------- osu.Game/Input/OsuConfineMouseMode.cs | 4 ++-- osu.Game/OsuGame.cs | 5 ++++ osu.Game/Screens/Play/Player.cs | 28 ++++++++++++---------- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 71cbdb345c..78179a781a 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -70,7 +70,7 @@ namespace osu.Game.Configuration Set(OsuSetting.MouseDisableButtons, false); Set(OsuSetting.MouseDisableWheel, false); - Set(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.WhenOverlaysDisabled); + Set(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); // Graphics Set(OsuSetting.ShowFpsDisplay, false); diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs index 3b54c03bb0..c1089874ae 100644 --- a/osu.Game/Input/ConfineMouseTracker.cs +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -7,30 +7,29 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Configuration; -using osu.Game.Overlays; namespace osu.Game.Input { /// - /// Connects with , - /// while binding . - /// It is assumed that while overlay activation is , we should also confine the - /// mouse cursor if it has been requested with . + /// Connects with . + /// If is true, we should also confine the mouse cursor if it has been + /// requested with . /// public class ConfineMouseTracker : Component { private Bindable frameworkConfineMode; private Bindable osuConfineMode; - private IBindable overlayActivationMode; + private IBindable isGameplay; [BackgroundDependencyLoader] private void load(OsuGame game, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) { frameworkConfineMode = frameworkConfigManager.GetBindable(FrameworkSetting.ConfineMouseMode); osuConfineMode = osuConfigManager.GetBindable(OsuSetting.ConfineMouseMode); + isGameplay = game.IsGameplay.GetBoundCopy(); + osuConfineMode.ValueChanged += _ => updateConfineMode(); - overlayActivationMode = game.OverlayActivationMode.GetBoundCopy(); - overlayActivationMode.BindValueChanged(_ => updateConfineMode(), true); + isGameplay.BindValueChanged(_ => updateConfineMode(), true); } private void updateConfineMode() @@ -45,8 +44,8 @@ namespace osu.Game.Input frameworkConfineMode.Value = ConfineMouseMode.Fullscreen; break; - case OsuConfineMouseMode.WhenOverlaysDisabled: - frameworkConfineMode.Value = overlayActivationMode?.Value == OverlayActivation.Disabled ? ConfineMouseMode.Always : ConfineMouseMode.Never; + case OsuConfineMouseMode.DuringGameplay: + frameworkConfineMode.Value = isGameplay.Value ? ConfineMouseMode.Always : ConfineMouseMode.Never; break; case OsuConfineMouseMode.Always: diff --git a/osu.Game/Input/OsuConfineMouseMode.cs b/osu.Game/Input/OsuConfineMouseMode.cs index e8411a3d9f..32b456395c 100644 --- a/osu.Game/Input/OsuConfineMouseMode.cs +++ b/osu.Game/Input/OsuConfineMouseMode.cs @@ -23,11 +23,11 @@ namespace osu.Game.Input Fullscreen, /// - /// The mouse cursor will be locked to the window bounds while overlays are disabled, + /// The mouse cursor will be locked to the window bounds during gameplay, /// but may otherwise move freely. /// [Description("During Gameplay")] - WhenOverlaysDisabled, + DuringGameplay, /// /// The mouse cursor will always be locked to the window bounds while the game has focus. diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 06e6e4bfb0..466ff13615 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -97,6 +97,11 @@ namespace osu.Game /// public readonly IBindable OverlayActivationMode = new Bindable(); + /// + /// Whether gameplay is currently active. + /// + public readonly Bindable IsGameplay = new BindableBool(); + protected OsuScreenStack ScreenStack; protected BackButton BackButton; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 175722c44e..a217d2a396 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -68,6 +68,8 @@ namespace osu.Game.Screens.Play private readonly Bindable storyboardReplacesBackground = new Bindable(); + private readonly Bindable isGameplay = new Bindable(); + public int RestartCount; [Resolved] @@ -156,7 +158,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config) + private void load(AudioManager audio, OsuConfigManager config, OsuGame game) { Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray(); @@ -172,6 +174,8 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); + isGameplay.BindTo(game.IsGameplay); + DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); ScoreProcessor = ruleset.CreateScoreProcessor(); @@ -219,9 +223,9 @@ namespace osu.Game.Screens.Play skipOverlay.Hide(); } - DrawableRuleset.IsPaused.BindValueChanged(_ => updateOverlayActivationMode()); - DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateOverlayActivationMode()); - breakTracker.IsBreakTime.BindValueChanged(_ => updateOverlayActivationMode()); + DrawableRuleset.IsPaused.BindValueChanged(_ => updateGameplayState()); + DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateGameplayState()); + breakTracker.IsBreakTime.BindValueChanged(_ => updateGameplayState()); DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); @@ -353,14 +357,11 @@ namespace osu.Game.Screens.Play HUDOverlay.KeyCounter.IsCounting = !isBreakTime.NewValue; } - private void updateOverlayActivationMode() + private void updateGameplayState() { - bool canTriggerOverlays = DrawableRuleset.IsPaused.Value || breakTracker.IsBreakTime.Value; - - if (DrawableRuleset.HasReplayLoaded.Value || canTriggerOverlays) - OverlayActivationMode.Value = OverlayActivation.UserTriggered; - else - OverlayActivationMode.Value = OverlayActivation.Disabled; + bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !DrawableRuleset.IsPaused.Value && !breakTracker.IsBreakTime.Value; + OverlayActivationMode.Value = inGameplay ? OverlayActivation.Disabled : OverlayActivation.UserTriggered; + isGameplay.Value = inGameplay; } private void updatePauseOnFocusLostState() => @@ -657,7 +658,7 @@ namespace osu.Game.Screens.Play foreach (var mod in Mods.Value.OfType()) mod.ApplyToTrack(musicController.CurrentTrack); - updateOverlayActivationMode(); + updateGameplayState(); } public override void OnSuspending(IScreen next) @@ -693,6 +694,9 @@ namespace osu.Game.Screens.Play musicController.ResetTrackAdjustments(); + // Ensure we reset the IsGameplay state + isGameplay.Value = false; + fadeOut(); return base.OnExiting(next); } From b2dad67adead750b3b3f377d61449bf53e00d5a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 21:28:59 +0900 Subject: [PATCH 14/68] Fix unresolvable dependency in settings test scene --- osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index c216d1efe9..c0a525e012 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private SettingsButton checkForUpdatesButton; - [Resolved] + [Resolved(CanBeNull = true)] private NotificationOverlay notifications { get; set; } [BackgroundDependencyLoader(true)] @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { if (!t.Result) { - notifications.Post(new SimpleNotification + notifications?.Post(new SimpleNotification { Text = $"You are running the latest release ({game.Version})", Icon = FontAwesome.Solid.CheckCircle, From 8847b88e653490a62d5319bc066702d9151d100c Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 11:44:41 +1030 Subject: [PATCH 15/68] Fix unit tests trying to resolve OsuGame --- osu.Game/Screens/Play/Player.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a217d2a396..932c5ba1df 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -158,7 +158,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config, OsuGame game) + private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game) { Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray(); @@ -174,7 +174,8 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); - isGameplay.BindTo(game.IsGameplay); + if (game is OsuGame osuGame) + isGameplay.BindTo(osuGame.IsGameplay); DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); From c8c5998af475a9860041eea732e61a10f949f069 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 16:02:35 +1030 Subject: [PATCH 16/68] Bail if FrameworkSetting.ConfineMouseMode is unavailable --- osu.Game/Input/ConfineMouseTracker.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs index c1089874ae..3d16e44607 100644 --- a/osu.Game/Input/ConfineMouseTracker.cs +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -34,6 +34,10 @@ namespace osu.Game.Input private void updateConfineMode() { + // confine mode is unavailable on some platforms + if (frameworkConfineMode.Disabled) + return; + switch (osuConfineMode.Value) { case OsuConfineMouseMode.Never: From 7fff762dfc91222fe1823f3b73f90aa588b5129c Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 16:14:49 +1030 Subject: [PATCH 17/68] Rename IsGameplay --- osu.Game/Input/ConfineMouseTracker.cs | 10 +++++----- osu.Game/OsuGame.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs index 3d16e44607..3dadae6317 100644 --- a/osu.Game/Input/ConfineMouseTracker.cs +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -12,24 +12,24 @@ namespace osu.Game.Input { /// /// Connects with . - /// If is true, we should also confine the mouse cursor if it has been + /// If is true, we should also confine the mouse cursor if it has been /// requested with . /// public class ConfineMouseTracker : Component { private Bindable frameworkConfineMode; private Bindable osuConfineMode; - private IBindable isGameplay; + private IBindable localUserPlaying; [BackgroundDependencyLoader] private void load(OsuGame game, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) { frameworkConfineMode = frameworkConfigManager.GetBindable(FrameworkSetting.ConfineMouseMode); osuConfineMode = osuConfigManager.GetBindable(OsuSetting.ConfineMouseMode); - isGameplay = game.IsGameplay.GetBoundCopy(); + localUserPlaying = game.LocalUserPlaying.GetBoundCopy(); osuConfineMode.ValueChanged += _ => updateConfineMode(); - isGameplay.BindValueChanged(_ => updateConfineMode(), true); + localUserPlaying.BindValueChanged(_ => updateConfineMode(), true); } private void updateConfineMode() @@ -49,7 +49,7 @@ namespace osu.Game.Input break; case OsuConfineMouseMode.DuringGameplay: - frameworkConfineMode.Value = isGameplay.Value ? ConfineMouseMode.Always : ConfineMouseMode.Never; + frameworkConfineMode.Value = localUserPlaying.Value ? ConfineMouseMode.Always : ConfineMouseMode.Never; break; case OsuConfineMouseMode.Always: diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 466ff13615..c09ad0eeb9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -98,9 +98,9 @@ namespace osu.Game public readonly IBindable OverlayActivationMode = new Bindable(); /// - /// Whether gameplay is currently active. + /// Whether the local user is currently interacting with the game in a way that should not be interrupted. /// - public readonly Bindable IsGameplay = new BindableBool(); + public readonly Bindable LocalUserPlaying = new BindableBool(); protected OsuScreenStack ScreenStack; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 932c5ba1df..e6c15521af 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Play private readonly Bindable storyboardReplacesBackground = new Bindable(); - private readonly Bindable isGameplay = new Bindable(); + private readonly Bindable localUserPlaying = new Bindable(); public int RestartCount; @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); if (game is OsuGame osuGame) - isGameplay.BindTo(osuGame.IsGameplay); + localUserPlaying.BindTo(osuGame.LocalUserPlaying); DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); @@ -362,7 +362,7 @@ namespace osu.Game.Screens.Play { bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !DrawableRuleset.IsPaused.Value && !breakTracker.IsBreakTime.Value; OverlayActivationMode.Value = inGameplay ? OverlayActivation.Disabled : OverlayActivation.UserTriggered; - isGameplay.Value = inGameplay; + localUserPlaying.Value = inGameplay; } private void updatePauseOnFocusLostState() => @@ -695,8 +695,8 @@ namespace osu.Game.Screens.Play musicController.ResetTrackAdjustments(); - // Ensure we reset the IsGameplay state - isGameplay.Value = false; + // Ensure we reset the LocalUserPlaying state + localUserPlaying.Value = false; fadeOut(); return base.OnExiting(next); From 485bd962c7d52d738547af4bcd43af535e2e935a Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 16:15:17 +1030 Subject: [PATCH 18/68] Also reset LocalUserPlaying in OnSuspending --- osu.Game/Screens/Play/Player.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e6c15521af..43dce0786d 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -666,6 +666,9 @@ namespace osu.Game.Screens.Play { screenSuspension?.Expire(); + // Ensure we reset the LocalUserPlaying state + localUserPlaying.Value = false; + fadeOut(); base.OnSuspending(next); } From d1ec3806927a3bb042e47b76be921582ed87575e Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 16:15:32 +1030 Subject: [PATCH 19/68] Don't cache ConfineMouseTracker --- osu.Game/OsuGame.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c09ad0eeb9..d22ac1aec8 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -549,7 +549,6 @@ namespace osu.Game BackButton.Receptor receptor; dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); - dependencies.Cache(confineMouseTracker = new ConfineMouseTracker()); AddRange(new Drawable[] { @@ -586,7 +585,7 @@ namespace osu.Game leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, topMostOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, idleTracker, - confineMouseTracker + confineMouseTracker = new ConfineMouseTracker() }); ScreenStack.ScreenPushed += screenPushed; From 8b8eb00bd75c4790bec72bdd8a79f5e6ddddf457 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 16:16:58 +1030 Subject: [PATCH 20/68] Permit nulls rather than casting to OsuGame --- osu.Game/Screens/Play/Player.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 43dce0786d..39a6ac4ded 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -157,8 +157,8 @@ namespace osu.Game.Screens.Play DrawableRuleset.SetRecordTarget(recordingReplay = new Replay()); } - [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game) + [BackgroundDependencyLoader(true)] + private void load(AudioManager audio, OsuConfigManager config, OsuGame game) { Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray(); @@ -174,8 +174,8 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); - if (game is OsuGame osuGame) - localUserPlaying.BindTo(osuGame.LocalUserPlaying); + if (game != null) + localUserPlaying.BindTo(game.LocalUserPlaying); DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); From d6d0bd90a39e48747ce34ba08629373470127829 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 15:34:03 +0900 Subject: [PATCH 21/68] Extract tuple into class --- osu.Game/Scoring/HitResultDisplayStatistic.cs | 41 +++++++++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 17 ++++---- 2 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 osu.Game/Scoring/HitResultDisplayStatistic.cs diff --git a/osu.Game/Scoring/HitResultDisplayStatistic.cs b/osu.Game/Scoring/HitResultDisplayStatistic.cs new file mode 100644 index 0000000000..d43d8bf0ba --- /dev/null +++ b/osu.Game/Scoring/HitResultDisplayStatistic.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Scoring +{ + /// + /// Compiled result data for a specific in a score. + /// + public class HitResultDisplayStatistic + { + /// + /// The associated result type. + /// + public HitResult Result { get; } + + /// + /// The count of successful hits of this type. + /// + public int Count { get; } + + /// + /// The maximum achievable hits of this type. May be null if undetermined. + /// + public int? MaxCount { get; } + + /// + /// A custom display name for the result type. May be provided by rulesets to give better clarity. + /// + public string DisplayName { get; } + + public HitResultDisplayStatistic(HitResult result, int count, int? maxCount, string displayName) + { + Result = result; + Count = count; + MaxCount = maxCount; + DisplayName = displayName; + } + } +} diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 0206989231..7cd9578ff1 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -213,22 +213,22 @@ namespace osu.Game.Scoring set => isLegacyScore = value; } - public IEnumerable<(HitResult result, int count, int? maxCount)> GetStatisticsForDisplay() + public IEnumerable GetStatisticsForDisplay() { - foreach (var key in OrderAttributeUtils.GetValuesInOrder()) + foreach (var r in Ruleset.CreateInstance().GetHitResults()) { - if (key.IsBonus()) + if (r.result.IsBonus()) continue; - int value = Statistics.GetOrDefault(key); + int value = Statistics.GetOrDefault(r.result); - switch (key) + switch (r.result) { case HitResult.SmallTickHit: { int total = value + Statistics.GetOrDefault(HitResult.SmallTickMiss); if (total > 0) - yield return (key, value, total); + yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); break; } @@ -237,7 +237,7 @@ namespace osu.Game.Scoring { int total = value + Statistics.GetOrDefault(HitResult.LargeTickMiss); if (total > 0) - yield return (key, value, total); + yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); break; } @@ -247,8 +247,7 @@ namespace osu.Game.Scoring break; default: - if (value > 0 || key == HitResult.Miss) - yield return (key, value, null); + yield return new HitResultDisplayStatistic(r.result, value, null, r.displayName); break; } From 3363c3399eaa8a5e0a4efbb2e1742d694639c232 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 15:34:23 +0900 Subject: [PATCH 22/68] Allow rulesets to specify valid HitResult types (and display names for them) --- osu.Game/Rulesets/Ruleset.cs | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 915544d010..48d94d2b3f 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -23,8 +23,10 @@ using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Users; using JetBrains.Annotations; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Utils; namespace osu.Game.Rulesets { @@ -220,5 +222,52 @@ namespace osu.Game.Rulesets /// The s to display. Each may contain 0 or more . [NotNull] public virtual StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => Array.Empty(); + + /// + /// Get all valid s for this ruleset. + /// Generally used for results display purposes, where it can't be determined if zero-count means the user has not achieved any or the type is not used by this ruleset. + /// + /// + /// All valid s along with a display-friendly name. + /// + public IEnumerable<(HitResult result, string displayName)> GetHitResults() + { + var validResults = GetValidHitResults(); + + // enumerate over ordered list to guarantee return order is stable. + foreach (var result in OrderAttributeUtils.GetValuesInOrder()) + { + switch (result) + { + // hard blocked types, should never be displayed even if the ruleset tells us to. + case HitResult.None: + case HitResult.IgnoreHit: + case HitResult.IgnoreMiss: + // display is handled as a completion count with corresponding "hit" type. + case HitResult.LargeTickMiss: + case HitResult.SmallTickMiss: + continue; + } + + if (result == HitResult.Miss || validResults.Contains(result)) + yield return (result, GetDisplayNameForHitResult(result)); + } + } + + /// + /// Get all valid s for this ruleset. + /// Generally used for results display purposes, where it can't be determined if zero-count means the user has not achieved any or the type is not used by this ruleset. + /// + /// + /// is implicitly included. Special types like are ignored even when specified. + /// + protected virtual IEnumerable GetValidHitResults() => OrderAttributeUtils.GetValuesInOrder(); + + /// + /// Get a display friendly name for the specified result type. + /// + /// The result type to get the name for. + /// The display name. + public virtual string GetDisplayNameForHitResult(HitResult result) => result.GetDescription(); } } From 6020ec9ca3e786d95640da84dfc573ef181c452a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 15:34:36 +0900 Subject: [PATCH 23/68] Add valid result types for all rulesets --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 29 ++++++++++++++++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 25 ++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 35 +++++++++++++++++++++++++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 22 ++++++++++++++++ 4 files changed, 111 insertions(+) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index ca75a816f1..eb845cdea6 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -141,6 +141,35 @@ namespace osu.Game.Rulesets.Catch public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetCatch }; + protected override IEnumerable GetValidHitResults() + { + return new[] + { + HitResult.Great, + + HitResult.LargeTickHit, + HitResult.SmallTickHit, + HitResult.LargeBonus, + }; + } + + public override string GetDisplayNameForHitResult(HitResult result) + { + switch (result) + { + case HitResult.LargeTickHit: + return "large droplet"; + + case HitResult.SmallTickHit: + return "small droplet"; + + case HitResult.LargeBonus: + return "bananas"; + } + + return base.GetDisplayNameForHitResult(result); + } + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap); public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new CatchLegacySkinTransformer(source); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 71ac85dd1b..d2feeb03af 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -319,6 +319,31 @@ namespace osu.Game.Rulesets.Mania return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast().OrderByDescending(i => i).First(v => variant >= v); } + protected override IEnumerable GetValidHitResults() + { + return new[] + { + HitResult.Perfect, + HitResult.Great, + HitResult.Good, + HitResult.Ok, + HitResult.Meh, + + HitResult.LargeTickHit, + }; + } + + public override string GetDisplayNameForHitResult(HitResult result) + { + switch (result) + { + case HitResult.LargeTickHit: + return "hold tick"; + } + + return base.GetDisplayNameForHitResult(result); + } + public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[] { new StatisticRow diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 7f4a0dcbbb..d946e7a113 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -191,6 +191,41 @@ namespace osu.Game.Rulesets.Osu public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo); + protected override IEnumerable GetValidHitResults() + { + return new[] + { + HitResult.Great, + HitResult.Ok, + HitResult.Meh, + + HitResult.LargeTickHit, + HitResult.SmallTickHit, + HitResult.SmallBonus, + HitResult.LargeBonus, + }; + } + + public override string GetDisplayNameForHitResult(HitResult result) + { + switch (result) + { + case HitResult.LargeTickHit: + return "slider tick"; + + case HitResult.SmallTickHit: + return "slider end"; + + case HitResult.SmallBonus: + return "spinner spin"; + + case HitResult.LargeBonus: + return "spinner bonus"; + } + + return base.GetDisplayNameForHitResult(result); + } + public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) { var timedHitEvents = score.HitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle)).ToList(); diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 9d485e3f20..f4c94c9248 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -159,6 +159,28 @@ namespace osu.Game.Rulesets.Taiko public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); + protected override IEnumerable GetValidHitResults() + { + return new[] + { + HitResult.Great, + HitResult.Ok, + + HitResult.SmallTickHit, + }; + } + + public override string GetDisplayNameForHitResult(HitResult result) + { + switch (result) + { + case HitResult.SmallTickHit: + return "drum tick"; + } + + return base.GetDisplayNameForHitResult(result); + } + public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) { var timedHitEvents = score.HitEvents.Where(e => e.HitObject is Hit).ToList(); From e281d724b85feabb494169b6bd007e8e91621e1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 15:35:04 +0900 Subject: [PATCH 24/68] Consume display name logic --- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 22 +++++++++++-------- .../Scores/TopScoreStatisticsSection.cs | 8 +++---- .../ContractedPanelMiddleContent.cs | 8 +++---- .../Expanded/ExpandedPanelMiddleContent.cs | 4 ++-- .../Expanded/Statistics/HitResultStatistic.cs | 8 +++---- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 968355c377..231d888a4e 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores /// /// The statistics that appear in the table, in order of appearance. /// - private readonly List statisticResultTypes = new List(); + private readonly List<(HitResult result, string displayName)> statisticResultTypes = new List<(HitResult, string)>(); private bool showPerformancePoints; @@ -101,15 +101,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }; // All statistics across all scores, unordered. - var allScoreStatistics = scores.SelectMany(s => s.GetStatisticsForDisplay().Select(stat => stat.result)).ToHashSet(); + var allScoreStatistics = scores.SelectMany(s => s.GetStatisticsForDisplay().Select(stat => stat.Result)).ToHashSet(); + + var ruleset = scores.First().Ruleset.CreateInstance(); foreach (var result in OrderAttributeUtils.GetValuesInOrder()) { if (!allScoreStatistics.Contains(result)) continue; - columns.Add(new TableColumn(result.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 35, maxSize: 60))); - statisticResultTypes.Add(result); + string displayName = ruleset.GetDisplayNameForHitResult(result); + + columns.Add(new TableColumn(displayName, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 35, maxSize: 60))); + statisticResultTypes.Add((result, displayName)); } if (showPerformancePoints) @@ -163,18 +167,18 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } }; - var availableStatistics = score.GetStatisticsForDisplay().ToDictionary(tuple => tuple.result); + var availableStatistics = score.GetStatisticsForDisplay().ToDictionary(tuple => tuple.Result); foreach (var result in statisticResultTypes) { - if (!availableStatistics.TryGetValue(result, out var stat)) - stat = (result, 0, null); + if (!availableStatistics.TryGetValue(result.result, out var stat)) + stat = new HitResultDisplayStatistic(result.result, 0, null, result.displayName); content.Add(new OsuSpriteText { - Text = stat.maxCount == null ? $"{stat.count}" : $"{stat.count}/{stat.maxCount}", + Text = stat.MaxCount == null ? $"{stat.Count}" : $"{stat.Count}/{stat.MaxCount}", Font = OsuFont.GetFont(size: text_size), - Colour = stat.count == 0 ? Color4.Gray : Color4.White + Colour = stat.Count == 0 ? Color4.Gray : Color4.White }); } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 05789e1fc0..93744dd6a3 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -15,7 +14,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osuTK; @@ -117,7 +115,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores ppColumn.Alpha = value.Beatmap?.Status == BeatmapSetOnlineStatus.Ranked ? 1 : 0; ppColumn.Text = $@"{value.PP:N0}"; - statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(s => createStatisticsColumn(s.result, s.count, s.maxCount)); + statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn); modsColumn.Mods = value.Mods; if (scoreManager != null) @@ -125,9 +123,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } } - private TextColumn createStatisticsColumn(HitResult hitResult, int count, int? maxCount) => new TextColumn(hitResult.GetDescription(), smallFont, bottom_columns_min_width) + private TextColumn createStatisticsColumn(HitResultDisplayStatistic stat) => new TextColumn(stat.DisplayName, smallFont, bottom_columns_min_width) { - Text = maxCount == null ? $"{count}" : $"{count}/{maxCount}" + Text = stat.MaxCount == null ? $"{stat.Count}" : $"{stat.Count}/{stat.MaxCount}" }; private class InfoColumn : CompositeDrawable diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 95ece1a9fb..9481f07342 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -13,7 +12,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Users; @@ -117,7 +115,7 @@ namespace osu.Game.Screens.Ranking.Contracted AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Spacing = new Vector2(0, 5), - ChildrenEnumerable = score.GetStatisticsForDisplay().Select(s => createStatistic(s.result, s.count, s.maxCount)) + ChildrenEnumerable = score.GetStatisticsForDisplay().Select(createStatistic) }, new FillFlowContainer { @@ -199,8 +197,8 @@ namespace osu.Game.Screens.Ranking.Contracted }; } - private Drawable createStatistic(HitResult result, int count, int? maxCount) - => createStatistic(result.GetDescription(), maxCount == null ? $"{count}" : $"{count}/{maxCount}"); + private Drawable createStatistic(HitResultDisplayStatistic result) + => createStatistic(result.DisplayName, result.MaxCount == null ? $"{result.Count}" : $"{result.Count}/{result.MaxCount}"); private Drawable createStatistic(string key, string value) => new Container { diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index ebab8c88f6..30b9f47f71 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -66,8 +66,8 @@ namespace osu.Game.Screens.Ranking.Expanded var bottomStatistics = new List(); - foreach (var (key, value, maxCount) in score.GetStatisticsForDisplay()) - bottomStatistics.Add(new HitResultStatistic(key, value, maxCount)); + foreach (var result in score.GetStatisticsForDisplay()) + bottomStatistics.Add(new HitResultStatistic(result)); statisticDisplays.AddRange(topStatistics); statisticDisplays.AddRange(bottomStatistics); diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/HitResultStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/HitResultStatistic.cs index a86033713f..31ef51a031 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/HitResultStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/HitResultStatistic.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Extensions; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; namespace osu.Game.Screens.Ranking.Expanded.Statistics { @@ -12,10 +12,10 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics { private readonly HitResult result; - public HitResultStatistic(HitResult result, int count, int? maxCount = null) - : base(result.GetDescription(), count, maxCount) + public HitResultStatistic(HitResultDisplayStatistic result) + : base(result.DisplayName, result.Count, result.MaxCount) { - this.result = result; + this.result = result.Result; } [BackgroundDependencyLoader] From c0bc6a75b35cb73cf960c1c444125cbea6c1155f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 16:17:08 +0900 Subject: [PATCH 25/68] Show auxiliary judgements on next line --- .../Ranking/Expanded/ExpandedPanelMiddleContent.cs | 14 ++++++++++++-- .../Expanded/Statistics/HitResultStatistic.cs | 6 +++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 30b9f47f71..b2d0c7a874 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Ranking.Expanded new CounterStatistic("pp", (int)(score.PP ?? 0)), }; - var bottomStatistics = new List(); + var bottomStatistics = new List(); foreach (var result in score.GetStatisticsForDisplay()) bottomStatistics.Add(new HitResultStatistic(result)); @@ -198,7 +198,17 @@ namespace osu.Game.Screens.Ranking.Expanded { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Content = new[] { bottomStatistics.Cast().ToArray() }, + Content = new[] { bottomStatistics.Where(s => s.Result <= HitResult.Perfect).ToArray() }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + } + }, + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { bottomStatistics.Where(s => s.Result > HitResult.Perfect).ToArray() }, RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize), diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/HitResultStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/HitResultStatistic.cs index 31ef51a031..ada8dfabf0 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/HitResultStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/HitResultStatistic.cs @@ -10,18 +10,18 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics { public class HitResultStatistic : CounterStatistic { - private readonly HitResult result; + public readonly HitResult Result; public HitResultStatistic(HitResultDisplayStatistic result) : base(result.DisplayName, result.Count, result.MaxCount) { - this.result = result.Result; + Result = result.Result; } [BackgroundDependencyLoader] private void load(OsuColour colours) { - HeaderText.Colour = colours.ForHitResult(result); + HeaderText.Colour = colours.ForHitResult(Result); } } } From 6ac70945f2be7f3be6c6daabaf12a5bda8619cf2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 16:17:28 +0900 Subject: [PATCH 26/68] Show bonus judgements on expanded panel --- osu.Game/Scoring/ScoreInfo.cs | 3 --- .../Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 7cd9578ff1..596e98a6bd 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -217,9 +217,6 @@ namespace osu.Game.Scoring { foreach (var r in Ruleset.CreateInstance().GetHitResults()) { - if (r.result.IsBonus()) - continue; - int value = Statistics.GetOrDefault(r.result); switch (r.result) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 9481f07342..24f1116d0e 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Users; @@ -115,7 +116,7 @@ namespace osu.Game.Screens.Ranking.Contracted AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Spacing = new Vector2(0, 5), - ChildrenEnumerable = score.GetStatisticsForDisplay().Select(createStatistic) + ChildrenEnumerable = score.GetStatisticsForDisplay().Where(s => !s.Result.IsBonus()).Select(createStatistic) }, new FillFlowContainer { From 2e0a9f53c11a4d1e4fe2b63e22a3af3de579904d Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 17:52:39 +1030 Subject: [PATCH 27/68] Add test coverage --- .../Visual/Gameplay/TestSceneOverlayActivation.cs | 10 ++++++++++ osu.Game/Screens/Play/Player.cs | 10 +++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs index ce04b940e7..41f7582d31 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Bindables; using osu.Game.Overlays; using osu.Game.Rulesets; @@ -23,32 +24,40 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestGameplayOverlayActivation() { + AddAssert("local user playing", () => Player.LocalUserPlaying.Value); AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled); } [Test] public void TestGameplayOverlayActivationPaused() { + AddAssert("local user playing", () => Player.LocalUserPlaying.Value); AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled); AddStep("pause gameplay", () => Player.Pause()); + AddAssert("local user not playing", () => !Player.LocalUserPlaying.Value); AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered); } [Test] public void TestGameplayOverlayActivationReplayLoaded() { + AddAssert("local user playing", () => Player.LocalUserPlaying.Value); AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled); AddStep("load a replay", () => Player.DrawableRuleset.HasReplayLoaded.Value = true); + AddAssert("local user not playing", () => !Player.LocalUserPlaying.Value); AddAssert("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered); } [Test] public void TestGameplayOverlayActivationBreaks() { + AddAssert("local user playing", () => Player.LocalUserPlaying.Value); AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled); AddStep("seek to break", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().StartTime)); + AddAssert("local user not playing", () => !Player.LocalUserPlaying.Value); AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered); AddStep("seek to break end", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().EndTime)); + AddAssert("local user playing", () => Player.LocalUserPlaying.Value); AddUntilStep("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled); } @@ -57,6 +66,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected class OverlayTestPlayer : TestPlayer { public new OverlayActivation OverlayActivationMode => base.OverlayActivationMode.Value; + public new Bindable LocalUserPlaying => base.LocalUserPlaying; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 39a6ac4ded..8830884a40 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Play private readonly Bindable storyboardReplacesBackground = new Bindable(); - private readonly Bindable localUserPlaying = new Bindable(); + protected readonly Bindable LocalUserPlaying = new Bindable(); public int RestartCount; @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); if (game != null) - localUserPlaying.BindTo(game.LocalUserPlaying); + LocalUserPlaying.BindTo(game.LocalUserPlaying); DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); @@ -362,7 +362,7 @@ namespace osu.Game.Screens.Play { bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !DrawableRuleset.IsPaused.Value && !breakTracker.IsBreakTime.Value; OverlayActivationMode.Value = inGameplay ? OverlayActivation.Disabled : OverlayActivation.UserTriggered; - localUserPlaying.Value = inGameplay; + LocalUserPlaying.Value = inGameplay; } private void updatePauseOnFocusLostState() => @@ -667,7 +667,7 @@ namespace osu.Game.Screens.Play screenSuspension?.Expire(); // Ensure we reset the LocalUserPlaying state - localUserPlaying.Value = false; + LocalUserPlaying.Value = false; fadeOut(); base.OnSuspending(next); @@ -699,7 +699,7 @@ namespace osu.Game.Screens.Play musicController.ResetTrackAdjustments(); // Ensure we reset the LocalUserPlaying state - localUserPlaying.Value = false; + LocalUserPlaying.Value = false; fadeOut(); return base.OnExiting(next); From 67398b5d9551b7e147fa7398b337ee110ee456c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 16:30:14 +0900 Subject: [PATCH 28/68] Move timestamp text out of flow and attach to bottom edge --- .../Expanded/ExpandedPanelMiddleContent.cs | 265 +++++++++--------- 1 file changed, 134 insertions(+), 131 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index b2d0c7a874..5aac449adb 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -72,157 +72,160 @@ namespace osu.Game.Screens.Ranking.Expanded statisticDisplays.AddRange(topStatistics); statisticDisplays.AddRange(bottomStatistics); - InternalChild = new FillFlowContainer + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(20), - Children = new Drawable[] + new FillFlowContainer { - new FillFlowContainer + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(20), + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] + new FillFlowContainer { - new OsuSpriteText + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)), - Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), - MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)), - Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), - MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, - }, - new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = 40 }, - RelativeSizeAxes = Axes.X, - Height = 230, - Child = new AccuracyCircle(score) + new OsuSpriteText { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - } - }, - scoreCounter = new TotalScoreCounter - { - Margin = new MarginPadding { Top = 0, Bottom = 5 }, - Current = { Value = 0 }, - Alpha = 0, - AlwaysPresent = true - }, - starAndModDisplay = new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5, 0), - Children = new Drawable[] + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)), + Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, + }, + new OsuSpriteText { - new StarRatingDisplay(beatmap) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - } - }, - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)), + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, + }, + new Container { - new OsuSpriteText + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = 40 }, + RelativeSizeAxes = Axes.X, + Height = 230, + Child = new AccuracyCircle(score) { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = beatmap.Version, - Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), - }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12)) + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + } + }, + scoreCounter = new TotalScoreCounter + { + Margin = new MarginPadding { Top = 0, Bottom = 5 }, + Current = { Value = 0 }, + Alpha = 0, + AlwaysPresent = true + }, + starAndModDisplay = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5, 0), + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - }.With(t => - { - if (!string.IsNullOrEmpty(creator)) + new StarRatingDisplay(beatmap) { - t.AddText("mapped by "); - t.AddText(creator, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); - } - }) - } - }, - } - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - Children = new Drawable[] + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + } + }, + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = beatmap.Version, + Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), + }, + new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12)) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + }.With(t => + { + if (!string.IsNullOrEmpty(creator)) + { + t.AddText("mapped by "); + t.AddText(creator, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); + } + }) + } + }, + } + }, + new FillFlowContainer { - new GridContainer + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { topStatistics.Cast().ToArray() }, - RowDimensions = new[] + new GridContainer { - new Dimension(GridSizeMode.AutoSize), - } - }, - new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { bottomStatistics.Where(s => s.Result <= HitResult.Perfect).ToArray() }, - RowDimensions = new[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { topStatistics.Cast().ToArray() }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + } + }, + new GridContainer { - new Dimension(GridSizeMode.AutoSize), - } - }, - new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { bottomStatistics.Where(s => s.Result > HitResult.Perfect).ToArray() }, - RowDimensions = new[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { bottomStatistics.Where(s => s.Result <= HitResult.Perfect).ToArray() }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + } + }, + new GridContainer { - new Dimension(GridSizeMode.AutoSize), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { bottomStatistics.Where(s => s.Result > HitResult.Perfect).ToArray() }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + } } } } - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Text = $"Played on {score.Date.ToLocalTime():d MMMM yyyy HH:mm}" } + }, + new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Text = $"Played on {score.Date.ToLocalTime():d MMMM yyyy HH:mm}" } }; From f88ba1734bd6cc687d86de8a2a02a87d386dbbb9 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 18:11:47 +1030 Subject: [PATCH 29/68] Remove ConfineMouseTracker field --- osu.Game/OsuGame.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d22ac1aec8..772f9ff145 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -90,8 +90,6 @@ namespace osu.Game private IdleTracker idleTracker; - private ConfineMouseTracker confineMouseTracker; - /// /// Whether overlays should be able to be opened game-wide. Value is sourced from the current active screen. /// @@ -585,7 +583,7 @@ namespace osu.Game leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, topMostOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, idleTracker, - confineMouseTracker = new ConfineMouseTracker() + new ConfineMouseTracker() }); ScreenStack.ScreenPushed += screenPushed; From 31d347be5cfd24d63d3e39b78ecec916bab843c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 16:30:26 +0900 Subject: [PATCH 30/68] Make extended score panel taller to better fit all information --- osu.Game/Screens/Ranking/ScorePanel.cs | 7 ++++++- osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 1904da7094..8c8a547277 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Ranking /// /// Height of the panel when expanded. /// - private const float expanded_height = 560; + private const float expanded_height = 586; /// /// Height of the top layer when the panel is expanded. @@ -105,11 +105,16 @@ namespace osu.Game.Screens.Ranking [BackgroundDependencyLoader] private void load() { + // ScorePanel doesn't include the top extruding area in its own size. + // Adding a manual offset here allows the expanded version to take on an "acceptable" vertical centre when at 100% UI scale. + const float vertical_fudge = 20; + InternalChild = content = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(40), + Y = vertical_fudge, Children = new Drawable[] { topLayerContainer = new Container diff --git a/osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs b/osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs index c8010d1c32..67533aaa24 100644 --- a/osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs +++ b/osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; namespace osu.Game.Screens.Ranking From f77ad8cf3907327834db7d12c129511f70c5b819 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 17:03:34 +0900 Subject: [PATCH 31/68] Remove unused using --- osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs b/osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs index 67533aaa24..c8010d1c32 100644 --- a/osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs +++ b/osu.Game/Screens/Ranking/ScorePanelTrackingContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; namespace osu.Game.Screens.Ranking From f90ac2e76c2bf20c88fafef834b3c9110156f660 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Wed, 7 Oct 2020 18:50:02 +1030 Subject: [PATCH 32/68] Ensure we assert after the seek has completed --- osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs index 41f7582d31..4fa4c00981 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs @@ -54,11 +54,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("local user playing", () => Player.LocalUserPlaying.Value); AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled); AddStep("seek to break", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().StartTime)); - AddAssert("local user not playing", () => !Player.LocalUserPlaying.Value); AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered); + AddAssert("local user not playing", () => !Player.LocalUserPlaying.Value); AddStep("seek to break end", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().EndTime)); - AddAssert("local user playing", () => Player.LocalUserPlaying.Value); AddUntilStep("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled); + AddAssert("local user playing", () => Player.LocalUserPlaying.Value); } protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OverlayTestPlayer(); From 6487f58e9ad11cc8f28592593e693057e3ecb38e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 17:52:35 +0900 Subject: [PATCH 33/68] Fix failing tests --- osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 19294d12fc..528689e67c 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -193,6 +193,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("Transition to Results", () => player.Push(results = new FadeAccessibleResults(new ScoreInfo { + Ruleset = new OsuRuleset().RulesetInfo, User = new User { Username = "osu!" }, Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }))); From 94a6e2856570bcb281d2073cd7eb6f716362b0bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Oct 2020 18:40:09 +0900 Subject: [PATCH 34/68] Add back second removed condition --- osu.Game/Updater/UpdateManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 30e28f0e95..f772c6d282 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -61,6 +61,9 @@ namespace osu.Game.Updater public async Task CheckForUpdateAsync() { + if (!CanCheckForUpdate) + return false; + Task waitTask; lock (updateTaskLock) From d9089ef93c7d6a052a86fa55f27129903d1fa649 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Oct 2020 12:52:52 +0900 Subject: [PATCH 35/68] Add missing bonus type for taiko ruleset --- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index f4c94c9248..7f8289aa91 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -167,6 +167,8 @@ namespace osu.Game.Rulesets.Taiko HitResult.Ok, HitResult.SmallTickHit, + + HitResult.SmallBonus, }; } @@ -176,6 +178,9 @@ namespace osu.Game.Rulesets.Taiko { case HitResult.SmallTickHit: return "drum tick"; + + case HitResult.SmallBonus: + return "strong bonus"; } return base.GetDisplayNameForHitResult(result); From f70252d07bc4ebea18b4cdf0d1e4bd44477354c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Oct 2020 12:52:58 +0900 Subject: [PATCH 36/68] Match plurality --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index eb845cdea6..bda595e840 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Catch return "small droplet"; case HitResult.LargeBonus: - return "bananas"; + return "banana"; } return base.GetDisplayNameForHitResult(result); From c9f069d7edd6add1e6625f0e28921acbd9ffa610 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Oct 2020 18:17:57 +0900 Subject: [PATCH 37/68] Fix taiko's HitObjectComposer not allowing movement o f selected hitobjects --- osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs index d5dd758e10..ac14e6131a 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs @@ -89,6 +89,8 @@ namespace osu.Game.Rulesets.Taiko.Edit yield return new TernaryStateMenuItem("Strong") { State = { BindTarget = selectionStrongState } }; } + public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; + protected override void UpdateTernaryStates() { base.UpdateTernaryStates(); From 0967db768ffb922e0d6e46fe71ada6bf94731533 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Oct 2020 18:25:40 +0900 Subject: [PATCH 38/68] Add xmldoc covering usage restrictions --- osu.Game/OsuGame.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 772f9ff145..e6f6d526cf 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -98,7 +98,11 @@ namespace osu.Game /// /// Whether the local user is currently interacting with the game in a way that should not be interrupted. /// - public readonly Bindable LocalUserPlaying = new BindableBool(); + /// + /// This is exclusively managed by . If other components are mutating this state, a more + /// resilient method should be used to ensure correct state. + /// + public Bindable LocalUserPlaying = new BindableBool(); protected OsuScreenStack ScreenStack; From 43a575484ad1440a799aa41433840b47f5299f1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Oct 2020 18:25:43 +0900 Subject: [PATCH 39/68] Remove pointless comments --- osu.Game/Screens/Play/Player.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8830884a40..80dd8ae92c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -666,7 +666,6 @@ namespace osu.Game.Screens.Play { screenSuspension?.Expire(); - // Ensure we reset the LocalUserPlaying state LocalUserPlaying.Value = false; fadeOut(); @@ -698,7 +697,6 @@ namespace osu.Game.Screens.Play musicController.ResetTrackAdjustments(); - // Ensure we reset the LocalUserPlaying state LocalUserPlaying.Value = false; fadeOut(); From dbdb25ccf756cf48ec3ecff87a81aec86f0f0224 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Oct 2020 18:29:19 +0900 Subject: [PATCH 40/68] Move reset logic to OsuGame --- osu.Game/OsuGame.cs | 3 +++ osu.Game/Screens/Play/Player.cs | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e6f6d526cf..d315b213ab 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -957,6 +957,9 @@ namespace osu.Game break; } + // reset on screen change for sanity. + LocalUserPlaying.Value = false; + if (current is IOsuScreen currentOsuScreen) OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 80dd8ae92c..45f194fc29 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -666,8 +666,6 @@ namespace osu.Game.Screens.Play { screenSuspension?.Expire(); - LocalUserPlaying.Value = false; - fadeOut(); base.OnSuspending(next); } @@ -697,8 +695,6 @@ namespace osu.Game.Screens.Play musicController.ResetTrackAdjustments(); - LocalUserPlaying.Value = false; - fadeOut(); return base.OnExiting(next); } From 2d0275ba958e9de5fbb8c68635177026a86487e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 03:07:01 +0900 Subject: [PATCH 41/68] Fix first hitobject in osu! hidden mod not getting correct fade applied --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 15 +++++++++++---- osu.Game/Rulesets/Mods/ModHidden.cs | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 80e40af717..d354a8a726 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -42,7 +42,11 @@ namespace osu.Game.Rulesets.Osu.Mods private double lastSliderHeadFadeOutStartTime; private double lastSliderHeadFadeOutDuration; - protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) + protected override void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject drawable, ArmedState state) => applyState(drawable, true); + + protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) => applyState(drawable, false); + + private void applyState(DrawableHitObject drawable, bool increaseVisibility) { if (!(drawable is DrawableOsuHitObject d)) return; @@ -86,9 +90,12 @@ namespace osu.Game.Rulesets.Osu.Mods lastSliderHeadFadeOutStartTime = fadeOutStartTime; } - // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - circle.ApproachCircle.Hide(); + if (!increaseVisibility) + { + // we don't want to see the approach circle + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + circle.ApproachCircle.Hide(); + } // fade out immediately after fade in. using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true)) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index a1915b974c..d81b439f6a 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -38,7 +38,13 @@ namespace osu.Game.Rulesets.Mods public virtual void ApplyToDrawableHitObjects(IEnumerable drawables) { if (IncreaseFirstObjectVisibility.Value) + { + var firstObject = drawables.FirstOrDefault(); + if (firstObject != null) + firstObject.ApplyCustomUpdateState += ApplyFirstObjectIncreaseVisibilityState; + drawables = drawables.SkipWhile(h => !IsFirstHideableObject(h)).Skip(1); + } foreach (var dho in drawables) dho.ApplyCustomUpdateState += ApplyHiddenState; @@ -65,6 +71,20 @@ namespace osu.Game.Rulesets.Mods } } + /// + /// Apply a special visibility state to the first object in a beatmap, if the user chooses to turn on the "increase first object visibility" setting. + /// + /// The hit object to apply the state change to. + /// The state of the hit object. + protected virtual void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + /// + /// Apply a hidden state to the provided object. + /// + /// The hit object to apply the state change to. + /// The state of the hit object. protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) { } From e7eda19b0723b8edd16d68989434e340c8e9992d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 03:31:01 +0900 Subject: [PATCH 42/68] Reset new combo button state after successful placement --- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 9b3314e2ad..0336c74386 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -201,7 +201,12 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void AddBlueprintFor(HitObject hitObject) { refreshTool(); + base.AddBlueprintFor(hitObject); + + // on successful placement, the new combo button should be reset as this is the most common user interaction. + if (Beatmap.SelectedHitObjects.Count == 0) + NewCombo.Value = TernaryState.False; } private void createPlacement() From a5b2c4195efb48684a69a44655a1165c1168563b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 04:41:45 +0900 Subject: [PATCH 43/68] Fix incorrect timing distribution display due to lack of rounding --- .../Ranking/TestSceneHitEventTimingDistributionGraph.cs | 6 ++++++ .../Ranking/Statistics/HitEventTimingDistributionGraph.cs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs index 144f8da2fa..9059fe34af 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs @@ -23,6 +23,12 @@ namespace osu.Game.Tests.Visual.Ranking createTest(CreateDistributedHitEvents()); } + [Test] + public void TestAroundCentre() + { + createTest(Enumerable.Range(-100, 100).Select(i => new HitEvent(i / 50f, HitResult.Perfect, new HitCircle(), new HitCircle(), null)).ToList()); + } + [Test] public void TestZeroTimeOffset() { diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index aa2a83774e..980fc68788 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Ranking.Statistics foreach (var e in hitEvents) { - int binOffset = (int)(e.TimeOffset / binSize); + int binOffset = (int)Math.Round(e.TimeOffset / binSize); bins[timing_distribution_centre_bin_index + binOffset]++; } From ff5a1937f5f17cba7eadd82d3157a9499f6e06ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 05:04:03 +0900 Subject: [PATCH 44/68] Fix test logic and stabilise rounding direction --- .../Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs | 2 +- .../Ranking/Statistics/HitEventTimingDistributionGraph.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs index 9059fe34af..4bc843096f 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestAroundCentre() { - createTest(Enumerable.Range(-100, 100).Select(i => new HitEvent(i / 50f, HitResult.Perfect, new HitCircle(), new HitCircle(), null)).ToList()); + createTest(Enumerable.Range(-150, 300).Select(i => new HitEvent(i / 50f, HitResult.Perfect, new HitCircle(), new HitCircle(), null)).ToList()); } [Test] diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index 980fc68788..93885b6e02 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Ranking.Statistics foreach (var e in hitEvents) { - int binOffset = (int)Math.Round(e.TimeOffset / binSize); + int binOffset = (int)Math.Round(e.TimeOffset / binSize, MidpointRounding.AwayFromZero); bins[timing_distribution_centre_bin_index + binOffset]++; } From 85b33fffd028c2a5153c038a13f116c9f19c7e0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 05:14:44 +0900 Subject: [PATCH 45/68] Fix incorrect comments --- .../Screens/Edit/Compose/Components/SelectionHandler.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 7808d7a5bc..8a80ddd17c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -141,7 +141,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Handles the selected s being rotated. /// /// The delta angle to apply to the selection. - /// Whether any s could be moved. + /// Whether any s could be rotated. public virtual bool HandleRotation(float angle) => false; /// @@ -149,14 +149,14 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The delta scale to apply, in playfield local coordinates. /// The point of reference where the scale is originating from. - /// Whether any s could be moved. + /// Whether any s could be scaled. public virtual bool HandleScale(Vector2 scale, Anchor anchor) => false; /// - /// Handled the selected s being flipped. + /// Handles the selected s being flipped. /// /// The direction to flip - /// Whether any s could be moved. + /// Whether any s could be flipped. public virtual bool HandleFlip(Direction direction) => false; public bool OnPressed(PlatformAction action) From eacc7dca9a0d3815e7a4055f50b77c92505be911 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 06:31:59 +0900 Subject: [PATCH 46/68] Fix SliderPath not handling Clear correctly --- osu.Game/Rulesets/Objects/SliderPath.cs | 1 + .../Screens/Edit/Compose/Components/SelectionHandler.cs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index d577e8fdda..3083fcfccb 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -57,6 +57,7 @@ namespace osu.Game.Rulesets.Objects c.Changed += invalidate; break; + case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Remove: foreach (var c in args.OldItems.Cast()) c.Changed -= invalidate; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 8a80ddd17c..3fe0ab4396 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -159,6 +159,12 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether any s could be flipped. public virtual bool HandleFlip(Direction direction) => false; + /// + /// Handles the selected s being reversed pattern-wise. + /// + /// Whether any s could be reversed. + public virtual bool HandleReverse() => false; + public bool OnPressed(PlatformAction action) { switch (action.ActionMethod) From 825e10ec8c350bae42e546c86370fada762473d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 06:32:23 +0900 Subject: [PATCH 47/68] Add reverse handler button to selection box --- .../Edit/Compose/Components/SelectionBox.cs | 19 +++++++++++++++++++ .../Compose/Components/SelectionHandler.cs | 1 + 2 files changed, 20 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 64191e48e2..b753c45cca 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -17,10 +17,28 @@ namespace osu.Game.Screens.Edit.Compose.Components public Action OnRotation; public Action OnScale; public Action OnFlip; + public Action OnReverse; public Action OperationStarted; public Action OperationEnded; + private bool canReverse; + + /// + /// Whether pattern reversing support should be enabled. + /// + public bool CanReverse + { + get => canReverse; + set + { + if (canReverse == value) return; + + canReverse = value; + recreate(); + } + } + private bool canRotate; /// @@ -125,6 +143,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (CanScaleX && CanScaleY) addFullScaleComponents(); if (CanScaleY) addYScaleComponents(); if (CanRotate) addRotationComponents(); + if (CanReverse) addButton(FontAwesome.Solid.Backward, "Reverse pattern", () => OnReverse?.Invoke()); } private void addRotationComponents() diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 3fe0ab4396..b74095455c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -103,6 +103,7 @@ namespace osu.Game.Screens.Edit.Compose.Components OnRotation = angle => HandleRotation(angle), OnScale = (amount, anchor) => HandleScale(amount, anchor), OnFlip = direction => HandleFlip(direction), + OnReverse = () => HandleReverse(), }; /// From 2a790c76d5f9c668a842bbc72b0ddf54c076d28a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 06:32:33 +0900 Subject: [PATCH 48/68] Add reverse implementation for osu! --- .../Edit/OsuSelectionHandler.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 7ae0730e39..762c4a04e7 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Utils; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Compose.Components; @@ -25,6 +26,7 @@ namespace osu.Game.Rulesets.Osu.Edit SelectionBox.CanRotate = canOperate; SelectionBox.CanScaleX = canOperate; SelectionBox.CanScaleY = canOperate; + SelectionBox.CanReverse = canOperate; } protected override void OnOperationEnded() @@ -41,6 +43,54 @@ namespace osu.Game.Rulesets.Osu.Edit /// private Vector2? referenceOrigin; + public override bool HandleReverse() + { + var hitObjects = selectedMovableObjects; + + double endTime = hitObjects.Max(h => h.GetEndTime()); + double startTime = hitObjects.Min(h => h.StartTime); + + bool moreThanOneObject = hitObjects.Length > 1; + + foreach (var h in hitObjects) + { + if (moreThanOneObject) + h.StartTime = endTime - (h.GetEndTime() - startTime); + + if (h is Slider slider) + { + var points = slider.Path.ControlPoints.ToArray(); + Vector2 endPos = points.Last().Position.Value; + + slider.Path.ControlPoints.Clear(); + + slider.Position += endPos; + + PathType? lastType = null; + + for (var i = 0; i < points.Length; i++) + { + var p = points[i]; + p.Position.Value -= endPos; + + // propagate types forwards to last null type + if (i == points.Length - 1) + p.Type.Value = lastType; + else if (p.Type.Value != null) + { + var newType = p.Type.Value; + p.Type.Value = lastType; + lastType = newType; + } + + slider.Path.ControlPoints.Insert(0, p); + } + } + } + + return true; + } + public override bool HandleFlip(Direction direction) { var hitObjects = selectedMovableObjects; From 6649cb220431e7b3807fc8ae2d98c40e4f9d3811 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 06:41:53 +0900 Subject: [PATCH 49/68] Fix incorrect first object logic --- osu.Game/Rulesets/Mods/ModHidden.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index d81b439f6a..ad01bf036c 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -39,11 +39,13 @@ namespace osu.Game.Rulesets.Mods { if (IncreaseFirstObjectVisibility.Value) { + drawables = drawables.SkipWhile(h => !IsFirstHideableObject(h)); + var firstObject = drawables.FirstOrDefault(); if (firstObject != null) firstObject.ApplyCustomUpdateState += ApplyFirstObjectIncreaseVisibilityState; - drawables = drawables.SkipWhile(h => !IsFirstHideableObject(h)).Skip(1); + drawables = drawables.Skip(1); } foreach (var dho in drawables) From c86b37f60d0f1c036ff4bdd2964ce7532d71bfe0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 13:11:24 +0900 Subject: [PATCH 50/68] Add check to ensure MusicController doesn't play a delete pending beatmap's track --- osu.Game/Overlays/MusicController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 0764f34697..12caf98021 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -150,7 +150,7 @@ namespace osu.Game.Overlays { if (IsUserPaused) return; - if (CurrentTrack.IsDummyDevice) + if (CurrentTrack.IsDummyDevice || beatmap.Value.BeatmapSetInfo.DeletePending) { if (beatmap.Disabled) return; From 68039cff4089bf84e233a3c99ef1f24ffd40836d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 13:11:44 +0900 Subject: [PATCH 51/68] Set beatmap to sane default on exiting editor --- osu.Game/Screens/Edit/Editor.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 3c5cbf30e9..d6dbfef2bd 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -469,10 +469,17 @@ namespace osu.Game.Screens.Edit private void confirmExit() { + // stop the track if playing to allow the parent screen to choose a suitable playback mode. + Beatmap.Value.Track.Stop(); + if (isNewBeatmap) { // confirming exit without save means we should delete the new beatmap completely. beatmapManager.Delete(playableBeatmap.BeatmapInfo.BeatmapSet); + + // in theory this shouldn't be required but due to EF core not sharing instance states 100% + // MusicController is unaware of the changed DeletePending state. + Beatmap.SetDefault(); } exitConfirmed = true; From 389ffe7da5b799e1b7e800e07d2ccce894e7cbb8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 13:23:18 +0900 Subject: [PATCH 52/68] Hide bonus result types from score table for the time being --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 231d888a4e..324299ccba 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -110,6 +110,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (!allScoreStatistics.Contains(result)) continue; + // for the time being ignore bonus result types. + // this is not being sent from the API and will be empty in all cases. + if (result.IsBonus()) + continue; + string displayName = ruleset.GetDisplayNameForHitResult(result); columns.Add(new TableColumn(displayName, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 35, maxSize: 60))); From 8be19fd82059e79f9141027b9e4e3cda086e32fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 13:26:09 +0900 Subject: [PATCH 53/68] Increase height of contracted score panel to fit mods again --- osu.Game/Screens/Ranking/ScorePanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 8c8a547277..ee97ee55eb 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Ranking /// /// Height of the panel when contracted. /// - private const float contracted_height = 355; + private const float contracted_height = 385; /// /// Width of the panel when expanded. From beec0e41930ee69aafe15c7303101cca09f3dcaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 14:03:13 +0900 Subject: [PATCH 54/68] Hide children of SelectionBlueprint when not selected --- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 71256093d5..4abdbfc244 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -89,9 +89,23 @@ namespace osu.Game.Rulesets.Edit } } - protected virtual void OnDeselected() => Hide(); + protected virtual void OnDeselected() + { + // selection blueprints are AlwaysPresent while the related DrawableHitObject is visible + // set the body piece's alpha directly to avoid arbitrarily rendering frame buffers etc. of children. + foreach (var d in InternalChildren) + d.Hide(); - protected virtual void OnSelected() => Show(); + Hide(); + } + + protected virtual void OnSelected() + { + foreach (var d in InternalChildren) + d.Show(); + + Show(); + } // When not selected, input is only required for the blueprint itself to receive IsHovering protected override bool ShouldBeConsideredForInput(Drawable child) => State == SelectionState.Selected; From 34d1439f8ef598cf155b2ef8fa21a003b009d112 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 14:04:26 +0900 Subject: [PATCH 55/68] Only update slider selection blueprints paths when visible --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 94862eb205..8fe4b8a9cf 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -66,13 +66,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders pathVersion = HitObject.Path.Version.GetBoundCopy(); pathVersion.BindValueChanged(_ => updatePath()); + + BodyPiece.UpdateFrom(HitObject); } protected override void Update() { base.Update(); - BodyPiece.UpdateFrom(HitObject); + if (IsSelected) + BodyPiece.UpdateFrom(HitObject); } private Vector2 rightClickPosition; From 6b9e94ae93370a686aa62f7efeca2aaa0f9b721b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 14:05:00 +0900 Subject: [PATCH 56/68] Avoid retaining slider selection blueprints FBO backing textures after deselection --- .../Sliders/Components/SliderBodyPiece.cs | 2 + .../Sliders/SliderSelectionBlueprint.cs | 39 ++++++++++++++----- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index 9349ef7a18..5581ce4bfd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -49,6 +49,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components OriginPosition = body.PathOffset; } + public void RecyclePath() => body.RecyclePath(); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => body.ReceivePositionalInputAt(screenSpacePos); } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 8fe4b8a9cf..67f8088dbb 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -24,10 +24,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { public class SliderSelectionBlueprint : OsuSelectionBlueprint { - protected readonly SliderBodyPiece BodyPiece; - protected readonly SliderCircleSelectionBlueprint HeadBlueprint; - protected readonly SliderCircleSelectionBlueprint TailBlueprint; - protected readonly PathControlPointVisualiser ControlPointVisualiser; + protected SliderBodyPiece BodyPiece; + protected SliderCircleSelectionBlueprint HeadBlueprint; + protected SliderCircleSelectionBlueprint TailBlueprint; + protected PathControlPointVisualiser ControlPointVisualiser; + + private readonly DrawableSlider slider; [Resolved(CanBeNull = true)] private HitObjectComposer composer { get; set; } @@ -44,17 +46,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders public SliderSelectionBlueprint(DrawableSlider slider) : base(slider) { - var sliderObject = (Slider)slider.HitObject; + this.slider = slider; + } + [BackgroundDependencyLoader] + private void load() + { InternalChildren = new Drawable[] { BodyPiece = new SliderBodyPiece(), HeadBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.Start), TailBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.End), - ControlPointVisualiser = new PathControlPointVisualiser(sliderObject, true) - { - RemoveControlPointsRequested = removeControlPoints - } }; } @@ -78,6 +80,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders BodyPiece.UpdateFrom(HitObject); } + protected override void OnSelected() + { + AddInternal(ControlPointVisualiser = new PathControlPointVisualiser((Slider)slider.HitObject, true) + { + RemoveControlPointsRequested = removeControlPoints + }); + + base.OnSelected(); + } + + protected override void OnDeselected() + { + base.OnDeselected(); + + // throw away frame buffers on deselection. + ControlPointVisualiser?.Expire(); + BodyPiece.RecyclePath(); + } + private Vector2 rightClickPosition; protected override bool OnMouseDown(MouseDownEvent e) From 9baf704942288a18b60011c5ab9dbee80ca14633 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 15:38:58 +0900 Subject: [PATCH 57/68] Add local pooling to TimelineTickDisplay --- .../Visualisations/PointVisualisation.cs | 13 ++- .../Timeline/TimelineTickDisplay.cs | 102 ++++++++++++------ 2 files changed, 78 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index 1ac960039e..ea093e6a4e 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -1,9 +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 osuTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osuTK; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations { @@ -13,15 +13,20 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations public class PointVisualisation : Box { public PointVisualisation(double startTime) + : this() + { + X = (float)startTime; + } + + public PointVisualisation() { Origin = Anchor.TopCentre; + RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Y; + Width = 1; EdgeSmoothness = new Vector2(1, 0); - - RelativePositionAxes = Axes.X; - X = (float)startTime; } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 36ee976bf7..745f3e393b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -12,7 +13,7 @@ using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class TimelineTickDisplay : TimelinePart + public class TimelineTickDisplay : TimelinePart { [Resolved] private EditorBeatmap beatmap { get; set; } @@ -31,15 +32,23 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Both; } - [BackgroundDependencyLoader] - private void load() - { - beatDivisor.BindValueChanged(_ => createLines(), true); - } + [Resolved(canBeNull: true)] + private Timeline timeline { get; set; } - private void createLines() + protected override void Update() { - Clear(); + base.Update(); + + int drawableIndex = 0; + + double minVisibleTime = double.MinValue; + double maxVisibleTime = double.MaxValue; + + if (timeline != null) + { + minVisibleTime = ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X / DrawWidth * Content.RelativeChildSize.X; + maxVisibleTime = ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X / DrawWidth * Content.RelativeChildSize.X; + } for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) { @@ -50,41 +59,68 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value) { - var indexInBeat = beat % beatDivisor.Value; - - if (indexInBeat == 0) + if (t >= minVisibleTime && t <= maxVisibleTime) { - Add(new PointVisualisation(t) - { - Colour = BindableBeatDivisor.GetColourFor(1, colours), - Origin = Anchor.TopCentre, - }); - } - else - { - var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); - var colour = BindableBeatDivisor.GetColourFor(divisor, colours); - var height = 0.1f - (float)divisor / BindableBeatDivisor.VALID_DIVISORS.Last() * 0.08f; + var indexInBeat = beat % beatDivisor.Value; - Add(new PointVisualisation(t) + if (indexInBeat == 0) { - Colour = colour, - Height = height, - Origin = Anchor.TopCentre, - }); + var downbeatPoint = getNextUsablePoint(); + downbeatPoint.X = (float)t; - Add(new PointVisualisation(t) + downbeatPoint.Colour = BindableBeatDivisor.GetColourFor(1, colours); + downbeatPoint.Anchor = Anchor.TopLeft; + downbeatPoint.Origin = Anchor.TopCentre; + downbeatPoint.Height = 1; + } + else { - Colour = colour, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomCentre, - Height = height, - }); + var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); + var colour = BindableBeatDivisor.GetColourFor(divisor, colours); + var height = 0.1f - (float)divisor / BindableBeatDivisor.VALID_DIVISORS.Last() * 0.08f; + + var topPoint = getNextUsablePoint(); + topPoint.X = (float)t; + topPoint.Colour = colour; + topPoint.Height = height; + topPoint.Anchor = Anchor.TopLeft; + topPoint.Origin = Anchor.TopCentre; + + var bottomPoint = getNextUsablePoint(); + bottomPoint.X = (float)t; + bottomPoint.Colour = colour; + bottomPoint.Anchor = Anchor.BottomLeft; + bottomPoint.Origin = Anchor.BottomCentre; + bottomPoint.Height = height; + } } beat++; } } + + int usedDrawables = drawableIndex; + + // save a few drawables beyond the currently used for edge cases. + while (drawableIndex < Math.Min(usedDrawables + 16, Count)) + Children[drawableIndex++].Hide(); + + // expire any excess + while (drawableIndex < Count) + Children[drawableIndex++].Expire(); + + Drawable getNextUsablePoint() + { + PointVisualisation point; + if (drawableIndex >= Count) + Add(point = new PointVisualisation()); + else + point = Children[drawableIndex++]; + + point.Show(); + + return point; + } } } } From 017a8ce496f74e19bf726ac9ab7a5fdf139aa679 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 15:57:31 +0900 Subject: [PATCH 58/68] Only recalculate when display actually changes --- .../Timeline/TimelineTickDisplay.cs | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 745f3e393b..76428d6fbc 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -32,6 +33,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Both; } + private readonly Cached tickCache = new Cached(); + + [BackgroundDependencyLoader] + private void load() + { + beatDivisor.BindValueChanged(_ => tickCache.Invalidate()); + } + + private (float min, float max) visibleRange = (float.MinValue, float.MaxValue); + [Resolved(canBeNull: true)] private Timeline timeline { get; set; } @@ -39,17 +50,26 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { base.Update(); - int drawableIndex = 0; - - double minVisibleTime = double.MinValue; - double maxVisibleTime = double.MaxValue; - if (timeline != null) { - minVisibleTime = ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X / DrawWidth * Content.RelativeChildSize.X; - maxVisibleTime = ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X / DrawWidth * Content.RelativeChildSize.X; + var newRange = ( + ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X / DrawWidth * Content.RelativeChildSize.X, + ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X / DrawWidth * Content.RelativeChildSize.X); + + if (visibleRange != newRange) + tickCache.Invalidate(); + + visibleRange = newRange; } + if (!tickCache.IsValid) + createTicks(); + } + + private void createTicks() + { + int drawableIndex = 0; + for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) { var point = beatmap.ControlPointInfo.TimingPoints[i]; @@ -59,7 +79,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value) { - if (t >= minVisibleTime && t <= maxVisibleTime) + if (t >= visibleRange.min && t <= visibleRange.max) { var indexInBeat = beat % beatDivisor.Value; @@ -109,6 +129,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline while (drawableIndex < Count) Children[drawableIndex++].Expire(); + tickCache.Validate(); + Drawable getNextUsablePoint() { PointVisualisation point; From 955836916b340c3400efac07e4e385fbb6b4b744 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 16:45:11 +0900 Subject: [PATCH 59/68] Fix timeline tick display test making two instances of the component --- osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs index e33040acdc..20e58c3d2a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs @@ -5,7 +5,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Compose.Components; -using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK; namespace osu.Game.Tests.Visual.Editing @@ -13,7 +12,7 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneTimelineTickDisplay : TimelineTestScene { - public override Drawable CreateTestComponent() => new TimelineTickDisplay(); + public override Drawable CreateTestComponent() => Empty(); // tick display is implicitly inside the timeline. [BackgroundDependencyLoader] private void load() From ceb1494c33285a0219f58111e3e10b1183509c55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 16:46:54 +0900 Subject: [PATCH 60/68] Only run regeneration when passing a new min/max tick boundary --- .../Timeline/TimelineTickDisplay.cs | 83 ++++++++++++------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 76428d6fbc..c6e435b6ae 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -41,8 +41,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline beatDivisor.BindValueChanged(_ => tickCache.Invalidate()); } + /// + /// The visible time/position range of the timeline. + /// private (float min, float max) visibleRange = (float.MinValue, float.MaxValue); + /// + /// The next time/position value to the left of the display when tick regeneration needs to be run. + /// + private float? nextMinTick; + + /// + /// The next time/position value to the right of the display when tick regeneration needs to be run. + /// + private float? nextMaxTick; + [Resolved(canBeNull: true)] private Timeline timeline { get; set; } @@ -57,9 +70,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X / DrawWidth * Content.RelativeChildSize.X); if (visibleRange != newRange) - tickCache.Invalidate(); + { + visibleRange = newRange; - visibleRange = newRange; + // actual regeneration only needs to occur if we've passed one of the known next min/max tick boundaries. + if (nextMinTick == null || nextMaxTick == null || (visibleRange.min < nextMinTick || visibleRange.max > nextMaxTick)) + tickCache.Invalidate(); + } } if (!tickCache.IsValid) @@ -69,6 +86,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void createTicks() { int drawableIndex = 0; + int highestDivisor = BindableBeatDivisor.VALID_DIVISORS.Last(); + + nextMinTick = null; + nextMaxTick = null; for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) { @@ -79,40 +100,39 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value) { - if (t >= visibleRange.min && t <= visibleRange.max) + float xPos = (float)t; + + if (t < visibleRange.min) + nextMinTick = xPos; + else if (t > visibleRange.max) + nextMaxTick ??= xPos; + else { + // if this is the first beat in the beatmap, there is no next min tick + if (beat == 0 && i == 0) + nextMinTick = float.MinValue; + var indexInBeat = beat % beatDivisor.Value; - if (indexInBeat == 0) - { - var downbeatPoint = getNextUsablePoint(); - downbeatPoint.X = (float)t; + var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); + var colour = BindableBeatDivisor.GetColourFor(divisor, colours); - downbeatPoint.Colour = BindableBeatDivisor.GetColourFor(1, colours); - downbeatPoint.Anchor = Anchor.TopLeft; - downbeatPoint.Origin = Anchor.TopCentre; - downbeatPoint.Height = 1; - } - else - { - var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); - var colour = BindableBeatDivisor.GetColourFor(divisor, colours); - var height = 0.1f - (float)divisor / BindableBeatDivisor.VALID_DIVISORS.Last() * 0.08f; + // even though "bar lines" take up the full vertical space, we render them in two pieces because it allows for less anchor/origin churn. + var height = indexInBeat == 0 ? 0.5f : 0.1f - (float)divisor / highestDivisor * 0.08f; - var topPoint = getNextUsablePoint(); - topPoint.X = (float)t; - topPoint.Colour = colour; - topPoint.Height = height; - topPoint.Anchor = Anchor.TopLeft; - topPoint.Origin = Anchor.TopCentre; + var topPoint = getNextUsablePoint(); + topPoint.X = xPos; + topPoint.Colour = colour; + topPoint.Height = height; + topPoint.Anchor = Anchor.TopLeft; + topPoint.Origin = Anchor.TopCentre; - var bottomPoint = getNextUsablePoint(); - bottomPoint.X = (float)t; - bottomPoint.Colour = colour; - bottomPoint.Anchor = Anchor.BottomLeft; - bottomPoint.Origin = Anchor.BottomCentre; - bottomPoint.Height = height; - } + var bottomPoint = getNextUsablePoint(); + bottomPoint.X = xPos; + bottomPoint.Colour = colour; + bottomPoint.Anchor = Anchor.BottomLeft; + bottomPoint.Origin = Anchor.BottomCentre; + bottomPoint.Height = height; } beat++; @@ -137,8 +157,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (drawableIndex >= Count) Add(point = new PointVisualisation()); else - point = Children[drawableIndex++]; + point = Children[drawableIndex]; + drawableIndex++; point.Show(); return point; From 5d888f687ae809c1086ebce68033978475a16c56 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 16:49:51 +0900 Subject: [PATCH 61/68] Account for the width of points so they don't suddenly appear at timeline edges --- .../Timelines/Summary/Visualisations/PointVisualisation.cs | 6 ++++-- .../Edit/Compose/Components/Timeline/TimelineTickDisplay.cs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index ea093e6a4e..b0ecffdd24 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -12,6 +12,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations /// public class PointVisualisation : Box { + public const float WIDTH = 1; + public PointVisualisation(double startTime) : this() { @@ -25,8 +27,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Y; - Width = 1; - EdgeSmoothness = new Vector2(1, 0); + Width = WIDTH; + EdgeSmoothness = new Vector2(WIDTH, 0); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index c6e435b6ae..ce73a2b50b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -66,8 +66,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (timeline != null) { var newRange = ( - ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X / DrawWidth * Content.RelativeChildSize.X, - ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X / DrawWidth * Content.RelativeChildSize.X); + (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X, + (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + PointVisualisation.WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X); if (visibleRange != newRange) { From a0af2eb6c880fc727d15fcfdb662914a89d32d37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 16:54:43 +0900 Subject: [PATCH 62/68] Private protect setters --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 67f8088dbb..9cfb02ab20 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -24,10 +24,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { public class SliderSelectionBlueprint : OsuSelectionBlueprint { - protected SliderBodyPiece BodyPiece; - protected SliderCircleSelectionBlueprint HeadBlueprint; - protected SliderCircleSelectionBlueprint TailBlueprint; - protected PathControlPointVisualiser ControlPointVisualiser; + protected SliderBodyPiece BodyPiece { get; private set; } + protected SliderCircleSelectionBlueprint HeadBlueprint { get; private set; } + protected SliderCircleSelectionBlueprint TailBlueprint { get; private set; } + protected PathControlPointVisualiser ControlPointVisualiser { get; private set; } private readonly DrawableSlider slider; From 144726e3c691aa2c0259cdae6e03f7d05759c8e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 17:12:01 +0900 Subject: [PATCH 63/68] Better guard against taiko swells becoming strong --- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 2 ++ osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs | 14 +++++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index ed7b8589ba..607eaf5dbd 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => { TaikoHitObject first = x.First(); - if (x.Skip(1).Any() && !(first is Swell)) + if (x.Skip(1).Any() && first.CanBeStrong) first.IsStrong = true; return first; }).ToList(); diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index eeae6e79f8..bf8b7bc178 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Objects set => Duration = value - StartTime; } + public override bool CanBeStrong => false; + public double Duration { get; set; } /// diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 2922010001..d2c37d965c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Threading; using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; @@ -30,6 +31,11 @@ namespace osu.Game.Rulesets.Taiko.Objects public readonly Bindable IsStrongBindable = new BindableBool(); + /// + /// Whether this can be made a "strong" (large) hit. + /// + public virtual bool CanBeStrong => true; + /// /// Whether this HitObject is a "strong" type. /// Strong hit objects give more points for hitting the hit object with both keys. @@ -37,7 +43,13 @@ namespace osu.Game.Rulesets.Taiko.Objects public bool IsStrong { get => IsStrongBindable.Value; - set => IsStrongBindable.Value = value; + set + { + if (value && !CanBeStrong) + throw new InvalidOperationException($"Object of type {GetType()} cannot be strong"); + + IsStrongBindable.Value = value; + } } protected override void CreateNestedHitObjects(CancellationToken cancellationToken) From cb96a40dd6eb389668ff2cfec630f47ec027029a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 17:12:10 +0900 Subject: [PATCH 64/68] Fix bindable propagation potentially making swells strong --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 9cd23383c4..d8d75a7614 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -158,7 +158,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { base.LoadSamples(); - isStrong.Value = getStrongSamples().Any(); + if (HitObject.CanBeStrong) + isStrong.Value = getStrongSamples().Any(); } private void updateSamplesFromStrong() From 21c6242f9064b8a8ac51af371640df29eb876718 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 18:35:44 +0900 Subject: [PATCH 65/68] Fix bar lines ("down beat" as people call it) showing up too often in timeline --- .../Edit/Compose/Components/Timeline/TimelineTickDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index ce73a2b50b..724256af8b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -112,13 +112,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (beat == 0 && i == 0) nextMinTick = float.MinValue; - var indexInBeat = beat % beatDivisor.Value; + var indexInBar = beat % ((int)point.TimeSignature * beatDivisor.Value); var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); var colour = BindableBeatDivisor.GetColourFor(divisor, colours); // even though "bar lines" take up the full vertical space, we render them in two pieces because it allows for less anchor/origin churn. - var height = indexInBeat == 0 ? 0.5f : 0.1f - (float)divisor / highestDivisor * 0.08f; + var height = indexInBar == 0 ? 0.5f : 0.1f - (float)divisor / highestDivisor * 0.08f; var topPoint = getNextUsablePoint(); topPoint.X = xPos; From febfe9cdd049e6bef918074c6406411f1db6884d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 18:43:16 +0900 Subject: [PATCH 66/68] Don't fade the approach circle on increased visibility --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index d354a8a726..f69cacd432 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -90,7 +90,14 @@ namespace osu.Game.Rulesets.Osu.Mods lastSliderHeadFadeOutStartTime = fadeOutStartTime; } - if (!increaseVisibility) + Drawable fadeTarget = circle; + + if (increaseVisibility) + { + // only fade the circle piece (not the approach circle) for the increased visibility object. + fadeTarget = circle.CirclePiece; + } + else { // we don't want to see the approach circle using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) @@ -99,8 +106,7 @@ namespace osu.Game.Rulesets.Osu.Mods // fade out immediately after fade in. using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true)) - circle.FadeOut(fadeOutDuration); - + fadeTarget.FadeOut(fadeOutDuration); break; case DrawableSlider slider: From 021777145fd279b0a2b80c9ba7e8cc9992b7729b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 18:51:52 +0900 Subject: [PATCH 67/68] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index d7817cf4cf..3df894fbcc 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fa2135580d..8b10f0a7f7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 20a51e5feb..88abbca73d 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From e618b62ccd2367a67b06b414bf47d487f9d71fca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Oct 2020 19:02:53 +0900 Subject: [PATCH 68/68] Update waveform tests --- osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs b/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs index 0c1296b82c..c3a5a0e944 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneWaveform.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -76,7 +77,7 @@ namespace osu.Game.Tests.Visual.Editing }; }); - AddUntilStep("wait for load", () => graph.ResampledWaveform != null); + AddUntilStep("wait for load", () => graph.Loaded.IsSet); } [Test] @@ -98,12 +99,18 @@ namespace osu.Game.Tests.Visual.Editing }; }); - AddUntilStep("wait for load", () => graph.ResampledWaveform != null); + AddUntilStep("wait for load", () => graph.Loaded.IsSet); } public class TestWaveformGraph : WaveformGraph { - public new Waveform ResampledWaveform => base.ResampledWaveform; + public readonly ManualResetEventSlim Loaded = new ManualResetEventSlim(); + + protected override void OnWaveformRegenerated(Waveform waveform) + { + base.OnWaveformRegenerated(waveform); + Loaded.Set(); + } } } }