diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 4b2e54da17..6102f4a8b2 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -24,6 +24,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty [JsonProperty("slider_factor")] public double SliderFactor { get; set; } + [JsonProperty("aim_difficult_strain_count")] + public int AimDifficultStrainCount { get; set; } + + [JsonProperty("speed_difficult_strain_count")] + public int SpeedDifficultStrainCount { get; set; } + [JsonProperty("approach_rate")] public double ApproachRate { get; set; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index ed42f333c0..ac228b0a32 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -40,6 +40,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1; + int aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).RelevantDifficultStrains(); + int speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).RelevantDifficultStrains(); + + // Total number of strains in a map can vary by clockrate, and this needs to be corrected for. + aimDifficultyStrainCount = (int)(aimDifficultyStrainCount * clockRate); + speedDifficultyStrainCount = (int)(aimDifficultyStrainCount * clockRate); + if (mods.Any(h => h is OsuModRelax)) speedRating = 0.0; @@ -78,6 +85,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty SpeedStrain = speedRating, FlashlightRating = flashlightRating, SliderFactor = sliderFactor, + AimDifficultStrainCount = aimDifficultyStrainCount, + SpeedDifficultStrainCount = speedDifficultyStrainCount, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, DrainRate = drainRate, diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 90caa64512..3efb8951b3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= lengthBonus; if (effectiveMissCount > 0) - aimValue *= calculateMissPenalty(effectiveMissCount); + aimValue *= calculateMissPenalty(effectiveMissCount, Attributes.AimDifficultStrainCount); double approachRateFactor = 0.0; if (Attributes.ApproachRate > 10.33) @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= lengthBonus; if (effectiveMissCount > 0) - speedValue *= calculateMissPenalty(effectiveMissCount); + speedValue *= calculateMissPenalty(effectiveMissCount, Attributes.SpeedDifficultStrainCount); double approachRateFactor = 0.0; if (Attributes.ApproachRate > 10.33) @@ -267,17 +267,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount)); } - private double calculateMissPenalty(double missCount) + private double calculateMissPenalty(double missCount, double strainCount) { - double leniency = 4.3; - - if (missCount > totalHits - leniency) - return 0; - - double missApprox = SpecialFunctions.ErfInv((totalHits - leniency - missCount) / totalHits); - double fcApprox = SpecialFunctions.ErfInv((totalHits - leniency) / totalHits); - - return Math.Pow(missApprox / fcApprox, 3.5); + return 0.95 / ((missCount / (3 * Math.Sqrt(strainCount))) + 1); } private int totalHits => countGreat + countOk + countMeh + countMiss; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index e47edc37cc..43e39482a7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -57,5 +57,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return difficulty * DifficultyMultiplier; } + + public int RelevantDifficultStrains() + { + List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); + + return strains.Count(s => s > strains[0] * 0.66); + } } }