diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs index 2df383aaa891..16548a005319 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs @@ -59,7 +59,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current) double travelDistance = osuPrevObj?.TravelDistance ?? 0; double distance = Math.Min(single_spacing_threshold, travelDistance + osuCurrObj.MinimumJumpDistance); - return (speedBonus + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) * doubletapness / strainTime; + return (speedBonus + Math.Pow(distance / single_spacing_threshold, 3.5)) * doubletapness / strainTime; } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 3b580a5b5906..3cb58467758e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -72,7 +72,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat ); double starRating = basePerformance > 0.00001 - ? Math.Cbrt(OsuPerformanceCalculator.PERFORMANCE_BASE_MULTIPLIER) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) + ? Math.Cbrt(OsuPerformanceCalculator.PERFORMANCE_BASE_MULTIPLIER) * 0.0245 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index b31f4ff5191d..e3ba9f3a13f5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -88,10 +88,6 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut { double aimValue = Math.Pow(5.0 * Math.Max(1.0, attributes.AimDifficulty / 0.0675) - 4.0, 3.0) / 100000.0; - double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + - (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); - aimValue *= lengthBonus; - // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (effectiveMissCount > 0) aimValue *= 0.97 * Math.Pow(1 - Math.Pow(effectiveMissCount / totalHits, 0.775), effectiveMissCount); @@ -107,7 +103,7 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut if (score.Mods.Any(h => h is OsuModRelax)) approachRateFactor = 0.0; - aimValue *= 1.0 + approachRateFactor * lengthBonus; // Buff for longer maps with high AR. + aimValue *= 1.0 + approachRateFactor; if (score.Mods.Any(m => m is OsuModBlinds)) aimValue *= 1.3 + (totalHits * (0.0016 / (1 + 2 * effectiveMissCount)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * attributes.DrainRate * attributes.DrainRate); @@ -141,10 +137,6 @@ private double computeSpeedValue(ScoreInfo score, OsuDifficultyAttributes attrib 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) + - (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); - speedValue *= lengthBonus; - // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (effectiveMissCount > 0) speedValue *= 0.97 * Math.Pow(1 - Math.Pow(effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875)); @@ -155,7 +147,7 @@ private double computeSpeedValue(ScoreInfo score, OsuDifficultyAttributes attrib if (attributes.ApproachRate > 10.33) approachRateFactor = 0.3 * (attributes.ApproachRate - 10.33); - speedValue *= 1.0 + approachRateFactor * lengthBonus; // Buff for longer maps with high AR. + speedValue *= 1.0 + approachRateFactor; if (score.Mods.Any(m => m is OsuModBlinds)) { diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 3d6d3f99c174..0be45b73e6d0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -24,7 +24,7 @@ public Flashlight(Mod[] mods) hasHiddenMod = mods.Any(m => m is OsuModHidden); } - private double skillMultiplier => 0.052; + private double skillMultiplier => 0.0575; private double strainDecayBase => 0.15; private double currentStrain; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 15b20a557279..2737943a864f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -42,7 +42,6 @@ protected OsuStrainSkill(Mod[] mods) public override double DifficultyValue() { double difficulty = 0; - double weight = 1; // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). // These sections will not contribute to the difficulty. @@ -57,12 +56,19 @@ public override double DifficultyValue() strains[i] *= Interpolation.Lerp(ReducedStrainBaseline, 1.0, scale); } + int index = 0; + // Difficulty is the weighted sum of the highest strains from every section. // We're sorting from highest to lowest strain. foreach (double strain in strains.OrderByDescending(d => d)) { + // Below uses harmonic sum scaling which makes the resulting summation logarithmic rather than geometric. + // Good for properly weighting difficulty across full map instead of using object count for LengthBonus. + // 1.44 and 7.5 are arbitrary constants that worked well. + double weight = 1.44 * ((1 + (7.5 / (1 + index))) / (index + 1 + (7.5 / (1 + index)))); + difficulty += strain * weight; - weight *= DecayWeight; + index += 1; } return difficulty * DifficultyMultiplier;