mirror of
https://github.com/ppy/osu.git
synced 2026-06-07 02:53:38 +08:00
refactors
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user