From 6d97da8b19151fdf9cee4cd46b1ae6733caa9c02 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Mon, 2 Oct 2017 04:42:38 +0300 Subject: [PATCH 01/38] Add replay speed adjustment --- osu.Game/Screens/Play/HUDOverlay.cs | 18 +++++++++------- osu.Game/Screens/Play/Player.cs | 2 ++ .../Play/ReplaySettings/PlaybackSettings.cs | 21 ++++++++++++------- .../Screens/Play/ReplaySettingsOverlay.cs | 8 ++++--- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index ddcf031bd4..c632b7d893 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -32,6 +32,7 @@ namespace osu.Game.Screens.Play public readonly HealthDisplay HealthDisplay; public readonly SongProgress Progress; public readonly ModDisplay ModDisplay; + public readonly ReplaySettingsOverlay ReplaySettingsOverlay; private Bindable showHud; private bool replayLoaded; @@ -55,7 +56,7 @@ namespace osu.Game.Screens.Play HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), - //ReplaySettingsOverlay = CreateReplaySettingsOverlay(), + ReplaySettingsOverlay = CreateReplaySettingsOverlay(), } }); } @@ -98,7 +99,10 @@ namespace osu.Game.Screens.Play // in the case a replay isn't loaded, we want some elements to only appear briefly. if (!replayLoaded) + { + ReplaySettingsOverlay.Hide(); ModDisplay.Delay(2000).FadeOut(200); + } } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) @@ -176,12 +180,12 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - //protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay - //{ - // Anchor = Anchor.TopRight, - // Origin = Anchor.TopRight, - // Margin = new MarginPadding { Top = 100, Right = 10 }, - //}; + protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding { Top = 100, Right = 10 }, + }; public virtual void BindProcessor(ScoreProcessor processor) { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e120c7f193..9d983fd0a9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -222,6 +222,8 @@ namespace osu.Game.Screens.Play hudOverlay.ModDisplay.Current.BindTo(working.Mods); + hudOverlay.ReplaySettingsOverlay.PlaybackSettings.BindClock(adjustableSourceClock); + // Bind ScoreProcessor to ourselves scoreProcessor.AllJudged += onCompletion; scoreProcessor.Failed += onFail; diff --git a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs index ea958d05f9..49b5ce4474 100644 --- a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs @@ -3,7 +3,7 @@ using osu.Framework.Allocation; using osu.Game.Configuration; -using osu.Framework.Graphics; +using osu.Framework.Timing; namespace osu.Game.Screens.Play.ReplaySettings { @@ -11,17 +11,24 @@ namespace osu.Game.Screens.Play.ReplaySettings { protected override string Title => @"playback"; + private ReplaySliderBar sliderbar; + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - Children = new Drawable[] + Child = sliderbar = new ReplaySliderBar { - new ReplaySliderBar - { - LabelText = "Playback speed", - Bindable = config.GetBindable(OsuSetting.PlaybackSpeed) - } + LabelText = "Playback speed", + Bindable = config.GetBindable(OsuSetting.PlaybackSpeed), }; } + + public void BindClock(IAdjustableClock clock) + { + var clockRate = clock.Rate; + sliderbar.Bindable.ValueChanged += (rateMultiplier) => clock.Rate = clockRate * rateMultiplier; + + sliderbar.Bindable.Value = 1; + } } } diff --git a/osu.Game/Screens/Play/ReplaySettingsOverlay.cs b/osu.Game/Screens/Play/ReplaySettingsOverlay.cs index 415f70005d..0edf4634ba 100644 --- a/osu.Game/Screens/Play/ReplaySettingsOverlay.cs +++ b/osu.Game/Screens/Play/ReplaySettingsOverlay.cs @@ -10,15 +10,17 @@ namespace osu.Game.Screens.Play { public class ReplaySettingsOverlay : FillFlowContainer { + public readonly PlaybackSettings PlaybackSettings; + public ReplaySettingsOverlay() { Direction = FillDirection.Vertical; AutoSizeAxes = Axes.Both; Spacing = new Vector2(0, 20); - Add(new CollectionSettings()); - Add(new DiscussionSettings()); - Add(new PlaybackSettings()); + //Add(new CollectionSettings()); + //Add(new DiscussionSettings()); + Add(PlaybackSettings = new PlaybackSettings()); } } } From c34cc07fdadcc00bd4328a532a88e4a6dd53efb7 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Mon, 2 Oct 2017 18:09:00 +0300 Subject: [PATCH 02/38] Remove default bindable from the config manager --- osu.Game/Configuration/OsuConfigManager.cs | 2 -- .../Play/ReplaySettings/PlaybackSettings.cs | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index b000f08369..b341c4413b 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -69,7 +69,6 @@ namespace osu.Game.Configuration Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.FloatingComments, false); - Set(OsuSetting.PlaybackSpeed, 1.0, 0.5f, 2); // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); @@ -93,7 +92,6 @@ namespace osu.Game.Configuration ShowStoryboard, KeyOverlay, FloatingComments, - PlaybackSpeed, ShowInterface, MouseDisableButtons, MouseDisableWheel, diff --git a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs index 49b5ce4474..e8f20e3087 100644 --- a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs @@ -1,9 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Allocation; -using osu.Game.Configuration; using osu.Framework.Timing; +using osu.Framework.Configuration; namespace osu.Game.Screens.Play.ReplaySettings { @@ -11,15 +10,20 @@ namespace osu.Game.Screens.Play.ReplaySettings { protected override string Title => @"playback"; - private ReplaySliderBar sliderbar; + private readonly ReplaySliderBar sliderbar; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private readonly BindableNumber current; + + public PlaybackSettings() { + current = new BindableDouble(1) as BindableNumber; + current.MinValue = 0.5; + current.MaxValue = 2; + Child = sliderbar = new ReplaySliderBar { LabelText = "Playback speed", - Bindable = config.GetBindable(OsuSetting.PlaybackSpeed), + Bindable = current, }; } @@ -27,8 +31,6 @@ namespace osu.Game.Screens.Play.ReplaySettings { var clockRate = clock.Rate; sliderbar.Bindable.ValueChanged += (rateMultiplier) => clock.Rate = clockRate * rateMultiplier; - - sliderbar.Bindable.Value = 1; } } } From 4a298098c506fb3e61a680d5872a83ad321239a4 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Mon, 2 Oct 2017 18:19:55 +0300 Subject: [PATCH 03/38] CI fixes --- .../Play/ReplaySettings/PlaybackSettings.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs index e8f20e3087..9129dcab94 100644 --- a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs @@ -12,25 +12,23 @@ namespace osu.Game.Screens.Play.ReplaySettings private readonly ReplaySliderBar sliderbar; - private readonly BindableNumber current; - public PlaybackSettings() { - current = new BindableDouble(1) as BindableNumber; - current.MinValue = 0.5; - current.MaxValue = 2; - Child = sliderbar = new ReplaySliderBar { LabelText = "Playback speed", - Bindable = current, + Bindable = new BindableDouble(1) + { + MinValue = 0.5, + MaxValue = 2 + }, }; } public void BindClock(IAdjustableClock clock) { var clockRate = clock.Rate; - sliderbar.Bindable.ValueChanged += (rateMultiplier) => clock.Rate = clockRate * rateMultiplier; + sliderbar.Bindable.ValueChanged += rateMultiplier => clock.Rate = clockRate * rateMultiplier; } } } From b94c78e99306c4728fcd79f6cb7c9c9b1557aacb Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Mon, 2 Oct 2017 19:33:58 +0300 Subject: [PATCH 04/38] Hide/Show Replay settings on pressing Ctrl+H --- .../Screens/Play/HUD/ReplaySettingsOverlay.cs | 66 +++++++++++++++++++ osu.Game/Screens/Play/HUDOverlay.cs | 7 +- .../Screens/Play/ReplaySettingsOverlay.cs | 26 -------- .../Visual/TestCaseReplaySettingsOverlay.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 5 files changed, 69 insertions(+), 34 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs delete mode 100644 osu.Game/Screens/Play/ReplaySettingsOverlay.cs diff --git a/osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs b/osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs new file mode 100644 index 0000000000..9f55ce8ce5 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.Play.ReplaySettings; +using OpenTK; +using osu.Framework.Input; +using OpenTK.Input; + +namespace osu.Game.Screens.Play.HUD +{ + public class ReplaySettingsOverlay : VisibilityContainer + { + private const int fade_duration = 200; + + public override bool HandleInput => true; + + public readonly PlaybackSettings PlaybackSettings; + //public readonly CollectionSettings CollectionSettings; + //public readonly DiscussionSettings DiscussionSettings; + + public ReplaySettingsOverlay() + { + AlwaysPresent = true; + RelativeSizeAxes = Axes.Both; + + Child = new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding { Top = 100, Right = 10 }, + Children = new [] + { + //CollectionSettings = new CollectionSettings(), + //DiscussionSettings = new DiscussionSettings(), + PlaybackSettings = new PlaybackSettings(), + } + }; + + State = Visibility.Visible; + } + + protected override void PopIn() => this.FadeIn(fade_duration); + protected override void PopOut() => this.FadeOut(fade_duration); + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) return false; + + if (state.Keyboard.ControlPressed) + { + if (args.Key == Key.H) + { + ToggleVisibility(); + return true; + } + } + + return base.OnKeyDown(state, args); + } + } +} diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index c632b7d893..1d7e0727ba 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -180,12 +180,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Margin = new MarginPadding { Top = 100, Right = 10 }, - }; + protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay(); public virtual void BindProcessor(ScoreProcessor processor) { diff --git a/osu.Game/Screens/Play/ReplaySettingsOverlay.cs b/osu.Game/Screens/Play/ReplaySettingsOverlay.cs deleted file mode 100644 index 0edf4634ba..0000000000 --- a/osu.Game/Screens/Play/ReplaySettingsOverlay.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Screens.Play.ReplaySettings; -using OpenTK; - -namespace osu.Game.Screens.Play -{ - public class ReplaySettingsOverlay : FillFlowContainer - { - public readonly PlaybackSettings PlaybackSettings; - - public ReplaySettingsOverlay() - { - Direction = FillDirection.Vertical; - AutoSizeAxes = Axes.Both; - Spacing = new Vector2(0, 20); - - //Add(new CollectionSettings()); - //Add(new DiscussionSettings()); - Add(PlaybackSettings = new PlaybackSettings()); - } - } -} diff --git a/osu.Game/Tests/Visual/TestCaseReplaySettingsOverlay.cs b/osu.Game/Tests/Visual/TestCaseReplaySettingsOverlay.cs index 256c3d25c9..3105a7d588 100644 --- a/osu.Game/Tests/Visual/TestCaseReplaySettingsOverlay.cs +++ b/osu.Game/Tests/Visual/TestCaseReplaySettingsOverlay.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.ReplaySettings; namespace osu.Game.Tests.Visual diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c8e42a3ad3..f04aa4708c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -656,7 +656,7 @@ - + From feb0b1852fe8e5e8107be177352fd81297058fb5 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 3 Oct 2017 20:05:50 +0300 Subject: [PATCH 05/38] Remove the dangerous function --- osu.Game/Screens/Play/Player.cs | 2 +- .../Play/ReplaySettings/PlaybackSettings.cs | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9d983fd0a9..7e5ab58adb 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -222,7 +222,7 @@ namespace osu.Game.Screens.Play hudOverlay.ModDisplay.Current.BindTo(working.Mods); - hudOverlay.ReplaySettingsOverlay.PlaybackSettings.BindClock(adjustableSourceClock); + hudOverlay.ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock; // Bind ScoreProcessor to ourselves scoreProcessor.AllJudged += onCompletion; diff --git a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs index 9129dcab94..24f128ef49 100644 --- a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs @@ -10,6 +10,13 @@ namespace osu.Game.Screens.Play.ReplaySettings { protected override string Title => @"playback"; + private IAdjustableClock adjustableClock; + public IAdjustableClock AdjustableClock + { + set { adjustableClock = value; } + get { return adjustableClock; } + } + private readonly ReplaySliderBar sliderbar; public PlaybackSettings() @@ -25,10 +32,15 @@ namespace osu.Game.Screens.Play.ReplaySettings }; } - public void BindClock(IAdjustableClock clock) + protected override void LoadComplete() { - var clockRate = clock.Rate; - sliderbar.Bindable.ValueChanged += rateMultiplier => clock.Rate = clockRate * rateMultiplier; + base.LoadComplete(); + + if (adjustableClock != null) + { + var clockRate = adjustableClock.Rate; + sliderbar.Bindable.ValueChanged += rateMultiplier => adjustableClock.Rate = clockRate * rateMultiplier; + } } } } From 1c132938dfdd57d338eed74751107e11e266b27e Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Tue, 3 Oct 2017 20:26:53 +0300 Subject: [PATCH 06/38] Allow visibility can be toggled only if replay is loaded --- osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs | 4 +++- osu.Game/Screens/Play/HUDOverlay.cs | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs b/osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs index 9f55ce8ce5..e44a738d55 100644 --- a/osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/ReplaySettingsOverlay.cs @@ -14,6 +14,8 @@ namespace osu.Game.Screens.Play.HUD { private const int fade_duration = 200; + public bool ReplayLoaded; + public override bool HandleInput => true; public readonly PlaybackSettings PlaybackSettings; @@ -53,7 +55,7 @@ namespace osu.Game.Screens.Play.HUD if (state.Keyboard.ControlPressed) { - if (args.Key == Key.H) + if (args.Key == Key.H && ReplayLoaded) { ToggleVisibility(); return true; diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 1d7e0727ba..f4b5efe1e5 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -97,6 +97,8 @@ namespace osu.Game.Screens.Play replayLoaded = rulesetContainer.HasReplayLoaded; + ReplaySettingsOverlay.ReplayLoaded = replayLoaded; + // in the case a replay isn't loaded, we want some elements to only appear briefly. if (!replayLoaded) { From 7a72f2e3f558f6ec765582dbdc74cc1965dfcdb4 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Sat, 14 Oct 2017 05:15:18 +0300 Subject: [PATCH 07/38] Make sure we restore the clock rate on exiting --- osu.Game/Screens/Play/Player.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7af260135e..c0a50b1a6f 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -49,6 +49,7 @@ namespace osu.Game.Screens.Play private IAdjustableClock adjustableSourceClock; private FramedOffsetClock offsetClock; private DecoupleableInterpolatingFramedClock decoupledClock; + private double clockRate; private PauseContainer pauseContainer; @@ -149,6 +150,8 @@ namespace osu.Game.Screens.Play foreach (var mod in working.Mods.Value.OfType()) mod.ApplyToClock(adjustableSourceClock); + clockRate = adjustableSourceClock.Rate; + decoupledClock.ChangeSource(adjustableSourceClock); }); @@ -334,6 +337,8 @@ namespace osu.Game.Screens.Play { if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || RulesetContainer?.HasReplayLoaded != false) { + // We want to make sure we restore the clock rate + adjustableSourceClock.Rate = clockRate; fadeOut(); return base.OnExiting(next); } From 826d806e814e8720c433dbee2f5f9dd4da6bdd49 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Thu, 26 Oct 2017 15:07:58 +0300 Subject: [PATCH 08/38] Submodules update --- osu-framework | 2 +- osu-resources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index 5986f21268..a9eb6eaab7 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 5986f2126832451a5a7ec832a483e1dcec1b38b8 +Subproject commit a9eb6eaab7cd77f881acfdc23664df45e5d31105 diff --git a/osu-resources b/osu-resources index a4418111f8..1e2d394e01 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit a4418111f8ed2350a6fd46fe69258884f0757745 +Subproject commit 1e2d394e017d98f086271ae07fb61859b5d8fadf From a7bcae48694a1ead3949cedcf866c59d3b77d751 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Thu, 26 Oct 2017 15:18:06 +0300 Subject: [PATCH 09/38] Add startup value for the slider --- osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs index 4fea69e259..7cabe1f3ab 100644 --- a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Play.ReplaySettings Child = sliderbar = new ReplaySliderBar { LabelText = "Playback speed", - Bindable = new BindableDouble + Bindable = new BindableDouble(1) { Default = 1, MinValue = 0.5, From f219b7f9fb8fa980c2556245b59781a1eb64abc4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 19:31:30 +0900 Subject: [PATCH 10/38] Fix bonusScore being stored locally instead of incrementally changing --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 5a54c679dd..ec5d47c7c7 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -174,6 +174,7 @@ namespace osu.Game.Rulesets.Scoring private double maxBaseScore; private double rollingMaxBaseScore; private double baseScore; + private double bonusScore; protected ScoreProcessor() { @@ -219,7 +220,6 @@ namespace osu.Game.Rulesets.Scoring protected virtual void OnNewJudgement(Judgement judgement) { - double bonusScore = 0; if (judgement.AffectsCombo) { @@ -271,6 +271,7 @@ namespace osu.Game.Rulesets.Scoring Hits = 0; baseScore = 0; rollingMaxBaseScore = 0; + bonusScore = 0; } } From 6883b3742ff1cc3eb4427194da84c4924829c47d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 20:23:28 +0900 Subject: [PATCH 11/38] Make initial DrawableOsuHitObject initial states not use transforms --- .../Objects/Drawables/DrawableHitCircle.cs | 20 +++++++++++-------- .../Objects/Drawables/DrawableOsuHitObject.cs | 5 +++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 3184b83202..ed0578d3a4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -86,15 +86,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateInitialState(); - // sane defaults - ring.Show(); - circle.Show(); - number.Show(); - glow.Show(); + // Hide() cannot be used here, because when rewinding, we need these to be the final values - ApproachCircle.Hide(); - ApproachCircle.ScaleTo(new Vector2(4)); - explode.Hide(); + ring.Alpha = 1; + circle.Alpha = 1; + number.Alpha = 1; + glow.Alpha = 1; + + ApproachCircle.Alpha = 0; + ApproachCircle.Scale = new Vector2(4); + explode.Alpha = 0; + flash.Alpha = 0; + + Scale = new Vector2(HitObject.Scale); } protected override void UpdatePreemptState() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 9205f43a6d..7429f084c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected sealed override void UpdateState(ArmedState state) { - FinishTransforms(); + ClearTransforms(true); using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true)) { @@ -38,7 +38,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected virtual void UpdateInitialState() { - Hide(); + // Hide() cannot be used here, because when rewinding, we need these to be the final values + Alpha = 0; } protected virtual void UpdatePreemptState() From fe00ac7e4136d125c97e99ce3c17a128a7c57d47 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:21:07 +0900 Subject: [PATCH 12/38] Make DrawableHitObject/ScoreProcessor support rewinding --- .../Objects/Drawables/DrawableHoldNoteTick.cs | 2 +- .../Tests/TestCaseHitObjects.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 2 +- .../Rulesets/Judgements/DrawableJudgement.cs | 2 +- osu.Game/Rulesets/Judgements/Judgement.cs | 13 +++ .../Objects/Drawables/DrawableHitObject.cs | 87 +++++++++++-------- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 39 ++++++++- osu.Game/Rulesets/UI/RulesetContainer.cs | 3 + 8 files changed, 107 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index 324f4e4e99..557fbf6ea8 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected override void UpdateState(ArmedState state) { - switch (State) + switch (State.Value) { case ArmedState.Hit: AccentColour = Color4.Green; diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs index 2ac15c55a7..99526b64ee 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Tests h.Depth = depth++; if (auto) - h.State = ArmedState.Hit; + h.State.Value = ArmedState.Hit; playfieldContainer.Add(h); var proxyable = h as IDrawableHitObjectWithProxiedApproach; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 55eaa8dbb8..6c14a71a4c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime; using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true)) { - switch (State) + switch (State.Value) { case ArmedState.Idle: this.Delay(HitObject.HitWindowMiss).Expire(); diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 12edfd802a..5ab4b7636b 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Judgements break; } - Expire(); + Expire(true); } } } diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index 0ae33272a7..a1920097d3 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -17,6 +17,19 @@ namespace osu.Game.Rulesets.Judgements /// public virtual HitResult MaxResult => HitResult.Perfect; + /// + /// The combo prior to this judgement occurring. + /// + internal int ComboAtJudgement { get; set; } + + /// + /// The highest combo achieved prior to this judgement occurring. + /// + internal int HighestComboAtJudgement { get; set; } + + /// + /// Whether a successful hit occurred. + /// public bool IsHit => Result > HitResult.Miss; /// diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index bcd6734af6..9b4f7e7fc7 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -13,6 +13,7 @@ using OpenTK.Graphics; using osu.Game.Audio; using System.Linq; using osu.Game.Graphics; +using osu.Framework.Configuration; namespace osu.Game.Rulesets.Objects.Drawables { @@ -30,6 +31,9 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public virtual bool DisplayJudgement => true; + public override bool RemoveCompletedTransforms => false; + public override bool RemoveWhenNotAlive => false; + protected DrawableHitObject(HitObject hitObject) { HitObject = hitObject; @@ -40,6 +44,7 @@ namespace osu.Game.Rulesets.Objects.Drawables where TObject : HitObject { public event Action OnJudgement; + public event Action OnJudgementRemoved; public new readonly TObject HitObject; @@ -56,31 +61,42 @@ namespace osu.Game.Rulesets.Objects.Drawables protected List Samples = new List(); + public readonly Bindable State = new Bindable(); + protected DrawableHitObject(TObject hitObject) : base(hitObject) { HitObject = hitObject; } - private ArmedState state; - public ArmedState State + [BackgroundDependencyLoader] + private void load(AudioManager audio) { - get { return state; } - - set + foreach (SampleInfo sample in HitObject.Samples) { - if (state == value) - return; - state = value; + SampleChannel channel = audio.Sample.Get($@"Gameplay/{sample.Bank}-{sample.Name}"); - if (!IsLoaded) - return; + if (channel == null) + continue; + channel.Volume.Value = sample.Volume; + Samples.Add(channel); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + State.ValueChanged += state => + { UpdateState(state); if (State == ArmedState.Hit) PlaySamples(); - } + }; + + State.TriggerChange(); } protected void PlaySamples() @@ -88,16 +104,8 @@ namespace osu.Game.Rulesets.Objects.Drawables Samples.ForEach(s => s?.Play()); } - protected override void LoadComplete() - { - base.LoadComplete(); - - //force application of the state that was set before we loaded. - UpdateState(State); - } - - private bool hasJudgementResult; private bool judgementOccurred; + private bool hasJudgementResult => Judgements.LastOrDefault()?.Result >= HitResult.Miss; /// /// Whether this and all of its nested s have been judged. @@ -110,7 +118,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// The . protected void AddJudgement(Judgement judgement) { - hasJudgementResult = judgement.Result >= HitResult.Miss; judgementOccurred = true; // Ensure that the judgement is given a valid time offset, because this may not get set by the caller @@ -124,10 +131,10 @@ namespace osu.Game.Rulesets.Objects.Drawables case HitResult.None: break; case HitResult.Miss: - State = ArmedState.Miss; + State.Value = ArmedState.Miss; break; default: - State = ArmedState.Hit; + State.Value = ArmedState.Hit; break; } @@ -170,6 +177,25 @@ namespace osu.Game.Rulesets.Objects.Drawables /// implies that this check occurred after the end time of . protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) { } + protected override void Update() + { + base.Update(); + + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + + while (judgements.Count > 0) + { + var lastJudgement = judgements[judgements.Count - 1]; + if (lastJudgement.TimeOffset + endTime <= Time.Current) + break; + + judgements.RemoveAt(judgements.Count - 1); + State.Value = ArmedState.Idle; + + OnJudgementRemoved?.Invoke(this, lastJudgement); + } + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -177,21 +203,6 @@ namespace osu.Game.Rulesets.Objects.Drawables UpdateJudgement(false); } - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - foreach (SampleInfo sample in HitObject.Samples) - { - SampleChannel channel = audio.Sample.Get($@"Gameplay/{sample.Bank}-{sample.Name}"); - - if (channel == null) - continue; - - channel.Volume.Value = sample.Volume; - Samples.Add(channel); - } - } - private List> nestedHitObjects; protected IEnumerable> NestedHitObjects => nestedHitObjects; diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index ec5d47c7c7..4dd88600b2 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -185,6 +185,7 @@ namespace osu.Game.Rulesets.Scoring Debug.Assert(base_portion + combo_portion == 1.0); rulesetContainer.OnJudgement += AddJudgement; + rulesetContainer.OnJudgementRemoved += RemoveJudgement; SimulateAutoplay(rulesetContainer.Beatmap); Reset(true); @@ -213,13 +214,26 @@ namespace osu.Game.Rulesets.Scoring protected void AddJudgement(Judgement judgement) { OnNewJudgement(judgement); - NotifyNewJudgement(judgement); + updateScore(); + NotifyNewJudgement(judgement); UpdateFailed(); } + protected void RemoveJudgement(Judgement judgement) + { + OnJudgementRemoved(judgement); + updateScore(); + } + + /// + /// Applies a judgement. + /// + /// The judgement to apply/ protected virtual void OnNewJudgement(Judgement judgement) { + judgement.ComboAtJudgement = Combo; + judgement.HighestComboAtJudgement = HighestCombo; if (judgement.AffectsCombo) { @@ -242,7 +256,30 @@ namespace osu.Game.Rulesets.Scoring } else if (judgement.IsHit) bonusScore += judgement.NumericResult; + } + /// + /// Removes a judgement. This should reverse everything in . + /// + /// The judgement to remove. + protected virtual void OnJudgementRemoved(Judgement judgement) + { + Combo.Value = judgement.ComboAtJudgement; + HighestCombo.Value = judgement.HighestComboAtJudgement; + + if (judgement.AffectsCombo) + { + baseScore -= judgement.NumericResult; + rollingMaxBaseScore -= judgement.MaxNumericResult; + + Hits--; + } + else if (judgement.IsHit) + bonusScore -= judgement.NumericResult; + } + + private void updateScore() + { if (rollingMaxBaseScore != 0) Accuracy.Value = baseScore / rollingMaxBaseScore; diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 6f53b76031..36dce7218d 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -104,6 +104,7 @@ namespace osu.Game.Rulesets.UI where TObject : HitObject { public event Action OnJudgement; + public event Action OnJudgementRemoved; /// /// The Beatmap @@ -241,6 +242,8 @@ namespace osu.Game.Rulesets.UI OnJudgement?.Invoke(j); }; + drawableObject.OnJudgementRemoved += (d, j) => { OnJudgementRemoved?.Invoke(j); }; + Playfield.Add(drawableObject); } From 8ee13ef0aea5c09b22ece39ba0e439048a8ce148 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:33:31 +0900 Subject: [PATCH 13/38] Properties are unnecessary --- osu.Game/Rulesets/Judgements/Judgement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index a1920097d3..684ee0b403 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Judgements /// /// The combo prior to this judgement occurring. /// - internal int ComboAtJudgement { get; set; } + internal int ComboAtJudgement; /// /// The highest combo achieved prior to this judgement occurring. /// - internal int HighestComboAtJudgement { get; set; } + internal int HighestComboAtJudgement; /// /// Whether a successful hit occurred. From 326891f51c3f7e0a58bb6b62863a1b20d0b1b92c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:54:28 +0900 Subject: [PATCH 14/38] Add "Final" to better determine when to stop processing the hitobject --- osu.Game/Rulesets/Judgements/Judgement.cs | 5 +++++ osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index 684ee0b403..2b5c4aae95 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -32,6 +32,11 @@ namespace osu.Game.Rulesets.Judgements /// public bool IsHit => Result > HitResult.Miss; + /// + /// Whether this judgement is the final judgement for the hit object. + /// + public bool Final = true; + /// /// The offset from a perfect hit at which this judgement occurred. /// Populated when added via . diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 9b4f7e7fc7..19bddd05e0 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -105,12 +105,12 @@ namespace osu.Game.Rulesets.Objects.Drawables } private bool judgementOccurred; - private bool hasJudgementResult => Judgements.LastOrDefault()?.Result >= HitResult.Miss; + private bool judgementFinalized => judgements.LastOrDefault()?.Final == true; /// /// Whether this and all of its nested s have been judged. /// - public virtual bool AllJudged => (!ProvidesJudgement || hasJudgementResult) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); + public virtual bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); /// /// Notifies that a new judgement has occurred for this . @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Objects.Drawables judgementOccurred |= d.UpdateJudgement(userTriggered); } - if (!ProvidesJudgement || hasJudgementResult || judgementOccurred) + if (!ProvidesJudgement || judgementFinalized || judgementOccurred) return judgementOccurred; var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; From e2b6003f9864ff412df4d3ebe842f1b13ecc9f17 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:55:50 +0900 Subject: [PATCH 15/38] Make taiko use the new "Final" field Ensures that the first hit on HitStrongs is _always_ non-final unless it was a miss. The second hit is always final. --- .../Judgements/TaikoStrongHitJudgement.cs | 4 +--- .../Objects/Drawables/DrawableHit.cs | 15 +++++++++++- .../Objects/Drawables/DrawableHitStrong.cs | 24 +++++++++---------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs index f0b57e5c09..07c499b56c 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs @@ -11,9 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements public TaikoStrongHitJudgement() { - base.Result = HitResult.Perfect; + Final = true; } - - public new HitResult Result => base.Result; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 6c14a71a4c..abb4c7770e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -17,6 +17,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// protected abstract TaikoAction[] HitActions { get; } + /// + /// Whether a second hit is allowed to be processed. + /// + protected bool SecondHitAllowed { get; private set; } + /// /// Whether the last key pressed is a valid hit key. /// @@ -45,7 +50,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (!validKeyPressed) AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); else if (hitOffset < HitObject.HitWindowGood) - AddJudgement(new TaikoJudgement { Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good }); + { + AddJudgement(new TaikoJudgement + { + Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good, + Final = !HitObject.IsStrong + }); + + SecondHitAllowed = true; + } else AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index 48812093c4..c07eaf4d8b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -24,27 +25,25 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - private bool processedSecondHit; - public override bool AllJudged => processedSecondHit && base.AllJudged; - protected override void CheckForJudgements(bool userTriggered, double timeOffset) { - if (!base.AllJudged) + if (!SecondHitAllowed) { base.CheckForJudgements(userTriggered, timeOffset); return; } if (!userTriggered) + { + if (timeOffset > second_hit_window) + AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Miss }); return; + } // If we get here, we're assured that the key pressed is the correct secondary key if (Math.Abs(firstHitTime - Time.Current) < second_hit_window) - { - AddJudgement(new TaikoStrongHitJudgement()); - processedSecondHit = true; - } + AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great }); } public override bool OnReleased(TaikoAction action) @@ -56,8 +55,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) { + if (AllJudged) + return false; + // Check if we've handled the first key - if (!base.AllJudged) + if (!SecondHitAllowed) { // First key hasn't been handled yet, attempt to handle it bool handled = base.OnPressed(action); @@ -72,10 +74,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return handled; } - // If we've already hit the second key, don't handle this object any further - if (processedSecondHit) - return false; - // Don't handle represses of the first key if (firstHitAction == action) return false; From 0620d0bd7a2e21034d7a96e10acc556c958b79b6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:56:09 +0900 Subject: [PATCH 16/38] AllJudged does not need to be virtual anymore --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 19bddd05e0..091af04106 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Whether this and all of its nested s have been judged. /// - public virtual bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); + public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); /// /// Notifies that a new judgement has occurred for this . From 240997e4fbbd95617e4cd02af875ad413e4b13cf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:56:18 +0900 Subject: [PATCH 17/38] Remove duplicate property --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 8a96640b1e..7199691ae6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool Tracking; - public override bool RemoveWhenNotAlive => false; - public override bool DisplayJudgement => false; public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) From 5c2b1d4be2941166b6e7c48c80bf44026375d8c9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:58:29 +0900 Subject: [PATCH 18/38] Update xmldoc --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index abb4c7770e..489eacf386 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected abstract TaikoAction[] HitActions { get; } /// - /// Whether a second hit is allowed to be processed. + /// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully. /// protected bool SecondHitAllowed { get; private set; } From f7540e28baf59f8c063a1abd11b39d5db550f1e6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 22:22:53 +0900 Subject: [PATCH 19/38] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index ef10edfc75..c8222d1dc9 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit ef10edfc750b39258edbff46019f1d10700548c2 +Subproject commit c8222d1dc932aafe17ec42bfbe6cbec81851f55d From 3f20caa543897e87b93f2a2da3aeda86b1f004a0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 23:31:50 +0900 Subject: [PATCH 20/38] Make taiko stop crashing for now --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 136da8a532..ac3796f5b8 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -244,7 +244,12 @@ namespace osu.Game.Rulesets.Taiko.UI if (judgedObject.X >= -0.05f && judgedObject is DrawableHit) { // If we're far enough away from the left stage, we should bring outselves in front of it - topLevelHitContainer.Add(judgedObject.CreateProxy()); + // Todo: The following try-catch is temporary for replay rewinding support + try + { + topLevelHitContainer.Add(judgedObject.CreateProxy()); + } + catch { } } hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); From 70ea3e50253210f52b72d8ae7aff00b6d4c19ff1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 15:29:16 +0900 Subject: [PATCH 21/38] Fix up initial scale of DrawableRepeatPoint --- .../Objects/Drawables/DrawableRepeatPoint.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index bb200c9ecd..235a646ac8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -47,16 +47,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss }); } + protected override void UpdateInitialState() + { + base.UpdateInitialState(); + + Scale = new Vector2(0.5f); + } + protected override void UpdatePreemptState() { var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime); - this.Animate( - d => d.FadeIn(animIn), - d => d.ScaleTo(0.5f).ScaleTo(1.2f, animIn) - ).Then( - d => d.ScaleTo(1, 150, Easing.Out) - ); + this.FadeIn(animIn).ScaleTo(1.2f, animIn) + .Then() + .ScaleTo(1, 150, Easing.Out); } protected override void UpdateCurrentState(ArmedState state) From 06a62edeb635602211eba9e1f7ab83acb0faa6d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 15:30:46 +0900 Subject: [PATCH 22/38] Make DrawableRepeatPoints show up when replayed Fixes #1458 --- .../Objects/Drawables/DrawableRepeatPoint.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 235a646ac8..200c697a0f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -18,13 +18,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public double FadeInTime; public double FadeOutTime; - public override bool RemoveWhenNotAlive => false; - - public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) : base(repeatPoint) + public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) + : base(repeatPoint) { this.repeatPoint = repeatPoint; this.drawableSlider = drawableSlider; + // The containing DrawableSlider is updated before us and clears our transforms, so we need to be + // present to get updated and have UpdateState correctly called when rewinding. + AlwaysPresent = true; + AutoSizeAxes = Axes.Both; Blending = BlendingMode.Additive; Origin = Anchor.Centre; From 60048e6cd1f65d74a0b947e66f8c106c4cccd0ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 15:33:24 +0900 Subject: [PATCH 23/38] Fix slider ticks not showing up again once replayed Fixes #1456 --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 7199691ae6..9fe475f4aa 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -28,6 +28,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = new Vector2(16) * sliderTick.Scale; + // The containing DrawableSlider is updated before us and clears our transforms, so we need to be + // present to get updated and have UpdateState correctly called when rewinding. + AlwaysPresent = true; + Masking = true; CornerRadius = Size.X / 2; From 5fd311514239d795cefbf272fe806f063c32b5b5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 15:58:12 +0900 Subject: [PATCH 24/38] Fix slider ball not animating fade/scale after rewinding Fixes #1455 --- .../Objects/Drawables/Pieces/SliderBall.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 1986b1431b..2068ad9205 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -101,14 +101,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces // If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos); + public override void ClearTransforms(bool propagateChildren = false, string targetMember = null) + { + // Consider the case of rewinding - children's transforms are handled internally, so propagating down + // any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point. + base.ClearTransforms(false, targetMember); + } + private bool tracking; public bool Tracking { get { return tracking; } private set { - if (value == tracking) return; - + if (value == tracking) + return; tracking = value; follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint); @@ -123,8 +130,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces base.Update(); // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. - if (Time.Current < slider.EndTime) - Tracking = canCurrentlyTrack && lastState != null && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + Tracking = canCurrentlyTrack + && lastState != null + && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) + && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); } public void UpdateProgress(double progress, int repeat) From 3b189c1ffed02f8e758efaaf2cce8c4e789636ca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 21:20:36 +0900 Subject: [PATCH 25/38] Fix BreakOverlay not properly working with rewinding In various ways: * It wouldn't show up if rewound after the break was complete. * The time would increase backwards if rewind happened during a break. * Etc. * Basically the fix is to use transformations everywhere. BreakOverlay could be refactored further, but this is enough to make it work for now. --- .../Play/BreaksOverlay/BreakOverlay.cs | 37 +++++++++------- .../BreaksOverlay/RemainingTimeCounter.cs | 44 +++++++------------ 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs index f5062aa40f..b3d08c0c82 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs @@ -30,6 +30,8 @@ namespace osu.Game.Screens.Play.BreaksOverlay } } + public override bool RemoveCompletedTransforms => false; + private readonly bool letterboxing; private readonly LetterboxOverlay letterboxOverlay; private readonly Container remainingTimeAdjustmentBox; @@ -101,38 +103,41 @@ namespace osu.Game.Screens.Play.BreaksOverlay if (!b.HasEffect) continue; + using (BeginAbsoluteSequence(b.StartTime, true)) + { + remainingTimeAdjustmentBox + .ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint) + .Delay(b.Duration - fade_duration) + .ResizeWidthTo(0); + + remainingTimeBox + .ResizeWidthTo(0, b.Duration - fade_duration) + .Then() + .ResizeWidthTo(1); + + remainingTimeCounter.CountTo(b.Duration); + } + using (BeginAbsoluteSequence(b.StartTime)) { - Schedule(() => onBreakIn(b)); + Schedule(() => showBreak(b)); using (BeginDelayedSequence(b.Duration - fade_duration)) - Schedule(onBreakOut); + Schedule(hideBreak); } } } - private void onBreakIn(BreakPeriod b) + private void showBreak(BreakPeriod b) { if (letterboxing) letterboxOverlay.Show(); - remainingTimeAdjustmentBox - .ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint) - .Delay(b.Duration - fade_duration) - .ResizeWidthTo(0); - - remainingTimeBox - .ResizeWidthTo(0, b.Duration - fade_duration) - .Then() - .ResizeWidthTo(1); - - remainingTimeCounter.StartCounting(b.EndTime); - remainingTimeCounter.Show(); info.Show(); arrowsOverlay.Show(); } - private void onBreakOut() + private void hideBreak() { if (letterboxing) letterboxOverlay.Hide(); diff --git a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs index b5d77d0d02..e144ac25da 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs @@ -9,18 +9,12 @@ using osu.Game.Beatmaps.Timing; namespace osu.Game.Screens.Play.BreaksOverlay { - public class RemainingTimeCounter : VisibilityContainer + public class RemainingTimeCounter : Container { private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; private readonly OsuSpriteText counter; - private int? previousSecond; - - private double endTime; - - private bool isCounting; - public RemainingTimeCounter() { AutoSizeAxes = Axes.Both; @@ -31,35 +25,27 @@ namespace osu.Game.Screens.Play.BreaksOverlay TextSize = 33, Font = "Venera", }; + + Alpha = 0; } - public void StartCounting(double endTime) + public void CountTo(double duration) { - this.endTime = endTime; - isCounting = true; - } + double offset = 0; - protected override void Update() - { - base.Update(); - - if (isCounting) + while (duration > 0) { - var currentTime = Clock.CurrentTime; - if (currentTime < endTime) - { - int currentSecond = (int)Math.Ceiling((endTime - Clock.CurrentTime) / 1000.0); - if (currentSecond != previousSecond) - { - counter.Text = currentSecond.ToString(); - previousSecond = currentSecond; - } - } - else isCounting = false; + int seconds = (int)Math.Ceiling(duration / 1000); + counter.Delay(offset).TransformTextTo(seconds.ToString()); + + double localOffset = duration - (seconds - 1) * 1000 + 1; // +1 because we want the duration to be the next second when ceiled + + offset += localOffset; + duration -= localOffset; } } - protected override void PopIn() => this.FadeIn(fade_duration); - protected override void PopOut() => this.FadeOut(fade_duration); + public override void Show() => this.FadeIn(fade_duration); + public override void Hide() => this.FadeOut(fade_duration); } } From edd0d166b1c774cf102cc83208c52c56d6ab8df4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 4 Nov 2017 00:42:36 +0900 Subject: [PATCH 26/38] Add text transforms to OsuSpriteText --- osu.Game/Graphics/Sprites/OsuSpriteText.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs index f5749846be..cbd9d9582d 100644 --- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs +++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Transforms; namespace osu.Game.Graphics.Sprites { @@ -40,4 +41,23 @@ namespace osu.Game.Graphics.Sprites return base.CreateFallbackCharacterDrawable(); } } + + public static class OsuSpriteTextTransformExtensions + { + /// + /// Sets to a new value after a duration. + /// + /// A to which further transforms can be added. + public static TransformSequence TransformTextTo(this T spriteText, string newText, double duration = 0, Easing easing = Easing.None) + where T : OsuSpriteText + => spriteText.TransformTo(nameof(OsuSpriteText.Text), newText, duration, easing); + + /// + /// Sets to a new value after a duration. + /// + /// A to which further transforms can be added. + public static TransformSequence TransformTextTo(this TransformSequence t, string newText, double duration = 0, Easing easing = Easing.None) + where T : OsuSpriteText + => t.Append(o => o.TransformTextTo(newText, duration, easing)); + } } From 15f69dff813f5ff46e7c31d1208627d12b2fcb6e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 4 Nov 2017 00:57:10 +0900 Subject: [PATCH 27/38] Make mania hit explosions not stick around when rewinding Fixes #1461. --- osu.Game.Rulesets.Mania/UI/HitExplosion.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs index 8164adcebd..433c518929 100644 --- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs @@ -57,8 +57,10 @@ namespace osu.Game.Rulesets.Mania.UI { base.LoadComplete(); - this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500).Expire(); + this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500); inner.FadeOut(250); + + Expire(true); } } } From 4854302aaa9065d62aeebc298596e5c4ddaf5414 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 4 Nov 2017 01:02:33 +0900 Subject: [PATCH 28/38] Fix follow points not showing up again after rewinding Fixes #1463. --- .../Objects/Drawables/Connections/FollowPoint.cs | 2 ++ .../Objects/Drawables/Connections/FollowPointRenderer.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs index dbf5c6c541..ee0b5e6c50 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { private const float width = 8; + public override bool RemoveWhenNotAlive => false; + public FollowPoint() { Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 2396e5d129..fca9187047 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -52,9 +52,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections } } + public override bool RemoveCompletedTransforms => false; + private void update() { Clear(); + if (hitObjects == null) return; From 2fbd49062690c2bc4dd2402552a1291425abcde3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 6 Nov 2017 14:58:05 +0900 Subject: [PATCH 29/38] Make RemainingTimeCounter into a Counter --- .../Play/BreaksOverlay/BreakOverlay.cs | 3 ++- .../BreaksOverlay/RemainingTimeCounter.cs | 21 ++++--------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs index b3d08c0c82..6128a8f3d7 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Scoring; using System.Collections.Generic; +using osu.Framework.Graphics.UserInterface; namespace osu.Game.Screens.Play.BreaksOverlay { @@ -115,7 +116,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay .Then() .ResizeWidthTo(1); - remainingTimeCounter.CountTo(b.Duration); + remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration); } using (BeginAbsoluteSequence(b.StartTime)) diff --git a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs index e144ac25da..9b043a6e90 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs @@ -6,10 +6,11 @@ using osu.Game.Graphics.Sprites; using osu.Framework.Graphics; using System; using osu.Game.Beatmaps.Timing; +using osu.Framework.Graphics.UserInterface; namespace osu.Game.Screens.Play.BreaksOverlay { - public class RemainingTimeCounter : Container + public class RemainingTimeCounter : Counter { private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; @@ -18,7 +19,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay public RemainingTimeCounter() { AutoSizeAxes = Axes.Both; - Child = counter = new OsuSpriteText + InternalChild = counter = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -29,21 +30,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay Alpha = 0; } - public void CountTo(double duration) - { - double offset = 0; - - while (duration > 0) - { - int seconds = (int)Math.Ceiling(duration / 1000); - counter.Delay(offset).TransformTextTo(seconds.ToString()); - - double localOffset = duration - (seconds - 1) * 1000 + 1; // +1 because we want the duration to be the next second when ceiled - - offset += localOffset; - duration -= localOffset; - } - } + protected override void OnCountChanged(double count) => counter.Text = ((int)Math.Ceiling(count / 1000)).ToString(); public override void Show() => this.FadeIn(fade_duration); public override void Hide() => this.FadeOut(fade_duration); From c7426ebed80d6187b65e1efca94877f48d99a498 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 6 Nov 2017 17:22:22 +0900 Subject: [PATCH 30/38] Fix spinners showing very weird numbers after rewinding Fixes #1462 --- .../Objects/Drawables/Pieces/SpinnerSpmCounter.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs index ebe978f659..774511313a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -61,6 +62,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void SetRotation(float currentRotation) { + // If we've gone back in time, it's fine to work with a fresh set of records for now + if (records.Count > 0 && Time.Current < records.Last().Time) + records.Clear(); + if (records.Count > 0) { var record = records.Peek(); From 348083f589e0faff13d33878fd71ed3e6dac94d0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Nov 2017 14:04:59 +0900 Subject: [PATCH 31/38] Update with framework state transformation Removes explicit initial state setting in DrawableOsuHitObjects. --- osu-framework | 2 +- .../Objects/Drawables/DrawableHitCircle.cs | 21 ++----------------- .../Objects/Drawables/DrawableOsuHitObject.cs | 15 +++++-------- .../Objects/Drawables/DrawableRepeatPoint.cs | 12 +---------- .../Objects/Drawables/DrawableSlider.cs | 14 +++---------- .../Objects/Drawables/DrawableSliderTick.cs | 4 ---- .../Objects/Drawables/DrawableHitObject.cs | 3 +++ 7 files changed, 15 insertions(+), 56 deletions(-) diff --git a/osu-framework b/osu-framework index c8222d1dc9..b70ca3de9e 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit c8222d1dc932aafe17ec42bfbe6cbec81851f55d +Subproject commit b70ca3de9ec1a8eb7fb73fcfd169ff38d00b07cd diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index ed0578d3a4..a973d2b580 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -58,6 +58,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, ApproachCircle = new ApproachCircle { + Alpha = 0, + Scale = new Vector2(4), Colour = AccentColour, } }; @@ -82,25 +84,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }); } - protected override void UpdateInitialState() - { - base.UpdateInitialState(); - - // Hide() cannot be used here, because when rewinding, we need these to be the final values - - ring.Alpha = 1; - circle.Alpha = 1; - number.Alpha = 1; - glow.Alpha = 1; - - ApproachCircle.Alpha = 0; - ApproachCircle.Scale = new Vector2(4); - explode.Alpha = 0; - flash.Alpha = 0; - - Scale = new Vector2(HitObject.Scale); - } - protected override void UpdatePreemptState() { base.UpdatePreemptState(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 7429f084c3..e968e5b9df 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -23,12 +23,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected sealed override void UpdateState(ArmedState state) { - ClearTransforms(true); + double transformTime = HitObject.StartTime - TIME_PREEMPT; - using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true)) + TransformStateTo(transformTime, true); + ClearTransformsAfter(transformTime, true); + + using (BeginAbsoluteSequence(transformTime, true)) { - UpdateInitialState(); - UpdatePreemptState(); using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true)) @@ -36,12 +37,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - protected virtual void UpdateInitialState() - { - // Hide() cannot be used here, because when rewinding, we need these to be the final values - Alpha = 0; - } - protected virtual void UpdatePreemptState() { this.FadeIn(TIME_FADEIN); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 200c697a0f..a9b63ea642 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -24,13 +24,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.repeatPoint = repeatPoint; this.drawableSlider = drawableSlider; - // The containing DrawableSlider is updated before us and clears our transforms, so we need to be - // present to get updated and have UpdateState correctly called when rewinding. - AlwaysPresent = true; - AutoSizeAxes = Axes.Both; Blending = BlendingMode.Additive; Origin = Anchor.Centre; + Scale = new Vector2(0.5f); Children = new Drawable[] { @@ -50,13 +47,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss }); } - protected override void UpdateInitialState() - { - base.UpdateInitialState(); - - Scale = new Vector2(0.5f); - } - protected override void UpdatePreemptState() { var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 2e6e82ce0c..74454ca555 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -43,7 +43,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ball = new SliderBall(s) { Scale = new Vector2(s.Scale), - AccentColour = AccentColour + AccentColour = AccentColour, + AlwaysPresent = true, + Alpha = 0 }, initialCircle = new DrawableHitCircle(new HitCircle { @@ -148,16 +150,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - protected override void UpdateInitialState() - { - base.UpdateInitialState(); - body.Alpha = 1; - - //we need to be present to handle input events. note that we still don't get enough events (we don't get a position if the mouse hasn't moved since the slider appeared). - ball.AlwaysPresent = true; - ball.Alpha = 0; - } - protected override void UpdateCurrentState(ArmedState state) { ball.FadeIn(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 9fe475f4aa..7199691ae6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -28,10 +28,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = new Vector2(16) * sliderTick.Scale; - // The containing DrawableSlider is updated before us and clears our transforms, so we need to be - // present to get updated and have UpdateState correctly called when rewinding. - AlwaysPresent = true; - Masking = true; CornerRadius = Size.X / 2; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 091af04106..99b62f265d 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -215,6 +215,9 @@ namespace osu.Game.Rulesets.Objects.Drawables nestedHitObjects.Add(h); } + protected override bool AllowStateTransformByParent => false; + protected override bool AllowTransformClearByParent => false; + protected abstract void UpdateState(ArmedState state); } } From 66ee9d163188908c7084bc77635222ec214f8847 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Nov 2017 17:04:04 +0900 Subject: [PATCH 32/38] Update in-line with framework changes --- osu-framework | 2 +- .../Objects/Drawables/DrawableOsuHitObject.cs | 9 +++++++-- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 3 --- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu-framework b/osu-framework index b70ca3de9e..ff1e04024d 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b70ca3de9ec1a8eb7fb73fcfd169ff38d00b07cd +Subproject commit ff1e04024d16c0a6cabec7792573b0d019bd1bba diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index e968e5b9df..3e0c23a1ab 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -25,8 +25,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { double transformTime = HitObject.StartTime - TIME_PREEMPT; - TransformStateTo(transformTime, true); - ClearTransformsAfter(transformTime, true); + base.ApplyTransformsAt(transformTime, true); + base.ClearTransformsAfter(transformTime, true); using (BeginAbsoluteSequence(transformTime, true)) { @@ -46,6 +46,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { } + // Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply + // transforms in the same way and don't rely on them not being cleared + public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { } + public override void ApplyTransformsAt(double time, bool propagateChildren = false) { } + private OsuInputManager osuActionInputManager; internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 99b62f265d..091af04106 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -215,9 +215,6 @@ namespace osu.Game.Rulesets.Objects.Drawables nestedHitObjects.Add(h); } - protected override bool AllowStateTransformByParent => false; - protected override bool AllowTransformClearByParent => false; - protected abstract void UpdateState(ArmedState state); } } From bd2de899183ebc28262a4466e35c3f2dc09991f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2017 19:26:25 +0900 Subject: [PATCH 33/38] Why weren't these fixed previously --- osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs | 2 -- osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs index 07c499b56c..e1fe19212b 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Objects.Drawables; - namespace osu.Game.Rulesets.Taiko.Judgements { public class TaikoStrongHitJudgement : TaikoJudgement diff --git a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs index 9b043a6e90..0df7bb97e0 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Framework.Graphics; using System; From 49731f4c050ed31ab155f4c3e8502801faf7c1ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2017 19:32:09 +0900 Subject: [PATCH 34/38] Remove unused parmeter --- osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs index 6128a8f3d7..7ef1ef8d8a 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs @@ -121,14 +121,14 @@ namespace osu.Game.Screens.Play.BreaksOverlay using (BeginAbsoluteSequence(b.StartTime)) { - Schedule(() => showBreak(b)); + Schedule(showBreak); using (BeginDelayedSequence(b.Duration - fade_duration)) Schedule(hideBreak); } } } - private void showBreak(BreakPeriod b) + private void showBreak() { if (letterboxing) letterboxOverlay.Show(); From 5277c3c1647c86b33c04ee98ea35d1c54fe52aca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Nov 2017 22:11:25 +0900 Subject: [PATCH 35/38] Set the frame time appropriately to reverse judgements a little better --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index f0d68c0467..06f7259f78 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -104,7 +104,13 @@ namespace osu.Game.Rulesets.Replays { //if we changed frames, we want to execute once *exactly* on the frame's time. if (currentDirection == time.CompareTo(NextFrame.Time) && advanceFrame()) + { + // If going backwards, we need to execute once _before_ the frame time to reverse any judgements + // that would occur as a result of this frame in forward playback + if (currentDirection == -1) + return currentTime = CurrentFrame.Time - 1; return currentTime = CurrentFrame.Time; + } //if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null. if (inImportantSection) From e742c07f7d3edaf3e703397493ad07f1268e2bae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Nov 2017 22:17:41 +0900 Subject: [PATCH 36/38] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 68e0072674..1a563d7ce0 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 68e0072674c4bbc4e608120f51cd1d7e91e9500c +Subproject commit 1a563d7ce0834cede2ef587762f98fe580badf3c From 3142832693d28ccc9e7c5d355c374554656a4b58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Nov 2017 13:00:29 +0900 Subject: [PATCH 37/38] Add precision to playback speed --- osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs index 7cabe1f3ab..bf2909b8ab 100644 --- a/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/ReplaySettings/PlaybackSettings.cs @@ -23,7 +23,8 @@ namespace osu.Game.Screens.Play.ReplaySettings { Default = 1, MinValue = 0.5, - MaxValue = 2 + MaxValue = 2, + Precision = 0.01, }, }; } From 7d4e1b6f22c9ee350b2cebde7a5cc5e9d8238fbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Nov 2017 13:00:54 +0900 Subject: [PATCH 38/38] Don't require a local storage variable for restoring playback speed --- osu.Game/Screens/Play/Player.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2daf8f0765..59d56a5a44 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -51,7 +51,6 @@ namespace osu.Game.Screens.Play private IAdjustableClock adjustableSourceClock; private FramedOffsetClock offsetClock; private DecoupleableInterpolatingFramedClock decoupledClock; - private double clockRate; private PauseContainer pauseContainer; @@ -157,10 +156,7 @@ namespace osu.Game.Screens.Play Schedule(() => { decoupledClock.ChangeSource(adjustableSourceClock); - foreach (var mod in working.Mods.Value.OfType()) - mod.ApplyToClock(adjustableSourceClock); - - clockRate = adjustableSourceClock.Rate; + applyRateFromMods(); }); }); @@ -249,6 +245,13 @@ namespace osu.Game.Screens.Play scoreProcessor.Failed += onFail; } + private void applyRateFromMods() + { + adjustableSourceClock.Rate = 1; + foreach (var mod in Beatmap.Value.Mods.Value.OfType()) + mod.ApplyToClock(adjustableSourceClock); + } + private void initializeStoryboard(bool asyncLoad) { var beatmap = Beatmap.Value.Beatmap; @@ -346,8 +349,9 @@ namespace osu.Game.Screens.Play { if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || RulesetContainer?.HasReplayLoaded != false) { - // We want to make sure we restore the clock rate - adjustableSourceClock.Rate = clockRate; + // In the case of replays, we may have changed the playback rate. + applyRateFromMods(); + fadeOut(); return base.OnExiting(next); }