diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 60ace8ea69..dcab964d6d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -115,6 +116,32 @@ namespace osu.Game.Tests.Visual.Gameplay assertPosition(4, 1f); } + [Test] + public void TestSliderMultiplierDoesNotAffectRelativeBeatLength() + { + var beatmap = createBeatmap(new TimingControlPoint { BeatLength = time_range }); + beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2; + + createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true); + AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 5000); + + for (int i = 0; i < 5; i++) + assertPosition(i, i / 5f); + } + + [Test] + public void TestSliderMultiplierAffectsNonRelativeBeatLength() + { + var beatmap = createBeatmap(new TimingControlPoint { BeatLength = time_range }); + beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2; + + createTest(beatmap); + AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 2000); + + assertPosition(0, 0); + assertPosition(1, 1); + } + private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}", () => Precision.AlmostEquals(drawableRuleset.Playfield.AllHitObjects.ElementAt(index).DrawPosition.Y, drawableRuleset.Playfield.HitObjectContainer.DrawHeight * relativeY)); @@ -193,6 +220,8 @@ namespace osu.Game.Tests.Visual.Gameplay protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; + public new Bindable TimeRange => base.TimeRange; + public TestDrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index b94de0df89..8d8f8a419f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -316,8 +316,8 @@ namespace osu.Game.Rulesets.Objects.Drawables get => lifetimeStart ?? (HitObject.StartTime - InitialLifetimeOffset); set { - base.LifetimeStart = value; lifetimeStart = value; + base.LifetimeStart = value; } } diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 64e491858b..f178c01fd6 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -131,7 +131,9 @@ namespace osu.Game.Rulesets.UI.Scrolling if (duration > maxDuration) { maxDuration = duration; - baseBeatLength = timingPoints[i].BeatLength; + // The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths + // the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here + baseBeatLength = timingPoints[i].BeatLength / Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier; } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index bd1f496dfa..e00597dd56 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -77,6 +77,9 @@ namespace osu.Game.Rulesets.UI.Scrolling if (!initialStateCache.IsValid) { + foreach (var cached in hitObjectInitialStateCache.Values) + cached.Invalidate(); + switch (direction.Value) { case ScrollingDirection.Up: