diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 79d575ab3f..c88f70021b 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.531832890435525d, "diffcalc-test")] - [TestCase(1.4644923495008817d, "zero-length-sliders")] + [TestCase(6.6975550434910005d, "diffcalc-test")] + [TestCase(1.4673500058356748d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.8067616302940852d, "diffcalc-test")] - [TestCase(1.7763214959309293d, "zero-length-sliders")] + [TestCase(8.938989502378238d, "diffcalc-test")] + [TestCase(1.779323508403831d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index e64cdbfa1d..aebc73f108 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills 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 velocity_change_multiplier = 0.75; private double currentStrain = 1; @@ -62,8 +63,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills prevVelocity = Math.Max(prevVelocity, movementVelocity + travelVelocity); } - double angleBonus = 0; + double wideAngleBonus = 0; + double acuteAngleBonus = 0; double sliderBonus = 0; + double velocityChangeBonus = 0; double aimStrain = currVelocity; // Start strain with regular velocity. @@ -76,10 +79,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double lastLastAngle = osuLastLastObj.Angle.Value; // Rewarding angles, take the smaller velocity as base. - angleBonus = Math.Min(currVelocity, prevVelocity); + double angleBonus = Math.Min(currVelocity, prevVelocity); - double wideAngleBonus = calcWideAngleBonus(currAngle); - double acuteAngleBonus = calcAcuteAngleBonus(currAngle); + wideAngleBonus = calcWideAngleBonus(currAngle); + acuteAngleBonus = calcAcuteAngleBonus(currAngle); if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2. acuteAngleBonus = 0; @@ -91,20 +94,48 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills * Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.JumpDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter). } - wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3))); // Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute. - acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastLastAngle), 3))); // Penalize acute angles if they're repeated, reducing the penalty as the lastLastAngle gets more obtuse. - - angleBonus = Math.Max(acuteAngleBonus * acute_angle_multiplier, wideAngleBonus * wide_angle_multiplier); // Take the max of the multipliers. + // Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute. + wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3))); + // Penalize acute angles if they're repeated, reducing the penalty as the lastLastAngle gets more obtuse. + acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastLastAngle), 3))); } } + if (Math.Max(prevVelocity, currVelocity) != 0) + { + // We want to use the average velocity over the whole object when awarding differences, not the individual jump and slider path velocities. + prevVelocity = (osuLastObj.JumpDistance + osuLastObj.TravelDistance) / osuLastObj.StrainTime; + currVelocity = (osuCurrObj.JumpDistance + osuCurrObj.TravelDistance) / osuCurrObj.StrainTime; + + // Scale with ratio of difference compared to 0.5 * max dist. + double distRatio = Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2); + + // Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing. + double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity)); + + // Reward for % distance slowed down compared to previous, paying attention to not award overlap + double nonOverlapVelocityBuff = Math.Abs(prevVelocity - currVelocity) + // do not award overlap + * Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, Math.Min(osuCurrObj.JumpDistance, osuLastObj.JumpDistance) / 100)), 2); + + // Choose the largest bonus, multiplied by ratio. + velocityChangeBonus = Math.Max(overlapVelocityBuff, nonOverlapVelocityBuff) * distRatio; + + // Penalize for rhythm changes. + velocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2); + } + if (osuCurrObj.TravelTime != 0) { - sliderBonus = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // add some slider rewards + // Reward sliders based on velocity. + sliderBonus = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; } - aimStrain += angleBonus; // Add in angle bonus. - aimStrain += sliderBonus * slider_multiplier; // Add in additional slider velocity. + // Add in acute angle bonus or wide angle bonus + velocity change bonus, whichever is larger. + aimStrain += Math.Max(acuteAngleBonus * acute_angle_multiplier, wideAngleBonus * wide_angle_multiplier + velocityChangeBonus * velocity_change_multiplier); + + // Add in additional slider velocity bonus. + aimStrain += sliderBonus * slider_multiplier; return aimStrain; }