From 970ea50269f8b248b69aa458a4baa0aa41e7e45b Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 20 Jul 2023 20:53:27 +0700 Subject: [PATCH 1/3] fix alive hitobjects blocking hits in taiko with hd --- .../Mods/TestSceneTaikoModHidden.cs | 55 +++++++++++++++++-- .../Mods/TaikoModHidden.cs | 6 ++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs index edc53429b1..6e6be26e43 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs @@ -1,24 +1,71 @@ // 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 System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Tests.Mods { public partial class TestSceneTaikoModHidden : TaikoModTestScene { + private Func checkAllMaxResultJudgements(int count) => () + => Player.ScoreProcessor.JudgedHits >= count + && Player.Results.All(result => result.Type == result.Judgement.MaxResult); + [Test] public void TestDefaultBeatmapTest() => CreateModTest(new ModTestData { Mod = new TaikoModHidden(), Autoplay = true, - PassCondition = checkSomeAutoplayHits + PassCondition = checkAllMaxResultJudgements(4), }); - private bool checkSomeAutoplayHits() - => Player.ScoreProcessor.JudgedHits >= 4 - && Player.Results.All(result => result.Type == result.Judgement.MaxResult); + [Test] + public void TestHitTwoNotesWithinShortPeriod() + { + const double hit_time = 1; + + var beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + Type = HitType.Rim, + StartTime = hit_time, + }, + new Hit + { + Type = HitType.Centre, + StartTime = hit_time * 2, + }, + }, + BeatmapInfo = + { + Difficulty = new BeatmapDifficulty + { + SliderTickRate = 4, + OverallDifficulty = 0, + }, + Ruleset = new TaikoRuleset().RulesetInfo + }, + }; + + beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f }); + + CreateModTest(new ModTestData + { + Mod = new TaikoModHidden(), + Autoplay = true, + PassCondition = checkAllMaxResultJudgements(2), + Beatmap = beatmap, + }); + } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index 2c3b4a8d18..ad17955f75 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -57,6 +57,12 @@ namespace osu.Game.Rulesets.Taiko.Mods { hitObject.FadeOut(duration); + // Keep updating the object even after it stopped being visible. + // This is required because of the custom logic in DrawableHit's Update method. + // Specifically, pressHandledThisFrame wasn't reset, which caused further hits + // within the object's lifetime to be rejected. + hitObject.AlwaysPresent = true; + // DrawableHitObject sets LifetimeEnd to LatestTransformEndTime if it isn't manually changed. // in order for the object to not be killed before its actual end time (as the latest transform ends earlier), set lifetime end explicitly. hitObject.LifetimeEnd = state == ArmedState.Idle || !hitObject.AllJudged From 5cedd428bb06cbd2e256ef45fe6b5f77e110c43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Jul 2023 20:05:37 +0200 Subject: [PATCH 2/3] Add test covering blocking of second hit on same frame --- .../Judgements/TestSceneHitJudgements.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs b/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs index 32966905c9..6fe61e78b7 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs @@ -36,6 +36,28 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements AssertResult(0, HitResult.Great); } + [Test] + public void TestHitWithBothKeysOnSameFrameDoesNotFallThroughToNextObject() + { + PerformTest(new List + { + new TaikoReplayFrame(0), + new TaikoReplayFrame(1000, TaikoAction.LeftCentre, TaikoAction.RightCentre), + }, CreateBeatmap(new Hit + { + Type = HitType.Centre, + StartTime = 1000, + }, new Hit + { + Type = HitType.Centre, + StartTime = 1020 + })); + + AssertJudgementCount(2); + AssertResult(0, HitResult.Great); + AssertResult(1, HitResult.Miss); + } + [Test] public void TestHitRimHit() { From 7e98d319d85daeab6a5871e397ae102efa8bd2b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Jul 2023 20:07:01 +0200 Subject: [PATCH 3/3] Use update-invariant way of checking for second press on same frame Fixes the same issue that 970ea50269f8b248b69aa458a4baa0aa41e7e45b did, but with no `AlwaysPresent` usage. --- osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 6 ------ .../Objects/Drawables/DrawableHit.cs | 18 +++++------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index ad17955f75..2c3b4a8d18 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -57,12 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { hitObject.FadeOut(duration); - // Keep updating the object even after it stopped being visible. - // This is required because of the custom logic in DrawableHit's Update method. - // Specifically, pressHandledThisFrame wasn't reset, which caused further hits - // within the object's lifetime to be rejected. - hitObject.AlwaysPresent = true; - // DrawableHitObject sets LifetimeEnd to LatestTransformEndTime if it isn't manually changed. // in order for the object to not be killed before its actual end time (as the latest transform ends earlier), set lifetime end explicitly. hitObject.LifetimeEnd = state == ArmedState.Idle || !hitObject.AllJudged diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 44225ab289..1ef426854e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private bool validActionPressed; - private bool pressHandledThisFrame; + private double? lastPressHandleTime; private readonly Bindable type = new Bindable(); @@ -76,7 +76,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables HitActions = null; HitAction = null; - validActionPressed = pressHandledThisFrame = false; + validActionPressed = false; + lastPressHandleTime = null; } private void updateActionsFromType() @@ -114,7 +115,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(KeyBindingPressEvent e) { - if (pressHandledThisFrame) + if (lastPressHandleTime == Time.Current) return true; if (Judged) return false; @@ -128,7 +129,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded // E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note - pressHandledThisFrame = true; + lastPressHandleTime = Time.Current; return result; } @@ -139,15 +140,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables base.OnReleased(e); } - protected override void Update() - { - base.Update(); - - // The input manager processes all input prior to us updating, so this is the perfect time - // for us to remove the extra press blocking, before input is handled in the next frame - pressHandledThisFrame = false; - } - protected override void UpdateHitStateTransforms(ArmedState state) { Debug.Assert(HitObject.HitWindows != null);