diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index 8573d36c70a8..4ee1bbb15096 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Utils; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; @@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators { public static class AimEvaluator { - private const double wide_angle_multiplier = 1.5; + private const double wide_angle_multiplier = 1.2; private const double acute_angle_multiplier = 2.3; private const double slider_multiplier = 1.5; private const double velocity_change_multiplier = 0.75; @@ -69,6 +70,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with double sliderBonus = 0; double velocityChangeBonus = 0; double wiggleBonus = 0; + double angleRepetitionNerf = 1; double aimStrain = currVelocity; // Start strain with regular velocity. @@ -80,6 +82,16 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with // Rewarding angles, take the smaller velocity as base. double angleBonus = Math.Min(currVelocity, prevVelocity); + double angleDifferenceAdjusted = Math.Cos(2 * Math.Min(double.DegreesToRadians(45), Math.Abs(currAngle - lastAngle))); + double vectorRepetition = angleVectorRepetition(osuCurrObj); + + double stackFactor = DifficultyCalculationUtils.Smootherstep(osuLastObj.LazyJumpDistance, 0, diameter); + + double baseFactor = 1 - 0.15 * calcAcuteAngleBonus(lastAngle) * angleDifferenceAdjusted; + + // Penalize angle repetition. + angleRepetitionNerf = Math.Pow(baseFactor + (1 - baseFactor) * 0.9 * vectorRepetition * stackFactor, 2); + if (Math.Max(osuCurrObj.AdjustedDeltaTime, osuLastObj.AdjustedDeltaTime) < 1.25 * Math.Min(osuCurrObj.AdjustedDeltaTime, osuLastObj.AdjustedDeltaTime)) // If rhythms are the same. { acuteAngleBonus = calcAcuteAngleBonus(currAngle); @@ -122,7 +134,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with if (distance < 1) { - wideAngleBonus *= 1 - 0.35 * (1 - distance); + wideAngleBonus *= 1 - 0.55 * (1 - distance); } } } @@ -154,6 +166,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with sliderBonus = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; } + aimStrain *= angleRepetitionNerf; + aimStrain += wiggleBonus * wiggle_multiplier; aimStrain += velocityChangeBonus * velocity_change_multiplier; @@ -178,6 +192,37 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with private static double highBpmBonus(double ms, double distance) => 1 / (1 - Math.Pow(0.15, ms / 1000)) * DifficultyCalculationUtils.Smootherstep(distance, 0, OsuDifficultyHitObject.NORMALISED_RADIUS); + private static double angleVectorRepetition(OsuDifficultyHitObject current) + { + const double note_limit = 6; + + double constantAngleCount = 0; + int index = 0; + double notesProcessed = 0; + + while (notesProcessed < note_limit) + { + var loopObj = (OsuDifficultyHitObject)current.Previous(index); + + if (loopObj.IsNull()) + break; + + if (Math.Abs(current.DeltaTime - loopObj.DeltaTime) > 25) + break; + + if (loopObj.NormalisedVectorAngle.IsNotNull() && current.NormalisedVectorAngle.IsNotNull()) + { + double angleDifference = Math.Abs(current.NormalisedVectorAngle.Value - loopObj.NormalisedVectorAngle.Value); + constantAngleCount += Math.Cos(8 * Math.Min(double.DegreesToRadians(11.25), angleDifference)); + } + + notesProcessed++; + index++; + } + + return Math.Pow(Math.Min(0.5 / constantAngleCount, 1), 2); + } + 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)); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 8b81788c73ad..60aa71f00835 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -131,6 +131,8 @@ public class OsuDifficultyHitObject : DifficultyHitObject /// public double SmallCircleBonus { get; private set; } + public double? NormalisedVectorAngle { get; private set; } + private readonly OsuDifficultyHitObject? lastLastDifficultyObject; private readonly OsuDifficultyHitObject? lastDifficultyObject; @@ -279,6 +281,9 @@ private void setDistances(double clockRate) double angle = calculateAngle(BaseObject.StackedPosition, lastCursorPosition, lastLastCursorPosition); double sliderAngle = calculateSliderAngle(lastDifficultyObject!, lastLastCursorPosition); + Vector2 v = BaseObject.StackedPosition - lastCursorPosition; + NormalisedVectorAngle = Math.Atan2(Math.Abs(v.Y), Math.Abs(v.X)); + Angle = Math.Min(angle, sliderAngle); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 146ef8167dd9..9f5f2d87e442 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -31,8 +31,8 @@ public Aim(Mod[] mods, bool includeSliders) private double currentAimStrain; private double currentSpeedStrain; - private double skillMultiplierAim => 26.0; - private double skillMultiplierSpeed => 1.3; + private double skillMultiplierAim => 30; + private double skillMultiplierSpeed => 0.7; private double skillMultiplierTotal => 1.01; private double meanExponent => 1.2;