From 07faf63470f52db85270dd210dcfeefe557f5216 Mon Sep 17 00:00:00 2001 From: Dark98 Date: Sat, 11 Jan 2025 07:42:04 +0100 Subject: [PATCH] Difficulty factor added + Length bonus rework --- .../Difficulty/TaikoDifficultyCalculator.cs | 32 +++++++++++++++++++ .../Difficulty/TaikoPerformanceCalculator.cs | 24 ++++---------- .../Difficulty/DifficultyAttributes.cs | 6 ++++ .../Difficulty/DifficultyCalculator.cs | 5 +++ osu.Game/Rulesets/Difficulty/Skills/Skill.cs | 5 +++ .../Rulesets/Difficulty/Skills/StrainSkill.cs | 4 +++ 6 files changed, 59 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index f8ff6f6065bb..7e6739b258dd 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -29,6 +29,16 @@ public class TaikoDifficultyCalculator : DifficultyCalculator private const double colour_skill_multiplier = 0.375 * difficulty_multiplier; private const double stamina_skill_multiplier = 0.375 * difficulty_multiplier; + //The bonus multiplier is a basic multiplier that indicate how strong the impact of Difficulty Factor is. + private const double bonus_multiplier = 0.3; + + + // The difficulty factor for all the skills interpolated. + private double totalDifficultyFactor; + + // Indicate a flat multiplier value base only on map length. + private double lengthBonusBase; + public override int Version => 20241007; public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) @@ -101,6 +111,10 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat return new TaikoDifficultyAttributes { Mods = mods }; bool isRelax = mods.Any(h => h is TaikoModRelax); + bool isFlashlight = mods.Any(h => h is TaikoModFlashlight); + bool isHidden = mods.Any(h => h is TaikoModHidden); + + bool isConvert = beatmap.BeatmapInfo.OnlineInfo!.Ruleset.OnlineID != 1; Rhythm rhythm = (Rhythm)skills.First(x => x is Rhythm); Reading reading = (Reading)skills.First(x => x is Reading); @@ -119,6 +133,17 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat double readingDifficultStrains = reading.CountTopWeightedStrains(); double staminaDifficultStrains = stamina.CountTopWeightedStrains(); + //We can have a base length bonus based only from the length of the map. + lengthBonusBase = Math.Max((Math.Log(10.0 + beatmap.HitObjects.Count / 1000.0) - 1.35) * 0.45 + 0.5, 1.0); + + //Old formula that need to be removed with the reading rework. + lengthBonusBase *= isFlashlight && isHidden || !isConvert ? 1.05 : 1.0; + + rhythmRating *= LengthBonusMultiplier(lengthBonusBase, rhythm.DifficultyFactor, bonus_multiplier); + readingRating *= LengthBonusMultiplier(lengthBonusBase, reading.DifficultyFactor, bonus_multiplier); + colourRating *= LengthBonusMultiplier(lengthBonusBase, colour.DifficultyFactor, bonus_multiplier); + staminaRating *= LengthBonusMultiplier(lengthBonusBase, stamina.DifficultyFactor, bonus_multiplier); + double combinedRating = combinedDifficultyValue(rhythm, reading, colour, stamina, isRelax); double starRating = rescale(combinedRating * 1.4); @@ -138,6 +163,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat TaikoDifficultyAttributes attributes = new TaikoDifficultyAttributes { StarRating = starRating, + TotalDifficultyFactor = totalDifficultyFactor, Mods = mods, RhythmDifficulty = rhythmRating, ReadingDifficulty = readingRating, @@ -192,6 +218,10 @@ private double combinedDifficultyValue(Rhythm rhythm, Reading reading, Colour co peaks.Add(peak); } + // We can calculate the difficulty factor by doing average pick difficulty / max peak difficulty. + // It resoult in a value that rappresent the consistency for all peaks (0 excluded) in a range number from 0 to 1. + totalDifficultyFactor = peaks.Average() / peaks.Max(); + double difficulty = 0; double weight = 1; @@ -201,6 +231,8 @@ private double combinedDifficultyValue(Rhythm rhythm, Reading reading, Colour co weight *= 0.9; } + difficulty *= LengthBonusMultiplier(lengthBonusBase, totalDifficultyFactor, bonus_multiplier); + return difficulty; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 5da18e79632b..0d1b4f1a723a 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -76,8 +76,12 @@ private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes double baseDifficulty = 5 * Math.Max(1.0, attributes.StarRating / 0.115) - 4.0; double difficultyValue = Math.Min(Math.Pow(baseDifficulty, 3) / 69052.51, Math.Pow(baseDifficulty, 2.25) / 1150.0); - double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0); - difficultyValue *= lengthBonus; + + if (score.Mods.Any(m => m is ModFlashlight)) + difficultyValue *= Math.Max(1, 1.050 - Math.Min(attributes.MonoStaminaFactor / 50, 1)); + + // We need to divide the effectiveMissCount by 1 + DifficultyFactor to account for the miss count while considering map consistency. + effectiveMissCount /= 1.0 + attributes.TotalDifficultyFactor; difficultyValue *= Math.Pow(0.986, effectiveMissCount); @@ -87,9 +91,6 @@ private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes if (score.Mods.Any(m => m is ModHidden)) difficultyValue *= 1.025; - if (score.Mods.Any(m => m is ModFlashlight)) - difficultyValue *= Math.Max(1, 1.050 - Math.Min(attributes.MonoStaminaFactor / 50, 1) * lengthBonus); - if (estimatedUnstableRate == null) return 0; @@ -102,18 +103,7 @@ private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes, bool isConvert) { - if (attributes.GreatHitWindow <= 0 || estimatedUnstableRate == null) - return 0; - - double accuracyValue = Math.Pow(70 / estimatedUnstableRate.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0; - - double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3)); - - // Slight HDFL Bonus for accuracy. A clamp is used to prevent against negative values. - if (score.Mods.Any(m => m is ModFlashlight) && score.Mods.Any(m => m is ModHidden) && !isConvert) - accuracyValue *= Math.Max(1.0, 1.05 * lengthBonus); - - return accuracyValue; + return attributes.GreatHitWindow <= 0 || estimatedUnstableRate == null ? 0.0 : Math.Pow(70 / estimatedUnstableRate.Value, 1.1) * Math.Pow(attributes.StarRating, 0.4) * 100.0; } /// diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index 1d6cee043bfb..f888602c29e8 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -44,6 +44,12 @@ public class DifficultyAttributes [JsonProperty("star_rating", Order = -3)] public double StarRating { get; set; } + /// + /// The difficulty corresponding to every skills. + /// + [JsonProperty("total_difficulty_factor")] + public double TotalDifficultyFactor { get; set; } + /// /// The maximum achievable combo. /// diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 14acc9b90825..72ba424ea9b5 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.Difficulty { public abstract class DifficultyCalculator { + /// + /// Calculating the length bonus as a multiplier considering also the Difficulty Factor. + /// + protected virtual double LengthBonusMultiplier(double offsetLengthBonus, double difficultyFactor, double multiplierDifficultyFactor) => offsetLengthBonus + difficultyFactor * multiplierDifficultyFactor; + /// /// The beatmap for which difficulty will be calculated. /// diff --git a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs index 8b8892113b8a..1896cb3166b7 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs @@ -27,6 +27,11 @@ protected Skill(Mod[] mods) this.mods = mods; } + /// + /// Value that rappresent the consistency for all s (0 excluded) that have been processed up to this point in a range number from 0 to 1. + /// + public double DifficultyFactor; + /// /// Process a . /// diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index 3ba67793dc44..f3d895bfd69a 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -128,6 +128,10 @@ public override double DifficultyValue() // These sections will not contribute to the difficulty. var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + // We can calculate the difficulty factor by doing average pick difficulty / max peak difficulty. + // It resoult in a value that rappresent the consistency for all peaks (0 excluded) in a range number from 0 to 1. + DifficultyFactor = peaks.Average() / peaks.Max(); + // Difficulty is the weighted sum of the highest strains from every section. // We're sorting from highest to lowest strain. foreach (double strain in peaks.OrderDescending())