From 5409017f3c097a2c7150f08f43bb51bffd90d9cf Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Sun, 21 Apr 2024 15:32:07 +0800
Subject: [PATCH 01/21] add PreviousMaxCombo and CurrentMaxCombo to
OsuDifficultyHitObject
---
.../Preprocessing/OsuDifficultyHitObject.cs | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 0e537632b164..073169978625 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -83,6 +83,16 @@ public class OsuDifficultyHitObject : DifficultyHitObject
///
public double HitWindowGreat { get; private set; }
+ ///
+ /// The maximum combo played before this .
+ ///
+ public int PreviousMaxCombo { get; private set; }
+
+ ///
+ /// The maximum combo played after this .
+ ///
+ public int CurrentMaxCombo { get; private set; }
+
private readonly OsuHitObject? lastLastObject;
private readonly OsuHitObject lastObject;
@@ -105,6 +115,13 @@ public OsuDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObje
}
setDistances(clockRate);
+
+ if (index > 0)
+ PreviousMaxCombo = ((OsuDifficultyHitObject)Previous(0)).CurrentMaxCombo;
+ else
+ PreviousMaxCombo = getObjectCombo(lastObject);
+
+ CurrentMaxCombo = PreviousMaxCombo + getObjectCombo(hitObject);
}
public double OpacityAt(double time, bool hidden)
@@ -327,5 +344,18 @@ private Vector2 getEndCursorPosition(OsuHitObject hitObject)
return pos;
}
+
+ private int getObjectCombo(HitObject hitObject)
+ {
+ if (hitObject is Slider slider)
+ {
+ if (slider.NestedHitObjects[1] is SliderRepeat)
+ return slider.RepeatCount + 2;
+ else
+ return slider.NestedHitObjects.Count;
+ }
+
+ return 1;
+ }
}
}
From 6f110a800227ac0227df6ef01dc1019cff5a2a59 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Thu, 25 Apr 2024 13:06:41 +0800
Subject: [PATCH 02/21] consider jump from lazy end position for sliders
---
.../Difficulty/Evaluators/FlashlightEvaluator.cs | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index 5cb5a8f93417..246ae30c2165 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -5,6 +5,7 @@
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Objects;
+using osuTK;
namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
{
@@ -54,13 +55,20 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
if (!(currentObj.BaseObject is Spinner))
{
- double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
+ double pixelJumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
+
+ // Consider the jump from the lazy end position for sliders.
+ if (currentHitObject is Slider currentSlider)
+ {
+ Vector2 lazyEndPosition = currentSlider.LazyEndPosition ?? currentSlider.StackedPosition;
+ pixelJumpDistance = Math.Min(pixelJumpDistance, (osuHitObject.StackedPosition - lazyEndPosition).Length);
+ }
cumulativeStrainTime += lastObj.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);
+ smallDistNerf = Math.Min(1.0, pixelJumpDistance / 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, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);
@@ -68,7 +76,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
// Bonus based on how visible the object is.
double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.OpacityAt(currentHitObject.StartTime, hidden));
- result += stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime;
+ result += stackNerf * opacityBonus * scalingFactor * pixelJumpDistance / cumulativeStrainTime;
if (currentObj.Angle != null && osuCurrent.Angle != null)
{
From cd1bebd57e15726c0162134e8b5ed5ebd22fb066 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Thu, 25 Apr 2024 17:13:53 +0800
Subject: [PATCH 03/21] adjust accuracy value for hd and hdfl
---
.../Difficulty/OsuPerformanceCalculator.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 18a4b8be0caf..c965b36a4c58 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -212,12 +212,12 @@ private double computeAccuracyValue(ScoreInfo score, OsuDifficultyAttributes att
// Increasing the accuracy value by object count for Blinds isn't ideal, so the minimum buff is given.
if (score.Mods.Any(m => m is OsuModBlinds))
accuracyValue *= 1.14;
+ // Use different multiplier when adding hidden or traceable to flashlight.
+ else if (score.Mods.Any(m => m is OsuModFlashlight))
+ accuracyValue *= score.Mods.Any(m => m is OsuModHidden || m is OsuModTraceable) ? 1.12 : 1.08;
else if (score.Mods.Any(m => m is OsuModHidden || m is OsuModTraceable))
accuracyValue *= 1.08;
- if (score.Mods.Any(m => m is OsuModFlashlight))
- accuracyValue *= 1.02;
-
return accuracyValue;
}
From d89f5584393dd332b95a4f288a2eef69e47432bf Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Thu, 25 Apr 2024 17:26:00 +0800
Subject: [PATCH 04/21] fix spinners not increasing cumulative strain time
---
.../Difficulty/Evaluators/FlashlightEvaluator.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index 5cb5a8f93417..9d05f0b0742a 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -52,12 +52,12 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
var currentObj = (OsuDifficultyHitObject)current.Previous(i);
var currentHitObject = (OsuHitObject)(currentObj.BaseObject);
+ cumulativeStrainTime += lastObj.StrainTime;
+
if (!(currentObj.BaseObject is Spinner))
{
double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
- cumulativeStrainTime += lastObj.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);
From 6fd3d58a63a4e700554538990123fd65ed8b4ccc Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Thu, 25 Apr 2024 21:17:35 +0800
Subject: [PATCH 05/21] new flashlight slider difficulty calculation
---
.../Evaluators/FlashlightEvaluator.cs | 22 +++++++++++--------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index 5cb5a8f93417..797c710fa06f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -14,7 +14,8 @@ public static class FlashlightEvaluator
private const double hidden_bonus = 0.2;
private const double min_velocity = 0.5;
- private const double slider_multiplier = 1.3;
+ private const double max_velocity = 1.5;
+ private const double slider_multiplier = 0.3;
private const double min_angle_multiplier = 0.2;
@@ -97,18 +98,21 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
// Invert the scaling factor to determine the true travel distance independent of circle size.
double pixelTravelDistance = osuSlider.LazyTravelDistance / scalingFactor;
- // Reward sliders based on velocity.
- sliderBonus = Math.Pow(Math.Max(0.0, pixelTravelDistance / osuCurrent.TravelTime - min_velocity), 0.5);
+ // Reward sliders based on cursor velocity.
+ sliderBonus = Math.Log(pixelTravelDistance / osuCurrent.TravelTime + 1);
- // Longer sliders require more memorisation.
- sliderBonus *= pixelTravelDistance;
+ // More cursor movement requires more memorisation.
+ sliderBonus *= osuSlider.LazyTravelDistance;
- // Nerf sliders with repeats, as less memorisation is required.
- if (osuSlider.RepeatCount > 0)
- sliderBonus /= (osuSlider.RepeatCount + 1);
+ // Nerf slow slider velocity.
+ double sliderVelocity = osuSlider.Distance / osuCurrent.TravelTime;
+ sliderBonus *= Math.Clamp((sliderVelocity - min_velocity) / (max_velocity - min_velocity), 0, 1);
+
+ // Nerf sliders the more repeats they have, as less memorisation is required.
+ sliderBonus /= 0.75 * osuSlider.RepeatCount + 1;
}
- result += sliderBonus * slider_multiplier;
+ result += Math.Pow(sliderBonus, 1.2) * slider_multiplier;
return result;
}
From bc96fd2054f612d1f741309e6b43140a5b0c1805 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Tue, 14 May 2024 20:35:33 +0800
Subject: [PATCH 06/21] account for visible radius in flashlight evaluator
---
.../Evaluators/FlashlightEvaluator.cs | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index 5cb5a8f93417..ab2c7a9ee27f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -12,6 +12,7 @@ public static class FlashlightEvaluator
{
private const double max_opacity_bonus = 0.4;
private const double hidden_bonus = 0.2;
+ private const double flashlight_padding = 80;
private const double min_velocity = 0.5;
private const double slider_multiplier = 1.3;
@@ -60,7 +61,10 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
// 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);
+ {
+ float flashlightRadius = 200 * getComboScaleFor(osuCurrent.PreviousMaxCombo);
+ smallDistNerf = Math.Min(1.0, jumpDistance / (flashlightRadius + osuHitObject.Radius - flashlight_padding));
+ }
// We also want to nerf stacks so that only the first object of the stack is accounted for.
double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);
@@ -112,5 +116,16 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
return result;
}
+
+ private static float getComboScaleFor(int combo)
+ {
+ // Taken from ModFlashlight.
+ if (combo >= 200)
+ return 0.625f;
+ if (combo >= 100)
+ return 0.8125f;
+
+ return 1.0f;
+ }
}
}
From 5bfc6c07c78249f200ab6fe5992a020865c39861 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Mon, 20 May 2024 17:16:29 +0800
Subject: [PATCH 07/21] account for misses causing increased flashlight radius
in performance
---
.../Difficulty/OsuPerformanceCalculator.cs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 18a4b8be0caf..fd62b119988b 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -238,6 +238,17 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
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 scores where the flashlight radius is increased due to misses.
+ int missingCombo = attributes.MaxCombo - scoreMaxCombo;
+ double missingComboPercentage = (double)missingCombo / attributes.MaxCombo;
+ double averageMissingComboLength = Math.Max(missingCombo, 1) / Math.Max(effectiveMissCount, 1);
+
+ double maximumSizePercentage = Math.Clamp(averageMissingComboLength, 0, 100) / averageMissingComboLength * missingComboPercentage;
+ double mediumSizePercentage = Math.Clamp(averageMissingComboLength - 100, 0, 100) / averageMissingComboLength * missingComboPercentage;
+ double minimumSizePercentage = 1.0 - mediumSizePercentage - maximumSizePercentage;
+
+ flashlightValue *= minimumSizePercentage + 0.6 * mediumSizePercentage + 0.2 * maximumSizePercentage;
+
// Scale the flashlight value with accuracy _slightly_.
flashlightValue *= 0.5 + accuracy / 2.0;
// It is important to also consider accuracy difficulty when doing that.
From 3d038cea0d1c5279759236becb094fe833dbd61b Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Mon, 20 May 2024 17:17:34 +0800
Subject: [PATCH 08/21] new flashlight length bonus based on max combo
---
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index fd62b119988b..ab1702e2d89f 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -234,9 +234,8 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
flashlightValue *= getComboScalingFactor(attributes);
- // 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 more time played at a larger flashlight radius.
+ flashlightValue *= Math.Min(1, 0.7 + 0.1 * Math.Max(0, Math.Log(attributes.MaxCombo / 100.0)) + 0.1 * Math.Max(0, Math.Log(attributes.MaxCombo / 200.0)));
// Account for scores where the flashlight radius is increased due to misses.
int missingCombo = attributes.MaxCombo - scoreMaxCombo;
From baacb9b4c4b7e9b9e2ba350a50ee27b91e3b8fa8 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Mon, 20 May 2024 18:09:28 +0800
Subject: [PATCH 09/21] dont assume worst case scenario for
averageMissingComboLength
---
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index ab1702e2d89f..ac994e2a21fe 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -240,7 +240,9 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
// Account for scores where the flashlight radius is increased due to misses.
int missingCombo = attributes.MaxCombo - scoreMaxCombo;
double missingComboPercentage = (double)missingCombo / attributes.MaxCombo;
- double averageMissingComboLength = Math.Max(missingCombo, 1) / Math.Max(effectiveMissCount, 1);
+
+ // For balancing purposes, assume the player made 3 misses for every memorisation mistake.
+ double averageMissingComboLength = Math.Max(missingCombo, 1) / Math.Max(effectiveMissCount / 3, 1);
double maximumSizePercentage = Math.Clamp(averageMissingComboLength, 0, 100) / averageMissingComboLength * missingComboPercentage;
double mediumSizePercentage = Math.Clamp(averageMissingComboLength - 100, 0, 100) / averageMissingComboLength * missingComboPercentage;
From 23f3732e42060c8429551517e5d9fb02b9ac4aa5 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Mon, 20 May 2024 18:31:18 +0800
Subject: [PATCH 10/21] convert into ?: expression
---
.../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 073169978625..8f0b23d5f211 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -116,11 +116,7 @@ public OsuDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObje
setDistances(clockRate);
- if (index > 0)
- PreviousMaxCombo = ((OsuDifficultyHitObject)Previous(0)).CurrentMaxCombo;
- else
- PreviousMaxCombo = getObjectCombo(lastObject);
-
+ PreviousMaxCombo = index > 0 ? ((OsuDifficultyHitObject)Previous(0)).CurrentMaxCombo : getObjectCombo(lastObject);
CurrentMaxCombo = PreviousMaxCombo + getObjectCombo(hitObject);
}
From db68fd731b994c33c7f3770a56da66b79760bf2f Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Tue, 21 May 2024 18:57:26 +0800
Subject: [PATCH 11/21] new flashlight distance nerfs
---
.../Evaluators/FlashlightEvaluator.cs | 21 +++++++++++--------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index ab2c7a9ee27f..c8fd07fdcf31 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -56,23 +56,26 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
if (!(currentObj.BaseObject is Spinner))
{
double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
+ float flashlightRadius = 200 * getComboScaleFor(currentObj.CurrentMaxCombo);
+ double objectOpacity = osuCurrent.OpacityAt(currentHitObject.StartTime, hidden);
cumulativeStrainTime += lastObj.StrainTime;
- // We want to nerf objects that can be easily seen within the Flashlight circle radius.
+ // Apply a nerf based on the visibility from the current object.
+ double distanceNerf = Math.Min(1.0, jumpDistance / (flashlightRadius + osuHitObject.Radius - flashlight_padding));
+ double visibilityNerf = 1.0 - objectOpacity * (1.0 - distanceNerf);
+
+ // Jumps within the visible Flashlight radius should be nerfed.
if (i == 0)
- {
- float flashlightRadius = 200 * getComboScaleFor(osuCurrent.PreviousMaxCombo);
- smallDistNerf = Math.Min(1.0, jumpDistance / (flashlightRadius + osuHitObject.Radius - flashlight_padding));
- }
+ smallDistNerf = Math.Min(1.0, jumpDistance / (flashlightRadius - 50));
- // We also want to nerf stacks so that only the first object of the stack is accounted for.
+ // Nerf stacks so that only the first object of the stack is accounted for.
double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);
- // Bonus based on how visible the object is.
- double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.OpacityAt(currentHitObject.StartTime, hidden));
+ // Bonus based on object opacity.
+ double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - objectOpacity);
- result += stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime;
+ result += visibilityNerf * stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime;
if (currentObj.Angle != null && osuCurrent.Angle != null)
{
From d5d295f6634b04ecfb54e554a7e4b52d28dcbecd Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Tue, 21 May 2024 19:00:26 +0800
Subject: [PATCH 12/21] flashlight cleanups
---
.../Difficulty/Evaluators/FlashlightEvaluator.cs | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index c8fd07fdcf31..70fd1af91588 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -12,7 +12,6 @@ public static class FlashlightEvaluator
{
private const double max_opacity_bonus = 0.4;
private const double hidden_bonus = 0.2;
- private const double flashlight_padding = 80;
private const double min_velocity = 0.5;
private const double slider_multiplier = 1.3;
@@ -56,13 +55,13 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
if (!(currentObj.BaseObject is Spinner))
{
double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
- float flashlightRadius = 200 * getComboScaleFor(currentObj.CurrentMaxCombo);
+ double flashlightRadius = getComboScaleFor(currentObj.CurrentMaxCombo);
double objectOpacity = osuCurrent.OpacityAt(currentHitObject.StartTime, hidden);
cumulativeStrainTime += lastObj.StrainTime;
// Apply a nerf based on the visibility from the current object.
- double distanceNerf = Math.Min(1.0, jumpDistance / (flashlightRadius + osuHitObject.Radius - flashlight_padding));
+ double distanceNerf = Math.Min(1.0, jumpDistance / (flashlightRadius + osuHitObject.Radius - 80));
double visibilityNerf = 1.0 - objectOpacity * (1.0 - distanceNerf);
// Jumps within the visible Flashlight radius should be nerfed.
@@ -120,15 +119,15 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
return result;
}
- private static float getComboScaleFor(int combo)
+ private static double getComboScaleFor(int combo)
{
// Taken from ModFlashlight.
if (combo >= 200)
- return 0.625f;
+ return 125.0;
if (combo >= 100)
- return 0.8125f;
+ return 162.5;
- return 1.0f;
+ return 200.0;
}
}
}
From 3aa05762590f22c2c20b370c5e1707c367a0c72c Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Tue, 21 May 2024 19:12:00 +0800
Subject: [PATCH 13/21] buff flashlight difficulty constants
---
osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +-
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 007cd977e599..b7ed1a33283e 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -66,7 +66,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
double baseFlashlightPerformance = 0.0;
if (mods.Any(h => h is OsuModFlashlight))
- baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 25.0;
+ baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 28.0;
double basePerformance =
Math.Pow(
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 894fbae7db7d..5701febee6c7 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -226,7 +226,7 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
if (!score.Mods.Any(h => h is OsuModFlashlight))
return 0.0;
- double flashlightValue = Math.Pow(attributes.FlashlightDifficulty, 2.0) * 25.0;
+ double flashlightValue = Math.Pow(attributes.FlashlightDifficulty, 2.0) * 28.0;
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
From 831485784031d68628ae6115835b24e8d6f6d922 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Tue, 21 May 2024 21:31:42 +0800
Subject: [PATCH 14/21] tweak flashlight constants some more
---
.../Difficulty/Evaluators/FlashlightEvaluator.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/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index bb6a4c9ff271..a320c7cd6f28 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -75,7 +75,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
// Jumps within the visible Flashlight radius should be nerfed.
if (i == 0)
- smallDistNerf = Math.Min(1.0, pixelJumpDistance / (flashlightRadius - 50));
+ smallDistNerf = Math.Min(1.0, pixelJumpDistance / (flashlightRadius - 45));
// Nerf stacks so that only the first object of the stack is accounted for.
double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index b7ed1a33283e..cb4606342897 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -66,7 +66,7 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat
double baseFlashlightPerformance = 0.0;
if (mods.Any(h => h is OsuModFlashlight))
- baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 28.0;
+ baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 28.727;
double basePerformance =
Math.Pow(
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 5701febee6c7..4ed6dc55f51d 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -226,7 +226,7 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
if (!score.Mods.Any(h => h is OsuModFlashlight))
return 0.0;
- double flashlightValue = Math.Pow(attributes.FlashlightDifficulty, 2.0) * 28.0;
+ double flashlightValue = Math.Pow(attributes.FlashlightDifficulty, 2.0) * 28.727;
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
if (effectiveMissCount > 0)
From e06e57c54a5d1bded1a3bb617958a3a54d0442d7 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Tue, 28 May 2024 14:48:53 +0800
Subject: [PATCH 15/21] better variable names
---
.../Difficulty/Evaluators/FlashlightEvaluator.cs | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index a320c7cd6f28..9d000cf07d8e 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -58,7 +58,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
if (!(currentObj.BaseObject is Spinner))
{
- double pixelJumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
+ double pixelDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
double flashlightRadius = getComboScaleFor(currentObj.CurrentMaxCombo);
double objectOpacity = osuCurrent.OpacityAt(currentHitObject.StartTime, hidden);
@@ -66,16 +66,16 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
if (currentHitObject is Slider currentSlider)
{
Vector2 lazyEndPosition = currentSlider.LazyEndPosition ?? currentSlider.StackedPosition;
- pixelJumpDistance = Math.Min(pixelJumpDistance, (osuHitObject.StackedPosition - lazyEndPosition).Length);
+ pixelDistance = Math.Min(pixelDistance, (osuHitObject.StackedPosition - lazyEndPosition).Length);
}
// Apply a nerf based on the visibility from the current object.
- double distanceNerf = Math.Min(1.0, pixelJumpDistance / (flashlightRadius + osuHitObject.Radius - 80));
- double visibilityNerf = 1.0 - objectOpacity * (1.0 - distanceNerf);
+ double radiusVisibility = Math.Min(1.0, pixelDistance / (flashlightRadius + osuHitObject.Radius - 80));
+ double visibilityNerf = 1.0 - objectOpacity * (1.0 - radiusVisibility);
- // Jumps within the visible Flashlight radius should be nerfed.
+ // Small jumps within the visible Flashlight radius should be nerfed.
if (i == 0)
- smallDistNerf = Math.Min(1.0, pixelJumpDistance / (flashlightRadius - 45));
+ smallDistNerf = Math.Min(1.0, pixelDistance / (flashlightRadius - 45));
// Nerf stacks so that only the first object of the stack is accounted for.
double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);
@@ -83,7 +83,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
// Bonus based on object opacity.
double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - objectOpacity);
- result += visibilityNerf * stackNerf * opacityBonus * scalingFactor * pixelJumpDistance / cumulativeStrainTime;
+ result += visibilityNerf * stackNerf * opacityBonus * scalingFactor * pixelDistance / cumulativeStrainTime;
if (currentObj.Angle != null && osuCurrent.Angle != null)
{
From a0c45bb9d8cbfac4d0d72826ac85d479e27bacee Mon Sep 17 00:00:00 2001
From: tsunyoku
Date: Thu, 7 Nov 2024 16:15:32 +0000
Subject: [PATCH 16/21] support custom flashlight settings
---
.../Evaluators/FlashlightEvaluator.cs | 24 +++++++++++++------
.../Difficulty/Skills/Flashlight.cs | 4 +++-
2 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index 9d000cf07d8e..6ddd30c09752 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -4,6 +4,7 @@
using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
@@ -30,7 +31,7 @@ public static class FlashlightEvaluator
/// - and whether the hidden mod is enabled.
///
///
- public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidden)
+ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidden, OsuModFlashlight osuModFlashlight)
{
if (current.BaseObject is Spinner)
return 0;
@@ -59,7 +60,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
if (!(currentObj.BaseObject is Spinner))
{
double pixelDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
- double flashlightRadius = getComboScaleFor(currentObj.CurrentMaxCombo);
+ double flashlightRadius = getSize(currentObj.CurrentMaxCombo, osuModFlashlight);
double objectOpacity = osuCurrent.OpacityAt(currentHitObject.StartTime, hidden);
// Consider the jump from the lazy end position for sliders.
@@ -131,15 +132,24 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
return result;
}
- private static double getComboScaleFor(int combo)
+ private static float getSize(int combo, OsuModFlashlight osuModFlashlight)
+ {
+ float size = osuModFlashlight.DefaultFlashlightSize * osuModFlashlight.SizeMultiplier.Value;
+
+ if (osuModFlashlight.ComboBasedSize.Value)
+ size *= getComboScaleFor(combo);
+
+ return size;
+ }
+
+ private static float getComboScaleFor(int combo)
{
- // Taken from ModFlashlight.
if (combo >= 200)
- return 125.0;
+ return 0.625f;
if (combo >= 100)
- return 162.5;
+ return 0.8125f;
- return 200.0;
+ return 1.0f;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
index 3d6d3f99c174..7a1d52bb40ad 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
@@ -17,11 +17,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
public class Flashlight : StrainSkill
{
private readonly bool hasHiddenMod;
+ private readonly OsuModFlashlight osuModFlashlight;
public Flashlight(Mod[] mods)
: base(mods)
{
hasHiddenMod = mods.Any(m => m is OsuModHidden);
+ osuModFlashlight = (OsuModFlashlight)mods.Single(m => m is OsuModFlashlight);
}
private double skillMultiplier => 0.052;
@@ -36,7 +38,7 @@ public Flashlight(Mod[] mods)
protected override double StrainValueAt(DifficultyHitObject current)
{
currentStrain *= strainDecay(current.DeltaTime);
- currentStrain += FlashlightEvaluator.EvaluateDifficultyOf(current, hasHiddenMod) * skillMultiplier;
+ currentStrain += FlashlightEvaluator.EvaluateDifficultyOf(current, hasHiddenMod, osuModFlashlight) * skillMultiplier;
return currentStrain;
}
From 005a5de4e196258b68c1c847000890f660ae4b68 Mon Sep 17 00:00:00 2001
From: tsunyoku
Date: Thu, 7 Nov 2024 16:16:01 +0000
Subject: [PATCH 17/21] use better logic to calculate an object's combo
---
.../Preprocessing/OsuDifficultyHitObject.cs | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 8f0b23d5f211..aba4f1e143e3 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -343,15 +343,20 @@ private Vector2 getEndCursorPosition(OsuHitObject hitObject)
private int getObjectCombo(HitObject hitObject)
{
- if (hitObject is Slider slider)
+ int combo = 0;
+
+ addCombo(hitObject, ref combo);
+
+ return combo;
+
+ static void addCombo(HitObject hitObject, ref int combo)
{
- if (slider.NestedHitObjects[1] is SliderRepeat)
- return slider.RepeatCount + 2;
- else
- return slider.NestedHitObjects.Count;
- }
+ if (hitObject.Judgement.MaxResult.AffectsCombo())
+ combo++;
- return 1;
+ foreach (var nested in hitObject.NestedHitObjects)
+ addCombo(nested, ref combo);
+ }
}
}
}
From eb584079c882441e38c53184c6b7e1d731d8444b Mon Sep 17 00:00:00 2001
From: tsunyoku
Date: Thu, 7 Nov 2024 16:23:54 +0000
Subject: [PATCH 18/21] fix flashlight difficulty to performance formula
---
osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
index ead037ea42c6..f0738c510488 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
@@ -45,6 +45,6 @@ protected override double StrainValueAt(DifficultyHitObject current)
public override double DifficultyValue() => GetCurrentStrainPeaks().Sum();
- public static double DifficultyToPerformance(double difficulty) => 25 * Math.Pow(difficulty, 2);
+ public static double DifficultyToPerformance(double difficulty) => 28.727 * Math.Pow(difficulty, 2);
}
}
From de9b130185ecc617d26a35776b94bef98f4aa6ce Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Sun, 12 Jan 2025 09:29:34 +0800
Subject: [PATCH 19/21] general balancing
nerfs streams and very very long maps
---
.../Difficulty/Evaluators/FlashlightEvaluator.cs | 7 +++----
osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 5 ++---
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
index 6ddd30c09752..2e3992c7309b 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs
@@ -42,13 +42,12 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
double scalingFactor = 52.0 / osuHitObject.Radius;
double smallDistNerf = 1.0;
double cumulativeStrainTime = 0.0;
+ double angleRepeatCount = 0.0;
double result = 0.0;
OsuDifficultyHitObject lastObj = osuCurrent;
- double angleRepeatCount = 0.0;
-
// This is iterating backwards in time from the current object.
for (int i = 0; i < Math.Min(current.Index, 10); i++)
{
@@ -76,10 +75,10 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd
// Small jumps within the visible Flashlight radius should be nerfed.
if (i == 0)
- smallDistNerf = Math.Min(1.0, pixelDistance / (flashlightRadius - 45));
+ smallDistNerf = Math.Min(1.0, pixelDistance / (flashlightRadius - 35));
// Nerf stacks so that only the first object of the stack is accounted for.
- double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0);
+ double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 45.0);
// Bonus based on object opacity.
double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - objectOpacity);
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
index f0738c510488..4b1cd2e7b62a 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs
@@ -26,8 +26,9 @@ public Flashlight(Mod[] mods)
osuModFlashlight = (OsuModFlashlight)mods.Single(m => m is OsuModFlashlight);
}
- private double skillMultiplier => 0.05512;
+ private double skillMultiplier => 0.0727;
private double strainDecayBase => 0.15;
+ protected override double DecayWeight => 0.99984;
private double currentStrain;
@@ -43,8 +44,6 @@ protected override double StrainValueAt(DifficultyHitObject current)
return currentStrain;
}
- public override double DifficultyValue() => GetCurrentStrainPeaks().Sum();
-
public static double DifficultyToPerformance(double difficulty) => 28.727 * Math.Pow(difficulty, 2);
}
}
From b8740eab967fcc2dab388e3895e432a048281fff Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Sun, 12 Jan 2025 10:12:41 +0800
Subject: [PATCH 20/21] simplify flashlight max combo scaling
---
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 1919129fbbc0..1861c6c40c3b 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -294,7 +294,7 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
flashlightValue *= getComboScalingFactor(attributes);
// Account for shorter maps having more time played at a larger flashlight radius.
- flashlightValue *= Math.Min(1, 0.7 + 0.1 * Math.Max(0, Math.Log(attributes.MaxCombo / 100.0)) + 0.1 * Math.Max(0, Math.Log(attributes.MaxCombo / 200.0)));
+ flashlightValue *= Math.Min(1.2 - Math.Pow(0.997, attributes.MaxCombo), 1);
// Account for scores where the flashlight radius is increased due to misses.
int missingCombo = attributes.MaxCombo - scoreMaxCombo;
From f92696ba7803364a97ce452c33f6c05334822093 Mon Sep 17 00:00:00 2001
From: molneya <62799417+molneya@users.noreply.github.com>
Date: Wed, 15 Jan 2025 14:16:35 +0800
Subject: [PATCH 21/21] account for size multiplier setting in flashlight pp
---
.../Difficulty/OsuPerformanceCalculator.cs | 34 +++++++++++++------
1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index c8a7414aa9e6..103801afdecf 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -298,6 +298,8 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
if (!score.Mods.Any(h => h is OsuModFlashlight))
return 0.0;
+ var osuModFlashlight = (OsuModFlashlight)score.Mods.Single(m => m is OsuModFlashlight);
+
double flashlightValue = Flashlight.DifficultyToPerformance(attributes.FlashlightDifficulty);
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
@@ -306,21 +308,32 @@ private double computeFlashlightValue(ScoreInfo score, OsuDifficultyAttributes a
flashlightValue *= getComboScalingFactor(attributes);
- // Account for shorter maps having more time played at a larger flashlight radius.
+ // Account for shorter maps having more time played at a larger flashlight radius, and being generally more easily retryable.
flashlightValue *= Math.Min(1.2 - Math.Pow(0.997, attributes.MaxCombo), 1);
- // Account for scores where the flashlight radius is increased due to misses.
- int missingCombo = attributes.MaxCombo - scoreMaxCombo;
- double missingComboPercentage = (double)missingCombo / attributes.MaxCombo;
+ // Calculate time spent at each flashlight radius to account for scores where the radius increased due to misses.
+ double maximumSizePercentage = 1.0;
+ double mediumSizePercentage = 0.0;
+ double minimumSizePercentage = 0.0;
+
+ if (osuModFlashlight.ComboBasedSize.Value)
+ {
+ int missingCombo = attributes.MaxCombo - scoreMaxCombo;
+ double missingComboPercentage = (double)missingCombo / attributes.MaxCombo;
- // For balancing purposes, assume the player made 3 misses for every memorisation mistake.
- double averageMissingComboLength = Math.Max(missingCombo, 1) / Math.Max(effectiveMissCount / 3, 1);
+ // For balancing purposes, assume the player made 3 misses for every memorisation mistake.
+ double averageMissingComboLength = Math.Max(missingCombo, 1) / Math.Max(effectiveMissCount / 3, 1);
+
+ maximumSizePercentage = Math.Clamp(averageMissingComboLength, 0, 100) / averageMissingComboLength * missingComboPercentage;
+ mediumSizePercentage = Math.Clamp(averageMissingComboLength - 100, 0, 100) / averageMissingComboLength * missingComboPercentage;
+ minimumSizePercentage = 1.0 - mediumSizePercentage - maximumSizePercentage;
+ }
- double maximumSizePercentage = Math.Clamp(averageMissingComboLength, 0, 100) / averageMissingComboLength * missingComboPercentage;
- double mediumSizePercentage = Math.Clamp(averageMissingComboLength - 100, 0, 100) / averageMissingComboLength * missingComboPercentage;
- double minimumSizePercentage = 1.0 - mediumSizePercentage - maximumSizePercentage;
+ double maximumSizeScalingFactor = flashlightRadiusScalingFactor(osuModFlashlight.SizeMultiplier.Value);
+ double mediumSizeScalingFactor = flashlightRadiusScalingFactor(0.8125f * osuModFlashlight.SizeMultiplier.Value);
+ double minimumSizeScalingFactor = flashlightRadiusScalingFactor(0.625f * osuModFlashlight.SizeMultiplier.Value);
- flashlightValue *= minimumSizePercentage + 0.6 * mediumSizePercentage + 0.2 * maximumSizePercentage;
+ flashlightValue *= maximumSizeScalingFactor * maximumSizePercentage + mediumSizeScalingFactor * mediumSizePercentage + minimumSizeScalingFactor * minimumSizePercentage;
// Scale the flashlight value with accuracy _slightly_.
flashlightValue *= 0.5 + accuracy / 2.0;
@@ -437,6 +450,7 @@ private double calculateSpeedHighDeviationNerf(OsuDifficultyAttributes attribute
// to make it more punishing on maps with lower amount of hard sections.
private double calculateMissPenalty(double missCount, double difficultStrainCount) => 0.96 / ((missCount / (4 * Math.Pow(Math.Log(difficultStrainCount), 0.94))) + 1);
private double getComboScalingFactor(OsuDifficultyAttributes attributes) => attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(attributes.MaxCombo, 0.8), 1.0);
+ private double flashlightRadiusScalingFactor(double sizeMultiplier) => 1.2 / (1 + Math.Exp(8.58367 * (sizeMultiplier - 0.8125)));
private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countOk + countMeh;