From 09178b5dc408357ba54efbe0c8b99730552d2271 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Dec 2025 22:28:50 +0900 Subject: [PATCH] Add helper method and update some other similar preempt calculations --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- .../Objects/CatchHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 ++- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 25 +++++++++++++++++++ 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 02d266228a..dc163ed77e 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -300,7 +300,7 @@ namespace osu.Game.Rulesets.Catch Description = "Affects how early fruits fade in on the screen.", AdditionalMetrics = [ - new RulesetBeatmapAttribute.AdditionalMetric("Fade-in time", LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRange(effectiveDifficulty.ApproachRate, CatchHitObject.PREEMPT_RANGE):#,0.##} ms")) + new RulesetBeatmapAttribute.AdditionalMetric("Fade-in time", LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRangeInt(effectiveDifficulty.ApproachRate, CatchHitObject.PREEMPT_RANGE):#,0.##} ms")) ] }; yield return new RulesetBeatmapAttribute(SongSelectStrings.HPDrain, @"HP", originalDifficulty.DrainRate, effectiveDifficulty.DrainRate, 10) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 41deaa0d82..2f186f5ab4 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.Objects { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_RANGE); + TimePreempt = IBeatmapDifficultyInfo.DifficultyRangeInt(difficulty.ApproachRate, PREEMPT_RANGE); Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize); } diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index d623a98950..0a5365e652 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Objects { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (int)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_RANGE); + TimePreempt = IBeatmapDifficultyInfo.DifficultyRangeInt(difficulty.ApproachRate, PREEMPT_RANGE); // Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR. // This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above. diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 49d945e0aa..6bb8a187e3 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -412,7 +412,8 @@ namespace osu.Game.Rulesets.Osu Description = "Affects how early objects appear on screen relative to their hit time.", AdditionalMetrics = [ - new RulesetBeatmapAttribute.AdditionalMetric("Approach time", LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRange(effectiveDifficulty.ApproachRate, OsuHitObject.PREEMPT_RANGE):#,0.##} ms")) + new RulesetBeatmapAttribute.AdditionalMetric("Approach time", + LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRangeInt(effectiveDifficulty.ApproachRate, OsuHitObject.PREEMPT_RANGE):#,0.##} ms")) ] }; diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index 2dd73a2541..0875a60d75 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -95,6 +95,31 @@ namespace osu.Game.Beatmaps static double DifficultyRange(double difficulty, DifficultyRange range) => DifficultyRange(difficulty, range.Min, range.Mid, range.Max); + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// Floors the value to `int`, usually to match osu!stable spec. + /// + /// The difficulty value to be mapped. + /// The values that define the two linear ranges. + /// + /// + /// od0 + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// + /// + /// od5 + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// + /// + /// od10 + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// + /// + /// + /// Value to which the difficulty value maps in the specified range. + static int DifficultyRangeInt(double difficulty, DifficultyRange range) + => (int)DifficultyRange(difficulty, range.Min, range.Mid, range.Max); + /// /// Inverse function to . /// Maps a value returned by the function above back to the difficulty that produced it.