mirror of
https://github.com/ppy/osu.git
synced 2024-09-21 15:27:24 +08:00
Many changes
Refactoring Changed slow sliders penalty Made velocity change bonus work correctly
This commit is contained in:
parent
89662e3a5e
commit
13864a6a48
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
private const double acute_angle_multiplier = 1.95;
|
||||
private const double velocity_change_multiplier = 0.75;
|
||||
|
||||
private const double slider_aim_multiplier = 1.35;
|
||||
private const double slider_body_multiplier = 1.35;
|
||||
private const double slider_jump_multiplier = 0.08;
|
||||
|
||||
/// <summary>
|
||||
@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
double currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime;
|
||||
|
||||
// This multiplier exists to prevent slideraim having sliderjumps bonus
|
||||
double sliderJumpsAdjustingMultiplier = 1.0;
|
||||
double sliderJumpNerfFactor = 1.0;
|
||||
|
||||
// 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)
|
||||
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
double newVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity.
|
||||
|
||||
if (currVelocity > 0)
|
||||
sliderJumpsAdjustingMultiplier = currVelocity / newVelocity;
|
||||
sliderJumpNerfFactor = currVelocity / newVelocity;
|
||||
|
||||
currVelocity = newVelocity;
|
||||
}
|
||||
@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
|
||||
* Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
|
||||
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
|
||||
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.LazyJumpDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
|
||||
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.LazyJumpFromEndDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
|
||||
}
|
||||
|
||||
// Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute.
|
||||
@ -103,22 +103,48 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
}
|
||||
}
|
||||
|
||||
double sliderlessVelocityChangeBonus = 0;
|
||||
|
||||
if (Math.Max(prevVelocity, currVelocity) != 0)
|
||||
{
|
||||
// We want to use the average velocity over the whole object when awarding differences, not the individual jump and slider path velocities.
|
||||
prevVelocity = (osuLastObj.LazyJumpDistance + osuLastLastObj.TravelDistance) / osuLastObj.StrainTime;
|
||||
currVelocity = (osuCurrObj.LazyJumpDistance + osuLastObj.TravelDistance) / osuCurrObj.StrainTime;
|
||||
double normalVelocityChangeBonus = 0;
|
||||
|
||||
// Scale with ratio of difference compared to 0.5 * max dist.
|
||||
double distRatio = Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2);
|
||||
{
|
||||
// We want to use the average velocity over the whole object when awarding differences, not the individual jump and slider path velocities.
|
||||
prevVelocity = osuLastObj.JumpDistance / osuLastObj.StrainTime;
|
||||
currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime;
|
||||
|
||||
// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
|
||||
double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
|
||||
// Scale with ratio of difference compared to 0.5 * max dist.
|
||||
double distRatio = Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2);
|
||||
|
||||
velocityChangeBonus = overlapVelocityBuff * distRatio;
|
||||
// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
|
||||
double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
|
||||
|
||||
// Penalize for rhythm changes.
|
||||
velocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2);
|
||||
sliderlessVelocityChangeBonus = overlapVelocityBuff * distRatio;
|
||||
|
||||
// Penalize for rhythm changes.
|
||||
sliderlessVelocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2);
|
||||
}
|
||||
|
||||
if (withSliderTravelDistance)
|
||||
{
|
||||
// We want to use the average velocity over the whole object when awarding differences, not the individual jump and slider path velocities.
|
||||
prevVelocity = (osuLastObj.LazyJumpFromEndDistance + osuLastLastObj.TravelDistance) / osuLastObj.StrainTime;
|
||||
currVelocity = (osuCurrObj.LazyJumpFromEndDistance + osuLastObj.TravelDistance) / osuCurrObj.StrainTime;
|
||||
|
||||
// Scale with ratio of difference compared to 0.5 * max dist.
|
||||
double distRatio = Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2);
|
||||
|
||||
// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
|
||||
double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
|
||||
|
||||
normalVelocityChangeBonus = overlapVelocityBuff * distRatio;
|
||||
|
||||
// Penalize for rhythm changes.
|
||||
normalVelocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2);
|
||||
}
|
||||
|
||||
velocityChangeBonus = Math.Max(sliderlessVelocityChangeBonus, normalVelocityChangeBonus);
|
||||
}
|
||||
|
||||
// Add in acute angle bonus or wide angle bonus + velocity change bonus, whichever is larger.
|
||||
@ -127,8 +153,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
double sliderJumpBonus = 0;
|
||||
if (osuLastObj.BaseObject is Slider slider)
|
||||
{
|
||||
// Take just slider heads into account because we're computing sliderjumps, not slideraim
|
||||
sliderJumpBonus = slider_jump_multiplier * aimStrain * sliderJumpsAdjustingMultiplier;
|
||||
// Take the sliderless difficulty as a base
|
||||
sliderJumpBonus = slider_jump_multiplier * aimStrain * sliderJumpNerfFactor;
|
||||
|
||||
// Reward more if sliders and circles are alternating (actually it's still lower than several sliders in a row)
|
||||
if (osuLastLastObj.BaseObject is HitCircle)
|
||||
@ -136,14 +162,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
double alternatingBonus = slider_jump_multiplier * osuLastObj.JumpDistance / osuLastObj.StrainTime;
|
||||
|
||||
if (osuLastObj.StrainTime > osuLastLastObj.StrainTime)
|
||||
alternatingBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2);
|
||||
alternatingBonus *= CalcRhythmDifferenceMultiplier(osuCurrObj.StrainTime, osuLastObj.StrainTime);
|
||||
|
||||
sliderJumpBonus += alternatingBonus;
|
||||
}
|
||||
|
||||
// If slider was slower than notes before - punish it
|
||||
if (osuCurrObj.StrainTime > osuLastObj.StrainTime)
|
||||
sliderJumpBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2);
|
||||
sliderJumpBonus *= CalcRhythmDifferenceMultiplier(osuCurrObj.StrainTime, osuLastObj.StrainTime);
|
||||
|
||||
// Punish too short sliders to prevent cheesing (cheesing is still possible, but it's very rare)
|
||||
double sliderLength = slider.Velocity * slider.SpanDuration;
|
||||
@ -154,15 +180,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
aimStrain += sliderJumpBonus;
|
||||
|
||||
// Add in additional slider velocity bonus.
|
||||
double sliderBonus = 0;
|
||||
double sliderBodyBonus = 0;
|
||||
|
||||
if (withSliderTravelDistance && osuLastObj.BaseObject is Slider)
|
||||
{
|
||||
// Reward sliders based on velocity.
|
||||
sliderBonus = osuLastObj.TravelDistance / osuLastObj.TravelTime;
|
||||
sliderBodyBonus = osuLastObj.TravelDistance / osuLastObj.TravelTime;
|
||||
}
|
||||
|
||||
aimStrain += sliderBonus * slider_aim_multiplier;
|
||||
aimStrain += sliderBodyBonus * slider_body_multiplier;
|
||||
|
||||
return aimStrain;
|
||||
}
|
||||
@ -170,5 +196,18 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
private static double calcWideAngleBonus(double angle) => Math.Pow(Math.Sin(3.0 / 4 * (Math.Min(5.0 / 6 * Math.PI, Math.Max(Math.PI / 6, angle)) - Math.PI / 6)), 2);
|
||||
|
||||
private static double calcAcuteAngleBonus(double angle) => 1 - calcWideAngleBonus(angle);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
smallDistNerf = Math.Min(1.0, jumpDistance / 75.0);
|
||||
|
||||
// We also want to nerf stacks so that only the first object of the stack is accounted for.
|
||||
double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);
|
||||
double stackNerf = Math.Min(1.0, (currentObj.JumpDistance / scalingFactor) / 25.0);
|
||||
|
||||
// Bonus based on how visible the object is.
|
||||
double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.OpacityAt(currentHitObject.StartTime, hidden));
|
||||
|
@ -62,17 +62,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
|
||||
if (osuCurrObj.BaseObject is Slider slider && osuPrevObj?.BaseObject is Slider)
|
||||
{
|
||||
// Bonus base
|
||||
double sliderStreamBonus = 0.25;
|
||||
|
||||
// If slider was slower than notes before - punish it
|
||||
if (osuCurrObj.StrainTime > osuPrevObj.StrainTime)
|
||||
sliderStreamBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuPrevObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuPrevObj.StrainTime), 2);
|
||||
sliderStreamBonus *= AimEvaluator.CalcRhythmDifferenceMultiplier(osuCurrObj.StrainTime, osuPrevObj.StrainTime);
|
||||
|
||||
// Punish too short sliders to prevent cheesing (cheesing is still possible, but it's very rare)
|
||||
double sliderLength = slider.Velocity * slider.SpanDuration;
|
||||
if (sliderLength < slider.Radius / 2)
|
||||
sliderStreamBonus *= sliderLength / (slider.Radius / 2);
|
||||
if (sliderLength < slider.Radius)
|
||||
sliderStreamBonus *= sliderLength / slider.Radius;
|
||||
|
||||
sliderStreamMultiplier += sliderStreamBonus;
|
||||
}
|
||||
|
@ -42,19 +42,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
||||
/// The "lazy" end position is the position at which the cursor ends up if the previous hitobject is followed with as minimal movement as possible (i.e. on the edge of slider follow circles).
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public double LazyJumpDistance { get; private set; }
|
||||
public double LazyJumpFromEndDistance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalised shortest distance to consider for a jump between the previous <see cref="OsuDifficultyHitObject"/> and this <see cref="OsuDifficultyHitObject"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is bounded from above by <see cref="LazyJumpDistance"/>, and is smaller than the former if a more natural path is able to be taken through the previous <see cref="OsuDifficultyHitObject"/>.
|
||||
/// This is bounded from above by <see cref="LazyJumpFromEndDistance"/>, and is smaller than the former if a more natural path is able to be taken through the previous <see cref="OsuDifficultyHitObject"/>.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// Suppose a linear slider - circle pattern.
|
||||
/// <br />
|
||||
/// Following the slider lazily (see: <see cref="LazyJumpDistance"/>) will result in underestimating the true end position of the slider as being closer towards the start position.
|
||||
/// As a result, <see cref="LazyJumpDistance"/> overestimates the jump distance because the player is able to take a more natural path by following through the slider to its end,
|
||||
/// Following the slider lazily (see: <see cref="LazyJumpFromEndDistance"/>) will result in underestimating the true end position of the slider as being closer towards the start position.
|
||||
/// As a result, <see cref="LazyJumpFromEndDistance"/> overestimates the jump distance because the player is able to take a more natural path by following through the slider to its end,
|
||||
/// such that the jump is felt as only starting from the slider's true end position.
|
||||
/// <br />
|
||||
/// Now consider a slider - circle pattern where the circle is stacked along the path inside the slider.
|
||||
@ -168,9 +168,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
||||
|
||||
Vector2 lastCursorPosition = getEndCursorPosition(lastObject);
|
||||
|
||||
LazyJumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
|
||||
LazyJumpFromEndDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
|
||||
MinimumJumpTime = StrainTime;
|
||||
MinimumJumpDistance = LazyJumpDistance;
|
||||
MinimumJumpDistance = LazyJumpFromEndDistance;
|
||||
|
||||
if (lastObject is Slider lastSlider)
|
||||
{
|
||||
@ -200,7 +200,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
||||
//
|
||||
|
||||
float tailJumpDistance = Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length * scalingFactor;
|
||||
MinimumJumpDistance = Math.Max(0, Math.Min(LazyJumpDistance - (maximum_slider_radius - assumed_slider_radius), tailJumpDistance - maximum_slider_radius));
|
||||
MinimumJumpDistance = Math.Max(0, Math.Min(LazyJumpFromEndDistance - (maximum_slider_radius - assumed_slider_radius), tailJumpDistance - maximum_slider_radius));
|
||||
}
|
||||
|
||||
if (lastLastObject != null && !(lastLastObject is Spinner))
|
||||
|
Loading…
Reference in New Issue
Block a user