From 3b5d1a8e69f74a1c9c5df0b00aeb14f05c512c78 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 19 Jan 2025 23:19:02 +0200 Subject: [PATCH] refactors --- .../Difficulty/Evaluators/AimEvaluator.cs | 85 +++++++------------ .../Difficulty/Evaluators/SpeedEvaluator.cs | 21 +++-- 2 files changed, 43 insertions(+), 63 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index 91799ed5b6..da3469c3f9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -40,11 +40,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators const int radius = OsuDifficultyHitObject.NORMALISED_RADIUS; const int diameter = OsuDifficultyHitObject.NORMALISED_DIAMETER; - // Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle. - double currVelocity = osuCurrObj.LazyJumpDistance / osuCurrObj.StrainTime; + double sliderJumpBonus = getSliderJumpBonus(osuCurrObj, osuLastObj, osuLastLastObj) * slider_jump_multiplier; - // This multiplier exists to prevent slideraim having sliderjumps bonus - double sliderJumpNerfFactor = 1.0; + // Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle. + double currVelocity = osuCurrObj.LazyJumpDistance / osuCurrObj.StrainTime * (1 + sliderJumpBonus); // But if the last object is a slider, then we extend the travel velocity through the slider into the current object. if (osuLastObj.BaseObject is Slider && withSliderTravelDistance) @@ -52,12 +51,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators double travelVelocity = osuLastObj.TravelDistance / osuLastObj.TravelTime; // calculate the slider velocity from slider head to slider end. double movementVelocity = osuCurrObj.MinimumJumpDistance / osuCurrObj.MinimumJumpTime; // calculate the movement velocity from slider end to current object - double newVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity. - - if (currVelocity > 0) - sliderJumpNerfFactor = currVelocity / newVelocity; - - currVelocity = newVelocity; + currVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity. } // As above, do the same for the previous hitobject. @@ -138,35 +132,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators // Add in acute angle bonus or wide angle bonus + velocity change bonus, whichever is larger. aimStrain += Math.Max(acuteAngleBonus * acute_angle_multiplier, wideAngleBonus * wide_angle_multiplier + velocityChangeBonus * velocity_change_multiplier); - double sliderJumpBonus = 0; - - Slider? slider = osuLastObj.BaseObject as Slider; - Slider? sliderLast = osuLastLastObj.BaseObject as Slider; - - if (slider.IsNotNull() || sliderLast.IsNotNull()) - { - // Take the sliderless difficulty as a base - sliderJumpBonus = aimStrain * sliderJumpNerfFactor; - - // Punish the cases where 1/2 slider going into 1/2 note - if (osuCurrObj.StrainTime > osuLastObj.StrainTime) - sliderJumpBonus *= CalcRhythmDifferenceMultiplier(osuCurrObj.StrainTime, osuLastObj.StrainTime); - - // Punish the cases where 1/2 slider going into two 1/2 notes - if (slider.IsNull() && osuLastObj.StrainTime > osuCurrObj.StrainTime) - sliderJumpBonus *= CalcRhythmDifferenceMultiplier(osuCurrObj.StrainTime, osuLastObj.StrainTime); - - // Punish too short sliders to prevent cheesing (cheesing is still possible, but it's very rare) - static double length(Slider? slider) => slider.IsNotNull() ? slider.Velocity * slider.SpanDuration : 0; - double sliderLength = Math.Max(length(slider), length(sliderLast)); - - Slider sliderAny = slider.IsNotNull() ? slider : sliderLast!; - if (sliderLength < sliderAny.Radius) - sliderJumpBonus *= sliderLength / sliderAny.Radius; - } - - aimStrain += sliderJumpBonus * slider_jump_multiplier; - // Add in additional slider velocity bonus. double sliderBodyBonus = 0; @@ -181,21 +146,37 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators return aimStrain; } + private static double getSliderJumpBonus(OsuDifficultyHitObject osuCurrObj, OsuDifficultyHitObject osuLastObj, OsuDifficultyHitObject osuLastLastObj) + { + Slider? sliderCurr = osuLastObj.BaseObject as Slider; + Slider? sliderLast = osuLastLastObj.BaseObject as Slider; + + if (sliderCurr == null && sliderLast == null) + return 0.0; + + // Take the sliderless difficulty as a base + double sliderJumpBonus = 1.0; + + // Punish the cases where 1/2 slider going into 1/2 note + sliderJumpBonus *= DifficultyCalculationUtils.ReverseLerp(osuLastObj.StrainTime, osuCurrObj.StrainTime * 0.55, osuCurrObj.StrainTime * 0.75); + + // Punish the cases where 1/2 slider going into two 1/2 notes + if (sliderCurr == null) + sliderJumpBonus *= DifficultyCalculationUtils.ReverseLerp(osuCurrObj.StrainTime, osuLastObj.StrainTime * 0.55, osuLastObj.StrainTime * 0.75); + + // Punish too short sliders to prevent cheesing + static double length(Slider? slider) => slider.IsNotNull() ? slider.Velocity * slider.SpanDuration : 0; + double sliderLength = Math.Max(length(sliderCurr), length(sliderLast)); + + double threshold = ((OsuHitObject)osuCurrObj.BaseObject).Radius / 2; + if (sliderLength < threshold) + sliderJumpBonus *= sliderLength / threshold; + + return sliderJumpBonus; + } + private static double calcWideAngleBonus(double angle) => DifficultyCalculationUtils.Smoothstep(angle, double.DegreesToRadians(40), double.DegreesToRadians(140)); private static double calcAcuteAngleBonus(double angle) => DifficultyCalculationUtils.Smoothstep(angle, double.DegreesToRadians(140), double.DegreesToRadians(40)); - - public static double CalcRhythmDifferenceMultiplier(double time1, double time2) - { - const double point1 = 0.5, point2 = 0.75; - - double similarity = Math.Min(time1, time2) / Math.Max(time1, time2); - if (Math.Max(time1, time2) == 0) similarity = 1; - - if (similarity < point1) return 0.0; - if (similarity > point2) return 1.0; - - return (1 - Math.Cos((similarity - point1) * Math.PI / (point2 - point1))) / 2; // grows from 0 to 1 as similarity increase from point1 to point2 - } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs index d07e622f92..37e5b9296b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs @@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators private const double min_speed_bonus = 200; // 200 BPM 1/4th private const double speed_balancing_factor = 40; private const double distance_multiplier = 0.9; + private const double sliderstream_multiplier = 0.25; /// /// Evaluates the difficulty of tapping the current object, based on: @@ -60,28 +61,26 @@ 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; - double sliderStreamBonus = 1; + double sliderStreamBonus = 0; if (osuCurrObj.BaseObject is Slider slider && osuPrevObj?.BaseObject is Slider) { - double sliderStreamFactor = 0.25; + sliderStreamBonus = sliderstream_multiplier; - // If slider was slower than notes before - punish it - if (osuCurrObj.StrainTime > osuPrevObj.StrainTime) - sliderStreamFactor *= AimEvaluator.CalcRhythmDifferenceMultiplier(osuCurrObj.StrainTime, osuPrevObj.StrainTime); + // Don't buff burst into 2 sliders case + sliderStreamBonus *= DifficultyCalculationUtils.ReverseLerp(osuPrevObj.StrainTime, osuCurrObj.StrainTime * 0.55, osuCurrObj.StrainTime * 0.75); - // Punish too short sliders to prevent cheesing (cheesing is still possible, but it's very rare) + // Punish too short sliders to prevent cheesing double sliderLength = slider.Velocity * slider.SpanDuration; - if (sliderLength < slider.Radius) - sliderStreamFactor *= sliderLength / slider.Radius; - - sliderStreamBonus += sliderStreamFactor; + if (sliderLength < slider.Radius / 2) + sliderStreamBonus *= sliderLength / (slider.Radius / 2); } + if (mods.OfType().Any()) distanceBonus = 0; // Base difficulty with all bonuses - double difficulty = (1 + speedBonus + distanceBonus) * sliderStreamBonus * 1000 / strainTime; + double difficulty = (1 + speedBonus + distanceBonus) * (1 + sliderStreamBonus) * 1000 / strainTime; // Apply penalty if there's doubletappable doubles return difficulty * doubletapness;