From e9b08219e934ecebdfa66b92ffbfccf1589dc1c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Sep 2023 11:08:25 +0200 Subject: [PATCH] Block input to objects lying under already-hit slider heads before slider is fully judged when classic note lock is active --- .../TestSceneLegacyHitPolicy.cs | 3 ++- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 21 +++++++++++++++++++ .../Objects/Drawables/DrawableHitCircle.cs | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs index 8c311cce91..0a3639b9ab 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs @@ -506,7 +506,6 @@ namespace osu.Game.Rulesets.Osu.Tests } [Test] - [Ignore("Currently broken, first attempt at fixing broke even harder. See https://github.com/ppy/osu/issues/24743.")] public void TestInputDoesNotFallThroughOverlappingSliders() { const double time_first_slider = 1000; @@ -550,6 +549,8 @@ namespace osu.Game.Rulesets.Osu.Tests addJudgementOffsetAssert("first slider head", () => ((Slider)hitObjects[0]).HeadCircle, 0); addJudgementAssert(hitObjects[1], HitResult.Miss); // the slider head of the first slider prevents the second slider's head from being hit, so the judgement offset should be very late. + // this is not strictly done by the hit policy implementation itself (see `OsuModClassic.blockInputToObjectsUnderSliderHead()`), + // but we're testing this here anyways to just keep everything related to input handling and note lock in one place. addJudgementOffsetAssert("second slider head", () => ((Slider)hitObjects[1]).HeadCircle, referenceHitWindows.WindowFor(HitResult.Meh)); addClickActionAssert(0, ClickAction.Hit); } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index 5dbf23f7ea..0148ec1987 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -74,6 +74,10 @@ namespace osu.Game.Rulesets.Osu.Mods head.TrackFollowCircle = !NoSliderHeadMovement.Value; if (FadeHitCircleEarly.Value && !usingHiddenFading) applyEarlyFading(head); + + if (ClassicNoteLock.Value) + blockInputToObjectsUnderSliderHead(head); + break; case DrawableSliderTail tail: @@ -83,10 +87,27 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableHitCircle circle: if (FadeHitCircleEarly.Value && !usingHiddenFading) applyEarlyFading(circle); + break; } } + /// + /// On stable, slider heads that have already been hit block input from reaching objects that may be underneath them + /// until the sliders they're part of have been fully judged. + /// The purpose of this method is to restore that behaviour. + /// In order to avoid introducing yet another confusing config option, this behaviour is roped into the general notion of "note lock". + /// + private static void blockInputToObjectsUnderSliderHead(DrawableSliderHead slider) + { + var oldHitAction = slider.HitArea.Hit; + slider.HitArea.Hit = () => + { + oldHitAction?.Invoke(); + return !slider.DrawableSlider.AllJudged; + }; + } + private void applyEarlyFading(DrawableHitCircle circle) { circle.ApplyCustomUpdateState += (dho, state) => diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 932f6d3fff..6beed0294d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -261,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case OsuAction.RightButton: if (IsHovered && (Hit?.Invoke() ?? false)) { - HitAction = e.Action; + HitAction ??= e.Action; return true; }