From 9e6ae3587ae77d857052e1eb6e75e5e2d952bffc Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 23 Feb 2024 17:44:56 +0200 Subject: [PATCH] Changes to highAR and angle nerf HighAR now have passive strain bonus to buff low SR maps Angle nerf now applied inplace (copied from HD calc) --- .../Difficulty/Evaluators/ReadingEvaluator.cs | 152 +++++++++--------- .../Difficulty/Skills/Reading.cs | 1 - .../Difficulty/Skills/ReadingHighAR.cs | 25 ++- 3 files changed, 82 insertions(+), 96 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs index 90fb1758d0..219f325ae0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; -using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators @@ -20,23 +20,90 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators public static double CalculateDenstityOf(OsuDifficultyHitObject currObj) { - double pastObjectDifficultyInfluence = 0; + double density = 0; + double densityAnglesNerf = -2; // we have threshold of 2, so 2 or same angles won't be punished - foreach (var loopObj in retrievePastVisibleObjects(currObj)) + OsuDifficultyHitObject? prevObj0 = null; + OsuDifficultyHitObject? prevObj1 = null; + OsuDifficultyHitObject? prevObj2 = null; + + double prevConstantAngle = 0; + + foreach (var loopObj in retrievePastVisibleObjects(currObj).Reverse()) { double loopDifficulty = currObj.OpacityAt(loopObj.BaseObject.StartTime, false); // Small distances means objects may be cheesed, so it doesn't matter whether they are arranged confusingly. + // For HD: it's not subtracting anything cuz it's multiplied by the aim difficulty anyways. loopDifficulty *= logistic((loopObj.MinimumJumpDistance - 60) / 10); - //double timeBetweenCurrAndLoopObj = (currObj.BaseObject.StartTime - loopObj.BaseObject.StartTime) / clockRateEstimate; + // Reduce density bonus for this object if they're too apart in time + // Nerf starts on 1500ms and reaches maximum (*=0) on 3000ms double timeBetweenCurrAndLoopObj = currObj.StartTime - loopObj.StartTime; loopDifficulty *= getTimeNerfFactor(timeBetweenCurrAndLoopObj); - pastObjectDifficultyInfluence += loopDifficulty; + if (prevObj0.IsNull()) + { + prevObj0 = loopObj; + continue; + } + + density += loopDifficulty; + + // Angles nerf + + if (loopObj.Angle.IsNotNull() && prevObj0.Angle.IsNotNull()) + { + double angleDifference = Math.Abs(prevObj0.Angle.Value - loopObj.Angle.Value); + + // Nerf alternating angles case + if (prevObj1.IsNotNull() && prevObj2.IsNotNull() && prevObj1.Angle.IsNotNull() && prevObj2.Angle.IsNotNull()) + { + // Normalized difference + double angleDifference1 = Math.Abs(prevObj1.Angle.Value - loopObj.Angle.Value) / Math.PI; + double angleDifference2 = Math.Abs(prevObj2.Angle.Value - prevObj0.Angle.Value) / Math.PI; + + // Will be close to 1 if angleDifference1 and angleDifference2 was both close to 0 + double alternatingFactor = Math.Pow((1 - angleDifference1) * (1 - angleDifference2), 2); + + // Be sure to nerf only same rhythms + double rhythmFactor = 1 - getRhythmDifference(loopObj.StrainTime, prevObj0.StrainTime); // 0 on different rhythm, 1 on same rhythm + rhythmFactor *= 1 - getRhythmDifference(prevObj0.StrainTime, prevObj1.StrainTime); + rhythmFactor *= 1 - getRhythmDifference(prevObj1.StrainTime, prevObj2.StrainTime); + + double acuteAngleFactor = 1 - Math.Min(loopObj.Angle.Value, prevObj0.Angle.Value) / Math.PI; + + double prevAngleAdjust = Math.Max(angleDifference - angleDifference1, 0); + + prevAngleAdjust *= alternatingFactor; // Nerf if alternating + prevAngleAdjust *= rhythmFactor; // Nerf if same rhythms + prevAngleAdjust *= acuteAngleFactor; + + angleDifference -= prevAngleAdjust; + } + + // Reduce angles nerf if objects are too apart in time + // Angle nerf is starting being reduced from 200ms (150BPM jump) and it reduced to 0 on 2000ms + double longIntervalFactor = Math.Clamp(1 - (loopObj.StrainTime - 200) / (2000 - 200), 0, 1); + + // Current angle nerf. Angle difference less than 15 degrees is considered the same + double currConstantAngle = Math.Cos(4 * Math.Min(Math.PI / 12, angleDifference)) * longIntervalFactor; + + // Apply the nerf only when it's repeated + double currentAngleNerf = Math.Min(currConstantAngle, prevConstantAngle); + + densityAnglesNerf += Math.Min(currentAngleNerf, loopDifficulty); + prevConstantAngle = currConstantAngle; + } + + prevObj2 = prevObj1; + prevObj1 = prevObj0; + prevObj0 = loopObj; } - return pastObjectDifficultyInfluence; + // Apply angles nerf + density -= Math.Max(0, densityAnglesNerf); + return density; } public static double CalculateOverlapDifficultyOf(OsuDifficultyHitObject currObj) @@ -71,8 +138,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators screenOverlapDifficulty = Math.Max(0, screenOverlapDifficulty - 0.5); // make overlap value =1 cost significantly less double overlapBonus = overlap_multiplier * screenOverlapDifficulty * difficulty; - - difficulty *= getConstantAngleNerfFactor(currObj); difficulty += overlapBonus; //difficulty *= 1 + overlap_multiplier * screenOverlapDifficulty; @@ -185,77 +250,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators } } - private static double getConstantAngleNerfFactor(OsuDifficultyHitObject current) - { - const double time_limit = 2000; - const double time_limit_low = 200; - - double constantAngleCount = 0; - int index = 0; - double currentTimeGap = 0; - - OsuDifficultyHitObject prevLoopObj = current; - - OsuDifficultyHitObject? prevLoopObj1 = null; - OsuDifficultyHitObject? prevLoopObj2 = null; - - double prevConstantAngle = 0; - - while (currentTimeGap < time_limit) - { - var loopObj = (OsuDifficultyHitObject)current.Previous(index); - - if (loopObj.IsNull()) - break; - - double longIntervalFactor = Math.Clamp(1 - (loopObj.StrainTime - time_limit_low) / (time_limit - time_limit_low), 0, 1); - - if (loopObj.Angle.IsNotNull() && prevLoopObj.Angle.IsNotNull()) - { - double angleDifference = Math.Abs(prevLoopObj.Angle.Value - loopObj.Angle.Value); - - // Nerf alternating angles case - if (prevLoopObj1.IsNotNull() && prevLoopObj2.IsNotNull() && prevLoopObj1.Angle.IsNotNull() && prevLoopObj2.Angle.IsNotNull()) - { - // Normalized difference - double angleDifference1 = Math.Abs(prevLoopObj1.Angle.Value - loopObj.Angle.Value) / Math.PI; - double angleDifference2 = Math.Abs(prevLoopObj2.Angle.Value - prevLoopObj.Angle.Value) / Math.PI; - - // Will be close to 1 if angleDifference1 and angleDifference2 was both close to 0 - double alternatingFactor = Math.Pow((1 - angleDifference1) * (1 - angleDifference2), 2); - - // Be sure to nerf only same rhythms - double rhythmFactor = 1 - getRhythmDifference(loopObj.StrainTime, prevLoopObj.StrainTime); // 0 on different rhythm, 1 on same rhythm - rhythmFactor *= 1 - getRhythmDifference(prevLoopObj.StrainTime, prevLoopObj1.StrainTime); - rhythmFactor *= 1 - getRhythmDifference(prevLoopObj1.StrainTime, prevLoopObj2.StrainTime); - - double acuteAngleFactor = 1 - Math.Min(loopObj.Angle.Value, prevLoopObj.Angle.Value) / Math.PI; - - double prevAngleAdjust = Math.Max(angleDifference - angleDifference1, 0); - - prevAngleAdjust *= alternatingFactor; // Nerf if alternating - prevAngleAdjust *= rhythmFactor; // Nerf if same rhythms - prevAngleAdjust *= acuteAngleFactor; - - angleDifference -= prevAngleAdjust; - } - - double currConstantAngle = Math.Cos(4 * Math.Min(Math.PI / 8, angleDifference)) * longIntervalFactor; - constantAngleCount += Math.Min(currConstantAngle, prevConstantAngle); - prevConstantAngle = currConstantAngle; - } - - currentTimeGap = current.StartTime - loopObj.StartTime; - index++; - - prevLoopObj2 = prevLoopObj1; - prevLoopObj1 = prevLoopObj; - prevLoopObj = loopObj; - } - - return Math.Pow(Math.Min(1, 2 / constantAngleCount), 2); - } - private static double getTimeNerfFactor(double deltaTime) { return Math.Clamp(2 - deltaTime / (reading_window_size / 2), 0, 1); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs index 1e5a4f73ac..3fbac64215 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs @@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public class ReadingLowAR : GraphSkill { private readonly List difficulties = new List(); - //private double skillMultiplier => 2.3; private double skillMultiplier => 2; public ReadingLowAR(Mod[] mods) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/ReadingHighAR.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/ReadingHighAR.cs index 5c070242e4..6e3049aec6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/ReadingHighAR.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/ReadingHighAR.cs @@ -64,15 +64,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } public override double DifficultyValue() { - // Get number how much high AR adjust changed difficulty - double difficultyRatio = aimComponent.DifficultyValue() / aimComponentNoAdjust.DifficultyValue(); - - // Calculate how much preempt should change to account for high AR adjust - double difficulty = ReadingHighAREvaluator.GetDifficulty(preempt) * difficultyRatio; - double adjustedPreempt = ReadingHighAREvaluator.GetPreempt(difficulty); - - Console.WriteLine($"Degree of High AR Complexity = {difficultyRatio:0.##}, {preempt:0} -> {adjustedPreempt:0}"); - // Simulating summing to get the most correct value possible double aimValue = Math.Sqrt(aimComponent.DifficultyValue()) * OsuDifficultyCalculator.DIFFICULTY_MULTIPLIER; double speedValue = Math.Sqrt(speedComponent.DifficultyValue()) * OsuDifficultyCalculator.DIFFICULTY_MULTIPLIER; @@ -83,15 +74,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double power = OsuDifficultyCalculator.SUM_POWER; double totalPerformance = Math.Pow(Math.Pow(aimPerformance, power) + Math.Pow(speedPerformance, power), 1.0 / power); - // First half of length bonus is in SR to not inflate short AR11 maps + // First half of length bonus is in SR to not inflate Star Rating short AR11 maps double lengthBonus = OsuPerformanceCalculator.CalculateDefaultLengthBonus(objectsCount); totalPerformance *= lengthBonus; double adjustedDifficulty = OsuStrainSkill.PerformanceToDifficulty(totalPerformance); double difficultyValue = Math.Pow(adjustedDifficulty / OsuDifficultyCalculator.DIFFICULTY_MULTIPLIER, 2.0); - return 75 * Math.Sqrt(difficultyValue * difficulty); - // return difficultyValue; + // have the same value as difficultyValue at 500pp point + return 75 * Math.Sqrt(difficultyValue); } } @@ -106,7 +97,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private bool adjustHighAR; private double currentStrain; - private double skillMultiplier => 18.5; + private double skillMultiplier => 17.8; + private double defaultValueMultiplier => 30; private double strainDecayBase => 0.15; private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); @@ -118,12 +110,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills currentStrain *= strainDecay(current.DeltaTime); double aimDifficulty = AimEvaluator.EvaluateDifficultyOf(current, true); - aimDifficulty *= ReadingHighAREvaluator.EvaluateDifficultyOf(current, adjustHighAR); + double readingDifficulty = ReadingHighAREvaluator.EvaluateDifficultyOf(current, adjustHighAR); + aimDifficulty *= Math.Pow(readingDifficulty, 2); aimDifficulty *= skillMultiplier; currentStrain += aimDifficulty; - return currentStrain; + return currentStrain + defaultValueMultiplier * ReadingHighAREvaluator.EvaluateDifficultyOf(current, adjustHighAR); } } @@ -151,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills currentStrain *= strainDecay(currODHO.StrainTime); double speedDifficulty = SpeedEvaluator.EvaluateDifficultyOf(current) * skillMultiplier; - speedDifficulty *= ReadingHighAREvaluator.EvaluateDifficultyOf(current, false); + speedDifficulty *= Math.Pow(ReadingHighAREvaluator.EvaluateDifficultyOf(current, false), 2); currentStrain += speedDifficulty; currentRhythm = currODHO.RhythmDifficulty;