Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sliderless aim in fact sliderless #29993

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
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 currDistance = withSliderTravelDistance ? osuCurrObj.LazyJumpFromEndDistance : osuCurrObj.JumpDistance;
double currVelocity = currDistance / osuCurrObj.StrainTime;

// 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)
Expand All @@ -50,7 +51,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
}

// As above, do the same for the previous hitobject.
double prevVelocity = osuLastObj.LazyJumpDistance / osuLastObj.StrainTime;
double prevDistance = withSliderTravelDistance ? osuLastObj.LazyJumpFromEndDistance : osuLastObj.JumpDistance;
double prevVelocity = prevDistance / osuLastObj.StrainTime;

if (osuLastLastObj.BaseObject is Slider && withSliderTravelDistance)
{
Expand Down Expand Up @@ -88,7 +90,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
* Math.Min(angleBonus, diameter * 1.25 / 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, radius, diameter) - radius) / radius), 2); // Buff distance exceeding radius up to diameter.
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(currDistance, radius, diameter) - radius) / radius), 2); // Buff distance exceeding radius up to diameter.
}

// Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute.
Expand All @@ -100,9 +102,12 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with

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;
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
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.LazyJumpFromEndDistance / 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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
if (mods.Any(h => h is OsuModFlashlight))
flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;

double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
double sliderFactor = aimRating > 0 ? Math.Min(aimRating, aimRatingNoSliders) / aimRating : 1;

double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountTopWeightedStrains();
double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountTopWeightedStrains();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,30 @@ public class OsuDifficultyHitObject : DifficultyHitObject
/// </summary>
public readonly double StrainTime;

/// <summary>
/// Normalised distance from the start position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
/// </summary>
public double JumpDistance { get; private set; }

/// <summary>
/// Normalised distance from the "lazy" end position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
/// <para>
/// 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.
Expand Down Expand Up @@ -182,9 +187,10 @@ private void setDistances(double clockRate)

Vector2 lastCursorPosition = getEndCursorPosition(lastObject);

LazyJumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
JumpDistance = (lastObject.StackedPosition - BaseObject.StackedPosition).Length * scalingFactor;
LazyJumpFromEndDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
MinimumJumpTime = StrainTime;
MinimumJumpDistance = LazyJumpDistance;
MinimumJumpDistance = LazyJumpFromEndDistance;

if (lastObject is Slider lastSlider)
{
Expand Down Expand Up @@ -214,7 +220,7 @@ private void setDistances(double clockRate)
//

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))
Expand Down
Loading