1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-15 00:53:10 +08:00

Alternating angle nerf

Fixed alternating angle (fiery jumps for example) gaining too much unfair reading pp
Reworked similar-angle nerf as a whole
Normalised global pp multiplier
This commit is contained in:
Givikap120 2024-01-28 23:51:56 +02:00
parent 7c294c864f
commit 51eb5c0a01
4 changed files with 108 additions and 32 deletions

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
private const double overlap_multiplier = 0.8;
private static double calculateDenstityOf(OsuDifficultyHitObject currObj)
public static double CalculateDenstityOf(OsuDifficultyHitObject currObj)
{
double pastObjectDifficultyInfluence = 0;
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
return pastObjectDifficultyInfluence;
}
private static double calculateOverlapDifficultyOf(OsuDifficultyHitObject currObj)
public static double CalculateOverlapDifficultyOf(OsuDifficultyHitObject currObj)
{
double screenOverlapDifficulty = 0;
@ -62,15 +62,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
var currObj = (OsuDifficultyHitObject)current;
double pastObjectDifficultyInfluence = calculateDenstityOf(currObj);
double screenOverlapDifficulty = calculateOverlapDifficultyOf(currObj);
double pastObjectDifficultyInfluence = CalculateDenstityOf(currObj);
double screenOverlapDifficulty = CalculateOverlapDifficultyOf(currObj);
double difficulty = Math.Pow(4 * Math.Log(Math.Max(1, pastObjectDifficultyInfluence)), 2.3);
screenOverlapDifficulty = Math.Max(0, screenOverlapDifficulty - 0.5); // make overlap value =1 cost significantly less
difficulty *= getConstantAngleNerfFactor(currObj);
difficulty *= 1 + overlap_multiplier * screenOverlapDifficulty;
double overlapBonus = overlap_multiplier * screenOverlapDifficulty * difficulty;
difficulty *= getConstantAngleNerfFactor(currObj);
difficulty += overlapBonus;
//difficulty *= 1 + overlap_multiplier * screenOverlapDifficulty;
return difficulty;
}
@ -103,12 +107,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double hdDifficulty = 0;
double timeSpentInvisible = getDurationSpentInvisible(currObj) / currObj.ClockRate;
double timeDifficultyFactor = calculateDenstityOf(currObj) / 1000;
double density = 1 + Math.Max(0, CalculateDenstityOf(currObj) - 1);
density *= getConstantAngleNerfFactor(currObj);
double timeDifficultyFactor = density / 1000;
double visibleObjectFactor = Math.Clamp(retrieveCurrentVisibleObjects(currObj).Count - 2, 0, 15);
hdDifficulty += Math.Pow(visibleObjectFactor * timeSpentInvisible * timeDifficultyFactor, 1) +
(8 + visibleObjectFactor) * aimDifficulty;
(6 + visibleObjectFactor) * aimDifficulty;
hdDifficulty *= 0.95 + 0.15 * EvaluateInpredictabilityOf(current); // Max multiplier is 1.1
@ -175,13 +183,28 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
lastDelta = Math.Max(0, lastDelta);
}
rhythmChangeBonus = 1 - Math.Min(currDelta, lastDelta) / Math.Max(currDelta, lastDelta);
rhythmChangeBonus = getRhythmDifference(currDelta, lastDelta);
}
double result = velocity_change_part * velocityChangeBonus + angle_change_part * angleChangeBonus + rhythm_change_part * rhythmChangeBonus;
return result;
}
public static double EvaluateLowDensityBonusOf(DifficultyHitObject current)
{
//var currObj = (OsuDifficultyHitObject)current;
//// Density = 2 in general means 3 notes on screen (it's not including current note)
//double density = CalculateDenstityOf(currObj);
//// We are considering density = 1.5 as starting point, 1.0 is noticably uncomfy and 0.5 is severely uncomfy
//double bonus = 1.5 - density;
//if (bonus <= 0) return 0;
//return Math.Pow(bonus, 2);
return 0;
}
// Returns a list of objects that are visible on screen at
// the point in time at which the current object becomes visible.
private static IEnumerable<OsuDifficultyHitObject> retrievePastVisibleObjects(OsuDifficultyHitObject current)
@ -237,6 +260,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
int index = 0;
double currentTimeGap = 0;
OsuDifficultyHitObject prevLoopObj = current;
OsuDifficultyHitObject? prevLoopObj1 = null;
OsuDifficultyHitObject? prevLoopObj2 = null;
double prevConstantAngle = 0;
while (currentTimeGap < time_limit)
{
var loopObj = (OsuDifficultyHitObject)current.Previous(index);
@ -246,18 +276,52 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double longIntervalFactor = Math.Clamp(1 - (loopObj.StrainTime - time_limit_low) / (time_limit - time_limit_low), 0, 1);
if (loopObj.Angle.IsNotNull() && current.Angle.IsNotNull())
if (loopObj.Angle.IsNotNull() && prevLoopObj.Angle.IsNotNull())
{
double angleDifference = Math.Abs(current.Angle.Value - loopObj.Angle.Value);
constantAngleCount += Math.Cos(4 * Math.Min(Math.PI / 8, angleDifference)) * longIntervalFactor;
double angleDifference = Math.Abs(prevLoopObj.Angle.Value - loopObj.Angle.Value);
// Nerf alternating angles case
if (prevLoopObj1.IsNotNull() && prevLoopObj2.IsNotNull() && prevLoopObj1.Angle.IsNotNull() && prevLoopObj2.Angle.IsNotNull())
{
// Normalized difference
double angleDifference1 = Math.Abs(prevLoopObj1.Angle.Value - loopObj.Angle.Value) / Math.PI;
double angleDifference2 = Math.Abs(prevLoopObj2.Angle.Value - prevLoopObj.Angle.Value) / Math.PI;
// Will be close to 1 if angleDifference1 and angleDifference2 was both close to 0
double alternatingFactor = Math.Pow((1 - angleDifference1) * (1 - angleDifference2), 2);
// Be sure to nerf only same rhythms
double rhythmFactor = 1 - getRhythmDifference(loopObj.StrainTime, prevLoopObj.StrainTime); // 0 on different rhythm, 1 on same rhythm
rhythmFactor *= 1 - getRhythmDifference(prevLoopObj.StrainTime, prevLoopObj1.StrainTime);
rhythmFactor *= 1 - getRhythmDifference(prevLoopObj1.StrainTime, prevLoopObj2.StrainTime);
double acuteAngleFactor = 1 - Math.Min(loopObj.Angle.Value, prevLoopObj.Angle.Value) / Math.PI;
double prevAngleAdjust = Math.Max(angleDifference - angleDifference1, 0);
prevAngleAdjust *= alternatingFactor; // Nerf if alternating
prevAngleAdjust *= rhythmFactor; // Nerf if same rhythms
prevAngleAdjust *= acuteAngleFactor;
angleDifference -= prevAngleAdjust;
}
double currConstantAngle = Math.Cos(4 * Math.Min(Math.PI / 8, angleDifference)) * longIntervalFactor;
constantAngleCount += Math.Min(currConstantAngle, prevConstantAngle);
prevConstantAngle = currConstantAngle;
}
currentTimeGap = current.StartTime - loopObj.StartTime;
index++;
prevLoopObj2 = prevLoopObj1;
prevLoopObj1 = prevLoopObj;
prevLoopObj = loopObj;
}
return Math.Pow(Math.Min(1, 2 / constantAngleCount), 2);
}
private static double getTimeNerfFactor(double deltaTime)
{
return Math.Clamp(2 - deltaTime / (reading_window_size / 2), 0, 1);
@ -270,6 +334,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
value = softmin(value, 2, 1.7); // use softmin to achieve full-memory cap, 2 times more than AR11 (300ms)
return value;
}
private static double getRhythmDifference(double t1, double t2) => 1 - Math.Min(t1, t2) / Math.Max(t1, t2);
private static double logistic(double x) => 1 / (1 + Math.Exp(-x));
// We are using mutiply and divide instead of add and subtract, so values won't be negative

View File

@ -22,10 +22,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public class OsuDifficultyCalculator : DifficultyCalculator
{
private const double difficulty_multiplier = 0.0675;
private const double difficulty_multiplier = 0.067;
public override int Version => 20220902;
public static double SumPower => 1.1;
public static double FLSumPower => 1.6;
public OsuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
@ -75,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double baseReadingHighARPerformance = Math.Pow(5 * Math.Max(1, readingHighARRating / 0.0675) - 4, 3) / 100000;
double baseReadingARPerformance = Math.Pow(Math.Pow(baseReadingLowARPerformance, SumPower) + Math.Pow(baseReadingHighARPerformance, SumPower), 1.0 / SumPower);
double baseFlashlightARPerformance = Math.Pow(Math.Pow(baseFlashlightPerformance, 1.8) + Math.Pow(baseReadingARPerformance, 1.8), 1.0 / 1.8);
double baseFlashlightARPerformance = Math.Pow(Math.Pow(baseFlashlightPerformance, FLSumPower) + Math.Pow(baseReadingARPerformance, FLSumPower), 1.0 / FLSumPower);
double baseReadingHiddenPerformance = 0;
if (mods.Any(h => h is OsuModHidden))

View File

@ -16,8 +16,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
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.
public static double SumPower => 1.1; // Maybe it should just use OsuDifficultyCalculator SumPower
private double accuracy;
private int scoreMaxCombo;
private int countGreat;
@ -64,9 +62,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
effectiveMissCount = Math.Min(effectiveMissCount + countOk * okMultiplier + countMeh * mehMultiplier, totalHits);
}
double power = OsuDifficultyCalculator.SumPower;
double aimValue = computeAimValue(score, osuAttributes);
double speedValue = computeSpeedValue(score, osuAttributes);
double mechanicalValue = Math.Pow(Math.Pow(aimValue, SumPower) + Math.Pow(speedValue, SumPower), 1.0 / SumPower);
double mechanicalValue = Math.Pow(Math.Pow(aimValue, power) + Math.Pow(speedValue, power), 1.0 / power);
// Cognition
@ -77,19 +77,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty
flashlightValue = 0.0;
double readingARValue = computeReadingARValue(score, osuAttributes);
// Reduce AR reading bonus if FL is present
double flashlightARValue = Math.Pow(Math.Pow(flashlightValue, 1.8) + Math.Pow(readingARValue, 1.8), 1.0 / 1.8);
double flPower = OsuDifficultyCalculator.FLSumPower;
double flashlightARValue = Math.Pow(Math.Pow(flashlightValue, flPower) + Math.Pow(readingARValue, flPower), 1.0 / flPower);
double readingNonARValue = computeReadingNonARValue(score, osuAttributes);
double cognitionValue = Math.Pow(Math.Pow(flashlightARValue, SumPower) + Math.Pow(readingNonARValue, SumPower), 1.0 / SumPower);
double cognitionValue = Math.Pow(Math.Pow(flashlightARValue, power) + Math.Pow(readingNonARValue, power), 1.0 / power);
cognitionValue = AdjustCognitionPerformance(cognitionValue, mechanicalValue, potentialFlashlightValue);
double accuracyValue = computeAccuracyValue(score, osuAttributes);
double totalValue =
Math.Pow(
Math.Pow(mechanicalValue, SumPower) +
Math.Pow(cognitionValue, SumPower) +
Math.Pow(accuracyValue, SumPower), 1.0 / SumPower
Math.Pow(mechanicalValue, power) +
Math.Pow(cognitionValue, power) +
Math.Pow(accuracyValue, power), 1.0 / power
) * multiplier;
return new OsuPerformanceAttributes
@ -247,9 +248,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private double computeReadingARValue(ScoreInfo score, OsuDifficultyAttributes attributes)
{
//double readingARValue = Math.Max(computeReadingLowARValue(score, attributes), computeReadingHighARValue(score, attributes));
double power = OsuDifficultyCalculator.SumPower;
double readingValue = Math.Pow(
Math.Pow(computeReadingLowARValue(score, attributes), SumPower) +
Math.Pow(computeReadingHighARValue(score, attributes), SumPower), 1.0 / SumPower);
Math.Pow(computeReadingLowARValue(score, attributes), power) +
Math.Pow(computeReadingHighARValue(score, attributes), power), 1.0 / power);
return readingValue;
}

View File

@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
public class ReadingLowAR : GraphSkill
{
private readonly List<double> difficulties = new List<double>();
//private double skillMultiplier => 5.5;
private double skillMultiplier => 2.3;
//private double skillMultiplier => 2.3;
private double skillMultiplier => 2;
public ReadingLowAR(Mod[] mods)
: base(mods)
@ -132,7 +132,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
private double currentStrain;
// private double currentRhythm;
private double skillMultiplier => 13;
//private double skillMultiplier => 13;
private double skillMultiplier => 14;
private double strainDecayBase => 0.15;
private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
@ -145,18 +146,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
double aimDifficulty = AimEvaluator.EvaluateDifficultyOf(current, true, ((OsuDifficultyHitObject)current).Preempt);
aimDifficulty *= ReadingEvaluator.EvaluateHighARDifficultyOf(current, true);
currentStrain += aimDifficulty * skillMultiplier;
// currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);
aimDifficulty *= skillMultiplier;
double totalStrain = currentStrain;
currentStrain += aimDifficulty;
// Warning: this line is unstable, so increasing amount of objects can decrease pp
totalStrain += aimDifficulty * (1 + ReadingEvaluator.EvaluateLowDensityBonusOf(current));
//Console.WriteLine($"{current.StartTime} - {ReadingEvaluator.EvaluateLowDensityBonusOf(current)}");
return totalStrain;
}
}
public class HighARSpeedComponent : OsuStrainSkill
{
private double skillMultiplier => 650;
private double skillMultiplier => 675;
private double strainDecayBase => 0.3;
private double currentStrain;
@ -197,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
}
private readonly List<double> difficulties = new List<double>();
private double skillMultiplier => 2.1;
private double skillMultiplier => 2.3;
public override void Process(DifficultyHitObject current)
{