diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index 09424e741a..a9640e5791 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -62,6 +62,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
[JsonProperty("flashlight_difficulty")]
public double FlashlightDifficulty { get; set; }
+ ///
+ /// The difficulty corresponding to the flashlight skill with HD (used in capping cognition performance).
+ ///
+ [JsonProperty("hidden_flashlight_difficulty")]
+ public double HiddenFlashlightDifficulty { get; set; }
+
///
/// Describes how much of is contributed to by hitcircles or sliders.
/// A value closer to 1.0 indicates most of is contributed by hitcircles.
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index c04455f232..b16ad5a21f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -47,6 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double readingHighARRating = Math.Sqrt(skills[5].DifficultyValue()) * DIFFICULTY_MULTIPLIER;
double readingSlidersRating = 0;
double hiddenRating = Math.Sqrt(skills[6].DifficultyValue()) * DIFFICULTY_MULTIPLIER;
+ double hiddenFlashlightRating = Math.Sqrt(skills[7].DifficultyValue()) * DIFFICULTY_MULTIPLIER;
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
@@ -69,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Cognition
double baseFlashlightPerformance = 0.0;
if (mods.Any(h => h is OsuModFlashlight))
- baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 25.0;
+ baseFlashlightPerformance = Flashlight.DifficultyToPerformance(flashlightRating);
double baseReadingLowARPerformance = ReadingLowAR.DifficultyToPerformance(readingLowARRating);
double baseReadingHighARPerformance = OsuStrainSkill.DifficultyToPerformance(readingHighARRating);
@@ -79,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double baseReadingHiddenPerformance = 0;
if (mods.Any(h => h is OsuModHidden))
- baseReadingHiddenPerformance = Math.Pow(hiddenRating, 2.0) * 25.0;
+ baseReadingHiddenPerformance = ReadingHidden.DifficultyToPerformance(hiddenRating);
double baseReadingSliderPerformance = 0;
double baseReadingNonARPerformance = baseReadingHiddenPerformance + baseReadingSliderPerformance;
@@ -96,8 +97,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Limit cognition by full memorisation difficulty
double cognitionPerformance = Math.Pow(Math.Pow(baseFlashlightARPerformance, SUM_POWER) + Math.Pow(baseReadingNonARPerformance, SUM_POWER), 1.0 / SUM_POWER);
double mechanicalPerformance = Math.Pow(Math.Pow(baseAimPerformance, SUM_POWER) + Math.Pow(baseSpeedPerformance, SUM_POWER), 1.0 / SUM_POWER);
- double potentialFlashlightPerformance = OsuPerformanceCalculator.ComputePerfectFlashlightValue(flashlightRating, hitCirclesCount + sliderCount);
- cognitionPerformance = OsuPerformanceCalculator.AdjustCognitionPerformance(cognitionPerformance, mechanicalPerformance, potentialFlashlightPerformance);
+
+ double maxHiddenFlashlightPerformance = OsuPerformanceCalculator.ComputePerfectFlashlightValue(hiddenFlashlightRating, hitCirclesCount + sliderCount);
+
+ cognitionPerformance = OsuPerformanceCalculator.AdjustCognitionPerformance(cognitionPerformance, mechanicalPerformance, maxHiddenFlashlightPerformance);
double basePerformance =
Math.Pow(
@@ -115,8 +118,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate;
- //var test = ((ReadingHighAR)skills[5]).GetAimSpeed();
-
OsuDifficultyAttributes attributes = new OsuDifficultyAttributes
{
StarRating = starRating,
@@ -129,6 +130,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
ReadingDifficultySliders = readingSlidersRating,
HiddenDifficulty = hiddenRating,
FlashlightDifficulty = flashlightRating,
+ HiddenFlashlightDifficulty = hiddenFlashlightRating,
SliderFactor = sliderFactor,
ApproachRate = IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, 1800, 1200, 450),
OverallDifficulty = (80 - hitWindowGreat) / 6,
@@ -168,6 +170,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
new ReadingLowAR(mods),
new ReadingHighAR(mods),
new ReadingHidden(mods),
+ new HiddenFlashlight(mods),
};
if (mods.Any(h => h is OsuModFlashlight))
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 23045f4331..8e3ab1a2ba 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -69,8 +69,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Cognition
+ // Preferably this value should be used for capping low AR reading performance
+ // Because it should have lower cap (you can actually see)
+ // But it will make formula really weird and overcomplicated
double potentialFlashlightValue = computeFlashlightValue(score, osuAttributes);
+ // Get HDFL value for capping reading performance
+ double potentialHiddenFlashlightValue = computeFlashlightValue(score, osuAttributes, true);
+
double flashlightValue = potentialFlashlightValue;
if (!score.Mods.Any(h => h is OsuModFlashlight))
flashlightValue = 0.0;
@@ -91,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double readingNonARValue = readingHDValue + readingSlidersValue;
double cognitionValue = Math.Pow(Math.Pow(flashlightARValue, power) + Math.Pow(readingNonARValue, power), 1.0 / power);
- cognitionValue = AdjustCognitionPerformance(cognitionValue, mechanicalValue, potentialFlashlightValue);
+ cognitionValue = AdjustCognitionPerformance(cognitionValue, mechanicalValue, potentialHiddenFlashlightValue);
double accuracyValue = computeAccuracyValue(score, osuAttributes);
@@ -222,9 +228,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return accuracyValue;
}
- private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes attributes)
+ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes attributes, bool alwaysUseHD = false)
{
- double flashlightValue = Math.Pow(attributes.FlashlightDifficulty, 2.0) * 25.0;
+ double flashlightValue = Math.Pow(alwaysUseHD ? attributes.HiddenFlashlightDifficulty : attributes.FlashlightDifficulty, 2.0) * 25.0;
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
@@ -246,7 +252,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
public static double ComputePerfectFlashlightValue(double flashlightDifficulty, int objectsCount)
{
- double flashlightValue = Math.Pow(flashlightDifficulty, 2.0) * 25.0;
+ double flashlightValue = Flashlight.DifficultyToPerformance(flashlightDifficulty);
flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, objectsCount / 200.0) +
(objectsCount > 200 ? 0.2 * Math.Min(1.0, (objectsCount - 200) / 200.0) : 0.0);
@@ -341,8 +347,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return 0.0;
double rawReading = attributes.HiddenDifficulty;
- //double readingValue = Math.Pow(rawReading, 2.0) * 25.0;
- double readingValue = Math.Pow(rawReading, 2.0) * 25.0;
+ double readingValue = ReadingHidden.DifficultyToPerformance(attributes.HiddenDifficulty);
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
@@ -386,7 +391,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Avoid it being broken on millions of pp, ruins it being continious, but it will never happen on normal circumstances
if (capPerformance > 10000 || cognitionPerformance > 10000) cognitionPerformance = Math.Min(capPerformance, cognitionPerformance);
- else cognitionPerformance = 100 * softmin(capPerformance / 100, cognitionPerformance / 100, 100);
+ else cognitionPerformance = 1000 * softmin(capPerformance / 1000, cognitionPerformance / 1000, 100);
return cognitionPerformance;
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 19478cd753..e1431d6978 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -263,12 +263,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
}
double fadeInStartTime = BaseObject.StartTime - BaseObject.TimePreempt;
- double fadeInDuration = BaseObject.TimeFadeIn;
+ double fadeInDuration = BaseObject.TimeFadeInRaw;
if (hidden)
{
// Taken from OsuModHidden.
- double fadeOutStartTime = BaseObject.StartTime - BaseObject.TimePreempt + BaseObject.TimeFadeIn;
+ double fadeOutStartTime = BaseObject.StartTime - BaseObject.TimePreempt + BaseObject.TimeFadeInRaw;
double fadeOutDuration = BaseObject.TimePreempt * OsuModHidden.FADE_OUT_DURATION_MULTIPLIER;
return Math.Min
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
index 3d6d3f99c1..9fafeacb9c 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
@@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
public class Flashlight : StrainSkill
{
private readonly bool hasHiddenMod;
+ protected virtual bool HasHiddenMod => hasHiddenMod;
public Flashlight(Mod[] mods)
: base(mods)
@@ -36,11 +37,23 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double StrainValueAt(DifficultyHitObject current)
{
currentStrain *= strainDecay(current.DeltaTime);
- currentStrain += FlashlightEvaluator.EvaluateDifficultyOf(current, hasHiddenMod) * skillMultiplier;
+ currentStrain += FlashlightEvaluator.EvaluateDifficultyOf(current, HasHiddenMod) * skillMultiplier;
return currentStrain;
}
public override double DifficultyValue() => GetCurrentStrainPeaks().Sum() * OsuStrainSkill.DEFAULT_DIFFICULTY_MULTIPLIER;
+
+ public static double DifficultyToPerformance(double difficulty) => Math.Pow(difficulty, 2) * 25.0;
+ }
+
+ public class HiddenFlashlight : Flashlight
+ {
+ protected override bool HasHiddenMod => true;
+
+ public HiddenFlashlight(Mod[] mods)
+ : base(mods)
+ {
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs
index 592db63144..dcfe624db2 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs
@@ -113,5 +113,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
return currentStrain;
}
+
+ public static double DifficultyToPerformance(double difficulty) => Math.Pow(difficulty, 2) * 25.0;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 74631400ca..84487ec0bd 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -49,6 +49,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double TimePreempt = 600;
public double TimeFadeIn = 400;
+ public double TimeFadeInRaw = 400;
private HitObjectProperty position;
@@ -165,6 +166,7 @@ namespace osu.Game.Rulesets.Osu.Objects
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
// This adjustment is necessary for AR>10, otherwise TimePreempt can become smaller leading to hitcircles not fully fading in.
TimeFadeIn = 400 * Math.Min(1, TimePreempt / PREEMPT_MIN);
+ TimeFadeInRaw = TimeFadeIn;
Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize, true);
}