From 264afd0994849bf23f34b102c4408381b70b34c6 Mon Sep 17 00:00:00 2001 From: tsunyoku Date: Mon, 20 May 2024 18:39:19 +0100 Subject: [PATCH 1/4] Don't punish sliders in rhythm calculations when slider head accuracy is in use --- .../Difficulty/Evaluators/RhythmEvaluator.cs | 10 +++++++--- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs index f2218a89a755..051801e0806e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs @@ -2,8 +2,12 @@ // 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; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators @@ -16,7 +20,7 @@ public static class RhythmEvaluator /// /// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current . /// - public static double EvaluateDifficultyOf(DifficultyHitObject current) + public static double EvaluateDifficultyOf(DifficultyHitObject current, IReadOnlyList mods) { if (current.BaseObject is Spinner) return 0; @@ -67,10 +71,10 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current) } else { - if (currObj.BaseObject is Slider) // bpm change is into slider, this is easy acc window + if (currObj.BaseObject is Slider && mods.Any(m => m is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value)) // bpm change is into slider, this is easy acc window without slider head accuracy effectiveRatio *= 0.125; - if (prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle + if (prevObj.BaseObject is Slider && mods.Any(m => m is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value)) // bpm change was from a slider, this is easier typically than circle -> circle without slider head accuracy effectiveRatio *= 0.25; if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 40aac013ab8a..6341c0650781 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -41,7 +41,7 @@ protected override double StrainValueAt(DifficultyHitObject current) currentStrain *= strainDecay(((OsuDifficultyHitObject)current).StrainTime); currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current) * skillMultiplier; - currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current); + currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current, Mods); double totalStrain = currentStrain * currentRhythm; From f4c154f76045b98faec21993ff470a40345ecd24 Mon Sep 17 00:00:00 2001 From: tsunyoku Date: Mon, 20 May 2024 18:45:20 +0100 Subject: [PATCH 2/4] improve readability --- .../Difficulty/Evaluators/RhythmEvaluator.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs index 051801e0806e..ae76e6c1595a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs @@ -71,10 +71,13 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, IReadOnly } else { - if (currObj.BaseObject is Slider && mods.Any(m => m is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value)) // bpm change is into slider, this is easy acc window without slider head accuracy + bool noSliderHeadAccuracy = + mods.Any(m => m is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value); + + if (noSliderHeadAccuracy && currObj.BaseObject is Slider) // bpm change is into slider, this is easy acc window without slider head accuracy effectiveRatio *= 0.125; - if (prevObj.BaseObject is Slider && mods.Any(m => m is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value)) // bpm change was from a slider, this is easier typically than circle -> circle without slider head accuracy + if (noSliderHeadAccuracy && prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle without slider head accuracy effectiveRatio *= 0.25; if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) From 40bbd022a5427dbcb5cda224705426eaa3939fad Mon Sep 17 00:00:00 2001 From: tsunyoku Date: Mon, 20 May 2024 18:56:14 +0100 Subject: [PATCH 3/4] slider -> circle should still nerf --- .../Difficulty/Evaluators/RhythmEvaluator.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs index ae76e6c1595a..3053f4f42963 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs @@ -71,13 +71,10 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, IReadOnly } else { - bool noSliderHeadAccuracy = - mods.Any(m => m is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value); - - if (noSliderHeadAccuracy && currObj.BaseObject is Slider) // bpm change is into slider, this is easy acc window without slider head accuracy + if (currObj.BaseObject is Slider && mods.Any(m => m is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value)) // bpm change is into slider, this is easy acc window without slider head accuracy effectiveRatio *= 0.125; - if (noSliderHeadAccuracy && prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle without slider head accuracy + if (prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle effectiveRatio *= 0.25; if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) From 65389bd9eae3f22a7c4760f23696dccee6988034 Mon Sep 17 00:00:00 2001 From: tsunyoku Date: Mon, 20 May 2024 19:16:37 +0100 Subject: [PATCH 4/4] fix hit windows for sliders with slider head accuracy remaining nerfed --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Difficulty/ManiaDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 4 ++-- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 5 +++-- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 5 +++-- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index f12c41a4152a..d783c7f75fcb 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -50,7 +50,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat return attributes; } - protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods, double clockRate) { CatchHitObject? lastObject = null; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 4190e74e5174..3ae5f11c49eb 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -67,7 +67,7 @@ private static int maxComboForObject(HitObject hitObject) return 1; } - protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods, double clockRate) { var sortedObjects = beatmap.HitObjects.ToArray(); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 007cd977e599..4282f4533e55 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -113,7 +113,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat return attributes; } - protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods, double clockRate) { List objects = new List(); @@ -122,7 +122,7 @@ protected override IEnumerable CreateDifficultyHitObjects(I for (int i = 1; i < beatmap.HitObjects.Count; i++) { var lastLast = i > 1 ? beatmap.HitObjects[i - 2] : null; - objects.Add(new OsuDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], lastLast, clockRate, objects, objects.Count)); + objects.Add(new OsuDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], lastLast, clockRate, objects, objects.Count, mods)); } return objects; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 0e537632b164..7d6d7f7f841f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; @@ -86,7 +87,7 @@ public class OsuDifficultyHitObject : DifficultyHitObject private readonly OsuHitObject? lastLastObject; private readonly OsuHitObject lastObject; - public OsuDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject? lastLastObject, double clockRate, List objects, int index) + public OsuDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject? lastLastObject, double clockRate, List objects, int index, IReadOnlyList mods) : base(hitObject, lastObject, clockRate, objects, index) { this.lastLastObject = lastLastObject as OsuHitObject; @@ -95,7 +96,7 @@ public OsuDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObje // Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects. StrainTime = Math.Max(DeltaTime, min_delta_time); - if (BaseObject is Slider sliderObject) + if (BaseObject is Slider sliderObject && mods.Any(m => m is OsuModClassic cl && cl.NoSliderHeadAccuracy.Value)) { HitWindowGreat = 2 * sliderObject.HeadCircle.HitWindows.WindowFor(HitResult.Great) / clockRate; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index b84c2d25ee47..237ad238e5de 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -46,7 +46,7 @@ protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clo new TaikoModHardRock(), }; - protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods, double clockRate) { List difficultyHitObjects = new List(); List centreObjects = new List(); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index d37cfc28b9dc..9b591a554791 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -160,7 +160,7 @@ public IEnumerable CalculateAllLegacyCombinations(Cancella /// /// Retrieves the s to calculate against. /// - private IEnumerable getDifficultyHitObjects() => SortObjects(CreateDifficultyHitObjects(Beatmap, clockRate)); + private IEnumerable getDifficultyHitObjects() => SortObjects(CreateDifficultyHitObjects(Beatmap, playableMods, clockRate)); /// /// Performs required tasks before every calculation. @@ -277,9 +277,10 @@ static IEnumerable createDifficultyAdjustmentModCombinations(ReadOnlyMemory /// Enumerates s to be processed from s in the . /// /// The providing the s to enumerate. + /// The s that difficulty was calculated with. /// The rate at which the gameplay clock is run at. /// The enumerated s. - protected abstract IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate); + protected abstract IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods, double clockRate); /// /// Creates the s to calculate the difficulty of an .