1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-07 02:53:38 +08:00

refactors

This commit is contained in:
Givikap120
2025-01-19 23:19:02 +02:00
Unverified
parent 841283869d
commit 3b5d1a8e69
2 changed files with 43 additions and 63 deletions
@@ -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
}
}
}
@@ -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;
/// <summary>
/// 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<OsuModAutopilot>().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;