mirror of
https://github.com/ppy/osu.git
synced 2025-03-24 19:17:20 +08:00
Use total deviation to scale accuracy on aim, general aim buff (#31498)
* Make aim accuracy scaling harsher * Use deviation-based scaling * Bring the balancing multiplier down * Adjust multipliers, fix incorrect deviation when using slider accuracy * Adjust multipliers * Update osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs Co-authored-by: James Wilson <tsunyoku@gmail.com> * Change high speed deviation threshold to 22-27 instead of 20-24 * Update tests --------- Co-authored-by: James Wilson <tsunyoku@gmail.com>
This commit is contained in:
parent
b21c6457b1
commit
c53188cf45
@ -15,21 +15,21 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu.Tests";
|
||||
|
||||
[TestCase(6.6860329680488437d, 239, "diffcalc-test")]
|
||||
[TestCase(1.4485740324170036d, 54, "zero-length-sliders")]
|
||||
[TestCase(6.7443067697205539d, 239, "diffcalc-test")]
|
||||
[TestCase(1.4630292101418947d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.43052813047866129d, 4, "very-fast-slider")]
|
||||
[TestCase(0.14143808967817237d, 2, "nan-slider")]
|
||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||
|
||||
[TestCase(9.6300773538770041d, 239, "diffcalc-test")]
|
||||
[TestCase(1.7550155729445993d, 54, "zero-length-sliders")]
|
||||
[TestCase(9.7058844423552308d, 239, "diffcalc-test")]
|
||||
[TestCase(1.7724929629205366d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.55785578988249407d, 4, "very-fast-slider")]
|
||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
||||
|
||||
[TestCase(6.6860329680488437d, 239, "diffcalc-test")]
|
||||
[TestCase(1.4485740324170036d, 54, "zero-length-sliders")]
|
||||
[TestCase(6.7443067697205539d, 239, "diffcalc-test")]
|
||||
[TestCase(1.4630292101418947d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.43052813047866129d, 4, "very-fast-slider")]
|
||||
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
||||
|
@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
|
||||
// Penalize angle repetition.
|
||||
wideAngleBonus *= 1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3));
|
||||
acuteAngleBonus *= 0.1 + 0.9 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3)));
|
||||
acuteAngleBonus *= 0.09 + 0.91 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3)));
|
||||
|
||||
// Apply full wide angle bonus for distance more than one diameter
|
||||
wideAngleBonus *= angleBonus * DifficultyCalculationUtils.Smootherstep(osuCurrObj.LazyJumpDistance, 0, diameter);
|
||||
|
@ -24,6 +24,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
[JsonProperty("effective_miss_count")]
|
||||
public double EffectiveMissCount { get; set; }
|
||||
|
||||
[JsonProperty("total_deviation")]
|
||||
public double? TotalDeviation { get; set; }
|
||||
|
||||
[JsonProperty("speed_deviation")]
|
||||
public double? SpeedDeviation { get; set; }
|
||||
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -41,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
/// </summary>
|
||||
private double effectiveMissCount;
|
||||
|
||||
private double? totalDeviation;
|
||||
private double? speedDeviation;
|
||||
|
||||
public OsuPerformanceCalculator()
|
||||
@ -113,6 +115,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
effectiveMissCount = Math.Min(effectiveMissCount + countOk * okMultiplier + countMeh * mehMultiplier, totalHits);
|
||||
}
|
||||
|
||||
totalDeviation = calculateTotalDeviation(osuAttributes);
|
||||
speedDeviation = calculateSpeedDeviation(osuAttributes);
|
||||
|
||||
double aimValue = computeAimValue(score, osuAttributes);
|
||||
@ -135,6 +138,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
Accuracy = accuracyValue,
|
||||
Flashlight = flashlightValue,
|
||||
EffectiveMissCount = effectiveMissCount,
|
||||
TotalDeviation = totalDeviation,
|
||||
SpeedDeviation = speedDeviation,
|
||||
Total = totalValue
|
||||
};
|
||||
@ -145,6 +149,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
if (score.Mods.Any(h => h is OsuModAutopilot))
|
||||
return 0.0;
|
||||
|
||||
if (totalDeviation == null)
|
||||
return 0;
|
||||
|
||||
double aimDifficulty = attributes.AimDifficulty;
|
||||
|
||||
if (attributes.SliderCount > 0 && attributes.AimDifficultSliderCount > 0)
|
||||
@ -196,9 +203,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
aimValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate);
|
||||
}
|
||||
|
||||
aimValue *= accuracy;
|
||||
// It is important to consider accuracy difficulty when scaling with accuracy.
|
||||
aimValue *= 0.98 + Math.Pow(Math.Max(0, attributes.OverallDifficulty), 2) / 2500;
|
||||
aimValue *= SpecialFunctions.Erf(25.0 / (Math.Sqrt(2) * totalDeviation.Value));
|
||||
|
||||
return aimValue;
|
||||
}
|
||||
@ -317,6 +322,48 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
return flashlightValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using <see cref="calculateDeviation"/> estimates player's deviation on accuracy objects.
|
||||
/// Returns deviation for circles and sliders if score was set with slideracc.
|
||||
/// Returns the min between deviation of circles and deviation on circles and sliders (assuming slider hits are 50s), if score was set without slideracc.
|
||||
/// </summary>
|
||||
private double? calculateTotalDeviation(OsuDifficultyAttributes attributes)
|
||||
{
|
||||
if (totalSuccessfulHits == 0)
|
||||
return null;
|
||||
|
||||
int accuracyObjectCount = attributes.HitCircleCount;
|
||||
|
||||
if (!usingClassicSliderAccuracy)
|
||||
accuracyObjectCount += attributes.SliderCount;
|
||||
|
||||
// Assume worst case: all mistakes was on accuracy objects
|
||||
int relevantCountMiss = Math.Min(countMiss, accuracyObjectCount);
|
||||
int relevantCountMeh = Math.Min(countMeh, accuracyObjectCount - relevantCountMiss);
|
||||
int relevantCountOk = Math.Min(countOk, accuracyObjectCount - relevantCountMiss - relevantCountMeh);
|
||||
int relevantCountGreat = Math.Max(0, accuracyObjectCount - relevantCountMiss - relevantCountMeh - relevantCountOk);
|
||||
|
||||
// Calculate deviation on accuracy objects
|
||||
double? deviation = calculateDeviation(attributes, relevantCountGreat, relevantCountOk, relevantCountMeh, relevantCountMiss);
|
||||
if (deviation == null)
|
||||
return null;
|
||||
|
||||
if (!usingClassicSliderAccuracy)
|
||||
return deviation.Value;
|
||||
|
||||
// If score was set without slider accuracy - also compute deviation with sliders
|
||||
// Assume that all hits was 50s
|
||||
int totalCountWithSliders = attributes.HitCircleCount + attributes.SliderCount;
|
||||
int missCountWithSliders = Math.Min(totalCountWithSliders, countMiss);
|
||||
int hitCountWithSliders = totalCountWithSliders - missCountWithSliders;
|
||||
|
||||
double hitProbabilityWithSliders = hitCountWithSliders / (totalCountWithSliders + 1.0);
|
||||
double deviationWithSliders = attributes.MehHitWindow / (Math.Sqrt(2) * SpecialFunctions.ErfInv(hitProbabilityWithSliders));
|
||||
|
||||
// Min is needed for edgecase maps with 1 circle and 999 sliders, as deviation on sliders can be lower in this case
|
||||
return Math.Min(deviation.Value, deviationWithSliders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Estimates player's deviation on speed notes using <see cref="calculateDeviation"/>, assuming worst-case.
|
||||
/// Treats all speed notes as hit circles.
|
||||
@ -412,8 +459,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
const double scale = 50;
|
||||
double adjustedSpeedValue = scale * (Math.Log((speedValue - excessSpeedDifficultyCutoff) / scale + 1) + excessSpeedDifficultyCutoff / scale);
|
||||
|
||||
// 200 UR and less are considered tapped correctly to ensure that normal scores will be punished as little as possible
|
||||
double lerp = 1 - Math.Clamp((speedDeviation.Value - 20) / (24 - 20), 0, 1);
|
||||
// 220 UR and less are considered tapped correctly to ensure that normal scores will be punished as little as possible
|
||||
double lerp = 1 - DifficultyCalculationUtils.ReverseLerp(speedDeviation.Value, 22.0, 27.0);
|
||||
adjustedSpeedValue = double.Lerp(adjustedSpeedValue, speedValue, lerp);
|
||||
|
||||
return adjustedSpeedValue / speedValue;
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
|
||||
private double currentStrain;
|
||||
|
||||
private double skillMultiplier => 25.18;
|
||||
private double skillMultiplier => 25.7;
|
||||
private double strainDecayBase => 0.15;
|
||||
|
||||
private readonly List<double> sliderStrains = new List<double>();
|
||||
|
Loading…
x
Reference in New Issue
Block a user