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