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:
parent
7c294c864f
commit
51eb5c0a01
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user