diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs index 9cbc5bf2de..8e73744b53 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Evaluators/RhythmEvaluator.cs @@ -29,6 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators double sameRhythm = 0; double samePattern = 0; double intervalPenalty = 0; + double gapPenalty = 0; double hitWindow = hitObject.HitWindow(HitResult.Great); @@ -36,12 +37,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators { sameRhythm += 10.0 * evaluateDifficultyOf(rhythmData.SameRhythmGroupedHitObjects, hitWindow); intervalPenalty = repeatedIntervalPenalty(rhythmData.SameRhythmGroupedHitObjects, hitWindow); + gapPenalty = longGapPenalty(rhythmData.SameRhythmGroupedHitObjects.Previous); } if (rhythmData.SamePatternsGroupedHitObjects?.FirstHitObject == hitObject) // Difficulty for SamePatternsGroupedHitObjects samePattern += 1.15 * ratioDifficulty(rhythmData.SamePatternsGroupedHitObjects.IntervalRatio); - difficulty += Math.Max(sameRhythm, samePattern) * intervalPenalty; + difficulty += Math.Max(sameRhythm, samePattern) * intervalPenalty * gapPenalty; return difficulty; } @@ -125,6 +127,32 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators } } + /// + /// Frequent rhythm changes containing long gaps (i.e. 1/4 + 1/6 with 1/2 gaps) award more difficulty than expected. + /// Due to limitations of the current rhythm evaluation, these cases are targeted and penalised. + /// The previous hit object grouping is used as often the rhythm change *two* rhythms after a long gap awards the unexpected difficulty. + /// + private static double longGapPenalty(SameRhythmHitObjectGrouping? previous) + { + if (previous == null) + return 1.0; + + double gapInterval = previous.FirstHitObject.DeltaTime; + double rhythmInterval = previous.HitObjectInterval ?? gapInterval; + double rhythmLength = previous.HitObjects.Count; + + // The ratio of the gap before this rhythm to the rhythm itself. + double gapRatio = gapInterval / Math.Max(rhythmInterval, 1); + + // The gap ratio normalised to represent if the gap is long. + double gapFactor = DifficultyCalculationUtils.Logistic(gapRatio, 1.75, 20); + + // The length in objects of this rhythm normalised to represent if the rhythm change is frequent enough to be penalised. + double lengthFactor = DifficultyCalculationUtils.ReverseLerp(rhythmLength, 8, 2); + + return 1.0 - 0.75 * gapFactor * lengthFactor; + } + /// /// Calculates the difficulty of a given ratio using a combination of periodic penalties and bonuses. /// diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 64af2861ec..43edbfb151 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public class TaikoDifficultyCalculator : DifficultyCalculator { private const double difficulty_multiplier = 0.084375; - private const double rhythm_skill_multiplier = 0.750 * difficulty_multiplier; + private const double rhythm_skill_multiplier = 0.770 * difficulty_multiplier; private const double reading_skill_multiplier = 0.100 * difficulty_multiplier; private const double colour_skill_multiplier = 0.375 * difficulty_multiplier; private const double stamina_skill_multiplier = 0.445 * difficulty_multiplier;