From e7d04564545cb4fe87d04add0792d6d14c1284c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Sat, 26 Sep 2020 16:25:17 +0200 Subject: [PATCH 01/24] Add SpinnerNoBlink to LegacySettings --- osu.Game/Skinning/LegacySkinConfiguration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/LegacySkinConfiguration.cs b/osu.Game/Skinning/LegacySkinConfiguration.cs index 1d5412d93f..a0e8fb2f92 100644 --- a/osu.Game/Skinning/LegacySkinConfiguration.cs +++ b/osu.Game/Skinning/LegacySkinConfiguration.cs @@ -19,6 +19,7 @@ namespace osu.Game.Skinning ComboOverlap, AnimationFramerate, LayeredHitSounds, + SpinnerNoBlink } } } From 33d000e5325af0ca813c9c26de761761332f51b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Sat, 26 Sep 2020 16:25:57 +0200 Subject: [PATCH 02/24] Add support for SpinnerNoBlink in legacy spinner --- .../Skinning/LegacyOldStyleSpinner.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index e157842fd1..ada3a825d0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; +using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning { @@ -25,12 +26,16 @@ namespace osu.Game.Rulesets.Osu.Skinning private Sprite metreSprite; private Container metre; + private bool spinnerNoBlink; + private const float sprite_scale = 1 / 1.6f; private const float final_metre_height = 692 * sprite_scale; [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { + spinnerNoBlink = source.GetConfig(LegacySetting.SpinnerNoBlink)?.Value ?? false; + drawableSpinner = (DrawableSpinner)drawableObject; RelativeSizeAxes = Axes.Both; @@ -117,12 +122,15 @@ namespace osu.Game.Rulesets.Osu.Skinning private float getMetreHeight(float progress) { - progress = Math.Min(99, progress * 100); + progress *= 100; + + // the spinner should still blink at 100% progress. + if (!spinnerNoBlink) + progress = Math.Min(99, progress); int barCount = (int)progress / 10; - // todo: add SpinnerNoBlink support - if (RNG.NextBool(((int)progress % 10) / 10f)) + if (!spinnerNoBlink && RNG.NextBool(((int)progress % 10) / 10f)) barCount++; return (float)barCount / total_bars * final_metre_height; From 40a4654ef91c2ec9f92cf8b944eb48edd1ab9972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Mon, 28 Sep 2020 12:21:43 +0200 Subject: [PATCH 03/24] Invert spinnerNoBlink to spinnerBlink locally --- osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index ada3a825d0..cce50b24ac 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning private Sprite metreSprite; private Container metre; - private bool spinnerNoBlink; + private bool spinnerBlink; private const float sprite_scale = 1 / 1.6f; private const float final_metre_height = 692 * sprite_scale; @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Skinning [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { - spinnerNoBlink = source.GetConfig(LegacySetting.SpinnerNoBlink)?.Value ?? false; + spinnerBlink = !source.GetConfig(LegacySetting.SpinnerNoBlink)?.Value ?? true; drawableSpinner = (DrawableSpinner)drawableObject; @@ -125,12 +125,12 @@ namespace osu.Game.Rulesets.Osu.Skinning progress *= 100; // the spinner should still blink at 100% progress. - if (!spinnerNoBlink) + if (spinnerBlink) progress = Math.Min(99, progress); int barCount = (int)progress / 10; - if (!spinnerNoBlink && RNG.NextBool(((int)progress % 10) / 10f)) + if (spinnerBlink && RNG.NextBool(((int)progress % 10) / 10f)) barCount++; return (float)barCount / total_bars * final_metre_height; From 54852991f36f2136dd5240058e5db758f81e65ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Mon, 28 Sep 2020 12:24:30 +0200 Subject: [PATCH 04/24] Move SpinnerNoBlink to OsuSkinConfiguration --- osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 3 +-- osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 3 ++- osu.Game/Skinning/LegacySkinConfiguration.cs | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index cce50b24ac..63cd48676e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -12,7 +12,6 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; -using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning { @@ -34,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Skinning [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { - spinnerBlink = !source.GetConfig(LegacySetting.SpinnerNoBlink)?.Value ?? true; + spinnerBlink = !source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value ?? true; drawableSpinner = (DrawableSpinner)drawableObject; diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index e034e14eb0..63c9b53278 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Skinning CursorRotate, HitCircleOverlayAboveNumber, HitCircleOverlayAboveNumer, // Some old skins will have this typo - SpinnerFrequencyModulate + SpinnerFrequencyModulate, + SpinnerNoBlink } } diff --git a/osu.Game/Skinning/LegacySkinConfiguration.cs b/osu.Game/Skinning/LegacySkinConfiguration.cs index a0e8fb2f92..828804b9cb 100644 --- a/osu.Game/Skinning/LegacySkinConfiguration.cs +++ b/osu.Game/Skinning/LegacySkinConfiguration.cs @@ -18,8 +18,7 @@ namespace osu.Game.Skinning ComboPrefix, ComboOverlap, AnimationFramerate, - LayeredHitSounds, - SpinnerNoBlink + LayeredHitSounds } } } From 5d1c3773790beb4b40ae02b11a230ddef47d7e05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Sep 2020 15:07:55 +0900 Subject: [PATCH 05/24] Fix HitObject samples getting stuck in a playing state on seeking far into the future --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 6 ++++++ osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 6 ++++++ osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 7 +++++++ osu.Game/Skinning/SkinnableSound.cs | 3 ++- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 68f203db47..ba328e15c6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -110,6 +110,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + public override void StopAllSamples() + { + base.StopAllSamples(); + slidingSample?.Stop(); + } + private void updateSlidingSample(ValueChangedEvent tracking) { if (tracking.NewValue) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index b2a706833c..9e552981ea 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -124,6 +124,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + public override void StopAllSamples() + { + base.StopAllSamples(); + spinningSample?.Stop(); + } + protected override void AddNestedHitObject(DrawableHitObject hitObject) { base.AddNestedHitObject(hitObject); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 796b8f7aae..56e3a98ca3 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -387,6 +387,11 @@ namespace osu.Game.Rulesets.Objects.Drawables } } + /// + /// Stops playback of all samples. Automatically called when 's lifetime has been exceeded. + /// + public virtual void StopAllSamples() => Samples?.Stop(); + protected override void Update() { base.Update(); @@ -455,6 +460,8 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var nested in NestedHitObjects) nested.OnKilled(); + StopAllSamples(); + UpdateResult(false); } diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 07b759843c..c1f0b78d3b 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -73,7 +73,8 @@ namespace osu.Game.Skinning // it's not easy to know if a sample has finished playing (to end). // to keep things simple only resume playing looping samples. else if (Looping) - play(); + // schedule so we don't start playing a sample which is no longer alive. + Schedule(play); } }); } From 2f26728cdb6a28235a9e15c9e1acd0296938fb05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Sep 2020 15:29:56 +0900 Subject: [PATCH 06/24] Add test coverage of editor sample playback --- .../Editing/TestSceneEditorSamplePlayback.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneEditorSamplePlayback.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSamplePlayback.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSamplePlayback.cs new file mode 100644 index 0000000000..039a21fd94 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSamplePlayback.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics.Audio; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneEditorSamplePlayback : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + [Test] + public void TestSlidingSampleStopsOnSeek() + { + DrawableSlider slider = null; + DrawableSample[] samples = null; + + AddStep("get first slider", () => + { + slider = Editor.ChildrenOfType().OrderBy(s => s.HitObject.StartTime).First(); + samples = slider.ChildrenOfType().ToArray(); + }); + + AddStep("start playback", () => EditorClock.Start()); + + AddUntilStep("wait for slider sliding then seek", () => + { + if (!slider.Tracking.Value) + return false; + + if (!samples.Any(s => s.Playing)) + return false; + + EditorClock.Seek(20000); + return true; + }); + + AddAssert("slider samples are not playing", () => samples.Length == 5 && samples.All(s => s.Played && !s.Playing)); + } + } +} From cc9fa4675c00a905272c63d2e963d48d0fb2b94a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 29 Sep 2020 17:59:42 +0900 Subject: [PATCH 07/24] Adjust HP increases --- osu.Game/Rulesets/Judgements/Judgement.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index ea7a8d8d25..d80ebfe402 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -124,19 +124,19 @@ namespace osu.Game.Rulesets.Judgements return -DEFAULT_MAX_HEALTH_INCREASE; case HitResult.Meh: - return -DEFAULT_MAX_HEALTH_INCREASE * 0.05; + return -DEFAULT_MAX_HEALTH_INCREASE * 0.5; case HitResult.Ok: - return -DEFAULT_MAX_HEALTH_INCREASE * 0.01; + return -DEFAULT_MAX_HEALTH_INCREASE * 0.3; case HitResult.Good: - return DEFAULT_MAX_HEALTH_INCREASE * 0.5; + return DEFAULT_MAX_HEALTH_INCREASE * 0.1; case HitResult.Great: - return DEFAULT_MAX_HEALTH_INCREASE; + return DEFAULT_MAX_HEALTH_INCREASE * 0.8; case HitResult.Perfect: - return DEFAULT_MAX_HEALTH_INCREASE * 1.05; + return DEFAULT_MAX_HEALTH_INCREASE; case HitResult.SmallBonus: return DEFAULT_MAX_HEALTH_INCREASE * 0.1; From 297168ecc41e7dd053fac1d0a17c0ac898315e79 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 29 Sep 2020 18:55:06 +0900 Subject: [PATCH 08/24] Fix scores sometimes not being re-standardised correctly --- .../Requests/Responses/APILegacyScoreInfo.cs | 1 + osu.Game/Scoring/ScoreInfo.cs | 28 +++++++++++++++ osu.Game/Scoring/ScoreManager.cs | 34 ++++++++++++++----- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index b941cd8973..3d3c07a5ad 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -38,6 +38,7 @@ namespace osu.Game.Online.API.Requests.Responses Rank = Rank, Ruleset = ruleset, Mods = mods, + IsLegacyScore = true }; if (Statistics != null) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 4ed3f92e25..0206989231 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -185,6 +185,34 @@ namespace osu.Game.Scoring [JsonProperty("position")] public int? Position { get; set; } + private bool isLegacyScore; + + /// + /// Whether this represents a legacy (osu!stable) score. + /// + [JsonIgnore] + [NotMapped] + public bool IsLegacyScore + { + get + { + if (isLegacyScore) + return true; + + // The above check will catch legacy online scores that have an appropriate UserString + UserId. + // For non-online scores such as those imported in, a heuristic is used based on the following table: + // + // Mode | UserString | UserId + // --------------- | ---------- | --------- + // stable | | 1 + // lazer | | + // lazer (offline) | Guest | 1 + + return ID > 0 && UserID == 1 && UserString != "Guest"; + } + set => isLegacyScore = value; + } + public IEnumerable<(HitResult result, int count, int? maxCount)> GetStatisticsForDisplay() { foreach (var key in OrderAttributeUtils.GetValuesInOrder()) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 561ca631b3..8e8147ff39 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -10,6 +10,7 @@ using System.Threading; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -149,23 +150,38 @@ namespace osu.Game.Scoring return; } - int? beatmapMaxCombo = score.Beatmap.MaxCombo; + int beatmapMaxCombo; - if (beatmapMaxCombo == null) + if (score.IsLegacyScore) { - if (score.Beatmap.ID == 0 || difficulties == null) + // This score is guaranteed to be an osu!stable score. + // The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used. + if (score.Beatmap.MaxCombo == null) { - // We don't have enough information (max combo) to compute the score, so let's use the provided score. - Value = score.TotalScore; + if (score.Beatmap.ID == 0 || difficulties == null) + { + // We don't have enough information (max combo) to compute the score, so use the provided score. + Value = score.TotalScore; + return; + } + + // We can compute the max combo locally after the async beatmap difficulty computation. + difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods, (difficultyCancellationSource = new CancellationTokenSource()).Token); + difficultyBindable.BindValueChanged(d => updateScore(d.NewValue.MaxCombo), true); + return; } - // We can compute the max combo locally after the async beatmap difficulty computation. - difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods, (difficultyCancellationSource = new CancellationTokenSource()).Token); - difficultyBindable.BindValueChanged(d => updateScore(d.NewValue.MaxCombo), true); + beatmapMaxCombo = score.Beatmap.MaxCombo.Value; } else - updateScore(beatmapMaxCombo.Value); + { + // This score is guaranteed to be an osu!lazer score. + // The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values. + beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum(); + } + + updateScore(beatmapMaxCombo); } private void updateScore(int beatmapMaxCombo) From bc943dee53e7208e57aa1dc77f38bfcac3e818a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Sep 2020 17:52:12 +0900 Subject: [PATCH 09/24] Add textbox entry for speed multiplier and volume --- .../Screens/Edit/Timing/DifficultySection.cs | 12 +-- osu.Game/Screens/Edit/Timing/SampleSection.cs | 12 ++- .../Edit/Timing/SliderWithTextBoxInput.cs | 74 +++++++++++++++++++ 3 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs diff --git a/osu.Game/Screens/Edit/Timing/DifficultySection.cs b/osu.Game/Screens/Edit/Timing/DifficultySection.cs index 58a7f97e5f..78766d9777 100644 --- a/osu.Game/Screens/Edit/Timing/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Timing/DifficultySection.cs @@ -2,27 +2,23 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Overlays.Settings; namespace osu.Game.Screens.Edit.Timing { internal class DifficultySection : Section { - private SettingsSlider multiplier; + private SliderWithTextBoxInput multiplierSlider; [BackgroundDependencyLoader] private void load() { Flow.AddRange(new[] { - multiplier = new SettingsSlider + multiplierSlider = new SliderWithTextBoxInput("Speed Multiplier") { - LabelText = "Speed Multiplier", - Bindable = new DifficultyControlPoint().SpeedMultiplierBindable, - RelativeSizeAxes = Axes.X, + Current = new DifficultyControlPoint().SpeedMultiplierBindable } }); } @@ -31,7 +27,7 @@ namespace osu.Game.Screens.Edit.Timing { if (point.NewValue != null) { - multiplier.Bindable = point.NewValue.SpeedMultiplierBindable; + multiplierSlider.Current = point.NewValue.SpeedMultiplierBindable; } } diff --git a/osu.Game/Screens/Edit/Timing/SampleSection.cs b/osu.Game/Screens/Edit/Timing/SampleSection.cs index 4665c77991..de986e28ca 100644 --- a/osu.Game/Screens/Edit/Timing/SampleSection.cs +++ b/osu.Game/Screens/Edit/Timing/SampleSection.cs @@ -2,18 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.UserInterfaceV2; -using osu.Game.Overlays.Settings; namespace osu.Game.Screens.Edit.Timing { internal class SampleSection : Section { private LabelledTextBox bank; - private SettingsSlider volume; + private SliderWithTextBoxInput volume; [BackgroundDependencyLoader] private void load() @@ -24,10 +23,9 @@ namespace osu.Game.Screens.Edit.Timing { Label = "Bank Name", }, - volume = new SettingsSlider + volume = new SliderWithTextBoxInput("Volume") { - Bindable = new SampleControlPoint().SampleVolumeBindable, - LabelText = "Volume", + Current = new SampleControlPoint().SampleVolumeBindable, } }); } @@ -37,7 +35,7 @@ namespace osu.Game.Screens.Edit.Timing if (point.NewValue != null) { bank.Current = point.NewValue.SampleBankBindable; - volume.Bindable = point.NewValue.SampleVolumeBindable; + volume.Current = point.NewValue.SampleVolumeBindable; } } diff --git a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs new file mode 100644 index 0000000000..07b914c506 --- /dev/null +++ b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs @@ -0,0 +1,74 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Screens.Edit.Timing +{ + internal class SliderWithTextBoxInput : CompositeDrawable, IHasCurrentValue + where T : struct, IEquatable, IComparable, IConvertible + { + private readonly SettingsSlider slider; + + public SliderWithTextBoxInput(string labelText) + { + LabelledTextBox textbox; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + textbox = new LabelledTextBox + { + Label = labelText, + }, + slider = new SettingsSlider + { + RelativeSizeAxes = Axes.X, + } + } + }, + }; + + textbox.OnCommit += (t, isNew) => + { + if (!isNew) return; + + try + { + slider.Bindable.Parse(t.Text); + } + catch + { + // will restore the previous text value on failure. + Current.TriggerChange(); + } + }; + + Current.BindValueChanged(val => + { + textbox.Text = val.NewValue.ToString(); + }, true); + } + + public Bindable Current + { + get => slider.Bindable; + set => slider.Bindable = value; + } + } +} From 61e62929eeccb402000af318ba438c0c78e2e6ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Oct 2020 12:51:33 +0900 Subject: [PATCH 10/24] Apply changes in line with framework event logic update --- osu.Game.Tournament/Components/DateTextBox.cs | 2 +- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 3 ++- osu.Game/Overlays/ChatOverlay.cs | 3 ++- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs | 3 ++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Components/DateTextBox.cs b/osu.Game.Tournament/Components/DateTextBox.cs index aee5241e35..a1b5ac38ea 100644 --- a/osu.Game.Tournament/Components/DateTextBox.cs +++ b/osu.Game.Tournament/Components/DateTextBox.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tournament.Components { base.Bindable = new Bindable(); - ((OsuTextBox)Control).OnCommit = (sender, newText) => + ((OsuTextBox)Control).OnCommit += (sender, newText) => { try { diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index f8810c778f..8b0caddbc6 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -59,12 +59,13 @@ namespace osu.Game.Online.Chat RelativeSizeAxes = Axes.X, Height = textbox_height, PlaceholderText = "type your message", - OnCommit = postMessage, ReleaseFocusOnCommit = false, HoldFocus = true, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, }); + + textbox.OnCommit += postMessage; } Channel.BindValueChanged(channelChanged); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index c53eccf78b..8bc7e21047 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -146,7 +146,6 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Height = 1, PlaceholderText = "type your message", - OnCommit = postMessage, ReleaseFocusOnCommit = false, HoldFocus = true, } @@ -186,6 +185,8 @@ namespace osu.Game.Overlays }, }; + textbox.OnCommit += postMessage; + ChannelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue; ChannelTabControl.ChannelSelectorActive.ValueChanged += active => ChannelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden; ChannelSelectionOverlay.State.ValueChanged += state => diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 050e687dfb..b8d04eab4e 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music }, }; - filter.Search.OnCommit = (sender, newText) => + filter.Search.OnCommit += (sender, newText) => { BeatmapInfo toSelect = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault(); diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 34e5da4ef4..f96e204f62 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -236,7 +236,6 @@ namespace osu.Game.Overlays.Settings.Sections.General PlaceholderText = "password", RelativeSizeAxes = Axes.X, TabbableContentContainer = this, - OnCommit = (sender, newText) => performLogin() }, new SettingsCheckbox { @@ -276,6 +275,8 @@ namespace osu.Game.Overlays.Settings.Sections.General } } }; + + password.OnCommit += (sender, newText) => performLogin(); } public override bool AcceptsFocus => true; From e0a0902a15f9e1dac34b8795035c2ef48e47dbec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Oct 2020 13:06:24 +0900 Subject: [PATCH 11/24] Ensure textbox always reverts to sane state on out-of-range failures --- .../Edit/Timing/SliderWithTextBoxInput.cs | 7 +++++-- osu.Game/Screens/Edit/Timing/TimingSection.cs | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs index 07b914c506..14023b0c35 100644 --- a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs @@ -54,9 +54,12 @@ namespace osu.Game.Screens.Edit.Timing } catch { - // will restore the previous text value on failure. - Current.TriggerChange(); + // TriggerChange below will restore the previous text value on failure. } + + // This is run regardless of parsing success as the parsed number may not actually trigger a change + // due to bindable clamping. Even in such a case we want to update the textbox to a sane visual state. + Current.TriggerChange(); }; Current.BindValueChanged(val => diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index cc79dd2acc..0202441537 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -65,18 +65,19 @@ namespace osu.Game.Screens.Edit.Timing { if (!isNew) return; - if (double.TryParse(Current.Value, out double doubleVal)) + try { - try - { + if (double.TryParse(Current.Value, out double doubleVal) && doubleVal > 0) beatLengthBindable.Value = beatLengthToBpm(doubleVal); - } - catch - { - // will restore the previous text value on failure. - beatLengthBindable.TriggerChange(); - } } + catch + { + // TriggerChange below will restore the previous text value on failure. + } + + // This is run regardless of parsing success as the parsed number may not actually trigger a change + // due to bindable clamping. Even in such a case we want to update the textbox to a sane visual state. + beatLengthBindable.TriggerChange(); }; beatLengthBindable.BindValueChanged(val => From 482c23901b4b9aed6f62be5d2fbe719874e72ff6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Oct 2020 16:54:56 +0900 Subject: [PATCH 12/24] Check RequestedPlaying state before allowing scheduled resume of looped sample --- osu.Game/Skinning/PausableSkinnableSound.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/PausableSkinnableSound.cs b/osu.Game/Skinning/PausableSkinnableSound.cs index d080e2ccd9..9819574b1d 100644 --- a/osu.Game/Skinning/PausableSkinnableSound.cs +++ b/osu.Game/Skinning/PausableSkinnableSound.cs @@ -41,8 +41,14 @@ namespace osu.Game.Skinning // it's not easy to know if a sample has finished playing (to end). // to keep things simple only resume playing looping samples. else if (Looping) + { // schedule so we don't start playing a sample which is no longer alive. - Schedule(base.Play); + Schedule(() => + { + if (RequestedPlaying) + base.Play(); + }); + } } }); } From ffc1e9c35881288b24a2e11b5a405190bb14e860 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Oct 2020 18:23:38 +0900 Subject: [PATCH 13/24] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index afc5d4ec52..78ceaa8616 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5fa1685d9b..3a839ac1a4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 60708a39e2..48c91f0d53 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 7e5ecd84bc39dabad82b32e926e3a73ceee4ae46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=BCbner?= Date: Thu, 1 Oct 2020 12:41:44 +0200 Subject: [PATCH 14/24] Add braces to clear up operator precedence --- osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 63cd48676e..a5f20378fe 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Skinning [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { - spinnerBlink = !source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value ?? true; + spinnerBlink = !(source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value) ?? true; drawableSpinner = (DrawableSpinner)drawableObject; From 3e6af7ce43975aea05208ef52654038a7984360a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Oct 2020 20:09:09 +0900 Subject: [PATCH 15/24] Refactor for readability --- osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index a5f20378fe..c952500bbf 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Skinning [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { - spinnerBlink = !(source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value) ?? true; + spinnerBlink = source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value != true; drawableSpinner = (DrawableSpinner)drawableObject; From ba76089219cd9489cf10b160898cafea7414192c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Oct 2020 20:24:32 +0900 Subject: [PATCH 16/24] Fix spinner flashing yellow glow before completion --- .../Objects/Drawables/Pieces/DefaultSpinnerDisc.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 2862fe49bd..587bd415ee 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -93,6 +93,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces drawableSpinner.RotationTracker.Complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200)); drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; + + updateStateTransforms(drawableSpinner, drawableSpinner.State.Value); } protected override void Update() @@ -124,6 +126,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { + if (!(drawableHitObject is DrawableSpinner)) + return; + centre.ScaleTo(0); mainContainer.ScaleTo(0); From 6d3f4c8699f713ab5146401254647c9aab421f6b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Oct 2020 20:38:47 +0900 Subject: [PATCH 17/24] Fix a few more similar cases --- osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs | 5 +++++ osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index bcb2af8e3e..1dfc9c0772 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -72,10 +72,15 @@ namespace osu.Game.Rulesets.Osu.Skinning this.FadeOut(); drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; + + updateStateTransforms(drawableSpinner, drawableSpinner.State.Value); } private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { + if (!(drawableHitObject is DrawableSpinner)) + return; + var spinner = (Spinner)drawableSpinner.HitObject; using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index a45d91801d..c498179eef 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -86,10 +86,15 @@ namespace osu.Game.Rulesets.Osu.Skinning this.FadeOut(); drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; + + updateStateTransforms(drawableSpinner, drawableSpinner.State.Value); } private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { + if (!(drawableHitObject is DrawableSpinner)) + return; + var spinner = drawableSpinner.HitObject; using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) From d7f9b8045cc99a153342527217246953c146401f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Oct 2020 21:33:54 +0900 Subject: [PATCH 18/24] Safeguard againts multiple ApplyResult() invocations --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index f159d28eed..abfe7eb58c 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -469,6 +469,9 @@ namespace osu.Game.Rulesets.Objects.Drawables /// The callback that applies changes to the . protected void ApplyResult(Action application) { + if (Result.HasResult) + throw new InvalidOperationException($"Cannot apply result on a hitobject that already has a result."); + application?.Invoke(Result); if (!Result.HasResult) From 042c39ae1b04f3653cb068acb2089f01213a3aed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Oct 2020 21:48:45 +0900 Subject: [PATCH 19/24] Remove redundant string interpolation --- 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 abfe7eb58c..a2d222d0a8 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -470,7 +470,7 @@ namespace osu.Game.Rulesets.Objects.Drawables protected void ApplyResult(Action application) { if (Result.HasResult) - throw new InvalidOperationException($"Cannot apply result on a hitobject that already has a result."); + throw new InvalidOperationException("Cannot apply result on a hitobject that already has a result."); application?.Invoke(Result); From ab33434a8a80f3c50228e52e7e7f643a3f71e40c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Oct 2020 21:54:43 +0900 Subject: [PATCH 20/24] Reword xmldocs to better describe nested events --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index f159d28eed..24ee3f629d 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -51,12 +51,12 @@ namespace osu.Game.Rulesets.Objects.Drawables public override bool PropagateNonPositionalInputSubTree => HandleUserInput; /// - /// Invoked when a has been applied by this or a nested . + /// Invoked by this or a nested after a has been applied. /// public event Action OnNewResult; /// - /// Invoked when a is being reverted by this or a nested . + /// Invoked by this or a nested prior to a being reverted. /// public event Action OnRevertResult; @@ -236,7 +236,7 @@ namespace osu.Game.Rulesets.Objects.Drawables #region State / Transform Management /// - /// Bind to apply a custom state which can override the default implementation. + /// Invoked by this or a nested to apply a custom state that can override the default implementation. /// public event Action ApplyCustomUpdateState; From c72dbf1ba0e84ff3d19c2eb780d292ad1c4d56cf Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2020 17:15:49 +0000 Subject: [PATCH 21/24] Bump ppy.osu.Framework.NativeLibs from 2020.213.0 to 2020.923.0 Bumps [ppy.osu.Framework.NativeLibs](https://github.com/ppy/osu-framework) from 2020.213.0 to 2020.923.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2020.213.0...2020.923.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 48c91f0d53..31f1af135d 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -85,6 +85,6 @@ - + From 50722cc754f8cfc20042c11e2a27da2ffb5cdd7b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 2 Oct 2020 14:48:56 +0900 Subject: [PATCH 22/24] Update slider test scene sliders to fit better --- .../TestSceneSlider.cs | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index 6a689a1f80..c79cae2fe5 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - StartTime = Time.Current + 1000, + StartTime = Time.Current + time_offset, Position = new Vector2(239, 176), Path = new SliderPath(PathType.PerfectCurve, new[] { @@ -185,22 +185,26 @@ namespace osu.Game.Rulesets.Osu.Tests private Drawable testSlowSpeed() => createSlider(speedMultiplier: 0.5); - private Drawable testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5); + private Drawable testShortSlowSpeed(int repeats = 0) => createSlider(distance: max_length / 4, repeats: repeats, speedMultiplier: 0.5); private Drawable testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15); - private Drawable testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); + private Drawable testShortHighSpeed(int repeats = 0) => createSlider(distance: max_length / 4, repeats: repeats, speedMultiplier: 15); - private Drawable createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) + private const double time_offset = 1500; + + private const float max_length = 200; + + private Drawable createSlider(float circleSize = 2, float distance = max_length, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) { var slider = new Slider { - StartTime = Time.Current + 1000, - Position = new Vector2(-(distance / 2), 0), + StartTime = Time.Current + time_offset, + Position = new Vector2(0, -(distance / 2)), Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, - new Vector2(distance, 0), + new Vector2(0, distance), }, distance), RepeatCount = repeats, StackHeight = stackHeight @@ -213,14 +217,14 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - StartTime = Time.Current + 1000, - Position = new Vector2(-200, 0), + StartTime = Time.Current + time_offset, + Position = new Vector2(-max_length / 2, 0), Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, - new Vector2(200, 200), - new Vector2(400, 0) - }, 600), + new Vector2(max_length / 2, max_length / 2), + new Vector2(max_length, 0) + }, max_length * 1.5f), RepeatCount = repeats, }; @@ -233,16 +237,16 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - StartTime = Time.Current + 1000, - Position = new Vector2(-200, 0), + StartTime = Time.Current + time_offset, + Position = new Vector2(-max_length / 2, 0), Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, - new Vector2(150, 75), - new Vector2(200, 0), - new Vector2(300, -200), - new Vector2(400, 0), - new Vector2(430, 0) + new Vector2(max_length * 0.375f, max_length * 0.18f), + new Vector2(max_length / 2, 0), + new Vector2(max_length * 0.75f, -max_length / 2), + new Vector2(max_length * 0.95f, 0), + new Vector2(max_length, 0) }), RepeatCount = repeats, }; @@ -256,15 +260,15 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - StartTime = Time.Current + 1000, - Position = new Vector2(-200, 0), + StartTime = Time.Current + time_offset, + Position = new Vector2(-max_length / 2, 0), Path = new SliderPath(PathType.Bezier, new[] { Vector2.Zero, - new Vector2(150, 75), - new Vector2(200, 100), - new Vector2(300, -200), - new Vector2(430, 0) + new Vector2(max_length * 0.375f, max_length * 0.18f), + new Vector2(max_length / 2, max_length / 4), + new Vector2(max_length * 0.75f, -max_length / 2), + new Vector2(max_length, 0) }), RepeatCount = repeats, }; @@ -278,16 +282,16 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - StartTime = Time.Current + 1000, + StartTime = Time.Current + time_offset, Position = new Vector2(0, 0), Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, - new Vector2(-200, 0), + new Vector2(-max_length / 2, 0), new Vector2(0, 0), - new Vector2(0, -200), - new Vector2(-200, -200), - new Vector2(0, -200) + new Vector2(0, -max_length / 2), + new Vector2(-max_length / 2, -max_length / 2), + new Vector2(0, -max_length / 2) }), RepeatCount = repeats, }; @@ -305,14 +309,14 @@ namespace osu.Game.Rulesets.Osu.Tests var slider = new Slider { - StartTime = Time.Current + 1000, - Position = new Vector2(-100, 0), + StartTime = Time.Current + time_offset, + Position = new Vector2(-max_length / 4, 0), Path = new SliderPath(PathType.Catmull, new[] { Vector2.Zero, - new Vector2(50, -50), - new Vector2(150, 50), - new Vector2(200, 0) + new Vector2(max_length * 0.125f, max_length * 0.125f), + new Vector2(max_length * 0.375f, max_length * 0.125f), + new Vector2(max_length / 2, 0) }), RepeatCount = repeats, NodeSamples = repeatSamples From d6fe5482d30fe8f5197d18145a47ec8a2644dcca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 2 Oct 2020 15:28:08 +0900 Subject: [PATCH 23/24] Add failing test showing missing control point removal --- osu.Game.Tests/NonVisual/ControlPointInfoTest.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index 830e4bc603..90a487c0ac 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -139,6 +139,22 @@ namespace osu.Game.Tests.NonVisual Assert.That(cpi.Groups.Count, Is.EqualTo(0)); } + [Test] + public void TestRemoveGroupAlsoRemovedControlPoints() + { + var cpi = new ControlPointInfo(); + + var group = cpi.GroupAt(1000, true); + + group.Add(new SampleControlPoint()); + + Assert.That(cpi.SamplePoints.Count, Is.EqualTo(1)); + + cpi.RemoveGroup(group); + + Assert.That(cpi.SamplePoints.Count, Is.EqualTo(0)); + } + [Test] public void TestAddControlPointToGroup() { From f501c88b46f09b6bbcda0ddcc520151a882bdc64 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 2 Oct 2020 15:25:35 +0900 Subject: [PATCH 24/24] Fix individual control points not being removed from group when group is removed --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index e7788b75f3..22314f28c7 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -158,6 +158,9 @@ namespace osu.Game.Beatmaps.ControlPoints public void RemoveGroup(ControlPointGroup group) { + foreach (var item in group.ControlPoints.ToArray()) + group.Remove(item); + group.ItemAdded -= groupItemAdded; group.ItemRemoved -= groupItemRemoved;