diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 286e4bd775..362a86065d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -484,6 +484,50 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("no miss judgements recorded", () => judgementResults.All(r => r.Type.IsHit())); } + /// + /// Sliders are common to by 1/2 or 1/4 beat length in order to place the circle on the next beat. + /// This tests a user pressing the next circle in the window between the last tick and the end of the slider (). + /// + [Test] + public void TestHitNextCircleDuringTailLeniency() + { + const double bpm = 240; + const double beat_length = 60000 / bpm; + const double slider_start = time_slider_start; + const double slider_end = slider_start + beat_length; + const double last_tick_time = slider_end + SliderEventGenerator.TAIL_LENIENCY; + const double next_circle_time = slider_end + beat_length / 4; + + performTest(new List + { + new OsuReplayFrame { Position = Vector2.Zero, Actions = { OsuAction.LeftButton }, Time = time_slider_start }, + // This frame is weird because the "up" frame can be skipped if the current time passes it (and is thus no longer in an important section). + // So the idea is to instead generate another important frame at the intended time without yet hitting the next circle. + new OsuReplayFrame { Position = new Vector2(100, 0), Actions = { OsuAction.RightButton }, Time = last_tick_time + 5 }, + new OsuReplayFrame { Position = new Vector2(140, 0), Actions = { OsuAction.LeftButton }, Time = last_tick_time + 20 }, + }, + [ + new Slider + { + StartTime = slider_start, + Position = new Vector2(0, 0), + TickDistanceMultiplier = 10, // no ticks + Path = new SliderPath(PathType.PERFECT_CURVE, new[] + { + Vector2.Zero, + new Vector2(100, 0), + }, 100), + }, + new HitCircle + { + StartTime = next_circle_time, + Position = new Vector2(140, 0) + } + ], bpm: bpm); + + AddAssert("all judgements are hit", () => judgementResults.All(j => j.Type.IsHit())); + } + private void assertAllMaxJudgements() { AddAssert("All judgements max", () => @@ -522,6 +566,11 @@ namespace osu.Game.Rulesets.Osu.Tests }, slider_path_length), }; + performTest(frames, [slider], bpm, tickRate); + } + + private void performTest(List frames, List objects, double? bpm = null, int? tickRate = null) + { AddStep("load player", () => { var cpi = new ControlPointInfo(); @@ -531,7 +580,7 @@ namespace osu.Game.Rulesets.Osu.Tests Beatmap.Value = CreateWorkingBeatmap(new Beatmap { - HitObjects = { slider }, + HitObjects = objects, BeatmapInfo = { Difficulty = new BeatmapDifficulty