diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index d1c92ed6a7..15ccb8b1f0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -34,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators var osuCurrObj = (OsuDifficultyHitObject)current; var osuLastObj = (OsuDifficultyHitObject)current.Previous(0); var osuLastLastObj = (OsuDifficultyHitObject)current.Previous(1); + var osuLast2Obj = (OsuDifficultyHitObject)current.Previous(2); const int radius = OsuDifficultyHitObject.NORMALISED_RADIUS; const int diameter = OsuDifficultyHitObject.NORMALISED_DIAMETER; @@ -103,6 +104,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators * DifficultyCalculationUtils.Smootherstep(osuLastObj.LazyJumpDistance, radius, diameter) * Math.Pow(DifficultyCalculationUtils.ReverseLerp(osuLastObj.LazyJumpDistance, diameter * 3, diameter), 1.8) * DifficultyCalculationUtils.Smootherstep(lastAngle, double.DegreesToRadians(110), double.DegreesToRadians(60)); + + if (osuLast2Obj != null) + { + // If objects just go back and forth through a middle point - don't give as much wide bonus + // Use Previous(2) and Previous(0) because angles calculation is done prevprev-prev-curr, so any object's angle's center point is always the previous object + var lastBaseObject = (OsuHitObject)osuLastObj.BaseObject; + var last2BaseObject = (OsuHitObject)osuLast2Obj.BaseObject; + + float distance = (last2BaseObject.StackedPosition - lastBaseObject.StackedPosition).Length; + + if (distance < 1) + { + wideAngleBonus *= 1 - 0.35 * (1 - distance); + } + } } } @@ -139,6 +155,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators if (withSliderTravelDistance) aimStrain += sliderBonus * slider_multiplier; + // Apply high circle size bonus + aimStrain *= osuCurrObj.SmallCircleBonus; + return aimStrain; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs index 769220ece0..ee9b46eecb 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs @@ -60,6 +60,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators // Max distance bonus is 1 * `distance_multiplier` at single_spacing_threshold double distanceBonus = Math.Pow(distance / single_spacing_threshold, 3.95) * distance_multiplier; + // Apply reduced small circle bonus because flow aim difficulty on small circles doesn't scale as hard as jumps + distanceBonus *= Math.Sqrt(osuCurrObj.SmallCircleBonus); + if (mods.OfType().Any()) distanceBonus = 0; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 4329a25f34..8ad72daeb5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -105,6 +105,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing /// public double HitWindowGreat { get; private set; } + /// + /// Selective bonus for maps with higher circle size. + /// + public double SmallCircleBonus { get; private set; } + private readonly OsuDifficultyHitObject? lastLastDifficultyObject; private readonly OsuDifficultyHitObject? lastDifficultyObject; @@ -117,6 +122,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing // Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects. StrainTime = Math.Max(DeltaTime, MIN_DELTA_TIME); + SmallCircleBonus = Math.Max(1.0, 1.0 + (30 - BaseObject.Radius) / 40); + if (BaseObject is Slider sliderObject) { HitWindowGreat = 2 * sliderObject.HeadCircle.HitWindows.WindowFor(HitResult.Great) / clockRate; @@ -193,12 +200,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. float scalingFactor = NORMALISED_RADIUS / (float)BaseObject.Radius; - if (BaseObject.Radius < 30) - { - float smallCircleBonus = Math.Min(30 - (float)BaseObject.Radius, 5) / 50; - scalingFactor *= 1 + smallCircleBonus; - } - Vector2 lastCursorPosition = lastDifficultyObject != null ? getEndCursorPosition(lastDifficultyObject) : LastObject.StackedPosition; LazyJumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;