diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index f4bd515995..1d536eb5cb 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Catch.Objects TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); - Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; + Scale = IBeatmapDifficultyInfo.CalculateScaleFromCircleSize(difficulty.CircleSize); } protected override HitWindows CreateHitWindows() => HitWindows.Empty; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index c5c9556ed7..236a7290da 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.UI Size = new Vector2(BASE_SIZE); if (difficulty != null) - Scale = calculateScale(difficulty); + Scale = new Vector2(IBeatmapDifficultyInfo.CalculateScaleFromCircleSize(difficulty.CircleSize)); CatchWidth = CalculateCatchWidth(Scale); @@ -182,11 +182,6 @@ namespace osu.Game.Rulesets.Catch.UI /// public Drawable CreateProxiedContent() => caughtObjectContainer.CreateProxy(); - /// - /// Calculates the scale of the catcher based off the provided beatmap difficulty. - /// - private static Vector2 calculateScale(IBeatmapDifficultyInfo difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); - /// /// Calculates the width of the area used for attempting catches in gameplay. /// @@ -197,7 +192,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Calculates the width of the area used for attempting catches in gameplay. /// /// The beatmap difficulty. - public static float CalculateCatchWidth(IBeatmapDifficultyInfo difficulty) => CalculateCatchWidth(calculateScale(difficulty)); + public static float CalculateCatchWidth(IBeatmapDifficultyInfo difficulty) => CalculateCatchWidth(new Vector2(IBeatmapDifficultyInfo.CalculateScaleFromCircleSize(difficulty.CircleSize))); /// /// Determine if this catcher can catch a in the current position. diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs index 5adc50859f..0e5f8a8cf6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Tests { const double time_slider_start = 1000; - float circleRadius = OsuHitObject.OBJECT_RADIUS * (1.0f - 0.7f * (circleSize - 5) / 5) / 2; + float circleRadius = OsuHitObject.OBJECT_RADIUS * IBeatmapDifficultyInfo.CalculateScaleFromCircleSize(circleSize); float followCircleRadius = circleRadius * 1.2f; performTest(new Beatmap diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 80d3de75ea..0b1f413362 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -155,17 +155,7 @@ namespace osu.Game.Rulesets.Osu.Objects // This adjustment is necessary for AR>10, otherwise TimePreempt can become smaller leading to hitcircles not fully fading in. TimeFadeIn = 400 * Math.Min(1, TimePreempt / PREEMPT_MIN); - // The following comment is copied verbatim from osu-stable: - // - // Builds of osu! up to 2013-05-04 had the gamefield being rounded down, which caused incorrect radius calculations - // in widescreen cases. This ratio adjusts to allow for old replays to work post-fix, which in turn increases the lenience - // for all plays, but by an amount so small it should only be effective in replays. - // - // To match expectations of gameplay we need to apply this multiplier to circle scale. It's weird but is what it is. - // It works out to under 1 game pixel and is generally not meaningful to gameplay, but is to replay playback accuracy. - const float broken_gamefield_rounding_allowance = 1.00041f; - - Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2 * broken_gamefield_rounding_allowance; + Scale = IBeatmapDifficultyInfo.CalculateScaleFromCircleSize(difficulty.CircleSize); } protected override HitWindows CreateHitWindows() => new OsuHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index 6564a086fe..8a2c7ff13a 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -208,8 +208,7 @@ namespace osu.Game.Rulesets.Osu.Statistics if (score.HitEvents.Count == 0) return; - // Todo: This should probably not be done like this. - float radius = OsuHitObject.OBJECT_RADIUS * (1.0f - 0.7f * (playableBeatmap.Difficulty.CircleSize - 5) / 5) / 2; + float radius = OsuHitObject.OBJECT_RADIUS * IBeatmapDifficultyInfo.CalculateScaleFromCircleSize(playableBeatmap.Difficulty.CircleSize); foreach (var e in score.HitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle))) { diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index 78234a9dd9..af943b62e7 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -44,6 +44,21 @@ namespace osu.Game.Beatmaps /// double SliderTickRate { get; } + static float CalculateScaleFromCircleSize(float circleSize) + { + // The following comment is copied verbatim from osu-stable: + // + // Builds of osu! up to 2013-05-04 had the gamefield being rounded down, which caused incorrect radius calculations + // in widescreen cases. This ratio adjusts to allow for old replays to work post-fix, which in turn increases the lenience + // for all plays, but by an amount so small it should only be effective in replays. + // + // To match expectations of gameplay we need to apply this multiplier to circle scale. It's weird but is what it is. + // It works out to under 1 game pixel and is generally not meaningful to gameplay, but is to replay playback accuracy. + const float broken_gamefield_rounding_allowance = 1.00041f; + + return (float)(1.0f - 0.7f * DifficultyRange(circleSize)) / 2 * broken_gamefield_rounding_allowance; + } + /// /// Maps a difficulty value [0, 10] to a two-piece linear range of values. /// @@ -55,13 +70,20 @@ namespace osu.Game.Beatmaps static double DifficultyRange(double difficulty, double min, double mid, double max) { if (difficulty > 5) - return mid + (max - mid) * (difficulty - 5) / 5; + return mid + (max - mid) * DifficultyRange(difficulty); if (difficulty < 5) return mid - (mid - min) * (5 - difficulty) / 5; return mid; } + /// + /// Maps a difficulty value [0, 10] to a linear range of [-1, 1]. + /// + /// The difficulty value to be mapped. + /// Value to which the difficulty value maps in the specified range. + static double DifficultyRange(double difficulty) => (difficulty - 5) / 5; + /// /// Maps a difficulty value [0, 10] to a two-piece linear range of values. ///