From acdd08c96649b9162f27be19d9a85f3f0bb8b3db Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 8 Aug 2021 23:56:03 +1000 Subject: [PATCH 01/14] Add Flashlight skill --- .../Difficulty/OsuDifficultyAttributes.cs | 1 + .../Difficulty/OsuDifficultyCalculator.cs | 5 +- .../Difficulty/OsuPerformanceCalculator.cs | 51 ++++++++++---- .../Difficulty/Skills/Flashlight.cs | 67 +++++++++++++++++++ .../Difficulty/Skills/OsuStrainSkill.cs | 2 + 5 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 141138c125..1e870dac68 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public double AimStrain { get; set; } public double SpeedStrain { get; set; } + public double FlashlightStrain { get; set; } public double ApproachRate { get; set; } public double OverallDifficulty { get; set; } public int HitCircleCount { get; set; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e47f82fb39..b0dd4dc9b0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -34,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + double flashlightRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); @@ -56,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Mods = mods, AimStrain = aimRating, SpeedStrain = speedRating, + FlashlightStrain = flashlightRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, MaxCombo = maxCombo, @@ -82,7 +84,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Aim(mods), - new Speed(mods) + new Speed(mods), + new Flashlight(mods) }; protected override Mod[] DifficultyAdjustmentMods => new Mod[] diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index e6ab978dfb..d409eae9af 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -52,11 +52,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimValue = computeAimValue(); double speedValue = computeSpeedValue(); double accuracyValue = computeAccuracyValue(); + double flashlightValue = computeFlashlightValue(); double totalValue = Math.Pow( Math.Pow(aimValue, 1.1) + Math.Pow(speedValue, 1.1) + - Math.Pow(accuracyValue, 1.1), 1.0 / 1.1 + Math.Pow(accuracyValue, 1.1) + + Math.Pow(flashlightValue, 1.1), 1.0 / 1.1 ) * multiplier; if (categoryRatings != null) @@ -64,6 +66,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty categoryRatings.Add("Aim", aimValue); categoryRatings.Add("Speed", speedValue); categoryRatings.Add("Accuracy", accuracyValue); + categoryRatings.Add("Flashlight", flashlightValue); categoryRatings.Add("OD", Attributes.OverallDifficulty); categoryRatings.Add("AR", Attributes.ApproachRate); categoryRatings.Add("Max Combo", Attributes.MaxCombo); @@ -109,19 +112,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - double flashlightBonus = 1.0; - - if (mods.Any(h => h is OsuModFlashlight)) - { - // Apply object-based bonus for flashlight. - flashlightBonus = 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 - ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + - (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) - : 0.0); - } - - aimValue *= Math.Max(flashlightBonus, approachRateBonus); + aimValue *= approachRateBonus; // Scale the aim value with accuracy _slightly_ aimValue *= 0.5 + accuracy / 2.0; @@ -197,6 +188,38 @@ namespace osu.Game.Rulesets.Osu.Difficulty return accuracyValue; } + private double computeFlashlightValue() + { + double flashlightValue = 0.0; + + if (mods.Any(h => h is OsuModFlashlight)) { + flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; + + // Add an additional bonus for HDFL. + if (mods.Any(h => h is OsuModHidden)) + flashlightValue *= 1.2; + + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. + if (countMiss > 0) + flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); + + // Combo scaling + if (Attributes.MaxCombo > 0) + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + + // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. + flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); + + // Scale the aim value with accuracy _slightly_ + flashlightValue *= 0.5 + accuracy / 2.0; + // It is important to also consider accuracy difficulty when doing that + flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; + } + + return flashlightValue; + } + private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs new file mode 100644 index 0000000000..b48e6e30c0 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -0,0 +1,67 @@ +// 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.Linq; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Difficulty.Skills +{ + /// + /// Represents the skill required to memorise and hit every object in a map with the Flashlight mod enabled. + /// + public class Flashlight : OsuStrainSkill + { + public Flashlight(Mod[] mods) + : base(mods) + { + } + + protected override double SkillMultiplier => 0.065; + protected override double StrainDecayBase => 0.15; + protected override double DecayWeight => 1.0; + + protected override double StrainValueOf(DifficultyHitObject current) + { + if (current.BaseObject is Spinner) + return 0; + + var osuCurrent = (OsuDifficultyHitObject)current; + var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject); + + double scalingFactor = 52.0 / osuHitObject.Radius; + double smallDistNerf = 1.0; + + double result = 0.0; + + if (Previous.Count > 0) + { + double cumulativeStrainTime = 0.0; + + for (int i = 0; i < Previous.Count; i++) { + var osuPrevious = (OsuDifficultyHitObject)Previous[i]; + var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + + if (!(osuPrevious.BaseObject is Spinner)) { + double JumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + + cumulativeStrainTime += osuPrevious.StrainTime; + + // We want to nerf objects that can be easily seen within the Flashlight circle radius. + if (i == 0 && JumpDistance < 50.0) { + smallDistNerf = JumpDistance / 50.0; + } + + result += Math.Pow(0.8, i) * scalingFactor * JumpDistance / cumulativeStrainTime; + } + } + } + + return Math.Pow(smallDistNerf * result, 2.5); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index e47edc37cc..f74298cdca 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// protected virtual double DifficultyMultiplier => 1.06; + protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. + protected OsuStrainSkill(Mod[] mods) : base(mods) { From 6b1a4a53d44d980c28b510996269fe9f3742f270 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 9 Aug 2021 08:31:28 +1000 Subject: [PATCH 02/14] Cleanup of code --- .../Difficulty/OsuPerformanceCalculator.cs | 6 +++--- .../Difficulty/Skills/Flashlight.cs | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d409eae9af..3634374f50 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Math.Pow( Math.Pow(aimValue, 1.1) + Math.Pow(speedValue, 1.1) + - Math.Pow(accuracyValue, 1.1) + + Math.Pow(accuracyValue, 1.1) + Math.Pow(flashlightValue, 1.1), 1.0 / 1.1 ) * multiplier; @@ -194,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModFlashlight)) { flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; - + // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) flashlightValue *= 1.2; @@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Combo scaling if (Attributes.MaxCombo > 0) - flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index b48e6e30c0..fd771ab768 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -2,12 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; -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.Objects; -using osuTK; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -45,18 +43,18 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills for (int i = 0; i < Previous.Count; i++) { var osuPrevious = (OsuDifficultyHitObject)Previous[i]; var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - + if (!(osuPrevious.BaseObject is Spinner)) { - double JumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && JumpDistance < 50.0) { - smallDistNerf = JumpDistance / 50.0; + if (i == 0 && jumpDistance < 50.0) { + smallDistNerf = jumpDistance / 50.0; } - result += Math.Pow(0.8, i) * scalingFactor * JumpDistance / cumulativeStrainTime; + result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; } } } From f4ceb170642f802a2420b7e39d9b6887292ad270 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 10 Aug 2021 16:06:20 +1000 Subject: [PATCH 03/14] Cleanup of code --- .../Difficulty/OsuPerformanceCalculator.cs | 3 ++- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 3634374f50..267e332372 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -192,7 +192,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty { double flashlightValue = 0.0; - if (mods.Any(h => h is OsuModFlashlight)) { + if (mods.Any(h => h is OsuModFlashlight)) + { flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; // Add an additional bonus for HDFL. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index fd771ab768..b8a96b3310 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -40,19 +40,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { double cumulativeStrainTime = 0.0; - for (int i = 0; i < Previous.Count; i++) { + for (int i = 0; i < Previous.Count; i++) + { var osuPrevious = (OsuDifficultyHitObject)Previous[i]; var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - if (!(osuPrevious.BaseObject is Spinner)) { + if (!(osuPrevious.BaseObject is Spinner)) + { double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && jumpDistance < 50.0) { + if (i == 0 && jumpDistance < 50.0) smallDistNerf = jumpDistance / 50.0; - } result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; } From cee69eaad0c50c35031e74c59b99db52793e926c Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 06:14:38 +1000 Subject: [PATCH 04/14] Add a nerf to FL for TD plays --- .../Difficulty/OsuPerformanceCalculator.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 267e332372..2d4e4cf551 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -194,7 +194,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModFlashlight)) { - flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; + double rawFlashlight = Attributes.FlashlightStrain; + + if (mods.Any(m => m is OsuModTouchDevice)) + rawFlashlight = Math.Pow(rawFlashlight, 0.8); + + flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) From b1d25346a2e646200c759f1763f6b1da7367bfa9 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 13:30:40 +1000 Subject: [PATCH 05/14] Move HistoryLength override from OsuStrainSkill to Flashlight --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 1 + osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index b8a96b3310..dc60ab4041 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double SkillMultiplier => 0.065; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; + protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. protected override double StrainValueOf(DifficultyHitObject current) { diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index f74298cdca..e47edc37cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// protected virtual double DifficultyMultiplier => 1.06; - protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. - protected OsuStrainSkill(Mod[] mods) : base(mods) { From 1cadcb43d958d3e00e4941eff674b321b392db43 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 15:54:30 +1000 Subject: [PATCH 06/14] Apply nerf to Flashlight skill on high star maps --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index dc60ab4041..d225486cc8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - protected override double SkillMultiplier => 0.065; + protected override double SkillMultiplier => 0.13; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return Math.Pow(smallDistNerf * result, 2.5); + return Math.Pow(smallDistNerf * result, 2.0); } } } From 27918583e1143a2b8e717d9824998c0a28848ec4 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 15:55:13 +1000 Subject: [PATCH 07/14] Increase the multiplier when hidden is applied on the Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2d4e4cf551..2ca5145c6a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -203,7 +203,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) - flashlightValue *= 1.2; + flashlightValue *= 1.3; // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) From b06226e7385cbae56dce1729594003f5a5502cd4 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 12 Aug 2021 09:54:25 +1000 Subject: [PATCH 08/14] Change comments --- .../Difficulty/OsuPerformanceCalculator.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2ca5145c6a..2c8ee93819 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); // Custom multipliers for NoFail and SpunOut. - double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things + double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. if (mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * countMiss); @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimValue = Math.Pow(5.0 * Math.Max(1.0, rawAim / 0.0675) - 4.0, 3.0) / 100000.0; - // Longer maps are worth more + // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -114,9 +114,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= approachRateBonus; - // Scale the aim value with accuracy _slightly_ + // Scale the aim value with accuracy _slightly_. aimValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that + // It is important to also consider accuracy difficulty when doing that. aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; return aimValue; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0; - // Longer maps are worth more + // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); speedValue *= lengthBonus; @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - // Scale the speed value with accuracy and OD + // Scale the speed value with accuracy and OD. speedValue *= (0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeAccuracyValue() { - // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window + // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window. double betterAccuracyPercentage; int amountHitObjectsWithAccuracy = Attributes.HitCircleCount; @@ -169,15 +169,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty else betterAccuracyPercentage = 0; - // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points + // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points. if (betterAccuracyPercentage < 0) betterAccuracyPercentage = 0; // Lots of arbitrary values from testing. - // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution + // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution. double accuracyValue = Math.Pow(1.52163, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83; - // Bonus for many hitcircles - it's harder to keep good accuracy up for longer + // Bonus for many hitcircles - it's harder to keep good accuracy up for longer. accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3)); if (mods.Any(m => m is OsuModHidden)) @@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -217,9 +217,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); - // Scale the aim value with accuracy _slightly_ + // Scale the flashlight value with accuracy _slightly_. flashlightValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that + // It is important to also consider accuracy difficulty when doing that. flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; } From eaa0d383158facbf8b779b17010ba9c8b0fcd00b Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 12 Aug 2021 10:00:24 +1000 Subject: [PATCH 09/14] Add a buff to short maps for Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2c8ee93819..ad7376a044 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -214,8 +214,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. - flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); + flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); // Scale the flashlight value with accuracy _slightly_. flashlightValue *= 0.5 + accuracy / 2.0; From 7188a3268fbdfc24fb66b8aa6783ca151833c72d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 24 Aug 2021 14:01:54 +1000 Subject: [PATCH 10/14] Apply a nerf to stacks for Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index d225486cc8..f048142b56 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - protected override double SkillMultiplier => 0.13; + protected override double SkillMultiplier => 0.15; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -53,10 +53,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && jumpDistance < 50.0) - smallDistNerf = jumpDistance / 50.0; + if (i == 0) + smallDistNerf = Math.Min(1.0, jumpDistance / 50.0); - result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; + // We also want to nerf stacks so that only the first object of the stack is accounted for. + double stackNerf = Math.Min(1.0, osuPrevious.JumpDistance * scalingFactor / 50.0); + + result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } } } From c91feb29684c3a80b9af50be9ba63a53f3754b91 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 25 Aug 2021 11:18:21 +1000 Subject: [PATCH 11/14] Fix multiplying instead of dividing by scalingFactor --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index f048142b56..f6760235b4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We want to nerf objects that can be easily seen within the Flashlight circle radius. if (i == 0) - smallDistNerf = Math.Min(1.0, jumpDistance / 50.0); + smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, osuPrevious.JumpDistance * scalingFactor / 50.0); + double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } From 0dc31a476f9ccf0eafdf7c1f16fe96da287977bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Sep 2021 16:39:05 +0900 Subject: [PATCH 12/14] Invert condition to reduce nesting --- .../Difficulty/OsuPerformanceCalculator.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index ad7376a044..f9a3423eab 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -190,38 +190,36 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeFlashlightValue() { - double flashlightValue = 0.0; + if (!mods.Any(h => h is OsuModFlashlight)) + return 0.0; - if (mods.Any(h => h is OsuModFlashlight)) - { - double rawFlashlight = Attributes.FlashlightStrain; + double rawFlashlight = Attributes.FlashlightStrain; - if (mods.Any(m => m is OsuModTouchDevice)) - rawFlashlight = Math.Pow(rawFlashlight, 0.8); + if (mods.Any(m => m is OsuModTouchDevice)) + rawFlashlight = Math.Pow(rawFlashlight, 0.8); - flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; + double flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; - // Add an additional bonus for HDFL. - if (mods.Any(h => h is OsuModHidden)) - flashlightValue *= 1.3; + // Add an additional bonus for HDFL. + if (mods.Any(h => h is OsuModHidden)) + flashlightValue *= 1.3; - // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. - if (countMiss > 0) - flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. + if (countMiss > 0) + flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling. - if (Attributes.MaxCombo > 0) - flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + // Combo scaling. + if (Attributes.MaxCombo > 0) + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); - // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. - flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); + // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. + flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); - // Scale the flashlight value with accuracy _slightly_. - flashlightValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that. - flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; - } + // Scale the flashlight value with accuracy _slightly_. + flashlightValue *= 0.5 + accuracy / 2.0; + // It is important to also consider accuracy difficulty when doing that. + flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; return flashlightValue; } From 178a3d1132801dbe0d0e6725def89622cc48932d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 14 Sep 2021 10:23:11 +1000 Subject: [PATCH 13/14] Remove redundant check --- .../Difficulty/Skills/Flashlight.cs | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index f6760235b4..abd900a80d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -34,33 +34,29 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double scalingFactor = 52.0 / osuHitObject.Radius; double smallDistNerf = 1.0; + double cumulativeStrainTime = 0.0; double result = 0.0; - if (Previous.Count > 0) + for (int i = 0; i < Previous.Count; i++) { - double cumulativeStrainTime = 0.0; + var osuPrevious = (OsuDifficultyHitObject)Previous[i]; + var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - for (int i = 0; i < Previous.Count; i++) + if (!(osuPrevious.BaseObject is Spinner)) { - var osuPrevious = (OsuDifficultyHitObject)Previous[i]; - var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; - if (!(osuPrevious.BaseObject is Spinner)) - { - double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + cumulativeStrainTime += osuPrevious.StrainTime; - cumulativeStrainTime += osuPrevious.StrainTime; + // We want to nerf objects that can be easily seen within the Flashlight circle radius. + if (i == 0) + smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); - // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0) - smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); + // We also want to nerf stacks so that only the first object of the stack is accounted for. + double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - - result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; - } + result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } } From c4fbae136a8c4e53785227a30cd9d92323d06376 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 14 Sep 2021 10:34:21 +1000 Subject: [PATCH 14/14] Rename FlashlightStrain to FlashlightRating --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 1e870dac68..ac77a93239 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public double AimStrain { get; set; } public double SpeedStrain { get; set; } - public double FlashlightStrain { get; set; } + public double FlashlightRating { get; set; } public double ApproachRate { get; set; } public double OverallDifficulty { get; set; } public int HitCircleCount { get; set; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index b0dd4dc9b0..28b2174071 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Mods = mods, AimStrain = aimRating, SpeedStrain = speedRating, - FlashlightStrain = flashlightRating, + FlashlightRating = flashlightRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, MaxCombo = maxCombo, diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index f9a3423eab..bf4d92652c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (!mods.Any(h => h is OsuModFlashlight)) return 0.0; - double rawFlashlight = Attributes.FlashlightStrain; + double rawFlashlight = Attributes.FlashlightRating; if (mods.Any(m => m is OsuModTouchDevice)) rawFlashlight = Math.Pow(rawFlashlight, 0.8);