mirror of
https://github.com/ppy/osu.git
synced 2025-01-26 21:03:21 +08:00
Merge pull request #19001 from stanriders/pp-balancing
osu! performance points balancing pass
This commit is contained in:
commit
f29d1b2ded
@ -17,18 +17,18 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase(6.6369583000323935d, 206, "diffcalc-test")]
|
[TestCase(6.7115569159190587d, 206, "diffcalc-test")]
|
||||||
[TestCase(1.4476531024675374d, 45, "zero-length-sliders")]
|
[TestCase(1.4391311903612753d, 45, "zero-length-sliders")]
|
||||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(8.8816128335486386d, 206, "diffcalc-test")]
|
[TestCase(8.9757300665532966d, 206, "diffcalc-test")]
|
||||||
[TestCase(1.7540389962596916d, 45, "zero-length-sliders")]
|
[TestCase(1.7437232654020756d, 45, "zero-length-sliders")]
|
||||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
||||||
|
|
||||||
[TestCase(6.6369583000323935d, 239, "diffcalc-test")]
|
[TestCase(6.7115569159190587d, 239, "diffcalc-test")]
|
||||||
[TestCase(1.4476531024675374d, 54, "zero-length-sliders")]
|
[TestCase(1.4391311903612753d, 54, "zero-length-sliders")]
|
||||||
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
public static class AimEvaluator
|
public static class AimEvaluator
|
||||||
{
|
{
|
||||||
private const double wide_angle_multiplier = 1.5;
|
private const double wide_angle_multiplier = 1.5;
|
||||||
private const double acute_angle_multiplier = 2.0;
|
private const double acute_angle_multiplier = 1.95;
|
||||||
private const double slider_multiplier = 1.5;
|
private const double slider_multiplier = 1.35;
|
||||||
private const double velocity_change_multiplier = 0.75;
|
private const double velocity_change_multiplier = 0.75;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -46,7 +46,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
|
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
|
||||||
|
|
||||||
if (mods.Any(h => h is OsuModRelax))
|
if (mods.Any(h => h is OsuModRelax))
|
||||||
|
{
|
||||||
|
aimRating *= 0.9;
|
||||||
speedRating = 0.0;
|
speedRating = 0.0;
|
||||||
|
flashlightRating *= 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000;
|
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;
|
double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000;
|
||||||
@ -62,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1
|
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 preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||||
double drainRate = beatmap.Difficulty.DrainRate;
|
double drainRate = beatmap.Difficulty.DrainRate;
|
||||||
|
@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
{
|
{
|
||||||
public class OsuPerformanceCalculator : PerformanceCalculator
|
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 double accuracy;
|
||||||
private int scoreMaxCombo;
|
private int scoreMaxCombo;
|
||||||
private int countGreat;
|
private int countGreat;
|
||||||
@ -41,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
||||||
effectiveMissCount = calculateEffectiveMissCount(osuAttributes);
|
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))
|
if (score.Mods.Any(m => m is OsuModNoFail))
|
||||||
multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount);
|
multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount);
|
||||||
@ -51,10 +53,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
if (score.Mods.Any(h => h is OsuModRelax))
|
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.
|
// https://www.desmos.com/calculator/bc9eybdthb
|
||||||
effectiveMissCount = Math.Min(effectiveMissCount + countOk + countMeh, totalHits);
|
// 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);
|
double aimValue = computeAimValue(score, osuAttributes);
|
||||||
@ -103,7 +109,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
if (attributes.ApproachRate > 10.33)
|
if (attributes.ApproachRate > 10.33)
|
||||||
approachRateFactor = 0.3 * (attributes.ApproachRate - 10.33);
|
approachRateFactor = 0.3 * (attributes.ApproachRate - 10.33);
|
||||||
else if (attributes.ApproachRate < 8.0)
|
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.
|
aimValue *= 1.0 + approachRateFactor * lengthBonus; // Buff for longer maps with high AR.
|
||||||
|
|
||||||
@ -134,6 +143,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
private double computeSpeedValue(ScoreInfo score, OsuDifficultyAttributes attributes)
|
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 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) +
|
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||||
@ -174,7 +186,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2);
|
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.
|
// 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;
|
return speedValue;
|
||||||
}
|
}
|
||||||
@ -266,6 +278,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
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 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 totalHits => countGreat + countOk + countMeh + countMiss;
|
||||||
private int totalSuccessfulHits => countGreat + countOk + countMeh;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
|
|
||||||
private double currentStrain;
|
private double currentStrain;
|
||||||
|
|
||||||
private double skillMultiplier => 23.25;
|
private double skillMultiplier => 23.55;
|
||||||
private double strainDecayBase => 0.15;
|
private double strainDecayBase => 0.15;
|
||||||
|
|
||||||
private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
|
private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
|
||||||
|
Loading…
Reference in New Issue
Block a user