From 57f16646a343f5911feabc7cce13ba879b87c404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 12 Jan 2026 11:11:11 +0100 Subject: [PATCH 1/2] Add failing tests --- .../Mods/TestSceneOsuModFreezeFrame.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs index 57d2b94188..31498295da 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; namespace osu.Game.Rulesets.Osu.Tests.Mods { @@ -18,5 +21,39 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Autoplay = false, }); } + + [Test] + public void TestSkipToFirstCircleNotSuppressed() + { + CreateModTest(new ModTestData + { + Mod = new OsuModFreezeFrame(), + CreateBeatmap = () => new OsuBeatmap + { + HitObjects = + { + new HitCircle { StartTime = 5000, Position = OsuPlayfield.BASE_SIZE / 2 } + } + }, + PassCondition = () => Player.GameplayClockContainer.GameplayStartTime > 0 + }); + } + + [Test] + public void TestSkipToFirstSpinnerNotSuppressed() + { + CreateModTest(new ModTestData + { + Mod = new OsuModFreezeFrame(), + CreateBeatmap = () => new OsuBeatmap + { + HitObjects = + { + new Spinner { StartTime = 5000, Position = OsuPlayfield.BASE_SIZE / 2 } + } + }, + PassCondition = () => Player.GameplayClockContainer.GameplayStartTime > 0 + }); + } } } From 38c89a914b7d57ea29edfb167b985eccbc4c018f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 12 Jan 2026 11:11:38 +0100 Subject: [PATCH 2/2] Fix Freeze Frame mod suppressing skip if the first object is a spinner Fixes https://osu.ppy.sh/community/forums/topics/2169899?n=1. `TimePreempt` doesn't affect the appearance of a spinner, but it *will* affect the value of `GameplayStartTime`: https://github.com/ppy/osu/blob/4bf90a5571bb508444178f3d98a2b2a10c549534/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs#L82-L91 While at parsing it is enforced that every object following a spinner has `NewCombo` set, it is *not* enforced that every spinner has `NewCombo` set. See also: https://github.com/ppy/osu/issues/24156. But that's sort of orthogonal to the entire issue as well because why touch the preempt of an object that's not visually affected by preempt to begin with. --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index e75ed24a7d..368d76d1ba 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -57,7 +57,8 @@ namespace osu.Game.Rulesets.Osu.Mods void applyFadeInAdjustment(OsuHitObject osuObject) { - osuObject.TimePreempt += osuObject.StartTime - lastNewComboTime; + if (osuObject is not Spinner) + osuObject.TimePreempt += osuObject.StartTime - lastNewComboTime; foreach (var nested in osuObject.NestedHitObjects.OfType()) {