diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 46f7c461f84f..7e995f2dde36 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -17,18 +17,18 @@ public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.6369583000323935d, 206, "diffcalc-test")] - [TestCase(1.4476531024675374d, 45, "zero-length-sliders")] + [TestCase(6.7115569159190587d, 206, "diffcalc-test")] + [TestCase(1.4391311903612753d, 45, "zero-length-sliders")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(8.8816128335486386d, 206, "diffcalc-test")] - [TestCase(1.7540389962596916d, 45, "zero-length-sliders")] + [TestCase(8.9757300665532966d, 206, "diffcalc-test")] + [TestCase(1.7437232654020756d, 45, "zero-length-sliders")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime()); - [TestCase(6.6369583000323935d, 239, "diffcalc-test")] - [TestCase(1.4476531024675374d, 54, "zero-length-sliders")] + [TestCase(6.7115569159190587d, 239, "diffcalc-test")] + [TestCase(1.4391311903612753d, 54, "zero-length-sliders")] public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic()); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs index b2d6aaf83c93..6d1b4d1a1586 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs @@ -13,8 +13,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators public static class AimEvaluator { private const double wide_angle_multiplier = 1.5; - private const double acute_angle_multiplier = 2.0; - private const double slider_multiplier = 1.5; + private const double acute_angle_multiplier = 1.95; + private const double slider_multiplier = 1.35; private const double velocity_change_multiplier = 0.75; /// diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 9f4a40511346..0ebfb9a283e7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -46,7 +46,11 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1; if (mods.Any(h => h is OsuModRelax)) + { + aimRating *= 0.9; speedRating = 0.0; + flashlightRating *= 0.7; + } double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; @@ -62,7 +66,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1 ); - double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; + double starRating = basePerformance > 0.00001 ? Math.Cbrt(OsuPerformanceCalculator.PERFORMANCE_BASE_MULTIPLIER) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index c3b78340096e..3c82c2dc331e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public class OsuPerformanceCalculator : PerformanceCalculator { + public const double PERFORMANCE_BASE_MULTIPLIER = 1.14; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. + private double accuracy; private int scoreMaxCombo; private int countGreat; @@ -41,7 +43,7 @@ protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo s countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); effectiveMissCount = calculateEffectiveMissCount(osuAttributes); - double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. + double multiplier = PERFORMANCE_BASE_MULTIPLIER; if (score.Mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount); @@ -51,10 +53,14 @@ protected override PerformanceAttributes CreatePerformanceAttributes(ScoreInfo s if (score.Mods.Any(h => h is OsuModRelax)) { - // As we're adding Oks and Mehs to an approximated number of combo breaks the result can be higher than total hits in specific scenarios (which breaks some calculations) so we need to clamp it. - effectiveMissCount = Math.Min(effectiveMissCount + countOk + countMeh, totalHits); + // https://www.desmos.com/calculator/bc9eybdthb + // we use OD13.3 as maximum since it's the value at which great hitwidow becomes 0 + // this is well beyond currently maximum achievable OD which is 12.17 (DTx2 + DA with OD11) + double okMultiplier = Math.Max(0.0, osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 13.33, 1.8) : 1.0); + double mehMultiplier = Math.Max(0.0, osuAttributes.OverallDifficulty > 0.0 ? 1 - Math.Pow(osuAttributes.OverallDifficulty / 13.33, 5) : 1.0); - multiplier *= 0.6; + // As we're adding Oks and Mehs to an approximated number of combo breaks the result can be higher than total hits in specific scenarios (which breaks some calculations) so we need to clamp it. + effectiveMissCount = Math.Min(effectiveMissCount + countOk * okMultiplier + countMeh * mehMultiplier, totalHits); } double aimValue = computeAimValue(score, osuAttributes); @@ -103,7 +109,10 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut if (attributes.ApproachRate > 10.33) approachRateFactor = 0.3 * (attributes.ApproachRate - 10.33); else if (attributes.ApproachRate < 8.0) - approachRateFactor = 0.1 * (8.0 - attributes.ApproachRate); + approachRateFactor = 0.05 * (8.0 - attributes.ApproachRate); + + if (score.Mods.Any(h => h is OsuModRelax)) + approachRateFactor = 0.0; aimValue *= 1.0 + approachRateFactor * lengthBonus; // Buff for longer maps with high AR. @@ -134,6 +143,9 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut private double computeSpeedValue(ScoreInfo score, OsuDifficultyAttributes attributes) { + if (score.Mods.Any(h => h is OsuModRelax)) + return 0.0; + double speedValue = Math.Pow(5.0 * Math.Max(1.0, attributes.SpeedDifficulty / 0.0675) - 4.0, 3.0) / 100000.0; double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + @@ -174,7 +186,7 @@ private double computeSpeedValue(ScoreInfo score, OsuDifficultyAttributes attrib speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. - speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); + speedValue *= Math.Pow(0.99, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); return speedValue; } @@ -266,6 +278,5 @@ private double calculateEffectiveMissCount(OsuDifficultyAttributes attributes) private double getComboScalingFactor(OsuDifficultyAttributes attributes) => attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(attributes.MaxCombo, 0.8), 1.0); private int totalHits => countGreat + countOk + countMeh + countMiss; - private int totalSuccessfulHits => countGreat + countOk + countMeh; } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 9b1fbf9a2e2d..38e0e5b67737 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -25,7 +25,7 @@ public Aim(Mod[] mods, bool withSliders) private double currentStrain; - private double skillMultiplier => 23.25; + private double skillMultiplier => 23.55; private double strainDecayBase => 0.15; private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);