From 2feaf2c74a9e6ccdff263a597157abf483180229 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Fri, 12 Jun 2020 19:17:52 +0200 Subject: [PATCH 01/40] added music during pause --- osu.Game/Screens/Play/Player.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 83991ad027..e37cf9a348 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -78,6 +78,8 @@ namespace osu.Game.Screens.Play private IAPIProvider api { get; set; } private SampleChannel sampleRestart; + + private SampleChannel samplePause; public BreakOverlay BreakOverlay; @@ -161,6 +163,9 @@ namespace osu.Game.Screens.Play return; sampleRestart = audio.Samples.Get(@"Gameplay/restart"); + + samplePause = audio.Samples.Get(@"Gameplay/pause-loop"); + samplePause.Looping = true; mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); @@ -407,7 +412,11 @@ namespace osu.Game.Screens.Play if (canPause) Pause(); else + { + samplePause?.Stop(); + Logger.LogPrint(@"_______sample stopped in performUserRequestedExit"); this.Exit(); + } } /// @@ -416,6 +425,8 @@ namespace osu.Game.Screens.Play /// public void Restart() { + Logger.LogPrint(@"_______sample stopped in Restart"); + samplePause?.Stop(); sampleRestart?.Play(); RestartRequested?.Invoke(); @@ -564,6 +575,8 @@ namespace osu.Game.Screens.Play GameplayClockContainer.Stop(); PauseOverlay.Show(); lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime; + + samplePause?.Play(); } public void Resume() @@ -583,6 +596,8 @@ namespace osu.Game.Screens.Play { GameplayClockContainer.Start(); IsResuming = false; + Logger.LogPrint(@"_______sample stopped in Resume"); + samplePause?.Stop(); } } From 6fd8548f79a772d90b08cefd4e508d32d92d3c5f Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Sat, 13 Jun 2020 10:13:41 +0200 Subject: [PATCH 02/40] no longer crash if the restart sample isn't found --- 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 e37cf9a348..4025bbd442 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Play sampleRestart = audio.Samples.Get(@"Gameplay/restart"); samplePause = audio.Samples.Get(@"Gameplay/pause-loop"); - samplePause.Looping = true; + if(samplePause != null) { samplePause.Looping = true; } mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); From 8b8f2dfda2ebfad979ce7fa148d824a80db7b418 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Sat, 13 Jun 2020 10:31:54 +0200 Subject: [PATCH 03/40] Removed duplicate samplepause.stop() calls, removed test lines Since restart() always call perform immediate exit when the function lead to a restart, there is no need to stop the pause sample in restart --- osu.Game/Screens/Play/Player.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4025bbd442..f9e18db581 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -414,7 +414,6 @@ namespace osu.Game.Screens.Play else { samplePause?.Stop(); - Logger.LogPrint(@"_______sample stopped in performUserRequestedExit"); this.Exit(); } } @@ -425,8 +424,6 @@ namespace osu.Game.Screens.Play /// public void Restart() { - Logger.LogPrint(@"_______sample stopped in Restart"); - samplePause?.Stop(); sampleRestart?.Play(); RestartRequested?.Invoke(); @@ -596,7 +593,7 @@ namespace osu.Game.Screens.Play { GameplayClockContainer.Start(); IsResuming = false; - Logger.LogPrint(@"_______sample stopped in Resume"); + samplePause?.Stop(); } } From 794b8673e21b7ccc60e7bac938426c48e3a6abc9 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Sat, 13 Jun 2020 10:56:02 +0200 Subject: [PATCH 04/40] formated using dotnet format --- osu.Game/Screens/Play/Player.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f9e18db581..ce7bb60048 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Play private IAPIProvider api { get; set; } private SampleChannel sampleRestart; - + private SampleChannel samplePause; public BreakOverlay BreakOverlay; @@ -163,9 +163,9 @@ namespace osu.Game.Screens.Play return; sampleRestart = audio.Samples.Get(@"Gameplay/restart"); - + samplePause = audio.Samples.Get(@"Gameplay/pause-loop"); - if(samplePause != null) { samplePause.Looping = true; } + if (samplePause != null) { samplePause.Looping = true; } mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); From 04c1efe298681c82da4b2342b9d0bb78e432d2ff Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Sat, 13 Jun 2020 14:33:55 +0200 Subject: [PATCH 05/40] resolved issues with inspect code script --- osu.Game/Screens/Play/Player.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ce7bb60048..d5e9c54e04 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -165,7 +165,8 @@ namespace osu.Game.Screens.Play sampleRestart = audio.Samples.Get(@"Gameplay/restart"); samplePause = audio.Samples.Get(@"Gameplay/pause-loop"); - if (samplePause != null) { samplePause.Looping = true; } + if (samplePause != null) + samplePause.Looping = true; mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); From a4eb6c81c5a90b15d298fe705cc0699c98da1772 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Mon, 22 Jun 2020 13:40:31 +0200 Subject: [PATCH 06/40] undid changes to the file --- osu.Game/Screens/Play/Player.cs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d5e9c54e04..b6d87e658b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -5,11 +5,13 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Humanizer; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Logging; @@ -78,9 +80,7 @@ namespace osu.Game.Screens.Play private IAPIProvider api { get; set; } private SampleChannel sampleRestart; - - private SampleChannel samplePause; - + public BreakOverlay BreakOverlay; private BreakTracker breakTracker; @@ -164,10 +164,6 @@ namespace osu.Game.Screens.Play sampleRestart = audio.Samples.Get(@"Gameplay/restart"); - samplePause = audio.Samples.Get(@"Gameplay/pause-loop"); - if (samplePause != null) - samplePause.Looping = true; - mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); @@ -414,7 +410,6 @@ namespace osu.Game.Screens.Play Pause(); else { - samplePause?.Stop(); this.Exit(); } } @@ -569,21 +564,20 @@ namespace osu.Game.Screens.Play DrawableRuleset.CancelResume(); IsResuming = false; } - GameplayClockContainer.Stop(); PauseOverlay.Show(); lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime; - - samplePause?.Play(); + } public void Resume() { if (!canResume) return; + IsResuming = true; PauseOverlay.Hide(); - + // breaks and time-based conditions may allow instant resume. if (breakTracker.IsBreakTime.Value) completeResume(); @@ -594,8 +588,6 @@ namespace osu.Game.Screens.Play { GameplayClockContainer.Start(); IsResuming = false; - - samplePause?.Stop(); } } @@ -672,7 +664,9 @@ namespace osu.Game.Screens.Play // as we are no longer the current screen, we cannot guarantee the track is still usable. GameplayClockContainer?.StopUsingBeatmapClock(); + fadeOut(); + return base.OnExiting(next); } @@ -717,7 +711,12 @@ namespace osu.Game.Screens.Play Background.EnableUserDim.Value = false; storyboardReplacesBackground.Value = false; } + #endregion + } + + + } From 9dea96e5fdff2a741af29b10948be6a65d63d634 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Mon, 22 Jun 2020 14:02:21 +0200 Subject: [PATCH 07/40] added pause sound with fading --- osu.Game/Screens/Play/PauseOverlay.cs | 39 ++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 6cc6027a03..6cca0c47fd 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -3,9 +3,18 @@ using System; using System.Linq; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using NUnit.Framework.Internal; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Audio; using osu.Game.Graphics; using osuTK.Graphics; +using osu.Framework.Logging; + namespace osu.Game.Screens.Play { @@ -16,14 +25,42 @@ namespace osu.Game.Screens.Play public override string Header => "paused"; public override string Description => "you're not going to do what i think you're going to do, are ya?"; + private DrawableSample pauseLoop; + protected override Action BackAction => () => InternalButtons.Children.First().Click(); [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { AddButton("Continue", colours.Green, () => OnResume?.Invoke()); AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); + + var sampleChannel = audio.Samples.Get(@"Gameplay/pause-loop"); + if (sampleChannel != null) + { + AddInternal(pauseLoop = new DrawableSample(sampleChannel) + { + Looping = true, + }); + pauseLoop?.VolumeTo(0.0f); + pauseLoop?.Play(); + } } + + + protected override void PopIn() + { + base.PopIn(); + pauseLoop?.VolumeTo(1.0f, 400, Easing.InQuint); + } + + protected override void PopOut() + { + base.PopOut(); + pauseLoop?.VolumeTo(0.0f); + } + + } } From 836386d03ba0da92f7b625d3cc361110512d15a8 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Mon, 22 Jun 2020 15:22:13 +0200 Subject: [PATCH 08/40] removed duplicate lines --- osu.Game/Screens/Play/PauseOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 6cca0c47fd..fc4e509c2c 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -14,7 +14,7 @@ using osu.Framework.Graphics.Audio; using osu.Game.Graphics; using osuTK.Graphics; using osu.Framework.Logging; - +using SharpCompress.Common; namespace osu.Game.Screens.Play { @@ -43,7 +43,6 @@ namespace osu.Game.Screens.Play { Looping = true, }); - pauseLoop?.VolumeTo(0.0f); pauseLoop?.Play(); } } @@ -58,7 +57,7 @@ namespace osu.Game.Screens.Play protected override void PopOut() { base.PopOut(); - pauseLoop?.VolumeTo(0.0f); + pauseLoop.VolumeTo(0.0f); } From 624ad65806da84f82078cc188bc80c2feb4d0d54 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Tue, 23 Jun 2020 13:09:24 +0200 Subject: [PATCH 09/40] formating --- osu.Game/Screens/Play/PauseOverlay.cs | 17 +++++------------ osu.Game/Screens/Play/Player.cs | 18 +++--------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index fc4e509c2c..191bf0d901 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -3,18 +3,12 @@ using System; using System.Linq; -using System.Runtime.CompilerServices; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using NUnit.Framework.Internal; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Game.Graphics; using osuTK.Graphics; -using osu.Framework.Logging; -using SharpCompress.Common; namespace osu.Game.Screens.Play { @@ -37,17 +31,18 @@ namespace osu.Game.Screens.Play AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); var sampleChannel = audio.Samples.Get(@"Gameplay/pause-loop"); + if (sampleChannel != null) { - AddInternal(pauseLoop = new DrawableSample(sampleChannel) + pauseLoop = new DrawableSample(sampleChannel) { Looping = true, - }); + }; + AddInternal(pauseLoop); pauseLoop?.Play(); } } - protected override void PopIn() { base.PopIn(); @@ -57,9 +52,7 @@ namespace osu.Game.Screens.Play protected override void PopOut() { base.PopOut(); - pauseLoop.VolumeTo(0.0f); + pauseLoop?.VolumeTo(0.0f); } - - } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ce790e1315..d3b88e56ae 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -5,13 +5,11 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Humanizer; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Logging; @@ -80,7 +78,7 @@ namespace osu.Game.Screens.Play private IAPIProvider api { get; set; } private SampleChannel sampleRestart; - + public BreakOverlay BreakOverlay; private BreakTracker breakTracker; @@ -412,9 +410,7 @@ namespace osu.Game.Screens.Play if (canPause) Pause(); else - { this.Exit(); - } } /// @@ -567,20 +563,19 @@ namespace osu.Game.Screens.Play DrawableRuleset.CancelResume(); IsResuming = false; } + GameplayClockContainer.Stop(); PauseOverlay.Show(); lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime; - } public void Resume() { if (!canResume) return; - IsResuming = true; PauseOverlay.Hide(); - + // breaks and time-based conditions may allow instant resume. if (breakTracker.IsBreakTime.Value) completeResume(); @@ -671,9 +666,7 @@ namespace osu.Game.Screens.Play // as we are no longer the current screen, we cannot guarantee the track is still usable. GameplayClockContainer?.StopUsingBeatmapClock(); - fadeOut(); - return base.OnExiting(next); } @@ -718,12 +711,7 @@ namespace osu.Game.Screens.Play Background.EnableUserDim.Value = false; storyboardReplacesBackground.Value = false; } - #endregion - } - - - } From 53107973a33a82bf5b1b70bb158d293b183536eb Mon Sep 17 00:00:00 2001 From: BananeVolante <42553638+BananeVolante@users.noreply.github.com> Date: Wed, 24 Jun 2020 14:01:13 +0200 Subject: [PATCH 10/40] merged 2 lines Co-authored-by: Salman Ahmed --- osu.Game/Screens/Play/PauseOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 191bf0d901..2656ef1ebd 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -34,11 +34,10 @@ namespace osu.Game.Screens.Play if (sampleChannel != null) { - pauseLoop = new DrawableSample(sampleChannel) + AddInternal(pauseLoop = new DrawableSample(sampleChannel) { Looping = true, - }; - AddInternal(pauseLoop); + }); pauseLoop?.Play(); } } From 2e8f30461f63db8c651b20a463f8e4f34ae23c8a Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Wed, 24 Jun 2020 14:22:12 +0200 Subject: [PATCH 11/40] play/stops music when entering the pause overlay, instead of letting it play silently in the background --- osu.Game/Screens/Play/PauseOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 2656ef1ebd..8b35c69aa7 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -38,13 +38,13 @@ namespace osu.Game.Screens.Play { Looping = true, }); - pauseLoop?.Play(); } } protected override void PopIn() { base.PopIn(); + pauseLoop?.Play(); pauseLoop?.VolumeTo(1.0f, 400, Easing.InQuint); } @@ -52,6 +52,7 @@ namespace osu.Game.Screens.Play { base.PopOut(); pauseLoop?.VolumeTo(0.0f); + pauseLoop?.Stop(); } } } From 9e5cc1b7a2d19bc7974065d9ed515425acf559dc Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Thu, 25 Jun 2020 13:26:42 +0200 Subject: [PATCH 12/40] added skin support for the pause loop --- osu.Game/Screens/Play/PauseOverlay.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 8b35c69aa7..fc13743fe5 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -7,7 +7,9 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; +using osu.Game.Audio; using osu.Game.Graphics; +using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Screens.Play @@ -24,13 +26,13 @@ namespace osu.Game.Screens.Play protected override Action BackAction => () => InternalButtons.Children.First().Click(); [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audio) + private void load(OsuColour colours, AudioManager audio, SkinManager skins) { AddButton("Continue", colours.Green, () => OnResume?.Invoke()); AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); - var sampleChannel = audio.Samples.Get(@"Gameplay/pause-loop"); + var sampleChannel = skins.GetSample(new SampleInfo("pause-loop")) ?? audio.Samples.Get(@"Gameplay/pause-loop"); if (sampleChannel != null) { From 7d2d6a52c92a6871d9443f1700721eb837716d0e Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Thu, 25 Jun 2020 18:58:04 +0200 Subject: [PATCH 13/40] now uses SkinnableSample instead of Drawable sample Still does not support switching skins after Pause overlay loading, there will be no sound for the first pause (works fine the the nexts) Also, the pause loop seems to play for approximately 1 second when exiting the screens via restart or quit finally, since SkinnableSound does not play a sound if its aggregate volume is at 0, i had turn up the volume a bit before playing the loop --- osu.Game/Screens/Play/PauseOverlay.cs | 29 +++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index fc13743fe5..990d85b1cf 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -3,8 +3,11 @@ using System; using System.Linq; +using Humanizer; +using NUnit.Framework.Internal; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Game.Audio; @@ -21,40 +24,40 @@ namespace osu.Game.Screens.Play public override string Header => "paused"; public override string Description => "you're not going to do what i think you're going to do, are ya?"; - private DrawableSample pauseLoop; + private SkinnableSound pauseLoop; protected override Action BackAction => () => InternalButtons.Children.First().Click(); [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audio, SkinManager skins) + private void load(OsuColour colours) { AddButton("Continue", colours.Green, () => OnResume?.Invoke()); AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); - var sampleChannel = skins.GetSample(new SampleInfo("pause-loop")) ?? audio.Samples.Get(@"Gameplay/pause-loop"); - - if (sampleChannel != null) + AddInternal(pauseLoop = new SkinnableSound(new SampleInfo("pause-loop")) { - AddInternal(pauseLoop = new DrawableSample(sampleChannel) - { - Looping = true, - }); - } + Looping = true, + }); + } protected override void PopIn() { base.PopIn(); + + //SkinnableSound only plays a sound if its aggregate volume is > 0, so the volume must be turned up before playing it + pauseLoop?.TransformBindableTo(pauseLoop.Volume, 0.00001); + pauseLoop?.TransformBindableTo(pauseLoop.Volume, 1.0f, 400, Easing.InQuint); pauseLoop?.Play(); - pauseLoop?.VolumeTo(1.0f, 400, Easing.InQuint); } protected override void PopOut() { base.PopOut(); - pauseLoop?.VolumeTo(0.0f); - pauseLoop?.Stop(); + pauseLoop?.Stop(); + pauseLoop?.TransformBindableTo(pauseLoop.Volume, 0.0f); } + } } From a4bb238c4534c4bce43404c79b2e2402fb78b3d1 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Fri, 26 Jun 2020 14:07:27 +0200 Subject: [PATCH 14/40] fixed bug preventing the pause loop from playing during the first pause after changing a skin --- osu.Game/Screens/Play/PauseOverlay.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 990d85b1cf..81c288f928 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -3,13 +3,8 @@ using System; using System.Linq; -using Humanizer; -using NUnit.Framework.Internal; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Graphics; -using osu.Framework.Graphics.Audio; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Skinning; @@ -39,7 +34,9 @@ namespace osu.Game.Screens.Play { Looping = true, }); - + // PopIn is called before updating the skin, and when a sample is updated, its "playing" value is reset + // the sample must be played again(and if it plays when it shouldn't, the volume will be at 0) + pauseLoop.OnSkinChanged += () => pauseLoop.Play(); } protected override void PopIn() @@ -55,9 +52,9 @@ namespace osu.Game.Screens.Play protected override void PopOut() { base.PopOut(); - pauseLoop?.Stop(); + + pauseLoop?.Stop(); pauseLoop?.TransformBindableTo(pauseLoop.Volume, 0.0f); } - } } From ab134c0ed7f92a2c83a503c679a18d1af1e8a1bc Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Wed, 1 Jul 2020 13:27:33 +0200 Subject: [PATCH 15/40] removed unneeded information in a comment --- osu.Game/Screens/Play/PauseOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 81c288f928..56d0e2d958 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play Looping = true, }); // PopIn is called before updating the skin, and when a sample is updated, its "playing" value is reset - // the sample must be played again(and if it plays when it shouldn't, the volume will be at 0) + // the sample must be played again pauseLoop.OnSkinChanged += () => pauseLoop.Play(); } From ab1eb469af357ecde23288cd14294e91c54dbe7e Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Wed, 1 Jul 2020 13:30:23 +0200 Subject: [PATCH 16/40] removed unneeded null checks --- osu.Game/Screens/Play/PauseOverlay.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 56d0e2d958..022183d82b 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -44,17 +44,17 @@ namespace osu.Game.Screens.Play base.PopIn(); //SkinnableSound only plays a sound if its aggregate volume is > 0, so the volume must be turned up before playing it - pauseLoop?.TransformBindableTo(pauseLoop.Volume, 0.00001); - pauseLoop?.TransformBindableTo(pauseLoop.Volume, 1.0f, 400, Easing.InQuint); - pauseLoop?.Play(); + pauseLoop.TransformBindableTo(pauseLoop.Volume, 0.00001); + pauseLoop.TransformBindableTo(pauseLoop.Volume, 1.0f, 400, Easing.InQuint); + pauseLoop.Play(); } protected override void PopOut() { base.PopOut(); - pauseLoop?.Stop(); - pauseLoop?.TransformBindableTo(pauseLoop.Volume, 0.0f); + pauseLoop.Stop(); + pauseLoop.TransformBindableTo(pauseLoop.Volume, 0.0f); } } } From e8f23e35a572d658536a19e083e451d29dc610fa Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Fri, 3 Jul 2020 14:33:42 +0200 Subject: [PATCH 17/40] WIP : replaced TransformBindableTo by VolumeTo Currently, the VolumeTO calls taht use a fading does not do anything. calling VolumeTo calls pauseLoop.samplesContainer.TransformBindableTo(....), while i used to call pauseLoop.TransformBindableTo(....) --- osu.Game/Screens/Play/PauseOverlay.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 022183d82b..a8d291d6c3 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -3,8 +3,10 @@ using System; using System.Linq; +using NUnit.Framework.Internal; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Audio; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Skinning; @@ -44,8 +46,8 @@ namespace osu.Game.Screens.Play base.PopIn(); //SkinnableSound only plays a sound if its aggregate volume is > 0, so the volume must be turned up before playing it - pauseLoop.TransformBindableTo(pauseLoop.Volume, 0.00001); - pauseLoop.TransformBindableTo(pauseLoop.Volume, 1.0f, 400, Easing.InQuint); + pauseLoop.VolumeTo(0.00001f); + pauseLoop.VolumeTo(1.0f, 400, Easing.InQuint); pauseLoop.Play(); } @@ -53,8 +55,9 @@ namespace osu.Game.Screens.Play { base.PopOut(); - pauseLoop.Stop(); - pauseLoop.TransformBindableTo(pauseLoop.Volume, 0.0f); + var transformSeq = pauseLoop.VolumeTo(0.0f, 190, Easing.OutQuad ); + transformSeq.Finally(_ => pauseLoop.Stop()); + } } } From f03303573ef1bd2fe972b670d0f7ea4a9eedfb83 Mon Sep 17 00:00:00 2001 From: BananeVolante Date: Wed, 8 Jul 2020 13:54:22 +0200 Subject: [PATCH 18/40] formating --- osu.Game/Screens/Play/PauseOverlay.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index a8d291d6c3..7b3fba7ddf 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -3,10 +3,8 @@ using System; using System.Linq; -using NUnit.Framework.Internal; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Audio; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Skinning; @@ -55,9 +53,8 @@ namespace osu.Game.Screens.Play { base.PopOut(); - var transformSeq = pauseLoop.VolumeTo(0.0f, 190, Easing.OutQuad ); + var transformSeq = pauseLoop.VolumeTo(0.0f, 190, Easing.OutQuad); transformSeq.Finally(_ => pauseLoop.Stop()); - } } } From a39c4236c7d8bdaaa7e86f50de4eb8282c4e0999 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Jul 2020 19:08:50 +0900 Subject: [PATCH 19/40] Fix multiple issues and standardise transforms --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 7 ++++--- osu.Game/Screens/Play/PauseOverlay.cs | 15 ++++++++++----- osu.Game/Skinning/SkinnableSound.cs | 6 ++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 6b37135c86..57403a0987 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -24,7 +24,8 @@ namespace osu.Game.Screens.Play { public abstract class GameplayMenuOverlay : OverlayContainer, IKeyBindingHandler { - private const int transition_duration = 200; + protected const int TRANSITION_DURATION = 200; + private const int button_height = 70; private const float background_alpha = 0.75f; @@ -156,8 +157,8 @@ namespace osu.Game.Screens.Play } } - protected override void PopIn() => this.FadeIn(transition_duration, Easing.In); - protected override void PopOut() => this.FadeOut(transition_duration, Easing.In); + protected override void PopIn() => this.FadeIn(TRANSITION_DURATION, Easing.In); + protected override void PopOut() => this.FadeOut(TRANSITION_DURATION, Easing.In); // Don't let mouse down events through the overlay or people can click circles while paused. protected override bool OnMouseDown(MouseDownEvent e) => true; diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 7b3fba7ddf..e74585990a 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -16,6 +16,8 @@ namespace osu.Game.Screens.Play { public Action OnResume; + public override bool IsPresent => base.IsPresent || pauseLoop.IsPlaying; + public override string Header => "paused"; public override string Description => "you're not going to do what i think you're going to do, are ya?"; @@ -23,6 +25,8 @@ namespace osu.Game.Screens.Play protected override Action BackAction => () => InternalButtons.Children.First().Click(); + private const float minimum_volume = 0.0001f; + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -34,18 +38,20 @@ namespace osu.Game.Screens.Play { Looping = true, }); + // PopIn is called before updating the skin, and when a sample is updated, its "playing" value is reset // the sample must be played again pauseLoop.OnSkinChanged += () => pauseLoop.Play(); + + // SkinnableSound only plays a sound if its aggregate volume is > 0, so the volume must be turned up before playing it + pauseLoop.VolumeTo(minimum_volume); } protected override void PopIn() { base.PopIn(); - //SkinnableSound only plays a sound if its aggregate volume is > 0, so the volume must be turned up before playing it - pauseLoop.VolumeTo(0.00001f); - pauseLoop.VolumeTo(1.0f, 400, Easing.InQuint); + pauseLoop.VolumeTo(1.0f, TRANSITION_DURATION, Easing.InQuint); pauseLoop.Play(); } @@ -53,8 +59,7 @@ namespace osu.Game.Screens.Play { base.PopOut(); - var transformSeq = pauseLoop.VolumeTo(0.0f, 190, Easing.OutQuad); - transformSeq.Finally(_ => pauseLoop.Stop()); + pauseLoop.VolumeTo(minimum_volume, TRANSITION_DURATION, Easing.OutQuad).Finally(_ => pauseLoop.Stop()); } } } diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 24d6648273..fb27ba0550 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -45,6 +45,10 @@ namespace osu.Game.Skinning public BindableNumber Tempo => samplesContainer.Tempo; + public override bool IsPresent => Scheduler.HasPendingTasks || IsPlaying; + + public bool IsPlaying => samplesContainer.Any(s => s.Playing); + /// /// Smoothly adjusts over time. /// @@ -94,8 +98,6 @@ namespace osu.Game.Skinning public void Stop() => samplesContainer.ForEach(c => c.Stop()); - public override bool IsPresent => Scheduler.HasPendingTasks; - protected override void SkinChanged(ISkinSource skin, bool allowFallback) { var channels = hitSamples.Select(s => From 77143952a91da519665c0a13a19abe01fb97275a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Jul 2020 19:17:48 +0900 Subject: [PATCH 20/40] Add test coverage --- .../Visual/Gameplay/TestScenePause.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 1961a224c1..420bf29429 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Rulesets; using osu.Game.Screens.Play; +using osu.Game.Skinning; using osuTK; using osuTK.Input; @@ -221,6 +222,31 @@ namespace osu.Game.Tests.Visual.Gameplay confirmExited(); } + [Test] + public void TestPauseSoundLoop() + { + AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000)); + + SkinnableSound getLoop() => Player.ChildrenOfType().FirstOrDefault()?.ChildrenOfType().FirstOrDefault(); + + pauseAndConfirm(); + AddAssert("loop is playing", () => getLoop().IsPlaying); + + resumeAndConfirm(); + AddUntilStep("loop is stopped", () => !getLoop().IsPlaying); + + AddUntilStep("pause again", () => + { + Player.Pause(); + return !Player.GameplayClockContainer.GameplayClock.IsRunning; + }); + + AddAssert("loop is playing", () => getLoop().IsPlaying); + + resumeAndConfirm(); + AddUntilStep("loop is stopped", () => !getLoop().IsPlaying); + } + private void pauseAndConfirm() { pause(); From 50f72ac9cb90074f647d76085b515d4ce8d9b45d Mon Sep 17 00:00:00 2001 From: jorolf Date: Wed, 22 Jul 2020 22:10:59 +0200 Subject: [PATCH 21/40] rename classes --- ...neHueAnimation.cs => TestSceneLogoAnimation.cs} | 10 +++++----- .../Sprites/{HueAnimation.cs => LogoAnimation.cs} | 14 +++++++------- osu.Game/Screens/Menu/IntroTriangles.cs | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestSceneHueAnimation.cs => TestSceneLogoAnimation.cs} (85%) rename osu.Game/Graphics/Sprites/{HueAnimation.cs => LogoAnimation.cs} (79%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHueAnimation.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoAnimation.cs similarity index 85% rename from osu.Game.Tests/Visual/UserInterface/TestSceneHueAnimation.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneLogoAnimation.cs index 9c5888d072..155d043bf9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneHueAnimation.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoAnimation.cs @@ -11,14 +11,14 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneHueAnimation : OsuTestScene + public class TestSceneLogoAnimation : OsuTestScene { [BackgroundDependencyLoader] private void load(LargeTextureStore textures) { - HueAnimation anim2; + LogoAnimation anim2; - Add(anim2 = new HueAnimation + Add(anim2 = new LogoAnimation { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, @@ -26,9 +26,9 @@ namespace osu.Game.Tests.Visual.UserInterface Colour = Colour4.White, }); - HueAnimation anim; + LogoAnimation anim; - Add(anim = new HueAnimation + Add(anim = new LogoAnimation { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, diff --git a/osu.Game/Graphics/Sprites/HueAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs similarity index 79% rename from osu.Game/Graphics/Sprites/HueAnimation.cs rename to osu.Game/Graphics/Sprites/LogoAnimation.cs index 8ad68ace05..b1383065fe 100644 --- a/osu.Game/Graphics/Sprites/HueAnimation.cs +++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs @@ -11,13 +11,13 @@ using osu.Framework.Graphics.Textures; namespace osu.Game.Graphics.Sprites { - public class HueAnimation : Sprite + public class LogoAnimation : Sprite { [BackgroundDependencyLoader] private void load(ShaderManager shaders, TextureStore textures) { - TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"HueAnimation"); - RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"HueAnimation"); // Masking isn't supported for now + TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"LogoAnimation"); + RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"LogoAnimation"); // Masking isn't supported for now } private float animationProgress; @@ -36,15 +36,15 @@ namespace osu.Game.Graphics.Sprites public override bool IsPresent => true; - protected override DrawNode CreateDrawNode() => new HueAnimationDrawNode(this); + protected override DrawNode CreateDrawNode() => new LogoAnimationDrawNode(this); - private class HueAnimationDrawNode : SpriteDrawNode + private class LogoAnimationDrawNode : SpriteDrawNode { - private HueAnimation source => (HueAnimation)Source; + private LogoAnimation source => (LogoAnimation)Source; private float progress; - public HueAnimationDrawNode(HueAnimation source) + public LogoAnimationDrawNode(LogoAnimation source) : base(source) { } diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index b56ba6c8a4..a9ef20436f 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -260,7 +260,7 @@ namespace osu.Game.Screens.Menu private class LazerLogo : CompositeDrawable { - private HueAnimation highlight, background; + private LogoAnimation highlight, background; public float Progress { @@ -282,13 +282,13 @@ namespace osu.Game.Screens.Menu { InternalChildren = new Drawable[] { - highlight = new HueAnimation + highlight = new LogoAnimation { RelativeSizeAxes = Axes.Both, Texture = textures.Get(@"Intro/Triangles/logo-highlight"), Colour = Color4.White, }, - background = new HueAnimation + background = new LogoAnimation { RelativeSizeAxes = Axes.Both, Texture = textures.Get(@"Intro/Triangles/logo-background"), From ee05d5cb14b7d946a0335f9f7208b6213da6ed57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Jul 2020 09:06:15 +0900 Subject: [PATCH 22/40] Remove no-longer-necessary play trigger on skin change --- osu.Game/Screens/Play/PauseOverlay.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index e74585990a..fa917cda32 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -39,10 +39,6 @@ namespace osu.Game.Screens.Play Looping = true, }); - // PopIn is called before updating the skin, and when a sample is updated, its "playing" value is reset - // the sample must be played again - pauseLoop.OnSkinChanged += () => pauseLoop.Play(); - // SkinnableSound only plays a sound if its aggregate volume is > 0, so the volume must be turned up before playing it pauseLoop.VolumeTo(minimum_volume); } From 897ab4a9bb2de4b5ffd2cbb9510cd74c758cfc20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 25 Jul 2020 11:53:38 +0200 Subject: [PATCH 23/40] Add example test resources to demonstrate fail case --- .../Resources/special-skin/pippidonclear.png | Bin 0 -> 8462 bytes .../Resources/special-skin/pippidonfail.png | Bin 0 -> 10643 bytes .../Resources/special-skin/pippidonidle.png | Bin 0 -> 10090 bytes .../Resources/special-skin/pippidonkiai.png | Bin 0 -> 12368 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonclear.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonfail.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonidle.png create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonkiai.png diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonclear.png b/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonclear.png new file mode 100644 index 0000000000000000000000000000000000000000..c5bcdbd3fc13b3ca847cfba5402c2db38cd962c7 GIT binary patch literal 8462 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonfail.png b/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonfail.png new file mode 100644 index 0000000000000000000000000000000000000000..39cf7372851ff962b02d9184fd9169faf06c702b GIT binary patch literal 10643 zcmXY1bzGBQ7au)(qooJZoyusWTRJ2L(jwh013^jzDe01u5D=sp-6$a;BHbyS@5Ar& z{_%Xa-RIt&y64<;&-X-YYbp`qQR9I?AVL*oc^wc4jr-vNV*?|8LGfO|2d=BKu?Gl* zPxA1f{agsX0R|s=Dj0d{y4ZU9Alz+0K0ZFY4zHa&tP!p@ye{r`pY|oFK_EtuioC3z zZ|2^Dk1v_m9nRsoo%fT7$939;;oy?cH<({b8{a9BoSbmqaNAKnm+vvx64R2b$#UPG zJ+03A(Og@jLS_te#*mdE##dHKBr!pEX#RJ3E1Nh{Z0FN1)SUC{--X2P?(XoXYCrK) zaZDIBJ3H@wQI}{`N2@1Yw7l|TMJCd7&^W1lv}HUFgt!Ac6yzy+a>NdDdwbWyAhVDs zeSp>h+D6k0D|wghLsFfT<}H}_<`H>DBp$Z2oy$mM=I}5b>{<<9mvIVBe)IbaUZ;8% z??&5B+;IUTHhKb-r4=>0C4Vp%?bI2@Oh@-l=S;4Ev*q%sIeuG0d8zS=>r;s`jCbQ7 zPfTzgd`sNge&@?{ncv z19}+jw(D3F<8KBYo!OLOL{BH71RH86|JSp>Jv^NoW5q_R;&iX}!v~@gB2tG{N)}{h%mNz; z0uBzr95?f#{{d@GJ4~rOQnM>A46K5y7rsZ~g|#sGz)2Ss)O8u9|GDZ74|{x_*vo-J z%ZW%wS5w)P2Zx57#mibZK!M_5QoRi*tcE0((n@0LA1#qm+&2G`G9Fh)=4&6j5w%}6 zqfg$9TX@mufvoYP)~ECCdTJ*oxb*vQ*>sH1bhN7lASd;3FPeeI>4(}_GFnn_e zVseqk{2?`;ez|3&HB##yTMig<4pvpYiw=>F4-a3!{b8^Z?W$yual2R+o)$-DK=V{h zwb!xfi~u9OtTu{YrcN1WB;$i9B4V4}78Wsuhd($-va@r626+v#1PCL^>5x9JD1HkJ zyk0DZ$No@3h!nK6{Qg$ZD~|v78qL!Hm{Q-$7|dQ=XW2Xv%^YG2!{u)jM z5u9pF-&?DZS*Pz)LO+VrNKaJ0;m{QH6W}_3&$qNkE%oMgeo)-@J zM0;5BM}8jagL+G2bGI$>vLvKQ_bY^0)~Nk<<^^rXuaZ$F`3}gRvd2OGg|X;RXoP;X z{Q$^pLOMB7TERDeErW7HBRj3d-0OoS{xxcli(8Uh{@@vX9t+A8iAJAa6#`#qL+rbxU(cbiMT<{DYAAdx+F3q=u@pFEj08&##+6FD z`aLBgq4JKX&#mCDY0H*Ie_siYfs{eC6<2R>SL^Eb%4#unjK~dwRR-MPPMTH(x`2i; zj6OSDB@dLAN|z9kup?@#Otp1B3^o~X+OTv(`?i}5c3->@eD3vL1~&%fPVvz^e-LB zK@0hS7{v;{FfI{4R%p#OyECE;l`&E=8#8GB5+w}C&o6G(vaG4q_F6Qt60YWyzCJ@N zr0ecBi?3rKhlx#E%tD#k+B;l4>nfmuSfflGLE|^`B7&iY$uE1(!qdjpqMU z0{zZ|zlAJCyLeK3XVA&Y7a?fpNHf&)#_!N7ABD+dHnNE?yxtT#h~%xf$^V*8aM!#| zD<@B|JpXkla!{*9GG!myW?cZILp^>7?wVMC1VCEVd*e5>9xu;dbE zMlP;}eI2?&SQmLpZKE%HO%LJsL}TzrZ?u%j-IF8fG9q{3XIU&allp%!s#j$PRl!qD z80L1eZZ&z4luy(V4^iF3Ia}+c1Q(28#VD(OOp1r^pb*}Vh-3%!hFK*_Ec0%gTP~{L zhp$uIIbp#ILYF8MC%d_Of&{-DxL~;yi$#sVZRy+b^ss5ms;9j_8Yt#rKI_&vJDa%3 zcNrUSQN*dj^Gg|Gi6KUu*$Dn{vy9XadQu3JS>C*%wRcAy|6G)faS;>?v#9$49*$^z zav5X9lwyw*F{8@VRRqMjuOkg}+FYkTm?dJ3+tZ>awp_2m6#PRrR_))rL0-d}?e`2_ za8H(NcMxu#c>{#W@@z-_v-*ItIo;sLNYDS=RpeU>1_u{xm{*fI_%jf9D1bM0)sA1X zY=z#_4tn9i-mp5@#ckYaSMtAXM*1o2dx9)vD;r3dGB+!BKCitj=Edt7l+!V?NKW-R zn)EdQQPI9T#f8Z^)~4=gNeMAq`&1jgBs{|db?C16Ve1%tIU8t~?)WNrp7;v`n+PQ$ z(fQ2vG&UWNG<_6QZW}<-G1Y1Qd>o&vy?@VAW`IMx)};N6 zjhll*>e&AvTM};q+3qF%<>=DSP=8-Htv55x%|y=R}2It3*K9{DSzn8lf*@t3nblS1mV{3(5a+)QdiwZZiVA zu6mu4?-9m0dDx3$2r3gS%+p@IyDGyglp9eLmJ>#6Pbo+H60zKlJ4*M$;odS9&W~nHBWfKBVjhk5Q!X;#-GV&s;oqYn}ac;h{dxV+ufrUlJC~X zx?9;K2*KZWMSOozNrk;nBu@pCZ(PPfKdQ?jqBPLZ8zz8U`FM)SMO9Bf$==F{=Pi5| zq5p84rtk0CgJJR)%*6rHfFDdI+IW^!pEPdM;DRrvDuDT(u|>T?1)DJ~4s1I``6qQgMRHN# z;t8HTy5M57i2ByK0B9)M-em=Z2?f16s5mko&rbMOUM;+AI`>Zuh6I7I7YBx&gQuT; z0=7rBCkcl2+!ZkXba23rMUB?^R{Ilvj7syDFF8=TsK}`ABW6E~QSQ9nIpD+9ZbKwh-JpDlf`PZCT4)Kxv-Ja-z1YDa>DUpYG%BK>P{Oj zUWh6Ao^0aWfMy+BnEVQfQ@+)h!`|>0UT!OOCkuKQ(q56KsYpLUq`mspQ;bJ@T91(s z4fnvde#r_Ep>JgX_YUvF_vE7fGFD-Ip-!ypLAif_VE{_?dinvNv-j@z@81+TjZ;<# zeWjjk>|8?`rUx=tp8MkbwMFSB+}u{#)M*lO;2*-+cU47x89Wu?&t$aAwZ!mX@;I43 zn-IMuV2n;ATZl3I1n|QGr7WIrvo!gpGc3sqvM~A0&A+xj_@B5d!nGWW7ICB=s78!B z?{oanpfEt~52}Eguvi{Mt~5pcDPsku&a({T%25G=Q3%;RIW-rGP%!QUGb14*FvX;W z346o{A2$ur;rL$#5+Jt4xl&L*nUjvANGaa>>cxW8j`UP3V5WJ}rq*wgbp2iq4E3D6 zfN`{=27!2n)z}l@kAyJ!UI>4lc(mYJW`SS<^5|S$Wx)rbm)YBt{JWxqBNWUZm1uzx zpw-hIrgoIoa@}|#U{YJirB|XBg04vy&hat~3CVXji!69@K?dyN-$tM#SRA1FxR|Q= zc>16d%p8=}w2hB87(tcs`e^HM4vHy~ECqt%Py4=wLWi*wIsog%kiGa$ZB7FMpsNUp z_kaqVLp_d`v)@|Ka&W)u9NaME5kJEg>t2a;?CZeSFr>B; z^MwmSKp9UBjCeY{Yifj3#kxj1`Jk8L@sh5DnqQ#YX^JQ4GO<6^0RuM9G)jd>qQCXQ zRgpG#pam2hsUY5nQ-T&cN|i4f572+sXZcqf0VQgK$bp%E)`!#Rza1kXGN4od_)rR* zq@XCQMnZbmYa>LI5fEVgKqqki@a|(hsspt33ipzQWkG~abhe2^j*8x|9+pw;;W8x) z2+Cr(IYevd}yR4V# zsiR)v1A~FF@^NX+C8IDTgonvXjPNJMvQav8@)uu?xT}r0%dF)IfMF~Jr_fyzJXmZ(C_CYM z9zeky@@fcN*f5iclONz({MdvtS=)dze&E&X7|DTaOQt}RWttCcAaXS^{^OjW(EvjQ zSfd_VV!)hYCI=V^TA=y=A?TN*eq9_s#@}Ska{|!TrG~+tI zMzA3HC2aNV<-e-2Bl)G`p6At64)Epy(cN=7E2DVeK|>e9>v(ap^ZGczPE3K+(NPCD ziIxyCJ+KnfmJMR8>Nmq>GXI+v!*z zDM!=e(s;wpD$n(c@koywjY8or&I3lIObUsZt# z>SlQi0_ErDL+I$@Hq(qex8}$*SjP^?ojZ&^WFfS$K|9Y$+tCWEyJd%Cv&6rx{IUq{ zoX%;m+~Je9-WiiFu<9e)U1-L+KHKeVYYpfeb~g9k;5GYEWNPTS^^;uE{|qH?dzG)X zc(*xG#c*}9_qID*QdXXaPgx#2--YP@r~$=vN5O6JLAh`TF8akHq24LDTdUmAdT*Pd z#(Tt(cBWmLeP_PGhxlx7A$&xmg@KYG!8(B^GOtVNZzGhM-*d*5fL22O+b|RP)*}5~ z!D)lTh2j;bV0ESSL@(jR{=7?d;FeB>(KoV>*Jq{iUfp+PoPo_&Vr^zMH~n=pGe)%1 z?qt33IiaLVIY=m5JaWp>*_MOp1tcj1vGt<;1G}mQ6Vwyu>_F*zGZ{-njP|~_b%=h^ zwz9L1nOWT0np#auAP0z13WlaKz@a>l8Fh3<*6qDr_%sika^=61h&rAv9^`U&K^x~= zjh5HmttyjWLPfBfuc8L4QuEr{+7^bkrzMa21&T+_n)5wUc{it9#hX0$A_3Ms-%0x( z1gd)yym~GQIuwIg=o?38Y&-5Q)ZzqqAD{qz*Z7RRzPTD2o4NYOH`7{v+x2z!m1w2} zFRlYAUBBy72xZRrz71~IY-ZML2Hy*Xa1>_%!{5@pvB&5Nzr46DnlLR^N0hdf6-vsT z=EGZ?+0Ht~m%51Wp1i`mBBqLxy1jzISy`2%iOWh$d#j}H-|&1Awc5*=x^Os6FlzSq z`V_;>38!{Q|7@k>P1MU8hcn>D=fT{Pnq}L5(U7V%z{T5&t3DPf^>MRgq1hX8r*tpd zX_sTUaW)+{pqvre`7FLi<#Xnnq~7c|q>6Eh4g1lunscJfoZidV_qW80+!m28DIsxj zJJa=7!!O6oTLmtMYx&oRH}aesUdw87z6qlesukQFWAF<)`Ggg*?*`A4IH{2jaDLHF zbRTqYVPy3FSD9Jcs96)E7!Tcm!y_t5TW+||7Rj6Buv(V``oMpuzg#IP5h^Or%QY$F z6%>~028^HDZAVZEug*XU$-?rJM7>q#kmPSIuE|n{*1k^9Sou7|{wLIOaDUG8Cul{4 z+f-+InL;VSy!CzXwuACOe|JezA97?34TyC2rr*)`Q9bN8jxbK|U3>3fCR8KtK;y+= zxf`nF2TYX%ZfUW`_o+!M3qBR#`(`nM07$9%&@w508s$%kR+z^4Ec((ZSn$01R`N=e zW_K(7sU#PT${CpvcM7DKKyuov;==z?+ptHzeIkCnh{(vOw5WqbWy>8l0gYQ=6v;d9 z3kmTBsmo2=VBkjJE-l}YBbTaD*PEh2b2MgXE_Y;DX&d9%kt_T3`sTi?)GiZRV>fhW zsE<1B*|@j}fAMN|@}yry9$BYL*SI{lGw#yS!MZ`}$bV?5*>sy%@cEL`pyt}An;mS% z{UdWOgF5hVf4)CxCufAd0Pc380;usG=A6&`%KH-jF1^OeSWId(h(S#?<`cJn`P4g( zkf)TXTFsi38-fs4xFj2o-_nnX8$6(D&@(w>#Rckb&$NoD^vwmvdh0RC;6jnc zsE`y3dF_51%@kcU%I%{K6rM-)=VIk}S*)I{6_b!#^+1N1E!4uDbq{W*SrgLggAC|~R5UApE zDL^7{QF5kj&#?VmSfeZ1WAs7rAwG@wdin;-IehI=mkjrm6+6KP=lc^l=hDY%yTNZq ziD$RYNT~w~1}0PpMv2Yo-DXlRHGcYVr zmsn1Aq&p67olP5-r?-AF-cM+e6~;(l3eQT8WZipX!(yHmwzbiZN!=MrTpm6vDL)m1 zqBEf$IFep(jrn2S%c)A`r%5G39TH+q|1~(|+v01VpdiWYtbgoxciptUaQ^KG`*h8I zr|ni&AKambDxsr9JNhPlvL5tRh(7t9`+=F!<*8u%IpF8-#Oc%G3STzAdAVH8!qVfr zo%pue<-olCyf>C6L0a;P3f|Mhp-ge+ZRoA`)ONc+F^)*+=PHE(Pz{w7k|Hv%?5``g zV%u+VQ!KC%@w{-AxZd8;F~H`Bi4cyp3J^1EO8huzB_B#z{zGrf}HRMpuea>r&F@??!$;&!?GX|a|C)_}aV|Jin2UrqqjZdLs# zz@@9HzVSA4Yvj)r^IW`fK>PRePdpX&T6^fB8SPl+b%VX9JFXkUJGVrYNySS|pImx) z&VRoxVSkiAXAhj;t~;$EsFP2&mxS1j&N~Lb#XPV@U6Oy|5zIkX7TQa`i)HX|zwKIr zttX(t~sc_l5@ONo9W^pU=RjJC;O`-wDp6q`p~GXzf_gm5M&xsp?xN zq?yFTXT}WN1d8Y*BNOkMUmAPki~mjKF?IbFCsbW-^?B^1@d8ugs^NRTM9_!)m-%0G zgtU4rvi*Bq$YO!Ozx2n{1vQ-$Vvx)&vDwrxyC9oO9zniWdigHQIV_sY*js?+@QXcu&geeuvzyA8`R_sE`{^RJ&xj|YT-)_-n~F;Qy8}JQ zWNB_Gs1V;YXCmP_W)|F6vAn!;bf`P_cV;D$LzNZ24S4wBOzqh9$LVihOppH3HRKf+ zn>-i%XLa~VDS>+~*?=&!do>7GWGH2QN{Agd}B`)}gdM@3770 z;nKxWQpmq%3a60->Ay8NcbKh-rh)QFMiUot)59}ms6iDqQ~dlMx*;+#^MEC>WbV5` zyNnUruAB{t*-bA=RAxMb7#{J>shg3rXwH@mi-PYzmi91gdEGQh+oIyT$qZvtNnw`S zJJoAah4`-@m~J+1&jX90(ARC(=3=(wk(yFAw6EI_(ArJ?qyo>KlPN?4BF$-wf0Td8 zuAjnRXt;g`5;x7{d$TvwMyMryOxX_Q;cGi~%#hO_wTaG$1X>8<|#zx3&bkdWb~ zYx760eCQCU?LrVk{MP)K-PQU1HN`>eWmCRhWS=?yoM%a?M#RS?hx^L!HI7VESM}0M z;XZ$k(`{>KUC(nEQpB&Am0tu(><4WkW~gbAA@^TGME&pW3$2$gA0UOrl(D)?KkW)9 zt@=};CG}rk68~ljf$F9Fo<2?;1iTJmYz!a>9gPrZ*K$Tux=aLGMQNIlfkw5+$?9z5Z+>WtlXzn9f zVNjkgkFu{Y;HpduZXFZW3X=R2D3xG>Pd%o3xSa zh31;-j`O+MB&|npfdsvfJ(0f&u#VN_fOTMT(9#X}GWIp|c!3=Y3*BbFAY9J~1TnR^ zWuh{LH__H!taiPaoVt{X*d(SjFJzD7kMZDZnc46H&Xe_!6AJ~0I;ShFs8i%6Q3;F&4tQ_^LJ*K0Tl5A{393FcrSB;X~hiNh3H2CKXLZ!UH59N z2IYPYK$U~0ajc9LzXck9U+fYZOZU%PT**P&M@6}w`L$^&mCe*(ED9gXEbyP`ju|=0 z@XFx#yUaZxcTuiNj43uRE!M?@%#{=r5cv|PigiD`RqQSnsBUd!FA`W}kn>4^u`y~Y zLY=8!7R}y|bWnzwWdG1JG%R?TcmV!zc0$muB!&AU`2vag__N)GI@g^eqEWie3IU){ zD?l+Y_gav8f(~R7cpUMF$HHX&yaz7HrSzN{VL+&q{iU+9_qa%nya*RwRk>A*{=FlV z!oWcBSQRoCL|&fwv3WtNYGs+!brAVd!2xN^GOG)rWH=CVHcoGj{;qM*!%zaG+pD)OdMyfe|;5wkm|U5apX=DZ!ZL-dIBpS0*gPUe&kdo)oYeKot_Odx9`B` zViWSfA-!iAAJTW2h&-LSD?h+^-Z!sHRs^+u!aKYF&wqaU%gk4Yf_!0FIf-j-C7lii z6Vl{=M*?0V9|^O%21D?n-$H{$$m6r!QeZzco3|!(3IttUsXJZJ2?-E;wl%eO_0>JL zH`IvF1&HoEJiGp)+Y=P1~ zcXp2n{%%2pFUkb<5i;bl$AKvcb4pfAfkQWrW)g`@FzuNwAR;1CV%Uj&%i6dbn_QK4 zWLy1vN38u2zD*GGzHZa5;C{K1zxuM6Oz6PNi4PkY4JW8;YVhDrZOg&m>%Iy)eFjqZVvy_FAoAXy zQ+Q4SeNEDI5u8dL{3|vg^(?ktl4MW>03T?H-u4ZpNTY)X)~y*QrzbC3;-jOVC(9#0 zZQ3s$=x8{<#aSgN;*3e<#hrX=_=Ws9PY}p~+lg{N$+iADb2Ks{Y=0?NXQ*Cr;8VKf zGcpeTbq}D78YPD1FJCS0o~(wTpOn4xYS4Ff4o7|9;f68_2-HYbjp!H=1Ph3d+sx@@U`E`Wb%g~wnx-*UCj1aJ(xmZyN;LU{Srq->Js3v5EGg=Vj^T2M|Aq zQJ;U7B%9C#KU*(BO@!A>;p!@0$0z3#p|^7Kp?Fyw^UA=oifRxe8;>(j#|AoopT4IY zGKYHla+l-LK)F7SL%(YT76`adF!7sVktXPIB2~69+a%b8ij2J8_fmZYmWnf9N?QFc z0U$vrGTqnKz+bg;n^8mH6#`AhAoA7^`}OmzXHm)>!{@7oMK{m9;@7E~9tbYn5rByw z@WQ9efAcb9lf}wB>bkX4MTFU^`fs0xiUV~$rd|GZTe}$-1G)kcbzPto8_9Q#1L|6Y zaU!Ez(m$rWO2dCWYdQ`uV{QvO@j<^Wq~@fnQakYQ|`+b0~OhI!C+xKoTR- zfOP~CtUG2`#Ik5#{1sijF2ain>gKI@>^vOU0HAtlQ0T~jX2Y1UDX{Q23T_hYHk&p4 z>OzP{jtUdQ)6m=hn6xasF0~Xm9gkxME3FUz@h?s1Ltx#E*&nk~1P#63$MXjv&_Zix zf}{l2(fCi^$H|q*)?>c-_%ZvYa{DnIIR&81|FHKQ&~IHY3K+pl%COM^RV`jUy2lVy zaW$yJ_=QCBj37e?7?4CeVH$ld`9Gjj6F{B4ezQH8$-$o!4Z!N5ay2^BPP(Ylico$o zZcx9f#saySh2$f~u0L+P^4icpF{M>fBlh73SaL>tB=m9VrMV@$tgI4f9mVhj2<)%T z`ZdtGk?`q=x_lZukAlB?XeqFgObz|RGsc5-*n7Ml^HorX0>Hb~5f(vB|&UucUs--DAQQvuePa=|Y8j{b^|lgS}4QzNAz zF3x7!zwH>x+F1%wL^C)j!dZv227MgdqNoSfxp?RZ_*$_T%>a$e#nvFXuULRq2R@Erm1AEXlwn(c1DgM_MPB%`U4@7w$q-)DI8Q|Z0AFZKNBzNe%>>LS zY|R|TOPFf$+FFANwex<15pM+%YGKW#`R?Q^`b+#FrQqkr*x?po zzhl^A5p)7t#OF@sb4<&?vXqb6FN{jJtUGV)`fGUd;&EcX&)v-=6;=ceP#BON;}2=t z{7X@z43#YV7ggOr)BU7x(vVHX zSVZGnwF#`fuuwdp9!E9}t)mQ{0Oo>&59|;BNQv<{H*fPRvPDc`W3jOG?KG{qfb2>~ znNV-=;kGt*bM4RRy1D=BTq!wu5tgty3zs1D?nsnCPzb^ye zS*1W21{TAzhs|LpoL+Wdw)oAt-41KsJykbOLryWc!sMHdV4_x?xOJ z6`l(;E+tx!^&G3I##@^;Q+KR^t^j$cPw_)T1pO?AfV*EgT>UZ7vlTTvY~t=_%t=KV z%1`d@fOKq@NgIQQKz2$8Jb-j$Dhx@M0$W>OHzBzqQWw)Rr$pZ@g6kq@2vK$w|8QEZ zoG;yr$9xN`2`c|<{uvFR7P2E{rs;5h0EjKi|2sax||NHHyWh)hM%Z|3gq^#z)O!VG)Km$7hZ*aNEg7Z_W3F6 zO&L55OwUmPPJW=UBV=EqutJ(^U0sO)3q*pb2rR*|wWi;Ju0Rk?TDAE^O?LDz)0UGU z72D&vS^1{rK&Ea0OUijTlK#NycUks}1Ww0sAG!*Q?_&=ozZ6)5awccNQHVsv^%@W3$59lN-Wp$H2OrH>b|+@xVtNq+>N4W b?-&p%=c-}*c9uXxB}hdQfLfd!P#rCUH62?6Pr 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonkiai.png b/osu.Game.Rulesets.Taiko.Tests/Resources/special-skin/pippidonkiai.png new file mode 100644 index 0000000000000000000000000000000000000000..7de00b5390494b7406e7595de865a8d6a659adcb GIT binary patch literal 12368 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 k73Wz0B#q`)9?)YNC#MoxTnmB!20&4eQ Date: Sat, 25 Jul 2020 12:03:54 +0200 Subject: [PATCH 24/40] Add support for indexless mascot texture lookups --- .../Skinning/TaikoLegacySkinTransformer.cs | 5 +---- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 23d675cfb0..f032c5f485 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -91,10 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning return null; case TaikoSkinComponents.Mascot: - if (GetTexture("pippidonclear0") != null) - return new DrawableTaikoMascot(); - - return null; + return new DrawableTaikoMascot(); } return Source.GetDrawableComponent(component); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 6f25a5f662..9c76aea54c 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -128,6 +128,13 @@ namespace osu.Game.Rulesets.Taiko.UI } private static Texture getAnimationFrame(ISkin skin, TaikoMascotAnimationState state, int frameIndex) - => skin.GetTexture($"pippidon{state.ToString().ToLower()}{frameIndex}"); + { + var texture = skin.GetTexture($"pippidon{state.ToString().ToLower()}{frameIndex}"); + + if (frameIndex == 0 && texture == null) + texture = skin.GetTexture($"pippidon{state.ToString().ToLower()}"); + + return texture; + } } } From f7a330becd68780ddbb91543350eee87b15d15a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 25 Jul 2020 12:13:19 +0200 Subject: [PATCH 25/40] Fix tests failing due to not checking state early enough --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index cb6a0decde..47d8a5c012 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -36,6 +37,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private TaikoScoreProcessor scoreProcessor; private IEnumerable mascots => this.ChildrenOfType(); + + private IEnumerable animatedMascots => + mascots.Where(mascot => mascot.ChildrenOfType().All(animation => animation.FrameCount > 0)); + private IEnumerable playfields => this.ChildrenOfType(); [SetUp] @@ -72,11 +77,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear)); AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss })); - AddAssert("skins with animations remain in clear state", () => someMascotsIn(TaikoMascotAnimationState.Clear)); + AddAssert("skins with animations remain in clear state", () => animatedMascotsIn(TaikoMascotAnimationState.Clear)); AddUntilStep("state reverts to fail", () => allMascotsIn(TaikoMascotAnimationState.Fail)); AddStep("set clear state again", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear)); - AddAssert("skins with animations change to clear", () => someMascotsIn(TaikoMascotAnimationState.Clear)); + AddAssert("skins with animations change to clear", () => animatedMascotsIn(TaikoMascotAnimationState.Clear)); } [Test] @@ -186,10 +191,18 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private void assertStateAfterResult(JudgementResult judgementResult, TaikoMascotAnimationState expectedState) { - AddStep($"{judgementResult.Type.ToString().ToLower()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}", - () => applyNewResult(judgementResult)); + TaikoMascotAnimationState[] mascotStates = null; - AddAssert($"state is {expectedState.ToString().ToLower()}", () => allMascotsIn(expectedState)); + AddStep($"{judgementResult.Type.ToString().ToLower()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}", + () => + { + applyNewResult(judgementResult); + // store the states as soon as possible, so that the delay between steps doesn't incorrectly fail the test + // due to not checking if the state changed quickly enough. + Schedule(() => mascotStates = animatedMascots.Select(mascot => mascot.State.Value).ToArray()); + }); + + AddAssert($"state is {expectedState.ToString().ToLower()}", () => mascotStates.All(state => state == expectedState)); } private void applyNewResult(JudgementResult judgementResult) @@ -211,6 +224,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning } private bool allMascotsIn(TaikoMascotAnimationState state) => mascots.All(d => d.State.Value == state); - private bool someMascotsIn(TaikoMascotAnimationState state) => mascots.Any(d => d.State.Value == state); + private bool animatedMascotsIn(TaikoMascotAnimationState state) => animatedMascots.Any(d => d.State.Value == state); } } From 648f9204f5c59a992080f7bd9d9777a4852ce7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Jul 2020 15:09:12 +0200 Subject: [PATCH 26/40] Add sample lifetime constraints for taiko --- .../Audio/DrumSampleContainer.cs | 64 +++++++++++++++++++ .../Audio/DrumSampleMapping.cs | 52 --------------- .../Skinning/LegacyInputDrum.cs | 4 +- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 10 +-- osu.Game/Skinning/SkinnableSound.cs | 3 + 5 files changed, 74 insertions(+), 59 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs delete mode 100644 osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs new file mode 100644 index 0000000000..7c39c040b1 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Taiko.Audio +{ + /// + /// Stores samples for the input drum. + /// The lifetime of the samples is adjusted so that they are only alive during the appropriate sample control point. + /// + public class DrumSampleContainer : LifetimeManagementContainer + { + private readonly ControlPointInfo controlPoints; + private readonly Dictionary mappings = new Dictionary(); + + public DrumSampleContainer(ControlPointInfo controlPoints) + { + this.controlPoints = controlPoints; + + IReadOnlyList samplePoints = controlPoints.SamplePoints.Count == 0 ? new[] { controlPoints.SamplePointAt(double.MinValue) } : controlPoints.SamplePoints; + + for (int i = 0; i < samplePoints.Count; i++) + { + var samplePoint = samplePoints[i]; + + var centre = samplePoint.GetSampleInfo(); + var rim = samplePoint.GetSampleInfo(HitSampleInfo.HIT_CLAP); + + var lifetimeStart = i > 0 ? samplePoint.Time : double.MinValue; + var lifetimeEnd = i + 1 < samplePoints.Count ? samplePoints[i + 1].Time : double.MaxValue; + + mappings[samplePoint.Time] = new DrumSample + { + Centre = addSound(centre, lifetimeStart, lifetimeEnd), + Rim = addSound(rim, lifetimeStart, lifetimeEnd) + }; + } + } + + private SkinnableSound addSound(HitSampleInfo hitSampleInfo, double lifetimeStart, double lifetimeEnd) + { + var drawable = new SkinnableSound(hitSampleInfo) + { + LifetimeStart = lifetimeStart, + LifetimeEnd = lifetimeEnd + }; + AddInternal(drawable); + return drawable; + } + + public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time]; + + public class DrumSample + { + public SkinnableSound Centre; + public SkinnableSound Rim; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs deleted file mode 100644 index c31b07344d..0000000000 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Taiko.Audio -{ - public class DrumSampleMapping - { - private readonly ControlPointInfo controlPoints; - private readonly Dictionary mappings = new Dictionary(); - - public readonly List Sounds = new List(); - - public DrumSampleMapping(ControlPointInfo controlPoints) - { - this.controlPoints = controlPoints; - - IEnumerable samplePoints = controlPoints.SamplePoints.Count == 0 ? new[] { controlPoints.SamplePointAt(double.MinValue) } : controlPoints.SamplePoints; - - foreach (var s in samplePoints) - { - var centre = s.GetSampleInfo(); - var rim = s.GetSampleInfo(HitSampleInfo.HIT_CLAP); - - mappings[s.Time] = new DrumSample - { - Centre = addSound(centre), - Rim = addSound(rim) - }; - } - } - - private SkinnableSound addSound(HitSampleInfo hitSampleInfo) - { - var drawable = new SkinnableSound(hitSampleInfo); - Sounds.Add(drawable); - return drawable; - } - - public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time]; - - public class DrumSample - { - public SkinnableSound Centre; - public SkinnableSound Rim; - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs index 81d645e294..b7b55b9ae0 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning public readonly Sprite Centre; [Resolved] - private DrumSampleMapping sampleMappings { get; set; } + private DrumSampleContainer sampleContainer { get; set; } public LegacyHalfDrum(bool flipped) { @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning public bool OnPressed(TaikoAction action) { Drawable target = null; - var drumSample = sampleMappings.SampleAt(Time.Current); + var drumSample = sampleContainer.SampleAt(Time.Current); if (action == CentreAction) { diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 06ccd45cb8..f76f3d851a 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Taiko.UI private const float middle_split = 0.025f; [Cached] - private DrumSampleMapping sampleMapping; + private DrumSampleContainer sampleContainer; public InputDrum(ControlPointInfo controlPoints) { - sampleMapping = new DrumSampleMapping(controlPoints); + sampleContainer = new DrumSampleContainer(controlPoints); RelativeSizeAxes = Axes.Both; } @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.UI } }); - AddRangeInternal(sampleMapping.Sounds); + AddRangeInternal(sampleContainer.Sounds); } /// @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Sprite centreHit; [Resolved] - private DrumSampleMapping sampleMappings { get; set; } + private DrumSampleContainer sampleContainer { get; set; } public TaikoHalfDrum(bool flipped) { @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Taiko.UI Drawable target = null; Drawable back = null; - var drumSample = sampleMappings.SampleAt(Time.Current); + var drumSample = sampleContainer.SampleAt(Time.Current); if (action == CentreAction) { diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 49f9f01cff..fb9cab74c8 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -22,6 +22,9 @@ namespace osu.Game.Skinning [Resolved] private ISampleStore samples { get; set; } + public override bool RemoveWhenNotAlive => false; + public override bool RemoveCompletedTransforms => false; + public SkinnableSound(ISampleInfo hitSamples) : this(new[] { hitSamples }) { From 8e6a0493b4f972f2d577c91b0f8cecfd6a74ba8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Jul 2020 15:15:01 +0200 Subject: [PATCH 27/40] Adjust InputDrum usage --- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 60 +++++++++++++------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index f76f3d851a..5966b24b34 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -37,39 +37,41 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load() { - Child = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Scale = new Vector2(0.9f), - Children = new Drawable[] + new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container { - new TaikoHalfDrum(false) + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(0.9f), + Children = new Drawable[] { - Name = "Left Half", - Anchor = Anchor.Centre, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.X, - X = -middle_split / 2, - RimAction = TaikoAction.LeftRim, - CentreAction = TaikoAction.LeftCentre - }, - new TaikoHalfDrum(true) - { - Name = "Right Half", - Anchor = Anchor.Centre, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.X, - X = middle_split / 2, - RimAction = TaikoAction.RightRim, - CentreAction = TaikoAction.RightCentre + new TaikoHalfDrum(false) + { + Name = "Left Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.X, + X = -middle_split / 2, + RimAction = TaikoAction.LeftRim, + CentreAction = TaikoAction.LeftCentre + }, + new TaikoHalfDrum(true) + { + Name = "Right Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.X, + X = middle_split / 2, + RimAction = TaikoAction.RightRim, + CentreAction = TaikoAction.RightCentre + } } - } - }); - - AddRangeInternal(sampleContainer.Sounds); + }), + sampleContainer + }; } /// From c78c346b627e7fa89ea99c44a521216812ed5012 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Jul 2020 14:11:01 +0900 Subject: [PATCH 28/40] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index e5b0245dd0..7e6f1469f5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5af28ae11a..ab434def38 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -25,7 +25,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 4a94ec33d8..618de5d19f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 5e7237bf567cb5cf1539acb1d6ef05427a490b5a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 27 Jul 2020 10:29:16 +0300 Subject: [PATCH 29/40] Fix incorrect default hitcircle font overlapping applied to legacy skins --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 3e5758ca01..95ef2d58b1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Skinning case OsuSkinComponents.HitCircleText: var font = GetConfig(OsuSkinConfiguration.HitCirclePrefix)?.Value ?? "default"; - var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0; + var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? -2; return !hasFont(font) ? null From 10101d5b31da4499d99f4564fc83dbc709ec7f4b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Jul 2020 18:06:14 +0900 Subject: [PATCH 30/40] Reduce spinner tick and bonus score --- osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs | 2 +- osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs b/osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs index 6ca2d4d72d..b59428e701 100644 --- a/osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects public class OsuSpinnerBonusTickJudgement : OsuSpinnerTickJudgement { - protected override int NumericResultFor(HitResult result) => 1100; + protected override int NumericResultFor(HitResult result) => 50; protected override double HealthIncreaseFor(HitResult result) => base.HealthIncreaseFor(result) * 2; } diff --git a/osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs b/osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs index c81348fbbf..346f949a4f 100644 --- a/osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Objects { public override bool AffectsCombo => false; - protected override int NumericResultFor(HitResult result) => 100; + protected override int NumericResultFor(HitResult result) => 10; protected override double HealthIncreaseFor(HitResult result) => result == MaxResult ? 0.6 * base.HealthIncreaseFor(result) : 0; } From 06c4fb717146b999ed2496c2dadfb48fe6e0b404 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Jul 2020 18:40:53 +0900 Subject: [PATCH 31/40] Update bonus score spec in test --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 6e277ff37e..23b440ced2 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Tests { // multipled by 2 to nullify the score multiplier. (autoplay mod selected) var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; - return totalScore == (int)(drawableSpinner.Disc.CumulativeRotation / 360) * 100; + return totalScore == (int)(drawableSpinner.Disc.CumulativeRotation / 360) * 10; }); addSeekStep(0); From bbc7d69524931143073058d6a20bcdb799482435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 Jul 2020 22:51:30 +0200 Subject: [PATCH 32/40] Add failing test cases --- .../TestSceneDrawableJudgement.cs | 102 ++++++++++++------ .../Objects/Drawables/DrawableOsuJudgement.cs | 21 ++-- 2 files changed, 83 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index f08f994b07..4bb4619a1b 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -4,62 +4,104 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; +using osu.Framework.Testing; +using osu.Game.Configuration; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneDrawableJudgement : OsuSkinnableTestScene { + [Resolved] + private OsuConfigManager config { get; set; } + + private readonly List> pools; + public TestSceneDrawableJudgement() { - var pools = new List>(); + pools = new List>(); foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) + showResult(result); + } + + [Test] + public void TestHitLightingDisabled() + { + AddStep("hit lighting disabled", () => config.Set(OsuSetting.HitLighting, false)); + + showResult(HitResult.Great); + + AddUntilStep("judgements shown", () => this.ChildrenOfType().Any()); + AddAssert("hit lighting hidden", + () => this.ChildrenOfType().All(judgement => judgement.Lighting.Alpha == 0)); + } + + [Test] + public void TestHitLightingEnabled() + { + AddStep("hit lighting enabled", () => config.Set(OsuSetting.HitLighting, true)); + + showResult(HitResult.Great); + + AddUntilStep("judgements shown", () => this.ChildrenOfType().Any()); + AddAssert("hit lighting shown", + () => this.ChildrenOfType().All(judgement => judgement.Lighting.Alpha > 0)); + } + + private void showResult(HitResult result) + { + AddStep("Show " + result.GetDescription(), () => { - AddStep("Show " + result.GetDescription(), () => + int poolIndex = 0; + + SetContents(() => { - int poolIndex = 0; + DrawablePool pool; - SetContents(() => + if (poolIndex >= pools.Count) + pools.Add(pool = new DrawablePool(1)); + else { - DrawablePool pool; + pool = pools[poolIndex]; - if (poolIndex >= pools.Count) - pools.Add(pool = new DrawablePool(1)); - else + // We need to make sure neither the pool nor the judgement get disposed when new content is set, and they both share the same parent. + ((Container)pool.Parent).Clear(false); + } + + var container = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - pool = pools[poolIndex]; - - // We need to make sure neither the pool nor the judgement get disposed when new content is set, and they both share the same parent. - ((Container)pool.Parent).Clear(false); - } - - var container = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + pool, + pool.Get(j => j.Apply(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null)).With(j => { - pool, - pool.Get(j => j.Apply(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null)).With(j => - { - j.Anchor = Anchor.Centre; - j.Origin = Anchor.Centre; - }) - } - }; + j.Anchor = Anchor.Centre; + j.Origin = Anchor.Centre; + }) + } + }; - poolIndex++; - return container; - }); + poolIndex++; + return container; }); - } + }); + } + + private class TestDrawableOsuJudgement : DrawableOsuJudgement + { + public new SkinnableSprite Lighting => base.Lighting; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 1493ddfcf3..3e45d3509d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -16,7 +16,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableOsuJudgement : DrawableJudgement { - private SkinnableSprite lighting; + protected SkinnableSprite Lighting; + private Bindable lightingColour; public DrawableOsuJudgement(JudgementResult result, DrawableHitObject judgedObject) @@ -33,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { if (config.Get(OsuSetting.HitLighting)) { - AddInternal(lighting = new SkinnableSprite("lighting") + AddInternal(Lighting = new SkinnableSprite("lighting") { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -60,32 +61,32 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables lightingColour?.UnbindAll(); - if (lighting != null) + if (Lighting != null) { - lighting.ResetAnimation(); + Lighting.ResetAnimation(); if (JudgedObject != null) { lightingColour = JudgedObject.AccentColour.GetBoundCopy(); - lightingColour.BindValueChanged(colour => lighting.Colour = Result.Type == HitResult.Miss ? Color4.Transparent : colour.NewValue, true); + lightingColour.BindValueChanged(colour => Lighting.Colour = Result.Type == HitResult.Miss ? Color4.Transparent : colour.NewValue, true); } else { - lighting.Colour = Color4.White; + Lighting.Colour = Color4.White; } } } - protected override double FadeOutDelay => lighting == null ? base.FadeOutDelay : 1400; + protected override double FadeOutDelay => Lighting == null ? base.FadeOutDelay : 1400; protected override void ApplyHitAnimations() { - if (lighting != null) + if (Lighting != null) { JudgementBody.FadeIn().Delay(FadeInDuration).FadeOut(400); - lighting.ScaleTo(0.8f).ScaleTo(1.2f, 600, Easing.Out); - lighting.FadeIn(200).Then().Delay(200).FadeOut(1000); + Lighting.ScaleTo(0.8f).ScaleTo(1.2f, 600, Easing.Out); + Lighting.FadeIn(200).Then().Delay(200).FadeOut(1000); } JudgementText?.TransformSpacingTo(Vector2.Zero).Then().TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); From 21ae33e28487a4939ed01ac03159c9e321a8f6b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Jul 2020 23:20:38 +0200 Subject: [PATCH 33/40] Determine whether to show lighting at prepare time --- .../Objects/Drawables/DrawableOsuJudgement.cs | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 3e45d3509d..8079ce4a3f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Bindable lightingColour; + [Resolved] + private OsuConfigManager config { get; set; } + public DrawableOsuJudgement(JudgementResult result, DrawableHitObject judgedObject) : base(result, judgedObject) { @@ -30,18 +33,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load() { - if (config.Get(OsuSetting.HitLighting)) + AddInternal(Lighting = new SkinnableSprite("lighting") { - AddInternal(Lighting = new SkinnableSprite("lighting") - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Blending = BlendingParameters.Additive, - Depth = float.MaxValue - }); - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Depth = float.MaxValue, + Alpha = 0 + }); } public override void Apply(JudgementResult result, DrawableHitObject judgedObject) @@ -61,19 +62,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables lightingColour?.UnbindAll(); - if (Lighting != null) - { - Lighting.ResetAnimation(); + Lighting.ResetAnimation(); - if (JudgedObject != null) - { - lightingColour = JudgedObject.AccentColour.GetBoundCopy(); - lightingColour.BindValueChanged(colour => Lighting.Colour = Result.Type == HitResult.Miss ? Color4.Transparent : colour.NewValue, true); - } - else - { - Lighting.Colour = Color4.White; - } + if (JudgedObject != null) + { + lightingColour = JudgedObject.AccentColour.GetBoundCopy(); + lightingColour.BindValueChanged(colour => Lighting.Colour = Result.Type == HitResult.Miss ? Color4.Transparent : colour.NewValue, true); + } + else + { + Lighting.Colour = Color4.White; } } @@ -81,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void ApplyHitAnimations() { - if (Lighting != null) + if (config.Get(OsuSetting.HitLighting)) { JudgementBody.FadeIn().Delay(FadeInDuration).FadeOut(400); From 5fc7039bf2c6b4a9878611beb72427aa18c40b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Jul 2020 23:22:31 +0200 Subject: [PATCH 34/40] Prevent DrawableJudgement from removing other children --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 052aaa3c65..e085334649 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -130,11 +130,16 @@ namespace osu.Game.Rulesets.Judgements if (type == currentDrawableType) return; - InternalChild = JudgementBody = new Container + // sub-classes might have added their own children that would be removed here if .InternalChild was used. + if (JudgementBody != null) + RemoveInternal(JudgementBody); + + AddInternal(JudgementBody = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, + Depth = -float.MaxValue, Child = bodyDrawable = new SkinnableDrawable(new GameplaySkinComponent(type), _ => JudgementText = new OsuSpriteText { Text = type.GetDescription().ToUpperInvariant(), @@ -142,7 +147,7 @@ namespace osu.Game.Rulesets.Judgements Colour = colours.ForHitResult(type), Scale = new Vector2(0.85f, 1), }, confineMode: ConfineMode.NoScaling) - }; + }); currentDrawableType = type; } From 7ad3101d082975980eb3990c9db1a2981211bc0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Jul 2020 23:26:21 +0200 Subject: [PATCH 35/40] Bring back custom fade-out delay if hit lighting is on --- .../TestSceneDrawableJudgement.cs | 5 +++++ .../Objects/Drawables/DrawableOsuJudgement.cs | 13 +++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index 4bb4619a1b..646f12f710 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -43,6 +43,8 @@ namespace osu.Game.Rulesets.Osu.Tests showResult(HitResult.Great); AddUntilStep("judgements shown", () => this.ChildrenOfType().Any()); + AddAssert("judgement body immediately visible", + () => this.ChildrenOfType().All(judgement => judgement.JudgementBody.Alpha == 1)); AddAssert("hit lighting hidden", () => this.ChildrenOfType().All(judgement => judgement.Lighting.Alpha == 0)); } @@ -55,6 +57,8 @@ namespace osu.Game.Rulesets.Osu.Tests showResult(HitResult.Great); AddUntilStep("judgements shown", () => this.ChildrenOfType().Any()); + AddAssert("judgement body not immediately visible", + () => this.ChildrenOfType().All(judgement => judgement.JudgementBody.Alpha > 0 && judgement.JudgementBody.Alpha < 1)); AddAssert("hit lighting shown", () => this.ChildrenOfType().All(judgement => judgement.Lighting.Alpha > 0)); } @@ -102,6 +106,7 @@ namespace osu.Game.Rulesets.Osu.Tests private class TestDrawableOsuJudgement : DrawableOsuJudgement { public new SkinnableSprite Lighting => base.Lighting; + public new Container JudgementBody => base.JudgementBody; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 8079ce4a3f..012d9f8878 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -75,17 +75,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - protected override double FadeOutDelay => Lighting == null ? base.FadeOutDelay : 1400; + private double fadeOutDelay; + protected override double FadeOutDelay => fadeOutDelay; protected override void ApplyHitAnimations() { - if (config.Get(OsuSetting.HitLighting)) + bool hitLightingEnabled = config.Get(OsuSetting.HitLighting); + + if (hitLightingEnabled) { JudgementBody.FadeIn().Delay(FadeInDuration).FadeOut(400); Lighting.ScaleTo(0.8f).ScaleTo(1.2f, 600, Easing.Out); Lighting.FadeIn(200).Then().Delay(200).FadeOut(1000); } + else + { + JudgementBody.Alpha = 1; + } + + fadeOutDelay = hitLightingEnabled ? 1400 : base.FadeOutDelay; JudgementText?.TransformSpacingTo(Vector2.Zero).Then().TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); base.ApplyHitAnimations(); From abdbeafc8ad4f52274ae1b65f29034f2fe676f2d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2020 17:21:15 +0000 Subject: [PATCH 36/40] Bump Sentry from 2.1.4 to 2.1.5 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 2.1.4 to 2.1.5. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/2.1.4...2.1.5) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ab434def38..7ebffc6d10 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + From dc577aa6fa2bc0df944d84bfbb27085217929737 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Jul 2020 11:22:58 +0900 Subject: [PATCH 37/40] Fix display of bonus score --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs | 2 +- .../Objects/Drawables/Pieces/SpinnerBonusDisplay.cs | 2 +- osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs | 4 +++- osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 23b440ced2..c36bec391f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Tests { // multipled by 2 to nullify the score multiplier. (autoplay mod selected) var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; - return totalScore == (int)(drawableSpinner.Disc.CumulativeRotation / 360) * 10; + return totalScore == (int)(drawableSpinner.Disc.CumulativeRotation / 360) * SpinnerTick.SCORE_PER_TICK; }); addSeekStep(0); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBonusDisplay.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBonusDisplay.cs index a8f5580735..b499d7a92b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBonusDisplay.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBonusDisplay.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; displayedCount = count; - bonusCounter.Text = $"{1000 * count}"; + bonusCounter.Text = $"{SpinnerBonusTick.SCORE_PER_TICK * count}"; bonusCounter.FadeOutFromOne(1500); bonusCounter.ScaleTo(1.5f).Then().ScaleTo(1f, 1000, Easing.OutQuint); } diff --git a/osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs b/osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs index b59428e701..9c4b6f774f 100644 --- a/osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SpinnerBonusTick.cs @@ -9,6 +9,8 @@ namespace osu.Game.Rulesets.Osu.Objects { public class SpinnerBonusTick : SpinnerTick { + public new const int SCORE_PER_TICK = 50; + public SpinnerBonusTick() { Samples.Add(new HitSampleInfo { Name = "spinnerbonus" }); @@ -18,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects public class OsuSpinnerBonusTickJudgement : OsuSpinnerTickJudgement { - protected override int NumericResultFor(HitResult result) => 50; + protected override int NumericResultFor(HitResult result) => SCORE_PER_TICK; protected override double HealthIncreaseFor(HitResult result) => base.HealthIncreaseFor(result) * 2; } diff --git a/osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs b/osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs index 346f949a4f..de3ae27e55 100644 --- a/osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SpinnerTick.cs @@ -9,6 +9,8 @@ namespace osu.Game.Rulesets.Osu.Objects { public class SpinnerTick : OsuHitObject { + public const int SCORE_PER_TICK = 10; + public override Judgement CreateJudgement() => new OsuSpinnerTickJudgement(); protected override HitWindows CreateHitWindows() => HitWindows.Empty; @@ -17,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Objects { public override bool AffectsCombo => false; - protected override int NumericResultFor(HitResult result) => 10; + protected override int NumericResultFor(HitResult result) => SCORE_PER_TICK; protected override double HealthIncreaseFor(HitResult result) => result == MaxResult ? 0.6 * base.HealthIncreaseFor(result) : 0; } From df3e2cc640ca60f5118927f43f67e71b1e0f69b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Jul 2020 12:08:15 +0900 Subject: [PATCH 38/40] Fix potential crash due to cross-thread TrackVirtualManual.Stop --- osu.Game/Tests/Visual/OsuTestScene.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index cb9ed40b00..866fc215d6 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -305,8 +305,10 @@ namespace osu.Game.Tests.Visual { double refTime = referenceClock.CurrentTime; - if (lastReferenceTime.HasValue) - accumulated += (refTime - lastReferenceTime.Value) * Rate; + double? lastRefTime = lastReferenceTime; + + if (lastRefTime != null) + accumulated += (refTime - lastRefTime.Value) * Rate; lastReferenceTime = refTime; } From a210deee9a3caa6cd20c841d808858f3e10331cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Jul 2020 12:16:01 +0900 Subject: [PATCH 39/40] Remove unnecessary depth setter --- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index e085334649..d24c81536e 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -139,7 +139,6 @@ namespace osu.Game.Rulesets.Judgements Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Depth = -float.MaxValue, Child = bodyDrawable = new SkinnableDrawable(new GameplaySkinComponent(type), _ => JudgementText = new OsuSpriteText { Text = type.GetDescription().ToUpperInvariant(), From a99c6698b7350b2f4362761cbaf64e932e2232fc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2020 04:25:01 +0000 Subject: [PATCH 40/40] Bump SharpCompress from 0.25.1 to 0.26.0 Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.25.1 to 0.26.0. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.25.1...0.26) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7ebffc6d10..5ac54a853f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -27,7 +27,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 618de5d19f..8b2d1346be 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -81,7 +81,7 @@ - +