From b31d1b3a08605daf8d0f8f6786eeadf5ec4aeeff Mon Sep 17 00:00:00 2001 From: Givy120 <89256026+Givikap120@users.noreply.github.com> Date: Thu, 26 Mar 2026 12:47:55 +0200 Subject: [PATCH] Account for alternating angles in reading angle factor (#36466) This rework target is C-type nerf, as this map gets absurd amount of reading pp, because angles are considered very unrepetitive. What results in this score being worth absurd 1.4k pp. This PR is up to heavy discussion because it can be made much more general, touching more maps. So I wait on pp committee opinion on what of the parts can be removed. Current checks for angle to be nerfed: - The smaller angle has to be very sharp: <20 degrees, full power on <5 degrees - The larger angle has to be wide: >60 degrees, full power on >120 degrees If pattern meets all the criteria - it would be considered repetitive. For now practically no map meets this criteria to significant amount except C-type. --- .../Difficulty/Evaluators/ReadingEvaluator.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs index 926abca971..2d1d5f4d73 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs @@ -208,6 +208,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators int index = 0; double currentTimeGap = 0; + OsuDifficultyHitObject loopObjPrev0 = current; + OsuDifficultyHitObject? loopObjPrev1 = null; + OsuDifficultyHitObject? loopObjPrev2 = null; + while (currentTimeGap < minimum_angle_relevancy_time) { var loopObj = (OsuDifficultyHitObject)current.Previous(index); @@ -221,13 +225,34 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators if (loopObj.Angle.IsNotNull() && current.Angle.IsNotNull()) { double angleDifference = Math.Abs(current.Angle.Value - loopObj.Angle.Value); + double angleDifferenceAlternating = Math.PI; + + if (loopObjPrev0.Angle != null && loopObjPrev1?.Angle != null && loopObjPrev2?.Angle != null) + { + angleDifferenceAlternating = Math.Abs(loopObjPrev1.Angle.Value - loopObj.Angle.Value); + angleDifferenceAlternating += Math.Abs(loopObjPrev2.Angle.Value - loopObjPrev0.Angle.Value); + + double weight = 1.0; + + // Be sure that one of the angles is very sharp, when other is wide + weight *= DifficultyCalculationUtils.ReverseLerp(Math.Min(loopObj.Angle.Value, loopObjPrev0.Angle.Value) * 180 / Math.PI, 20, 5); + weight *= DifficultyCalculationUtils.ReverseLerp(Math.Max(loopObj.Angle.Value, loopObjPrev0.Angle.Value) * 180 / Math.PI, 60, 120); + + // Lerp between max angle difference and rescaled alternating difference, with more harsh scaling compared to normal difference + angleDifferenceAlternating = double.Lerp(Math.PI, 0.1 * angleDifferenceAlternating, weight); + } + double stackFactor = DifficultyCalculationUtils.Smootherstep(loopObj.LazyJumpDistance, 0, OsuDifficultyHitObject.NORMALISED_RADIUS); - constantAngleCount += Math.Cos(3 * Math.Min(double.DegreesToRadians(30), angleDifference * stackFactor)) * longIntervalFactor; + constantAngleCount += Math.Cos(3 * Math.Min(double.DegreesToRadians(30), Math.Min(angleDifference, angleDifferenceAlternating) * stackFactor)) * longIntervalFactor; } currentTimeGap = current.StartTime - loopObj.StartTime; index++; + + loopObjPrev2 = loopObjPrev1; + loopObjPrev1 = loopObjPrev0; + loopObjPrev0 = loopObj; } return Math.Clamp(2 / constantAngleCount, 0.2, 1);