mirror of
https://github.com/ppy/osu.git
synced 2026-06-06 23:24:53 +08:00
Rebalance HD bonus (#33237)
* initial commit * changed HD curve * removed AR variable * update for new rework * nerf HD acc bonus for AR>10 * add another HD nerf for AR>10 * Update OsuDifficultyCalculator.cs * fix speed part being missing * Update OsuDifficultyCalculator.cs * rework to difficulty-based high AR nerf * move TC back to perfcalc * fix nvicka * fix comment * use utils function instead of manual one * Clean up * Use "visibility" term instead * Store `mechanicalDifficultyRating` field * Rename `isFullyHidden` to `isAlwaysPartiallyVisible` and clarify intent * Remove redundant comment * Add `calculateDifficultyRating` method --------- Co-authored-by: James Wilson <tsunyoku@gmail.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
ace74824b8
commit
01d9c526d9
@@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Skills;
|
||||
@@ -27,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
public override int Version => 20250306;
|
||||
|
||||
private double mechanicalDifficultyRating;
|
||||
|
||||
public OsuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
@@ -42,6 +45,30 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a visibility bonus that is applicable to Hidden and Traceable.
|
||||
/// </summary>
|
||||
public static double CalculateVisibilityBonus(Mod[] mods, double approachRate, double visibilityFactor = 1)
|
||||
{
|
||||
// NOTE: TC's effect is only noticeable in performance calculations until lazer mods are accounted for server-side.
|
||||
bool isAlwaysPartiallyVisible = mods.OfType<OsuModHidden>().Any(m => !m.OnlyFadeApproachCircles.Value) || mods.OfType<OsuModTraceable>().Any();
|
||||
|
||||
// Start from normal curve, rewarding lower AR up to AR5
|
||||
double readingBonus = 0.04 * (12.0 - Math.Max(approachRate, 5));
|
||||
|
||||
readingBonus *= visibilityFactor;
|
||||
|
||||
// For AR up to 0 - reduce reward for very low ARs when object is visible
|
||||
if (approachRate < 5)
|
||||
readingBonus += (isAlwaysPartiallyVisible ? 0.04 : 0.03) * (5.0 - Math.Max(approachRate, 0));
|
||||
|
||||
// Starting from AR0 - cap values so they won't grow to infinity
|
||||
if (approachRate < 0)
|
||||
readingBonus += (isAlwaysPartiallyVisible ? 0.1 : 0.075) * (1 - Math.Pow(1.5, approachRate));
|
||||
|
||||
return readingBonus;
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
if (beatmap.HitObjects.Count == 0)
|
||||
@@ -85,9 +112,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
double drainRate = beatmap.Difficulty.DrainRate;
|
||||
|
||||
double aimRating = computeAimRating(aim.DifficultyValue(), mods, totalHits, approachRate, overallDifficulty);
|
||||
double aimRatingNoSliders = computeAimRating(aimWithoutSliders.DifficultyValue(), mods, totalHits, approachRate, overallDifficulty);
|
||||
double speedRating = computeSpeedRating(speed.DifficultyValue(), mods, totalHits, approachRate, overallDifficulty);
|
||||
double aimDifficultyValue = aim.DifficultyValue();
|
||||
double aimNoSlidersDifficultyValue = aimWithoutSliders.DifficultyValue();
|
||||
double speedDifficultyValue = speed.DifficultyValue();
|
||||
|
||||
mechanicalDifficultyRating = calculateMechanicalDifficultyRating(aimDifficultyValue, speedDifficultyValue);
|
||||
|
||||
double aimRating = computeAimRating(aimDifficultyValue, mods, totalHits, approachRate, overallDifficulty);
|
||||
double aimRatingNoSliders = computeAimRating(aimNoSlidersDifficultyValue, mods, totalHits, approachRate, overallDifficulty);
|
||||
double speedRating = computeSpeedRating(speedDifficultyValue, mods, totalHits, approachRate, overallDifficulty);
|
||||
|
||||
double flashlightRating = 0.0;
|
||||
|
||||
@@ -108,10 +141,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
);
|
||||
|
||||
double multiplier = CalculateDifficultyMultiplier(mods, totalHits, spinnerCount);
|
||||
|
||||
double starRating = basePerformance > 0.00001
|
||||
? Math.Cbrt(multiplier) * star_rating_multiplier * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4)
|
||||
: 0;
|
||||
double starRating = calculateStarRating(basePerformance, multiplier);
|
||||
|
||||
double sliderNestedScorePerObject = LegacyScoreUtils.CalculateNestedScorePerObject(beatmap, totalHits);
|
||||
double legacyScoreBaseMultiplier = LegacyScoreUtils.CalculateDifficultyPeppyStars(beatmap);
|
||||
@@ -151,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
if (mods.Any(m => m is OsuModAutopilot))
|
||||
return 0;
|
||||
|
||||
double aimRating = Math.Sqrt(aimDifficultyValue) * difficulty_multiplier;
|
||||
double aimRating = calculateDifficultyRating(aimDifficultyValue);
|
||||
|
||||
if (mods.Any(m => m is OsuModTouchDevice))
|
||||
aimRating = Math.Pow(aimRating, 0.8);
|
||||
@@ -183,8 +213,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
if (mods.Any(m => m is OsuModHidden))
|
||||
{
|
||||
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
||||
ratingMultiplier *= 1.0 + 0.04 * (12.0 - approachRate);
|
||||
double visibilityFactor = calculateAimVisibilityFactor(approachRate);
|
||||
ratingMultiplier *= 1.0 + CalculateVisibilityBonus(mods, approachRate, visibilityFactor);
|
||||
}
|
||||
|
||||
// It is important to consider accuracy difficulty when scaling with accuracy.
|
||||
@@ -198,7 +228,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
if (mods.Any(m => m is OsuModRelax))
|
||||
return 0;
|
||||
|
||||
double speedRating = Math.Sqrt(speedDifficultyValue) * difficulty_multiplier;
|
||||
double speedRating = calculateDifficultyRating(speedDifficultyValue);
|
||||
|
||||
if (mods.Any(m => m is OsuModAutopilot))
|
||||
speedRating *= 0.5;
|
||||
@@ -226,8 +256,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
if (mods.Any(m => m is OsuModHidden))
|
||||
{
|
||||
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
||||
ratingMultiplier *= 1.0 + 0.04 * (12.0 - approachRate);
|
||||
double visibilityFactor = calculateSpeedVisibilityFactor(approachRate);
|
||||
ratingMultiplier *= 1.0 + CalculateVisibilityBonus(mods, approachRate, visibilityFactor);
|
||||
}
|
||||
|
||||
ratingMultiplier *= 0.95 + Math.Pow(Math.Max(0, overallDifficulty), 2) / 750;
|
||||
@@ -240,7 +270,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
if (!mods.Any(m => m is OsuModFlashlight))
|
||||
return 0;
|
||||
|
||||
double flashlightRating = Math.Sqrt(flashlightDifficultyValue) * difficulty_multiplier;
|
||||
double flashlightRating = calculateDifficultyRating(flashlightDifficultyValue);
|
||||
|
||||
if (mods.Any(m => m is OsuModTouchDevice))
|
||||
flashlightRating = Math.Pow(flashlightRating, 0.8);
|
||||
@@ -268,6 +298,46 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
return flashlightRating * Math.Sqrt(ratingMultiplier);
|
||||
}
|
||||
|
||||
private double calculateAimVisibilityFactor(double approachRate)
|
||||
{
|
||||
const double ar_factor_end_point = 11.5;
|
||||
|
||||
double mechanicalDifficultyFactor = DifficultyCalculationUtils.ReverseLerp(mechanicalDifficultyRating, 5, 10);
|
||||
double arFactorStartingPoint = double.Lerp(9, 10.33, mechanicalDifficultyFactor);
|
||||
|
||||
return DifficultyCalculationUtils.ReverseLerp(approachRate, ar_factor_end_point, arFactorStartingPoint);
|
||||
}
|
||||
|
||||
private double calculateSpeedVisibilityFactor(double approachRate)
|
||||
{
|
||||
const double ar_factor_end_point = 11.5;
|
||||
|
||||
double mechanicalDifficultyFactor = DifficultyCalculationUtils.ReverseLerp(mechanicalDifficultyRating, 5, 10);
|
||||
double arFactorStartingPoint = double.Lerp(10, 10.33, mechanicalDifficultyFactor);
|
||||
|
||||
return DifficultyCalculationUtils.ReverseLerp(approachRate, ar_factor_end_point, arFactorStartingPoint);
|
||||
}
|
||||
|
||||
private static double calculateMechanicalDifficultyRating(double aimDifficultyValue, double speedDifficultyValue)
|
||||
{
|
||||
double aimValue = OsuStrainSkill.DifficultyToPerformance(calculateDifficultyRating(aimDifficultyValue));
|
||||
double speedValue = OsuStrainSkill.DifficultyToPerformance(calculateDifficultyRating(speedDifficultyValue));
|
||||
|
||||
double totalValue = Math.Pow(Math.Pow(aimValue, 1.1) + Math.Pow(speedValue, 1.1), 1 / 1.1);
|
||||
|
||||
return calculateStarRating(totalValue, performance_base_multiplier);
|
||||
}
|
||||
|
||||
private static double calculateStarRating(double basePerformance, double multiplier)
|
||||
{
|
||||
if (basePerformance <= 0.00001)
|
||||
return 0;
|
||||
|
||||
return Math.Cbrt(multiplier) * star_rating_multiplier * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4);
|
||||
}
|
||||
|
||||
private static double calculateDifficultyRating(double difficultyValue) => Math.Sqrt(difficultyValue) * difficulty_multiplier;
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||
{
|
||||
List<DifficultyHitObject> objects = new List<DifficultyHitObject>();
|
||||
|
||||
@@ -210,8 +210,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
aimValue *= 1.3 + (totalHits * (0.0016 / (1 + 2 * effectiveMissCount)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * attributes.DrainRate * attributes.DrainRate);
|
||||
else if (score.Mods.Any(m => m is OsuModTraceable))
|
||||
{
|
||||
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
||||
aimValue *= 1.0 + 0.04 * (12.0 - approachRate);
|
||||
aimValue *= 1.0 + OsuDifficultyCalculator.CalculateVisibilityBonus(score.Mods, approachRate);
|
||||
}
|
||||
|
||||
aimValue *= accuracy;
|
||||
@@ -244,8 +243,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
}
|
||||
else if (score.Mods.Any(m => m is OsuModTraceable))
|
||||
{
|
||||
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
||||
speedValue *= 1.0 + 0.04 * (12.0 - approachRate);
|
||||
speedValue *= 1.0 + OsuDifficultyCalculator.CalculateVisibilityBonus(score.Mods, approachRate);
|
||||
}
|
||||
|
||||
double speedHighDeviationMultiplier = calculateSpeedHighDeviationNerf(attributes);
|
||||
@@ -295,7 +293,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
if (score.Mods.Any(m => m is OsuModBlinds))
|
||||
accuracyValue *= 1.14;
|
||||
else if (score.Mods.Any(m => m is OsuModHidden || m is OsuModTraceable))
|
||||
accuracyValue *= 1.08;
|
||||
{
|
||||
// Decrease bonus for AR > 10
|
||||
accuracyValue *= 1 + 0.08 * Math.Clamp((11.5 - approachRate) / (11.5 - 10), 0, 1);
|
||||
}
|
||||
|
||||
if (score.Mods.Any(m => m is OsuModFlashlight))
|
||||
accuracyValue *= 1.02;
|
||||
|
||||
Reference in New Issue
Block a user