diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index 12f4540924..dc803af46f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -61,9 +61,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
[JsonProperty("low_ar_difficult_strain_count")]
public double LowArDifficultStrainCount { get; set; }
- [JsonProperty("hidden_difficult_strain_count")]
- public double HiddenDifficultStrainCount { get; set; }
-
///
/// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
///
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 4922002ec7..3847ed6a8e 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public class OsuDifficultyCalculator : DifficultyCalculator
{
- public const double DIFFICULTY_MULTIPLIER = 0.0675;
+ private const double difficulty_multiplier = 0.0675;
public const double SUM_POWER = 1.1;
public const double FL_SUM_POWER = 1.5;
public override int Version => 20241007;
@@ -37,17 +37,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (beatmap.HitObjects.Count == 0)
return new OsuDifficultyAttributes { Mods = mods };
- double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * DIFFICULTY_MULTIPLIER;
- double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * DIFFICULTY_MULTIPLIER;
- double speedRating = Math.Sqrt(skills.OfType().First().DifficultyValue()) * DIFFICULTY_MULTIPLIER;
+ double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
+ double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
+ double speedRating = Math.Sqrt(skills.OfType().First().DifficultyValue()) * difficulty_multiplier;
double speedNotes = skills.OfType().First().RelevantNoteCount();
- double flashlightRating = Math.Sqrt(skills.OfType().First().DifficultyValue()) * DIFFICULTY_MULTIPLIER;
- double readingLowARRating = Math.Sqrt(skills.OfType().First().DifficultyValue()) * DIFFICULTY_MULTIPLIER;
+ double flashlightRating = Math.Sqrt(skills.OfType().First().DifficultyValue()) * difficulty_multiplier;
+ double readingLowARRating = Math.Sqrt(skills.OfType().First().DifficultyValue()) * difficulty_multiplier;
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
- double aimDifficultyStrainCount = skills[0].CountTopWeightedStrains();
+ double aimDifficultyStrainCount = skills.OfType().First().CountTopWeightedStrains();
double speedDifficultyStrainCount = skills.OfType().First().CountTopWeightedStrains();
double lowArDifficultyStrainCount = skills.OfType().First().CountTopWeightedStrains();
@@ -62,7 +62,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
aimRating *= 0.9;
speedRating = 0.0;
- readingLowARRating *= 0.95;
flashlightRating *= 0.7;
readingLowARRating *= 0.95;
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
index cfb8dec2b8..5ace17bdbc 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Evaluators;
@@ -20,17 +21,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
private readonly bool withSliders;
- protected double CurrentStrain;
- protected double SkillMultiplier => 25.3;
+ private double currentStrain;
- protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => CurrentStrain * StrainDecay(time - current.Previous(0).StartTime);
+ private double skillMultiplier => 25.3;
+ private double strainDecayBase => 0.15;
+
+ private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
+
+ protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => currentStrain * strainDecay(time - current.Previous(0).StartTime);
protected override double StrainValueAt(DifficultyHitObject current)
{
- CurrentStrain *= StrainDecay(current.DeltaTime);
- CurrentStrain += AimEvaluator.EvaluateDifficultyOf(current, withSliders) * SkillMultiplier;
+ currentStrain *= strainDecay(current.DeltaTime);
+ currentStrain += AimEvaluator.EvaluateDifficultyOf(current, withSliders) * skillMultiplier;
- return CurrentStrain;
+ return currentStrain;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index 367f3bee18..6823512cef 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -23,10 +23,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
protected virtual double ReducedStrainBaseline => 0.75;
- protected virtual double StrainDecayBase => 0.15;
-
- protected double StrainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
-
protected OsuStrainSkill(Mod[] mods)
: base(mods)
{
@@ -61,9 +57,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
return difficulty;
}
- ///
- /// Converts difficulty value from to base performance.
- ///
public static double DifficultyToPerformance(double difficulty) => Math.Pow(5.0 * Math.Max(1.0, difficulty / 0.0675) - 4.0, 3.0) / 100000.0;
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs
index 3a60f5843c..d51d5fdbb9 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Reading.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
private double currentDensityAimStrain = 0;
- public override void Process(DifficultyHitObject current)
+ protected override double StrainValueAt(DifficultyHitObject current)
{
double densityReadingDifficulty = ReadingEvaluator.EvaluateDifficultyOf(current);
double densityAimingFactor = ReadingEvaluator.EvaluateAimingDensityFactorOf(current);
@@ -37,22 +37,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
currentDensityAimStrain += densityAimingFactor * AimEvaluator.EvaluateDifficultyOf(current, true) * aimComponentMultiplier;
double totalDensityDifficulty = (currentDensityAimStrain + densityReadingDifficulty) * skillMultiplier;
-
- ObjectStrains.Add(totalDensityDifficulty);
-
- if (current.Index == 0)
- CurrentSectionEnd = Math.Ceiling(current.StartTime / SectionLength) * SectionLength;
-
- while (current.StartTime > CurrentSectionEnd)
- {
- StrainPeaks.Add(CurrentSectionPeak);
- CurrentSectionPeak = 0;
- CurrentSectionEnd += SectionLength;
- }
-
- CurrentSectionPeak = Math.Max(totalDensityDifficulty, CurrentSectionPeak);
+ return totalDensityDifficulty;
}
+ protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => 0;
+
private double reducedNoteCount => 5;
private double reducedNoteBaseline => 0.7;
public override double DifficultyValue()
@@ -85,15 +74,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
public static double DifficultyToPerformance(double difficulty) => Math.Max(
Math.Max(Math.Pow(difficulty, 1.5) * 20, Math.Pow(difficulty, 2) * 17.0),
Math.Max(Math.Pow(difficulty, 3) * 10.5, Math.Pow(difficulty, 4) * 6.00));
-
- protected override double StrainValueAt(DifficultyHitObject current)
- {
- throw new NotImplementedException();
- }
-
- protected override double CalculateInitialStrain(double time, DifficultyHitObject current)
- {
- throw new NotImplementedException();
- }
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
index 56b51a915e..d2c4bbb618 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
@@ -15,11 +15,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public class Speed : OsuStrainSkill
{
- protected double SkillMultiplier => 1.430;
- protected override double StrainDecayBase => 0.3;
+ private double skillMultiplier => 1.430;
+ private double strainDecayBase => 0.3;
- protected double CurrentStrain;
- protected double CurrentRhythm;
+ private double currentStrain;
+ private double currentRhythm;
protected override int ReducedSectionCount => 5;
@@ -28,17 +28,18 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
}
- protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => (CurrentStrain * CurrentRhythm) * StrainDecay(time - current.Previous(0).StartTime);
+ private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
+
+ protected override double CalculateInitialStrain(double time, DifficultyHitObject current) => (currentStrain * currentRhythm) * strainDecay(time - current.Previous(0).StartTime);
protected override double StrainValueAt(DifficultyHitObject current)
{
- OsuDifficultyHitObject currODHO = (OsuDifficultyHitObject)current;
+ currentStrain *= strainDecay(((OsuDifficultyHitObject)current).StrainTime);
+ currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current) * skillMultiplier;
- CurrentStrain *= StrainDecay(currODHO.StrainTime);
- CurrentStrain += SpeedEvaluator.EvaluateDifficultyOf(current) * SkillMultiplier;
+ currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);
- CurrentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);
- double totalStrain = CurrentStrain * CurrentRhythm;
+ double totalStrain = currentStrain * currentRhythm;
return totalStrain;
}
diff --git a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs
index 6b48fa041f..8b8892113b 100644
--- a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs
+++ b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs
@@ -1,9 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
using System.Collections.Generic;
-using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mods;
@@ -29,8 +27,6 @@ namespace osu.Game.Rulesets.Difficulty.Skills
this.mods = mods;
}
- protected List ObjectStrains = new List();
-
///
/// Process a .
///
@@ -41,23 +37,5 @@ namespace osu.Game.Rulesets.Difficulty.Skills
/// Returns the calculated difficulty value representing all s that have been processed up to this point.
///
public abstract double DifficultyValue();
-
- ///
- /// Calculates the number of strains weighted against the top strain.
- /// The result is scaled by clock rate as it affects the total number of strains.
- ///
- public virtual double CountTopWeightedStrains()
- {
- if (ObjectStrains.Count == 0)
- return 0.0;
-
- double consistentTopStrain = DifficultyValue() / 10; // What would the top strain be if all strain values were identical
-
- if (consistentTopStrain == 0)
- return ObjectStrains.Count;
-
- // Use a weighted sum of all strains. Constants are arbitrary and give nice values
- return ObjectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
- }
}
}
diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs
index 0d71ba0680..a76a0a5526 100644
--- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs
+++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs
@@ -20,21 +20,21 @@ namespace osu.Game.Rulesets.Difficulty.Skills
///
protected virtual double DecayWeight => 0.9;
- protected StrainSkill(Mod[] mods)
- : base(mods)
- {
- }
-
///
/// The length of each strain section.
///
protected virtual int SectionLength => 400;
- public double CurrentSectionPeak { get; protected set; } // We also keep track of the peak level in the current section.
+ private double currentSectionPeak; // We also keep track of the peak strain level in the current section.
+ private double currentSectionEnd;
- protected double CurrentSectionEnd;
+ private readonly List strainPeaks = new List();
+ protected readonly List ObjectStrains = new List(); // Store individual strains
- protected readonly List StrainPeaks = new List();
+ protected StrainSkill(Mod[] mods)
+ : base(mods)
+ {
+ }
///
/// Returns the strain value at . This value is calculated with or without respect to previous objects.
@@ -48,28 +48,46 @@ namespace osu.Game.Rulesets.Difficulty.Skills
{
// The first object doesn't generate a strain, so we begin with an incremented section end
if (current.Index == 0)
- CurrentSectionEnd = Math.Ceiling(current.StartTime / SectionLength) * SectionLength;
+ currentSectionEnd = Math.Ceiling(current.StartTime / SectionLength) * SectionLength;
- while (current.StartTime > CurrentSectionEnd)
+ while (current.StartTime > currentSectionEnd)
{
saveCurrentPeak();
- startNewSectionFrom(CurrentSectionEnd, current);
- CurrentSectionEnd += SectionLength;
+ startNewSectionFrom(currentSectionEnd, current);
+ currentSectionEnd += SectionLength;
}
double strain = StrainValueAt(current);
- CurrentSectionPeak = Math.Max(strain, CurrentSectionPeak);
+ currentSectionPeak = Math.Max(strain, currentSectionPeak);
// Store the strain value for the object
ObjectStrains.Add(strain);
}
+ ///
+ /// Calculates the number of strains weighted against the top strain.
+ /// The result is scaled by clock rate as it affects the total number of strains.
+ ///
+ public virtual double CountTopWeightedStrains()
+ {
+ if (ObjectStrains.Count == 0)
+ return 0.0;
+
+ double consistentTopStrain = DifficultyValue() / 10; // What would the top strain be if all strain values were identical
+
+ if (consistentTopStrain == 0)
+ return ObjectStrains.Count;
+
+ // Use a weighted sum of all strains. Constants are arbitrary and give nice values
+ return ObjectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
+ }
+
///
/// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
///
private void saveCurrentPeak()
{
- StrainPeaks.Add(CurrentSectionPeak);
+ strainPeaks.Add(currentSectionPeak);
}
///
@@ -81,7 +99,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills
{
// The maximum strain of the new section is not zero by default
// This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level.
- CurrentSectionPeak = CalculateInitialStrain(time, current);
+ currentSectionPeak = CalculateInitialStrain(time, current);
}
///
@@ -93,9 +111,10 @@ namespace osu.Game.Rulesets.Difficulty.Skills
protected abstract double CalculateInitialStrain(double time, DifficultyHitObject current);
///
- /// Returns a live enumerable of the difficulties
+ /// Returns a live enumerable of the peak strains for each section of the beatmap,
+ /// including the peak of the current section.
///
- public virtual IEnumerable GetCurrentStrainPeaks() => StrainPeaks.Append(CurrentSectionPeak);
+ public IEnumerable GetCurrentStrainPeaks() => strainPeaks.Append(currentSectionPeak);
///
/// Returns the calculated difficulty value representing all s that have been processed up to this point.