Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TappingFactor and use it to nerf HD bonus on speed pp #31478

Open
wants to merge 9 commits into
base: pp-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static class SpeedEvaluator
/// <item><description>and how easily they can be cheesed.</description></item>
/// </list>
/// </summary>
public static double EvaluateDifficultyOf(DifficultyHitObject current, IReadOnlyList<Mod> mods)
public static double EvaluateDifficultyOf(DifficultyHitObject current, bool withDistanceBonus, IReadOnlyList<Mod> mods)
{
if (current.BaseObject is Spinner)
return 0;
Expand Down Expand Up @@ -60,7 +60,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, IReadOnly
// Max distance bonus is 1 * `distance_multiplier` at single_spacing_threshold
double distanceBonus = Math.Pow(distance / single_spacing_threshold, 3.95) * distance_multiplier;

if (mods.OfType<OsuModAutopilot>().Any())
if (!withDistanceBonus || mods.OfType<OsuModAutopilot>().Any())
distanceBonus = 0;

// Base difficulty with all bonuses
Expand Down
8 changes: 8 additions & 0 deletions osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ public class OsuDifficultyAttributes : DifficultyAttributes
[JsonProperty("slider_factor")]
public double SliderFactor { get; set; }

/// <summary>
/// Describes how much of <see cref="SpeedDifficulty"/> is contributed to by flow.
/// A value closer to 1.0 indicates most of <see cref="SpeedDifficulty"/> is contributed by tapping.
/// A value closer to 0.0 indicates most of <see cref="SpeedDifficulty"/> is contributed by flow.
/// </summary>
[JsonProperty("flow_factor")]
public double FlowFactor { get; set; }

[JsonProperty("aim_difficult_strain_count")]
public double AimDifficultStrainCount { get; set; }

Expand Down
8 changes: 6 additions & 2 deletions osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
double speedRatingNoFlow = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
double speedNotes = ((Speed)skills[2]).RelevantNoteCount();
double difficultSliders = ((Aim)skills[0]).GetDifficultSliders();
double flashlightRating = 0.0;

if (mods.Any(h => h is OsuModFlashlight))
flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
flashlightRating = Math.Sqrt(skills[4].DifficultyValue()) * difficulty_multiplier;

double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
double flowFactor = speedRating > 0 ? Math.Pow(speedRatingNoFlow / speedRating, 3) : 1;

double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountTopWeightedStrains();
double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountTopWeightedStrains();
Expand Down Expand Up @@ -112,6 +114,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
SpeedNoteCount = speedNotes,
FlashlightDifficulty = flashlightRating,
SliderFactor = sliderFactor,
FlowFactor = flowFactor,
AimDifficultStrainCount = aimDifficultyStrainCount,
SpeedDifficultStrainCount = speedDifficultyStrainCount,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
Expand Down Expand Up @@ -150,7 +153,8 @@ protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clo
{
new Aim(mods, true),
new Aim(mods, false),
new Speed(mods)
new Speed(mods, true),
new Speed(mods, false),
};

if (mods.Any(h => h is OsuModFlashlight))
Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attribut

if (score.Mods.Any(m => m is OsuModBlinds))
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 OsuModHidden || m is OsuModTraceable))
else if (score.Mods.Any(m => /*m is OsuModHidden || */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 - attributes.ApproachRate);
Expand Down Expand Up @@ -234,7 +234,7 @@ private double computeSpeedValue(ScoreInfo score, OsuDifficultyAttributes attrib
else if (score.Mods.Any(m => m is OsuModHidden || 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 - attributes.ApproachRate);
speedValue *= 1.0 + 0.08 * (12.0 - attributes.ApproachRate) * (1 - attributes.FlowFactor);
}

double speedHighDeviationMultiplier = calculateSpeedHighDeviationNerf(attributes);
Expand Down
9 changes: 7 additions & 2 deletions osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,24 @@ public class Speed : OsuStrainSkill

protected override int ReducedSectionCount => 5;

public Speed(Mod[] mods)
public Speed(Mod[] mods, bool withTapping)
: base(mods)
{
this.withTapping = withTapping;
}

private readonly bool withTapping;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This naming is really confusing because the evaluator uses the same variable with the name withDistanceBonus and these are polar opposite things. Please give the variables the same intention and double check it's working as intended (the below !withTapping check concerns me)

Copy link
Contributor Author

@Finadoggie Finadoggie Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I probably just forgot to rename it when I inverted a bunch of other things

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is still wrong, speed has withoutFlow while speed evaluator has withDistanceBonus which are opposite behaviours - passing true to speed will still cause the distance bonus to be present

Copy link
Member

@tsunyoku tsunyoku Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I think I was getting confused by all of the naming and inverses of logic. I suggest something like the below diff to make it less confusing. Note that my diff will include rhythm difficulty regardless of tappingOnly - rhythm is a part of tapping so I would fairly assume that the flow factor would be more accurate if it's included. That much is open for debate, but changing that along with the renames made this read far less confusing to me.

diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
index e6a8a7104e..6c881d1a0f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
@@ -28,7 +28,7 @@ public static class SpeedEvaluator
         /// <item><description>and how easily they can be cheesed.</description></item>
         /// </list>
         /// </summary>
-        public static double EvaluateDifficultyOf(DifficultyHitObject current, bool withDistanceBonus, IReadOnlyList<Mod> mods)
+        public static double EvaluateDifficultyOf(DifficultyHitObject current, bool tappingOnly, IReadOnlyList<Mod> mods)
         {
             if (current.BaseObject is Spinner)
                 return 0;
@@ -60,7 +60,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
             // Max distance bonus is 1 * `distance_multiplier` at single_spacing_threshold
             double distanceBonus = Math.Pow(distance / single_spacing_threshold, 3.95) * distance_multiplier;
 
-            if (!withDistanceBonus || mods.OfType<OsuModAutopilot>().Any())
+            if (tappingOnly || mods.OfType<OsuModAutopilot>().Any())
                 distanceBonus = 0;
 
             // Base difficulty with all bonuses
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 14bba60d36..59e5cbfacc 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -39,7 +39,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
             double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
             double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
             double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
-            double speedRatingNoFlow = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
+            double speedRatingTappingOnly = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
             double speedNotes = ((Speed)skills[2]).RelevantNoteCount();
             double difficultSliders = ((Aim)skills[0]).GetDifficultSliders();
             double flashlightRating = 0.0;
@@ -48,7 +48,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
                 flashlightRating = Math.Sqrt(skills[4].DifficultyValue()) * difficulty_multiplier;
 
             double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
-            double flowFactor = speedRating > 0 ? Math.Pow(speedRatingNoFlow / speedRating, 3) : 1;
+            double flowFactor = speedRating > 0 ? Math.Pow(speedRatingTappingOnly / speedRating, 3) : 1;
 
             double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountTopWeightedStrains();
             double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountTopWeightedStrains();
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
index ff8188b60e..d36e0532f2 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
@@ -23,13 +23,13 @@ public class Speed : OsuStrainSkill
 
         protected override int ReducedSectionCount => 5;
 
-        public Speed(Mod[] mods, bool withoutFlow)
+        public Speed(Mod[] mods, bool tappingOnly)
             : base(mods)
         {
-            this.withoutFlow = withoutFlow;
+            this.tappingOnly = tappingOnly;
         }
 
-        private readonly bool withoutFlow;
+        private readonly bool tappingOnly;
 
         private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
 
@@ -38,9 +38,7 @@ public Speed(Mod[] mods, bool withoutFlow)
         protected override double StrainValueAt(DifficultyHitObject current)
         {
             currentStrain *= strainDecay(((OsuDifficultyHitObject)current).StrainTime);
-            currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current, withoutFlow, Mods) * skillMultiplier;
-
-            if (withoutFlow) return currentStrain;
+            currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current, tappingOnly, Mods) * skillMultiplier;
 
             currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);

Copy link
Contributor Author

@Finadoggie Finadoggie Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is still wrong, speed has withoutFlow while speed evaluator has withDistanceBonus which are opposite behaviours - passing true to speed will still cause the distance bonus to be present

didn't notice that when doing stuff in the other two files, oops.

- if (withoutFlow) return currentStrain;

If you remove this line, rhythm ends up not getting a bonus from HD. I'm still going back and forth on whether or not it should have a bonus, but I figure this should be mentioned anyways.


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)
{
currentStrain *= strainDecay(((OsuDifficultyHitObject)current).StrainTime);
currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current, Mods) * skillMultiplier;
currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current, withTapping, Mods) * skillMultiplier;

if (!withTapping) return currentStrain;

currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);

Expand Down
Loading